diff --git a/src/DebugBar/DataCollector/PDO/TracedStatement.php b/src/DebugBar/DataCollector/PDO/TracedStatement.php index 5912499..b1e999f 100644 --- a/src/DebugBar/DataCollector/PDO/TracedStatement.php +++ b/src/DebugBar/DataCollector/PDO/TracedStatement.php @@ -76,6 +76,9 @@ class TracedStatement public function checkParameters(array $params) : array { foreach ($params as &$param) { + if (is_resource($param)) { + $param = $this->getResourceContents($param); + } if (!mb_check_encoding($param ?? '', 'UTF-8')) { $param = '[BINARY DATA]'; } @@ -83,6 +86,27 @@ class TracedStatement return $params; } + /** + * @param resource $stream + */ + private function getResourceContents($stream, int $length = -1) : string + { + $meta = stream_get_meta_data($stream); + $isSeekable = (bool) $meta['seekable']; + if (! $isSeekable) { + return sprintf('[resource id #%d]', get_resource_id($stream)); + } + + fseek($stream, 0); + $contents = stream_get_contents($stream, $length); + if (-1 !== $length && ! feof($stream)) { + $contents .= '…'; + } + fseek($stream, 0, SEEK_END); + + return $contents; + } + /** * Returns the SQL string used for the query, without filled parameters * diff --git a/tests/DebugBar/Tests/TracedStatementTest.php b/tests/DebugBar/Tests/TracedStatementTest.php index 0e0171d..c62e1f7 100644 --- a/tests/DebugBar/Tests/TracedStatementTest.php +++ b/tests/DebugBar/Tests/TracedStatementTest.php @@ -119,6 +119,42 @@ class TracedStatementTest extends DebugBarTestCase $this->assertEquals($expected, $result); } + /** + * Check if stream resource query parameters are replaced without triggering a type error since PHP 8.0.0. + * This can happen when e.g. binding `PDO::PARAM_LOB` to your prepared statement. + * + * @link https://www.php.net/manual/en/pdo.lobs.php + */ + public function testReplacementParamsContainingLargeObjectResourceGeneratesCorrectString() + { + $sql = 'UPDATE user SET signature = :signature WHERE id = :id'; + + $seekableResource = fopen('php://memory', 'r+'); + fputs($seekableResource, 'Signature from rewindable LOB stream resource.'); + + $params = [ + 'id' => 1234, + 'signature' => $seekableResource, + ]; + + $traced = new TracedStatement($sql, $params); + $expected = 'UPDATE user SET signature = "Signature from rewindable LOB stream resource." WHERE id = "1234"'; + $result = $traced->getSqlWithParams('"'); + $this->assertEquals($expected, $result); + + $unseekableResource = fopen('php://stdout', 'r'); + + $params = [ + 'id' => 1234, + 'signature' => $unseekableResource, + ]; + + $traced = new TracedStatement($sql, $params); + $expected = sprintf('UPDATE user SET signature = "[resource id #%d]" WHERE id = "1234"', (int) $unseekableResource); + $result = $traced->getSqlWithParams('"'); + $this->assertEquals($expected, $result); + } + /** * Check if literal `NULL` query parameters are replaced without triggering a deprecation warning since PHP 8.0.0. * This can happen when e.g. binding `PDO::PARAM_NULL` to your prepared statement.