From 17cfa66a78e263f968e963f25b07e1e387d0ec5c Mon Sep 17 00:00:00 2001 From: Jaroslav Kuba Date: Wed, 27 Jul 2022 08:47:03 +0200 Subject: [PATCH 1/4] Added result caching for QueryBuilder --- docs/en/reference/query-builder.rst | 22 ++++++ src/Query/QueryBuilder.php | 56 +++++++++++--- tests/Query/QueryBuilderTest.php | 111 +++++++++++++++++++++++----- 3 files changed, 162 insertions(+), 27 deletions(-) diff --git a/docs/en/reference/query-builder.rst b/docs/en/reference/query-builder.rst index 7c33fc51587..b15a75f67ac 100644 --- a/docs/en/reference/query-builder.rst +++ b/docs/en/reference/query-builder.rst @@ -369,3 +369,25 @@ in your query as a return value: ->where('email = ' . $queryBuilder->createPositionalParameter($userInputEmail)) ; // SELECT id, name FROM users WHERE email = ? + +Caching +------- + +To use the result cache, it is necessary to call the method +``enableResultCache($cacheProfile)`` and pass a instance of +``Doctrine\DBAL\Cache\QueryCacheProfile`` with a cache lifetime +value in seconds. A cache key can optionally be added if needed. + +.. code-block:: php + + select('id', 'name') + ->from('users') + ->enableResultCache(new QueryCacheProfile(300, 'some-key')) + ; + +.. note:: + + See the :ref:`Caching ` section for more information. diff --git a/src/Query/QueryBuilder.php b/src/Query/QueryBuilder.php index 9d134e82009..b790cdeab46 100644 --- a/src/Query/QueryBuilder.php +++ b/src/Query/QueryBuilder.php @@ -2,6 +2,7 @@ namespace Doctrine\DBAL\Query; +use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Exception; use Doctrine\DBAL\ParameterType; @@ -139,6 +140,11 @@ class QueryBuilder */ private int $boundCounter = 0; + /** + * The query cache profile used for caching results. + */ + private ?QueryCacheProfile $resultCacheProfile = null; + /** * Initializes a new QueryBuilder. * @@ -227,7 +233,7 @@ public function getState() */ public function fetchAssociative() { - return $this->connection->fetchAssociative($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchAssociative(); } /** @@ -240,7 +246,7 @@ public function fetchAssociative() */ public function fetchNumeric() { - return $this->connection->fetchNumeric($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchNumeric(); } /** @@ -253,7 +259,7 @@ public function fetchNumeric() */ public function fetchOne() { - return $this->connection->fetchOne($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchOne(); } /** @@ -265,7 +271,7 @@ public function fetchOne() */ public function fetchAllNumeric(): array { - return $this->connection->fetchAllNumeric($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchAllNumeric(); } /** @@ -277,7 +283,7 @@ public function fetchAllNumeric(): array */ public function fetchAllAssociative(): array { - return $this->connection->fetchAllAssociative($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchAllAssociative(); } /** @@ -290,7 +296,7 @@ public function fetchAllAssociative(): array */ public function fetchAllKeyValue(): array { - return $this->connection->fetchAllKeyValue($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchAllKeyValue(); } /** @@ -304,7 +310,7 @@ public function fetchAllKeyValue(): array */ public function fetchAllAssociativeIndexed(): array { - return $this->connection->fetchAllAssociativeIndexed($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchAllAssociativeIndexed(); } /** @@ -316,7 +322,7 @@ public function fetchAllAssociativeIndexed(): array */ public function fetchFirstColumn(): array { - return $this->connection->fetchFirstColumn($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery()->fetchFirstColumn(); } /** @@ -326,7 +332,12 @@ public function fetchFirstColumn(): array */ public function executeQuery(): Result { - return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes); + return $this->connection->executeQuery( + $this->getSQL(), + $this->params, + $this->paramTypes, + $this->resultCacheProfile + ); } /** @@ -361,7 +372,7 @@ public function execute() 'QueryBuilder::execute() is deprecated, use QueryBuilder::executeQuery() for SQL queries instead.' ); - return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes); + return $this->executeQuery(); } Deprecation::trigger( @@ -1588,4 +1599,29 @@ public function __clone() $this->params[$name] = clone $param; } } + + /** + * Enables caching of the results of this query, for given amount of seconds + * and optionally specified witch key to use for the cache entry. + * + * @return $this + */ + public function enableResultCache(QueryCacheProfile $cacheProfile): self + { + $this->resultCacheProfile = $cacheProfile; + + return $this; + } + + /** + * Disables caching of the results of this query. + * + * @return $this + */ + public function disableResultCache(): self + { + $this->resultCacheProfile = null; + + return $this; + } } diff --git a/tests/Query/QueryBuilderTest.php b/tests/Query/QueryBuilderTest.php index 9c8c2be2e34..f73c3e92b03 100644 --- a/tests/Query/QueryBuilderTest.php +++ b/tests/Query/QueryBuilderTest.php @@ -2,6 +2,7 @@ namespace Doctrine\DBAL\Tests\Query; +use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\DBAL\Connection; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Query\Expression\ExpressionBuilder; @@ -965,11 +966,16 @@ public function testFetchAssociative( array $parameterTypes, string $expectedSql ): void { - $qb = new QueryBuilder($this->conn); + $qb = new QueryBuilder($this->conn); + $mockedResult = $this->createMock(Result::class); $this->conn->expects(self::once()) + ->method('executeQuery') + ->with($expectedSql, $parameters, $parameterTypes, null) + ->willReturn($mockedResult); + + $mockedResult->expects(self::once()) ->method('fetchAssociative') - ->with($expectedSql, $parameters, $parameterTypes) ->willReturn(['username' => 'jwage', 'password' => 'changeme']); $row = $qb->select($select) @@ -995,11 +1001,16 @@ public function testFetchNumeric( array $parameterTypes, string $expectedSql ): void { - $qb = new QueryBuilder($this->conn); + $qb = new QueryBuilder($this->conn); + $mockedResult = $this->createMock(Result::class); $this->conn->expects(self::once()) + ->method('executeQuery') + ->with($expectedSql, $parameters, $parameterTypes, null) + ->willReturn($mockedResult); + + $mockedResult->expects(self::once()) ->method('fetchNumeric') - ->with($expectedSql, $parameters, $parameterTypes) ->willReturn(['jwage', 'changeme']); $row = $qb->select($select) @@ -1025,11 +1036,16 @@ public function testFetchOne( array $parameterTypes, string $expectedSql ): void { - $qb = new QueryBuilder($this->conn); + $qb = new QueryBuilder($this->conn); + $mockedResult = $this->createMock(Result::class); $this->conn->expects(self::once()) + ->method('executeQuery') + ->with($expectedSql, $parameters, $parameterTypes, null) + ->willReturn($mockedResult); + + $mockedResult->expects(self::once()) ->method('fetchOne') - ->with($expectedSql, $parameters, $parameterTypes) ->willReturn('jwage'); $username = $qb->select($select) @@ -1055,11 +1071,16 @@ public function testFetchAllAssociative( array $parameterTypes, string $expectedSql ): void { - $qb = new QueryBuilder($this->conn); + $qb = new QueryBuilder($this->conn); + $mockedResult = $this->createMock(Result::class); $this->conn->expects(self::once()) + ->method('executeQuery') + ->with($expectedSql, $parameters, $parameterTypes, null) + ->willReturn($mockedResult); + + $mockedResult->expects(self::once()) ->method('fetchAllAssociative') - ->with($expectedSql, $parameters, $parameterTypes) ->willReturn( [ ['username' => 'jwage', 'password' => 'changeme'], @@ -1096,11 +1117,16 @@ public function testFetchAllNumeric( array $parameterTypes, string $expectedSql ): void { - $qb = new QueryBuilder($this->conn); + $qb = new QueryBuilder($this->conn); + $mockedResult = $this->createMock(Result::class); $this->conn->expects(self::once()) + ->method('executeQuery') + ->with($expectedSql, $parameters, $parameterTypes, null) + ->willReturn($mockedResult); + + $mockedResult->expects(self::once()) ->method('fetchAllNumeric') - ->with($expectedSql, $parameters, $parameterTypes) ->willReturn( [ ['jwage', 'changeme'], @@ -1137,11 +1163,16 @@ public function testFetchAllKeyValue( array $parameterTypes, string $expectedSql ): void { - $qb = new QueryBuilder($this->conn); + $qb = new QueryBuilder($this->conn); + $mockedResult = $this->createMock(Result::class); $this->conn->expects(self::once()) + ->method('executeQuery') + ->with($expectedSql, $parameters, $parameterTypes, null) + ->willReturn($mockedResult); + + $mockedResult->expects(self::once()) ->method('fetchAllKeyValue') - ->with($expectedSql, $parameters, $parameterTypes) ->willReturn( [ 'jwage' => 'changeme', @@ -1178,11 +1209,16 @@ public function testFetchAllAssociativeIndexed( array $parameterTypes, string $expectedSql ): void { - $qb = new QueryBuilder($this->conn); + $qb = new QueryBuilder($this->conn); + $mockedResult = $this->createMock(Result::class); $this->conn->expects(self::once()) + ->method('executeQuery') + ->with($expectedSql, $parameters, $parameterTypes, null) + ->willReturn($mockedResult); + + $mockedResult->expects(self::once()) ->method('fetchAllAssociativeIndexed') - ->with($expectedSql, $parameters, $parameterTypes) ->willReturn( [ 1 => [ @@ -1223,11 +1259,16 @@ public function testFetchFirstColumn( array $parameterTypes, string $expectedSql ): void { - $qb = new QueryBuilder($this->conn); + $qb = new QueryBuilder($this->conn); + $mockedResult = $this->createMock(Result::class); $this->conn->expects(self::once()) + ->method('executeQuery') + ->with($expectedSql, $parameters, $parameterTypes, null) + ->willReturn($mockedResult); + + $mockedResult->expects(self::once()) ->method('fetchFirstColumn') - ->with($expectedSql, $parameters, $parameterTypes) ->willReturn( [ 'jwage', @@ -1320,13 +1361,49 @@ public function testExecuteQuery( $this->conn->expects(self::once()) ->method('executeQuery') - ->with($expectedSql, $parameters, $parameterTypes) + ->with($expectedSql, $parameters, $parameterTypes, null) + ->willReturn($mockedResult); + + $results = $qb->select($select) + ->from($from) + ->where($where) + ->setParameters($parameters, $parameterTypes) + ->executeQuery(); + + self::assertSame( + $mockedResult, + $results + ); + } + + /** + * @param list|array $parameters + * @param array|array $parameterTypes + * + * @dataProvider fetchProvider + */ + public function testExecuteQueryWithResultCaching( + string $select, + string $from, + string $where, + array $parameters, + array $parameterTypes, + string $expectedSql + ): void { + $qb = new QueryBuilder($this->conn); + $qcp = new QueryCacheProfile(300); + $mockedResult = $this->createMock(Result::class); + + $this->conn->expects(self::once()) + ->method('executeQuery') + ->with($expectedSql, $parameters, $parameterTypes, $qcp) ->willReturn($mockedResult); $results = $qb->select($select) ->from($from) ->where($where) ->setParameters($parameters, $parameterTypes) + ->enableResultCache($qcp) ->executeQuery(); self::assertSame( From 7b0d5d015b21c12d2e4ef4ca87f8696ac2b7a153 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Sun, 31 Jul 2022 16:41:00 +0200 Subject: [PATCH 2/4] Fix upgrade note --- UPGRADE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPGRADE.md b/UPGRADE.md index 73d189c148c..10b163f1f6a 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -11,7 +11,7 @@ awareness about deprecated code. ## Deprecated `QueryBuilder` methods and constants. 1. The `QueryBuilder::getState()` method has been deprecated as the builder state is an internal concern. -2. Relying on the type of the query being built is deprecated by using `QueryBuilder::getType()` has been deprecated. +2. Relying on the type of the query being built by using `QueryBuilder::getType()` has been deprecated. If necessary, track the type of the query being built outside of the builder. The following `QueryBuilder` constants related to the above methods have been deprecated: From 2c1236199247d2a0f8c27bca3cec1163c7a4c257 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Sun, 31 Jul 2022 08:43:41 -0700 Subject: [PATCH 3/4] Deprecate passing parameters to Statement::execute*() --- UPGRADE.md | 7 +++++++ src/Connection.php | 23 ++++++++--------------- src/Driver/IBMDB2/Statement.php | 10 ++++++++++ src/Driver/Mysqli/Statement.php | 10 ++++++++++ src/Driver/OCI8/Statement.php | 8 ++++++++ src/Driver/PDO/SQLSrv/Connection.php | 8 ++++++-- src/Driver/PDO/Statement.php | 9 +++++++++ src/Driver/SQLSrv/Statement.php | 8 ++++++++ src/Statement.php | 18 ++++++++++++++++++ tests/Functional/ExceptionTest.php | 5 +++++ 10 files changed, 89 insertions(+), 17 deletions(-) diff --git a/UPGRADE.md b/UPGRADE.md index 73d189c148c..07a55085d01 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -8,6 +8,13 @@ awareness about deprecated code. # Upgrade to 3.4 +## Deprecated passing `$params` to `Statement::execute*()` methods. + +Passing `$params` to the driver-level `Statement::execute()` and the wrapper-level `Statement::executeQuery()` +and `Statement::executeStatement()` methods has been deprecated. + +Bind parameters using `Statement::bindParam()` or `Statement::bindValue()` instead. + ## Deprecated `QueryBuilder` methods and constants. 1. The `QueryBuilder::getState()` method has been deprecated as the builder state is an internal concern. diff --git a/src/Connection.php b/src/Connection.php index a85acbca446..67530dadca0 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -1021,12 +1021,10 @@ public function executeQuery( } $stmt = $connection->prepare($sql); - if (count($types) > 0) { - $this->_bindTypedValues($stmt, $params, $types); - $result = $stmt->execute(); - } else { - $result = $stmt->execute($params); - } + + $this->bindParameters($stmt, $params, $types); + + $result = $stmt->execute(); } else { $result = $connection->query($sql); } @@ -1128,15 +1126,10 @@ public function executeStatement($sql, array $params = [], array $types = []) $stmt = $connection->prepare($sql); - if (count($types) > 0) { - $this->_bindTypedValues($stmt, $params, $types); - - $result = $stmt->execute(); - } else { - $result = $stmt->execute($params); - } + $this->bindParameters($stmt, $params, $types); - return $result->rowCount(); + return $stmt->execute() + ->rowCount(); } return $connection->exec($sql); @@ -1668,7 +1661,7 @@ public function convertToPHPValue($value, $type) * * @throws Exception */ - private function _bindTypedValues(DriverStatement $stmt, array $params, array $types): void + private function bindParameters(DriverStatement $stmt, array $params, array $types): void { // Check whether parameters are positional or named. Mixing is not allowed. if (is_int(key($params))) { diff --git a/src/Driver/IBMDB2/Statement.php b/src/Driver/IBMDB2/Statement.php index 5047f1ee819..5642ec98e37 100644 --- a/src/Driver/IBMDB2/Statement.php +++ b/src/Driver/IBMDB2/Statement.php @@ -9,6 +9,7 @@ use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; use function assert; use function db2_bind_param; @@ -107,6 +108,15 @@ private function bind($position, &$variable, int $parameterType, int $dataType): */ public function execute($params = null): ResultInterface { + if ($params !== null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5556', + 'Passing $params to Statement::execute() is deprecated. Bind parameters using' + . ' Statement::bindParam() or Statement::bindValue() instead.' + ); + } + $handles = $this->bindLobs(); $result = @db2_execute($this->stmt, $params ?? $this->parameters); diff --git a/src/Driver/Mysqli/Statement.php b/src/Driver/Mysqli/Statement.php index fd43f41b800..d619a7164d9 100644 --- a/src/Driver/Mysqli/Statement.php +++ b/src/Driver/Mysqli/Statement.php @@ -10,6 +10,7 @@ use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; use mysqli_sql_exception; use mysqli_stmt; @@ -102,6 +103,15 @@ public function bindValue($param, $value, $type = ParameterType::STRING): bool */ public function execute($params = null): ResultInterface { + if ($params !== null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5556', + 'Passing $params to Statement::execute() is deprecated. Bind parameters using' + . ' Statement::bindParam() or Statement::bindValue() instead.' + ); + } + if ($params !== null && count($params) > 0) { if (! $this->bindUntypedValues($params)) { throw StatementError::new($this->stmt); diff --git a/src/Driver/OCI8/Statement.php b/src/Driver/OCI8/Statement.php index 076bc9ff791..9d65234119e 100644 --- a/src/Driver/OCI8/Statement.php +++ b/src/Driver/OCI8/Statement.php @@ -7,6 +7,7 @@ use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; use function is_int; use function oci_bind_by_name; @@ -113,6 +114,13 @@ private function convertParameterType(int $type): int public function execute($params = null): ResultInterface { if ($params !== null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5556', + 'Passing $params to Statement::execute() is deprecated. Bind parameters using' + . ' Statement::bindParam() or Statement::bindValue() instead.' + ); + foreach ($params as $key => $val) { if (is_int($key)) { $this->bindValue($key + 1, $val); diff --git a/src/Driver/PDO/SQLSrv/Connection.php b/src/Driver/PDO/SQLSrv/Connection.php index 0e3497e633b..cc9b571e932 100644 --- a/src/Driver/PDO/SQLSrv/Connection.php +++ b/src/Driver/PDO/SQLSrv/Connection.php @@ -41,8 +41,12 @@ public function lastInsertId($name = null) 'The usage of Connection::lastInsertId() with a sequence name is deprecated.' ); - return $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?') - ->execute([$name]) + $statement = $this->prepare( + 'SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?' + ); + $statement->bindValue(1, $name); + + return $statement->execute() ->fetchOne(); } diff --git a/src/Driver/PDO/Statement.php b/src/Driver/PDO/Statement.php index 9c61bd74ec4..aae7c68408e 100644 --- a/src/Driver/PDO/Statement.php +++ b/src/Driver/PDO/Statement.php @@ -96,6 +96,15 @@ public function bindParam( */ public function execute($params = null): ResultInterface { + if ($params !== null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5556', + 'Passing $params to Statement::execute() is deprecated. Bind parameters using' + . ' Statement::bindParam() or Statement::bindValue() instead.' + ); + } + try { $this->stmt->execute($params); } catch (PDOException $exception) { diff --git a/src/Driver/SQLSrv/Statement.php b/src/Driver/SQLSrv/Statement.php index 5255c7b9471..09c34e920a6 100644 --- a/src/Driver/SQLSrv/Statement.php +++ b/src/Driver/SQLSrv/Statement.php @@ -7,6 +7,7 @@ use Doctrine\DBAL\Driver\SQLSrv\Exception\Error; use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; use function assert; use function is_int; @@ -114,6 +115,13 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le public function execute($params = null): ResultInterface { if ($params !== null) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5556', + 'Passing $params to Statement::execute() is deprecated. Bind parameters using' + . ' Statement::bindParam() or Statement::bindValue() instead.' + ); + foreach ($params as $key => $val) { if (is_int($key)) { $this->bindValue($key + 1, $val); diff --git a/src/Statement.php b/src/Statement.php index 782feee260e..39a33f33e10 100644 --- a/src/Statement.php +++ b/src/Statement.php @@ -198,6 +198,15 @@ public function execute($params = null): Result */ public function executeQuery(array $params = []): Result { + if (func_num_args() > 0) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5556', + 'Passing $params to Statement::executeQuery() is deprecated. Bind parameters using' + . ' Statement::bindParam() or Statement::bindValue() instead.' + ); + } + if ($params === []) { $params = null; // Workaround as long execute() exists and used internally. } @@ -214,6 +223,15 @@ public function executeQuery(array $params = []): Result */ public function executeStatement(array $params = []): int { + if (func_num_args() > 0) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5556', + 'Passing $params to Statement::executeStatement() is deprecated. Bind parameters using' + . ' Statement::bindParam() or Statement::bindValue() instead.' + ); + } + if ($params === []) { $params = null; // Workaround as long execute() exists and used internally. } diff --git a/tests/Functional/ExceptionTest.php b/tests/Functional/ExceptionTest.php index 52e0bc09651..4cc911f1a32 100644 --- a/tests/Functional/ExceptionTest.php +++ b/tests/Functional/ExceptionTest.php @@ -22,6 +22,8 @@ use function touch; use function unlink; +use const E_ALL; +use const E_WARNING; use const PHP_OS_FAMILY; /** @@ -202,6 +204,9 @@ public function testInvalidFieldNameException(): void $table->addColumn('id', 'integer', []); $this->dropAndCreateTable($table); + // prevent the PHPUnit error handler from handling the warning that db2_bind_param() may trigger + $this->iniSet('error_reporting', (string) (E_ALL & ~E_WARNING)); + $this->expectException(Exception\InvalidFieldNameException::class); $this->connection->insert('bad_columnname_table', ['name' => 5]); } From ab2ad39d9a3656b50b6df815efb65161cbe8f517 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Sun, 31 Jul 2022 10:52:27 -0700 Subject: [PATCH 4/4] Deprecate not passing parameter type to bindParam() and bindValue() --- UPGRADE.md | 5 ++++ src/Connection.php | 10 ++++---- src/Driver/IBMDB2/Statement.php | 19 +++++++++++++++ .../AbstractStatementMiddleware.php | 21 +++++++++++++++++ src/Driver/Mysqli/Statement.php | 19 +++++++++++++++ src/Driver/OCI8/Statement.php | 23 +++++++++++++++++-- src/Driver/PDO/SQLSrv/Statement.php | 18 +++++++++++++++ src/Driver/PDO/Statement.php | 18 +++++++++++++++ src/Driver/SQLSrv/Statement.php | 23 +++++++++++++++++-- src/Logging/Statement.php | 20 ++++++++++++++++ 10 files changed, 168 insertions(+), 8 deletions(-) diff --git a/UPGRADE.md b/UPGRADE.md index f01959459a7..5b3425f82bb 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -8,6 +8,11 @@ awareness about deprecated code. # Upgrade to 3.4 +## Deprecated not passing parameter type to the driver-level `Statement::bind*()` methods. + +Not passing `$type` to the driver-level `Statement::bindParam()` and `::bindValue()` is deprecated. +Pass the type corresponding to the parameter being bound. + ## Deprecated passing `$params` to `Statement::execute*()` methods. Passing `$params` to the driver-level `Statement::execute()` and the wrapper-level `Statement::executeQuery()` diff --git a/src/Connection.php b/src/Connection.php index 67530dadca0..f587b6207cd 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -1671,7 +1671,6 @@ private function bindParameters(DriverStatement $stmt, array $params, array $typ if (isset($types[$key])) { $type = $types[$key]; [$value, $bindingType] = $this->getBindingInfo($value, $type); - $stmt->bindValue($bindIndex, $value, $bindingType); } else { if (array_key_exists($key, $types)) { Deprecation::trigger( @@ -1682,9 +1681,11 @@ private function bindParameters(DriverStatement $stmt, array $params, array $typ ); } - $stmt->bindValue($bindIndex, $value); + $bindingType = ParameterType::STRING; } + $stmt->bindValue($bindIndex, $value, $bindingType); + ++$bindIndex; } } else { @@ -1693,7 +1694,6 @@ private function bindParameters(DriverStatement $stmt, array $params, array $typ if (isset($types[$name])) { $type = $types[$name]; [$value, $bindingType] = $this->getBindingInfo($value, $type); - $stmt->bindValue($name, $value, $bindingType); } else { if (array_key_exists($name, $types)) { Deprecation::trigger( @@ -1704,8 +1704,10 @@ private function bindParameters(DriverStatement $stmt, array $params, array $typ ); } - $stmt->bindValue($name, $value); + $bindingType = ParameterType::STRING; } + + $stmt->bindValue($name, $value, $bindingType); } } } diff --git a/src/Driver/IBMDB2/Statement.php b/src/Driver/IBMDB2/Statement.php index 5642ec98e37..02bdd989196 100644 --- a/src/Driver/IBMDB2/Statement.php +++ b/src/Driver/IBMDB2/Statement.php @@ -16,6 +16,7 @@ use function db2_execute; use function error_get_last; use function fclose; +use function func_num_args; use function is_int; use function is_resource; use function stream_copy_to_stream; @@ -61,6 +62,15 @@ public function bindValue($param, $value, $type = ParameterType::STRING): bool { assert(is_int($param)); + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindValue() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.' + ); + } + return $this->bindParam($param, $value, $type); } @@ -71,6 +81,15 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le { assert(is_int($param)); + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.' + ); + } + switch ($type) { case ParameterType::INTEGER: $this->bind($param, $variable, DB2_PARAM_IN, DB2_LONG); diff --git a/src/Driver/Middleware/AbstractStatementMiddleware.php b/src/Driver/Middleware/AbstractStatementMiddleware.php index 3a3dc524eef..fba62cd6ed7 100644 --- a/src/Driver/Middleware/AbstractStatementMiddleware.php +++ b/src/Driver/Middleware/AbstractStatementMiddleware.php @@ -5,6 +5,9 @@ use Doctrine\DBAL\Driver\Result; use Doctrine\DBAL\Driver\Statement; use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; + +use function func_num_args; abstract class AbstractStatementMiddleware implements Statement { @@ -20,6 +23,15 @@ public function __construct(Statement $wrappedStatement) */ public function bindValue($param, $value, $type = ParameterType::STRING) { + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindValue() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.' + ); + } + return $this->wrappedStatement->bindValue($param, $value, $type); } @@ -28,6 +40,15 @@ public function bindValue($param, $value, $type = ParameterType::STRING) */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) { + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.' + ); + } + return $this->wrappedStatement->bindParam($param, $variable, $type, $length); } diff --git a/src/Driver/Mysqli/Statement.php b/src/Driver/Mysqli/Statement.php index d619a7164d9..8365ee1e68f 100644 --- a/src/Driver/Mysqli/Statement.php +++ b/src/Driver/Mysqli/Statement.php @@ -19,6 +19,7 @@ use function count; use function feof; use function fread; +use function func_num_args; use function get_resource_type; use function is_int; use function is_resource; @@ -70,6 +71,15 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le { assert(is_int($param)); + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.' + ); + } + if (! isset(self::$paramTypeMap[$type])) { throw UnknownParameterType::new($type); } @@ -87,6 +97,15 @@ public function bindValue($param, $value, $type = ParameterType::STRING): bool { assert(is_int($param)); + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindValue() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.' + ); + } + if (! isset(self::$paramTypeMap[$type])) { throw UnknownParameterType::new($type); } diff --git a/src/Driver/OCI8/Statement.php b/src/Driver/OCI8/Statement.php index 9d65234119e..1d5bd9584ca 100644 --- a/src/Driver/OCI8/Statement.php +++ b/src/Driver/OCI8/Statement.php @@ -9,6 +9,7 @@ use Doctrine\DBAL\ParameterType; use Doctrine\Deprecations\Deprecation; +use function func_num_args; use function is_int; use function oci_bind_by_name; use function oci_execute; @@ -55,6 +56,15 @@ public function __construct($connection, $statement, array $parameterMap, Execut */ public function bindValue($param, $value, $type = ParameterType::STRING): bool { + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindValue() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.' + ); + } + return $this->bindParam($param, $value, $type); } @@ -63,6 +73,15 @@ public function bindValue($param, $value, $type = ParameterType::STRING): bool */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.' + ); + } + if (is_int($param)) { if (! isset($this->parameterMap[$param])) { throw UnknownParameterIndex::new($param); @@ -123,9 +142,9 @@ public function execute($params = null): ResultInterface foreach ($params as $key => $val) { if (is_int($key)) { - $this->bindValue($key + 1, $val); + $this->bindValue($key + 1, $val, ParameterType::STRING); } else { - $this->bindValue($key, $val); + $this->bindValue($key, $val, ParameterType::STRING); } } } diff --git a/src/Driver/PDO/SQLSrv/Statement.php b/src/Driver/PDO/SQLSrv/Statement.php index 862aefbb509..80e018d5494 100644 --- a/src/Driver/PDO/SQLSrv/Statement.php +++ b/src/Driver/PDO/SQLSrv/Statement.php @@ -40,6 +40,15 @@ public function bindParam( $length = null, $driverOptions = null ): bool { + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.' + ); + } + if (func_num_args() > 4) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', @@ -70,6 +79,15 @@ public function bindParam( */ public function bindValue($param, $value, $type = ParameterType::STRING): bool { + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindValue() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.' + ); + } + return $this->bindParam($param, $value, $type); } } diff --git a/src/Driver/PDO/Statement.php b/src/Driver/PDO/Statement.php index aae7c68408e..497bc5e24e8 100644 --- a/src/Driver/PDO/Statement.php +++ b/src/Driver/PDO/Statement.php @@ -43,6 +43,15 @@ public function __construct(PDOStatement $stmt) */ public function bindValue($param, $value, $type = ParameterType::STRING) { + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindValue() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.' + ); + } + $type = $this->convertParamType($type); try { @@ -68,6 +77,15 @@ public function bindParam( $length = null, $driverOptions = null ): bool { + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.' + ); + } + if (func_num_args() > 4) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', diff --git a/src/Driver/SQLSrv/Statement.php b/src/Driver/SQLSrv/Statement.php index 09c34e920a6..2d7c1c6040a 100644 --- a/src/Driver/SQLSrv/Statement.php +++ b/src/Driver/SQLSrv/Statement.php @@ -10,6 +10,7 @@ use Doctrine\Deprecations\Deprecation; use function assert; +use function func_num_args; use function is_int; use function sqlsrv_execute; use function SQLSRV_PHPTYPE_STREAM; @@ -87,6 +88,15 @@ public function bindValue($param, $value, $type = ParameterType::STRING): bool { assert(is_int($param)); + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindValue() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.' + ); + } + $this->variables[$param] = $value; $this->types[$param] = $type; @@ -100,6 +110,15 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le { assert(is_int($param)); + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.' + ); + } + $this->variables[$param] =& $variable; $this->types[$param] = $type; @@ -124,9 +143,9 @@ public function execute($params = null): ResultInterface foreach ($params as $key => $val) { if (is_int($key)) { - $this->bindValue($key + 1, $val); + $this->bindValue($key + 1, $val, ParameterType::STRING); } else { - $this->bindValue($key, $val); + $this->bindValue($key, $val, ParameterType::STRING); } } } diff --git a/src/Logging/Statement.php b/src/Logging/Statement.php index 8824d7d4f20..672f5647cc7 100644 --- a/src/Logging/Statement.php +++ b/src/Logging/Statement.php @@ -8,10 +8,12 @@ use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; +use Doctrine\Deprecations\Deprecation; use Psr\Log\LoggerInterface; use function array_slice; use function func_get_args; +use function func_num_args; final class Statement extends AbstractStatementMiddleware { @@ -40,6 +42,15 @@ public function __construct(StatementInterface $statement, LoggerInterface $logg */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) { + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindParam() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.' + ); + } + $this->params[$param] = &$variable; $this->types[$param] = $type; @@ -51,6 +62,15 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le */ public function bindValue($param, $value, $type = ParameterType::STRING) { + if (func_num_args() < 3) { + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5558', + 'Not passing $type to Statement::bindValue() is deprecated.' + . ' Pass the type corresponding to the parameter being bound.' + ); + } + $this->params[$param] = $value; $this->types[$param] = $type;