Skip to content

Commit 3cb79ff

Browse files
committed
Leverage the new PDO subclasses
1 parent e0f3674 commit 3cb79ff

File tree

10 files changed

+117
-6
lines changed

10 files changed

+117
-6
lines changed

UPGRADE.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ awareness about deprecated code.
88

99
# Upgrade to 4.2
1010

11+
## Support for new PDO subclasses on PHP 8.4
12+
13+
On PHP 8.4, if you call `getNativeConnection()` on a connection established through one of the PDO drivers,
14+
you will get an instance of the new PDO subclasses, e.g. `Pdo\Mysql` or `Pdo\Ppgsql` instead of just `PDO`.
15+
16+
However, this currently does not apply to persistent connections.
17+
See https://github.com/php/php-src/issues/16314 for details.
18+
1119
## Minor BC break: incompatible query cache format
1220

1321
The query cache format has been changed to address the issue where a cached result with no rows would miss the metadata.

phpstan.neon.dist

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ parameters:
115115
# Type check for legacy implementations of the Result interface
116116
# TODO: remove in 5.0.0
117117
- '~^Call to function method_exists\(\) with Doctrine\\DBAL\\Driver\\Result and ''getColumnName'' will always evaluate to true\.$~'
118+
119+
# PHPStan does not know the new PDO classes yet.
120+
- '~^Class Pdo\\\w+ not found\.$~'
121+
- '~^Call to an undefined static method PDO\:\:connect\(\)\.$~'
118122
includes:
119123
- vendor/phpstan/phpstan-phpunit/extension.neon
120124
- vendor/phpstan/phpstan-phpunit/rules.neon

psalm.xml.dist

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,14 @@
293293
<file name="src/Driver/PgSQL/Statement.php"/>
294294
</errorLevel>
295295
</TypeDoesNotContainType>
296+
<UndefinedClass>
297+
<errorLevel type="suppress">
298+
<!-- New PDO classes introduced in PHP 8.4 -->
299+
<referencedClass name="Pdo\Mysql"/>
300+
<referencedClass name="Pdo\Pgsql"/>
301+
<referencedClass name="Pdo\Sqlite"/>
302+
</errorLevel>
303+
</UndefinedClass>
296304
<UndefinedDocblockClass>
297305
<errorLevel type="suppress">
298306
<!-- See https://github.com/vimeo/psalm/issues/5472 -->
@@ -304,6 +312,12 @@
304312
<referencedClass name="OCILob"/>
305313
</errorLevel>
306314
</UndefinedDocblockClass>
315+
<UndefinedMethod>
316+
<errorLevel type="suppress">
317+
<!-- New PDO static constructor introduced in PHP 8.4 -->
318+
<referencedMethod name="PDO::connect"/>
319+
</errorLevel>
320+
</UndefinedMethod>
307321
<UnsupportedPropertyReferenceUsage>
308322
<errorLevel type="suppress">
309323
<!-- This code is valid -->

src/Driver/PDO/MySQL/Driver.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Doctrine\DBAL\Driver\PDO\Connection;
99
use Doctrine\DBAL\Driver\PDO\Exception;
1010
use Doctrine\DBAL\Driver\PDO\Exception\InvalidConfiguration;
11+
use Doctrine\DBAL\Driver\PDO\PDOConnect;
1112
use PDO;
1213
use PDOException;
1314
use SensitiveParameter;
@@ -16,6 +17,8 @@
1617

1718
final class Driver extends AbstractMySQLDriver
1819
{
20+
use PDOConnect;
21+
1922
/**
2023
* {@inheritDoc}
2124
*/
@@ -39,7 +42,7 @@ public function connect(
3942
unset($safeParams['password']);
4043

4144
try {
42-
$pdo = new PDO(
45+
$pdo = $this->doConnect(
4346
$this->constructPdoDsn($safeParams),
4447
$params['user'] ?? '',
4548
$params['password'] ?? '',

src/Driver/PDO/OCI/Driver.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Doctrine\DBAL\Driver\PDO\Connection;
99
use Doctrine\DBAL\Driver\PDO\Exception;
1010
use Doctrine\DBAL\Driver\PDO\Exception\InvalidConfiguration;
11+
use Doctrine\DBAL\Driver\PDO\PDOConnect;
1112
use PDO;
1213
use PDOException;
1314
use SensitiveParameter;
@@ -16,6 +17,8 @@
1617

1718
final class Driver extends AbstractOracleDriver
1819
{
20+
use PDOConnect;
21+
1922
/**
2023
* {@inheritDoc}
2124
*/
@@ -39,7 +42,7 @@ public function connect(
3942
unset($safeParams['password']);
4043

4144
try {
42-
$pdo = new PDO(
45+
$pdo = $this->doConnect(
4346
$this->constructPdoDsn($params),
4447
$params['user'] ?? '',
4548
$params['password'] ?? '',

src/Driver/PDO/PDOConnect.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\DBAL\Driver\PDO;
6+
7+
use PDO;
8+
9+
use const PHP_VERSION_ID;
10+
11+
/** @internal */
12+
trait PDOConnect
13+
{
14+
/** @param array<int, mixed> $options */
15+
private function doConnect(
16+
string $dsn,
17+
string $username,
18+
string $password,
19+
array $options,
20+
): PDO {
21+
// see https://github.com/php/php-src/issues/16314
22+
if (PHP_VERSION_ID < 80400 || ($options[PDO::ATTR_PERSISTENT] ?? false) === true) {
23+
return new PDO($dsn, $username, $password, $options);
24+
}
25+
26+
return PDO::connect($dsn, $username, $password, $options);
27+
}
28+
}

src/Driver/PDO/PgSQL/Driver.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Doctrine\DBAL\Driver\PDO\Connection;
99
use Doctrine\DBAL\Driver\PDO\Exception;
1010
use Doctrine\DBAL\Driver\PDO\Exception\InvalidConfiguration;
11+
use Doctrine\DBAL\Driver\PDO\PDOConnect;
1112
use PDO;
1213
use PDOException;
1314
use SensitiveParameter;
@@ -16,6 +17,8 @@
1617

1718
final class Driver extends AbstractPostgreSQLDriver
1819
{
20+
use PDOConnect;
21+
1922
/**
2023
* {@inheritDoc}
2124
*/
@@ -39,7 +42,7 @@ public function connect(
3942
unset($safeParams['password']);
4043

4144
try {
42-
$pdo = new PDO(
45+
$pdo = $this->doConnect(
4346
$this->constructPdoDsn($safeParams),
4447
$params['user'] ?? '',
4548
$params['password'] ?? '',

src/Driver/PDO/SQLSrv/Driver.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Doctrine\DBAL\Driver\PDO\Connection as PDOConnection;
1111
use Doctrine\DBAL\Driver\PDO\Exception as PDOException;
1212
use Doctrine\DBAL\Driver\PDO\Exception\InvalidConfiguration;
13+
use Doctrine\DBAL\Driver\PDO\PDOConnect;
1314
use PDO;
1415
use SensitiveParameter;
1516

@@ -19,6 +20,8 @@
1920

2021
final class Driver extends AbstractSQLServerDriver
2122
{
23+
use PDOConnect;
24+
2225
/**
2326
* {@inheritDoc}
2427
*/
@@ -52,7 +55,7 @@ public function connect(
5255
unset($safeParams['password']);
5356

5457
try {
55-
$pdo = new PDO(
58+
$pdo = $this->doConnect(
5659
$this->constructDsn($safeParams, $dsnOptions),
5760
$params['user'] ?? '',
5861
$params['password'] ?? '',

src/Driver/PDO/SQLite/Driver.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use Doctrine\DBAL\Driver\PDO\Connection;
99
use Doctrine\DBAL\Driver\PDO\Exception;
1010
use Doctrine\DBAL\Driver\PDO\Exception\InvalidConfiguration;
11-
use PDO;
11+
use Doctrine\DBAL\Driver\PDO\PDOConnect;
1212
use PDOException;
1313
use SensitiveParameter;
1414

@@ -17,6 +17,8 @@
1717

1818
final class Driver extends AbstractSQLiteDriver
1919
{
20+
use PDOConnect;
21+
2022
/**
2123
* {@inheritDoc}
2224
*/
@@ -31,7 +33,7 @@ public function connect(
3133
}
3234

3335
try {
34-
$pdo = new PDO(
36+
$pdo = $this->doConnect(
3537
$this->constructPdoDsn(array_intersect_key($params, ['path' => true, 'memory' => true])),
3638
$params['user'] ?? '',
3739
$params['password'] ?? '',
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\DBAL\Tests\Functional\Driver\PDO;
6+
7+
use Doctrine\DBAL\Tests\FunctionalTestCase;
8+
use Doctrine\DBAL\Tests\TestUtil;
9+
use Pdo\Mysql;
10+
use Pdo\Pgsql;
11+
use Pdo\Sqlite;
12+
use PHPUnit\Framework\Attributes\RequiresPhp;
13+
14+
#[RequiresPhp('8.4')]
15+
final class PDOSubclassTest extends FunctionalTestCase
16+
{
17+
public function testMySQLSubclass(): void
18+
{
19+
if (! TestUtil::isDriverOneOf('pdo_mysql')) {
20+
self::markTestSkipped('This test requires the pdo_mysql driver.');
21+
}
22+
23+
self::assertInstanceOf(Mysql::class, $this->connection->getNativeConnection());
24+
}
25+
26+
public function testPgSQLSubclass(): void
27+
{
28+
if (! TestUtil::isDriverOneOf('pdo_pgsql')) {
29+
self::markTestSkipped('This test requires the pdo_pgsql driver.');
30+
}
31+
32+
self::assertInstanceOf(Pgsql::class, $this->connection->getNativeConnection());
33+
}
34+
35+
public function testSQLiteSubclass(): void
36+
{
37+
if (! TestUtil::isDriverOneOf('pdo_sqlite')) {
38+
self::markTestSkipped('This test requires the pdo_sqlite driver.');
39+
}
40+
41+
self::assertInstanceOf(Sqlite::class, $this->connection->getNativeConnection());
42+
}
43+
}

0 commit comments

Comments
 (0)