diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml
index 56725b50e3b..f7d5b250368 100644
--- a/.github/workflows/continuous-integration.yml
+++ b/.github/workflows/continuous-integration.yml
@@ -23,6 +23,7 @@ jobs:
php-version:
- "7.3"
- "7.4"
+ - "8.0"
deps:
- "fixed"
include:
@@ -183,6 +184,9 @@ jobs:
- "11"
- "12"
- "13"
+ include:
+ - php-version: "8.0"
+ postgres-version: "13"
services:
postgres:
@@ -247,6 +251,13 @@ jobs:
extension:
- "mysqli"
- "pdo_mysql"
+ include:
+ - php-version: "8.0"
+ mariadb-version: "10.5"
+ extension: "mysqli"
+ - php-version: "8.0"
+ mariadb-version: "10.5"
+ extension: "pdo_mysql"
services:
mariadb:
@@ -304,6 +315,7 @@ jobs:
matrix:
php-version:
- "7.4"
+ - "8.0"
mysql-version:
- "5.7"
- "8.0"
diff --git a/UPGRADE.md b/UPGRADE.md
index e8c8997dc67..132e058d08d 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -1,3 +1,18 @@
+# Upgrade to 2.12
+
+## PDO signature changes with php 8
+
+In php 8.0, the method signatures of two PDO classes which are extended by DBAL have changed. This affects the following classes:
+
+* `Doctrine\DBAL\Driver\PDOConnection`
+* `Doctrine\DBAL\Driver\PDOStatement`
+
+Code that extends either of the classes needs to be adjusted in order to function properly on php 8. The updated method signatures are:
+
+* `PDOConnection::query(?string $query = null, ?int $fetchMode = null, mixed ...$fetchModeArgs)`
+* `PDOStatement::setFetchMode($mode, ...$args)`
+* `PDOStatement::fetchAll($mode = null, ...$args)`
+
# Upgrade to 2.11
## Deprecated `Abstraction\Result`
diff --git a/composer.json b/composer.json
index de722c523a5..2571ac4193a 100644
--- a/composer.json
+++ b/composer.json
@@ -32,7 +32,7 @@
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"}
],
"require": {
- "php": "^7.3",
+ "php": "^7.3 || ^8",
"ext-pdo": "*",
"doctrine/cache": "^1.0",
"doctrine/event-manager": "^1.0"
diff --git a/composer.lock b/composer.lock
index 55d6b6e3b18..22f01fdd8f7 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "b9b5b86a282f25dc5f24bc422885e9c0",
+ "content-hash": "0e4428b976a1e1835abdff64014ad723",
"packages": [
{
"name": "doctrine/cache",
@@ -3922,7 +3922,7 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
- "php": "^7.3",
+ "php": "^7.3 || ^8",
"ext-pdo": "*"
},
"platform-dev": [],
diff --git a/lib/Doctrine/DBAL/Driver/PDOConnection.php b/lib/Doctrine/DBAL/Driver/PDOConnection.php
index 8409952cb4e..9b532a36383 100644
--- a/lib/Doctrine/DBAL/Driver/PDOConnection.php
+++ b/lib/Doctrine/DBAL/Driver/PDOConnection.php
@@ -11,7 +11,6 @@
use PDOStatement;
use function assert;
-use function func_get_args;
/**
* PDO implementation of the Connection interface.
@@ -21,6 +20,8 @@
*/
class PDOConnection extends PDO implements ConnectionInterface, ServerInfoAwareConnection
{
+ use PDOQueryImplementation;
+
/**
* @internal The connection can be only instantiated by its driver.
*
@@ -83,25 +84,6 @@ public function prepare($sql, $driverOptions = [])
}
}
- /**
- * {@inheritdoc}
- *
- * @return PDOStatement
- */
- public function query()
- {
- $args = func_get_args();
-
- try {
- $stmt = parent::query(...$args);
- assert($stmt instanceof PDOStatement);
-
- return $stmt;
- } catch (PDOException $exception) {
- throw Exception::new($exception);
- }
- }
-
/**
* {@inheritdoc}
*/
@@ -133,4 +115,20 @@ public function requiresQueryForServerVersion()
{
return false;
}
+
+ /**
+ * @param mixed ...$args
+ */
+ private function doQuery(...$args): PDOStatement
+ {
+ try {
+ $stmt = parent::query(...$args);
+ } catch (PDOException $exception) {
+ throw Exception::new($exception);
+ }
+
+ assert($stmt instanceof PDOStatement);
+
+ return $stmt;
+ }
}
diff --git a/lib/Doctrine/DBAL/Driver/PDOQueryImplementation.php b/lib/Doctrine/DBAL/Driver/PDOQueryImplementation.php
new file mode 100644
index 00000000000..be05bf64601
--- /dev/null
+++ b/lib/Doctrine/DBAL/Driver/PDOQueryImplementation.php
@@ -0,0 +1,39 @@
+= 80000) {
+ /**
+ * @internal
+ */
+ trait PDOQueryImplementation
+ {
+ /**
+ * @return PDOStatement
+ */
+ public function query(?string $query = null, ?int $fetchMode = null, mixed ...$fetchModeArgs)
+ {
+ return $this->doQuery($query, $fetchMode, ...$fetchModeArgs);
+ }
+ }
+} else {
+ /**
+ * @internal
+ */
+ trait PDOQueryImplementation
+ {
+ /**
+ * @return PDOStatement
+ */
+ public function query()
+ {
+ return $this->doQuery(...func_get_args());
+ }
+ }
+}
diff --git a/lib/Doctrine/DBAL/Driver/PDOStatement.php b/lib/Doctrine/DBAL/Driver/PDOStatement.php
index 4a244ab4617..86d6a15d49e 100644
--- a/lib/Doctrine/DBAL/Driver/PDOStatement.php
+++ b/lib/Doctrine/DBAL/Driver/PDOStatement.php
@@ -26,6 +26,8 @@
*/
class PDOStatement extends \PDOStatement implements StatementInterface, Result
{
+ use PDOStatementImplementations;
+
private const PARAM_TYPE_MAP = [
ParameterType::NULL => PDO::PARAM_NULL,
ParameterType::INTEGER => PDO::PARAM_INT,
@@ -54,34 +56,6 @@ protected function __construct()
{
}
- /**
- * {@inheritdoc}
- *
- * @deprecated Use one of the fetch- or iterate-related methods.
- */
- public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
- {
- $fetchMode = $this->convertFetchMode($fetchMode);
-
- // This thin wrapper is necessary to shield against the weird signature
- // of PDOStatement::setFetchMode(): even if the second and third
- // parameters are optional, PHP will not let us remove it from this
- // declaration.
- try {
- if ($arg2 === null && $arg3 === null) {
- return parent::setFetchMode($fetchMode);
- }
-
- if ($arg3 === null) {
- return parent::setFetchMode($fetchMode, $arg2);
- }
-
- return parent::setFetchMode($fetchMode, $arg2, $arg3);
- } catch (PDOException $exception) {
- throw Exception::new($exception);
- }
- }
-
/**
* {@inheritdoc}
*/
@@ -164,39 +138,6 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX
}
}
- /**
- * {@inheritdoc}
- *
- * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead.
- */
- public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
- {
- $args = func_get_args();
-
- if (isset($args[0])) {
- $args[0] = $this->convertFetchMode($args[0]);
- }
-
- if ($fetchMode === null && $fetchArgument === null && $ctorArgs === null) {
- $args = [];
- } elseif ($fetchArgument === null && $ctorArgs === null) {
- $args = [$fetchMode];
- } elseif ($ctorArgs === null) {
- $args = [$fetchMode, $fetchArgument];
- } else {
- $args = [$fetchMode, $fetchArgument, $ctorArgs];
- }
-
- try {
- $data = parent::fetchAll(...$args);
- assert(is_array($data));
-
- return $data;
- } catch (PDOException $exception) {
- throw Exception::new($exception);
- }
- }
-
/**
* {@inheritdoc}
*
@@ -264,6 +205,66 @@ public function free(): void
parent::closeCursor();
}
+ /**
+ * @param mixed ...$args
+ */
+ private function doSetFetchMode(int $fetchMode, ...$args): bool
+ {
+ $fetchMode = $this->convertFetchMode($fetchMode);
+
+ // This thin wrapper is necessary to shield against the weird signature
+ // of PDOStatement::setFetchMode(): even if the second and third
+ // parameters are optional, PHP will not let us remove it from this
+ // declaration.
+ $slice = [];
+
+ foreach ($args as $arg) {
+ if ($arg === null) {
+ break;
+ }
+
+ $slice[] = $arg;
+ }
+
+ try {
+ return parent::setFetchMode($fetchMode, ...$slice);
+ } catch (PDOException $exception) {
+ throw Exception::new($exception);
+ }
+ }
+
+ /**
+ * @param mixed ...$args
+ *
+ * @return mixed[]
+ */
+ private function doFetchAll(...$args): array
+ {
+ if (isset($args[0])) {
+ $args[0] = $this->convertFetchMode($args[0]);
+ }
+
+ $slice = [];
+
+ foreach ($args as $arg) {
+ if ($arg === null) {
+ break;
+ }
+
+ $slice[] = $arg;
+ }
+
+ try {
+ $data = parent::fetchAll(...$slice);
+ } catch (PDOException $exception) {
+ throw Exception::new($exception);
+ }
+
+ assert(is_array($data));
+
+ return $data;
+ }
+
/**
* Converts DBAL parameter type to PDO parameter type
*
diff --git a/lib/Doctrine/DBAL/Driver/PDOStatementImplementations.php b/lib/Doctrine/DBAL/Driver/PDOStatementImplementations.php
new file mode 100644
index 00000000000..ff1a4af504d
--- /dev/null
+++ b/lib/Doctrine/DBAL/Driver/PDOStatementImplementations.php
@@ -0,0 +1,73 @@
+= 80000) {
+ /**
+ * @internal
+ */
+ trait PDOStatementImplementations
+ {
+ /**
+ * @deprecated Use one of the fetch- or iterate-related methods.
+ *
+ * @param int $mode
+ * @param mixed ...$args
+ *
+ * @return bool
+ */
+ public function setFetchMode($mode, ...$args)
+ {
+ return $this->doSetFetchMode($mode, ...$args);
+ }
+
+ /**
+ * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead.
+ *
+ * @param int|null $mode
+ * @param mixed ...$args
+ *
+ * @return mixed[]
+ */
+ public function fetchAll($mode = null, ...$args)
+ {
+ return $this->doFetchAll($mode, ...$args);
+ }
+ }
+} else {
+ /**
+ * @internal
+ */
+ trait PDOStatementImplementations
+ {
+ /**
+ * @deprecated Use one of the fetch- or iterate-related methods.
+ *
+ * @param int $fetchMode
+ * @param mixed $arg2
+ * @param mixed $arg3
+ */
+ public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null): bool
+ {
+ return $this->doSetFetchMode(...func_get_args());
+ }
+
+ /**
+ * @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead.
+ *
+ * @param int|null $fetchMode
+ * @param mixed $fetchArgument
+ * @param mixed $ctorArgs
+ *
+ * @return mixed[]
+ */
+ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
+ {
+ return $this->doFetchAll(...func_get_args());
+ }
+ }
+}
diff --git a/lib/Doctrine/DBAL/Statement.php b/lib/Doctrine/DBAL/Statement.php
index ad5d29b9a5d..57843031eea 100644
--- a/lib/Doctrine/DBAL/Statement.php
+++ b/lib/Doctrine/DBAL/Statement.php
@@ -10,6 +10,7 @@
use Doctrine\DBAL\Types\Type;
use IteratorAggregate;
use PDO;
+use PDOStatement;
use Throwable;
use Traversable;
@@ -135,6 +136,10 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le
$this->params[$param] = $variable;
$this->types[$param] = $type;
+ if ($this->stmt instanceof PDOStatement) {
+ $length = $length ?? 0;
+ }
+
return $this->stmt->bindParam($param, $variable, $type, $length);
}
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
index 8c6b82da6b0..1d453f936b3 100644
--- a/phpcs.xml.dist
+++ b/phpcs.xml.dist
@@ -22,6 +22,7 @@
+
@@ -31,6 +32,11 @@
+
+ */lib/Doctrine/DBAL/Driver/PDOQueryImplementation.php
+ */lib/Doctrine/DBAL/Driver/PDOStatementImplementations.php
+
+
@@ -57,6 +63,8 @@
+ */lib/Doctrine/DBAL/Driver/PDOQueryImplementation.php
+ */lib/Doctrine/DBAL/Driver/PDOStatementImplementations.php
*/tests/*
@@ -115,6 +123,11 @@
lib/Doctrine/DBAL/Driver/PDOConnection.php
+
+ */lib/Doctrine/DBAL/Driver/PDOConnection.php
+ */lib/Doctrine/DBAL/Driver/PDOStatement.php
+
+
lib/Doctrine/DBAL/Driver/ExceptionConverterDriver.php
diff --git a/psalm.xml b/psalm.xml
index 999a5611126..4fcf11de05d 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -32,6 +32,13 @@
+
+
+
+
+
+
+
+
+
@@ -89,6 +98,8 @@
Doctrine\DBAL\Driver\Connection
-->
+
+
@@ -99,6 +110,16 @@
+
+
+
+
+
+
+
+
+
+