Skip to content

Commit

Permalink
Leverage the new PDO subclasses (#6532)
Browse files Browse the repository at this point in the history
|      Q       |   A
|------------- | -----------
| Type         | feature
| Fixed issues | N/A

#### Summary

This PR adds support for the new [PDO
subclasses](https://wiki.php.net/rfc/pdo_driver_specific_subclasses) to
DBAL.
  • Loading branch information
derrabus authored Oct 10, 2024
1 parent e0f3674 commit 54be50e
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 6 deletions.
8 changes: 8 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ awareness about deprecated code.

# Upgrade to 4.2

## Support for new PDO subclasses on PHP 8.4

On PHP 8.4, if you call `getNativeConnection()` on a connection established through one of the PDO drivers,
you will get an instance of the new PDO subclasses, e.g. `Pdo\Mysql` or `Pdo\Ppgsql` instead of just `PDO`.

However, this currently does not apply to persistent connections.
See https://github.com/php/php-src/issues/16314 for details.

## Minor BC break: incompatible query cache format

The query cache format has been changed to address the issue where a cached result with no rows would miss the metadata.
Expand Down
4 changes: 4 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ parameters:
# Type check for legacy implementations of the Result interface
# TODO: remove in 5.0.0
- '~^Call to function method_exists\(\) with Doctrine\\DBAL\\Driver\\Result and ''getColumnName'' will always evaluate to true\.$~'

# PHPStan does not know the new PDO classes yet.
- '~^Class Pdo\\\w+ not found\.$~'
- '~^Call to an undefined static method PDO\:\:connect\(\)\.$~'
includes:
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
Expand Down
14 changes: 14 additions & 0 deletions psalm.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,14 @@
<file name="src/Driver/PgSQL/Statement.php"/>
</errorLevel>
</TypeDoesNotContainType>
<UndefinedClass>
<errorLevel type="suppress">
<!-- New PDO classes introduced in PHP 8.4 -->
<referencedClass name="Pdo\Mysql"/>
<referencedClass name="Pdo\Pgsql"/>
<referencedClass name="Pdo\Sqlite"/>
</errorLevel>
</UndefinedClass>
<UndefinedDocblockClass>
<errorLevel type="suppress">
<!-- See https://github.com/vimeo/psalm/issues/5472 -->
Expand All @@ -304,6 +312,12 @@
<referencedClass name="OCILob"/>
</errorLevel>
</UndefinedDocblockClass>
<UndefinedMethod>
<errorLevel type="suppress">
<!-- New PDO static constructor introduced in PHP 8.4 -->
<referencedMethod name="PDO::connect"/>
</errorLevel>
</UndefinedMethod>
<UnsupportedPropertyReferenceUsage>
<errorLevel type="suppress">
<!-- This code is valid -->
Expand Down
5 changes: 4 additions & 1 deletion src/Driver/PDO/MySQL/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Doctrine\DBAL\Driver\PDO\Connection;
use Doctrine\DBAL\Driver\PDO\Exception;
use Doctrine\DBAL\Driver\PDO\Exception\InvalidConfiguration;
use Doctrine\DBAL\Driver\PDO\PDOConnect;
use PDO;
use PDOException;
use SensitiveParameter;
Expand All @@ -16,6 +17,8 @@

final class Driver extends AbstractMySQLDriver
{
use PDOConnect;

/**
* {@inheritDoc}
*/
Expand All @@ -39,7 +42,7 @@ public function connect(
unset($safeParams['password']);

try {
$pdo = new PDO(
$pdo = $this->doConnect(
$this->constructPdoDsn($safeParams),
$params['user'] ?? '',
$params['password'] ?? '',
Expand Down
5 changes: 4 additions & 1 deletion src/Driver/PDO/OCI/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Doctrine\DBAL\Driver\PDO\Connection;
use Doctrine\DBAL\Driver\PDO\Exception;
use Doctrine\DBAL\Driver\PDO\Exception\InvalidConfiguration;
use Doctrine\DBAL\Driver\PDO\PDOConnect;
use PDO;
use PDOException;
use SensitiveParameter;
Expand All @@ -16,6 +17,8 @@

final class Driver extends AbstractOracleDriver
{
use PDOConnect;

/**
* {@inheritDoc}
*/
Expand All @@ -39,7 +42,7 @@ public function connect(
unset($safeParams['password']);

try {
$pdo = new PDO(
$pdo = $this->doConnect(
$this->constructPdoDsn($params),
$params['user'] ?? '',
$params['password'] ?? '',
Expand Down
28 changes: 28 additions & 0 deletions src/Driver/PDO/PDOConnect.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Doctrine\DBAL\Driver\PDO;

use PDO;

use const PHP_VERSION_ID;

/** @internal */
trait PDOConnect
{
/** @param array<int, mixed> $options */
private function doConnect(
string $dsn,
string $username,
string $password,
array $options,
): PDO {
// see https://github.com/php/php-src/issues/16314
if (PHP_VERSION_ID < 80400 || ($options[PDO::ATTR_PERSISTENT] ?? false) === true) {
return new PDO($dsn, $username, $password, $options);
}

return PDO::connect($dsn, $username, $password, $options);
}
}
5 changes: 4 additions & 1 deletion src/Driver/PDO/PgSQL/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Doctrine\DBAL\Driver\PDO\Connection;
use Doctrine\DBAL\Driver\PDO\Exception;
use Doctrine\DBAL\Driver\PDO\Exception\InvalidConfiguration;
use Doctrine\DBAL\Driver\PDO\PDOConnect;
use PDO;
use PDOException;
use SensitiveParameter;
Expand All @@ -16,6 +17,8 @@

final class Driver extends AbstractPostgreSQLDriver
{
use PDOConnect;

/**
* {@inheritDoc}
*/
Expand All @@ -39,7 +42,7 @@ public function connect(
unset($safeParams['password']);

try {
$pdo = new PDO(
$pdo = $this->doConnect(
$this->constructPdoDsn($safeParams),
$params['user'] ?? '',
$params['password'] ?? '',
Expand Down
5 changes: 4 additions & 1 deletion src/Driver/PDO/SQLSrv/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Doctrine\DBAL\Driver\PDO\Connection as PDOConnection;
use Doctrine\DBAL\Driver\PDO\Exception as PDOException;
use Doctrine\DBAL\Driver\PDO\Exception\InvalidConfiguration;
use Doctrine\DBAL\Driver\PDO\PDOConnect;
use PDO;
use SensitiveParameter;

Expand All @@ -19,6 +20,8 @@

final class Driver extends AbstractSQLServerDriver
{
use PDOConnect;

/**
* {@inheritDoc}
*/
Expand Down Expand Up @@ -52,7 +55,7 @@ public function connect(
unset($safeParams['password']);

try {
$pdo = new PDO(
$pdo = $this->doConnect(
$this->constructDsn($safeParams, $dsnOptions),
$params['user'] ?? '',
$params['password'] ?? '',
Expand Down
6 changes: 4 additions & 2 deletions src/Driver/PDO/SQLite/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use Doctrine\DBAL\Driver\PDO\Connection;
use Doctrine\DBAL\Driver\PDO\Exception;
use Doctrine\DBAL\Driver\PDO\Exception\InvalidConfiguration;
use PDO;
use Doctrine\DBAL\Driver\PDO\PDOConnect;
use PDOException;
use SensitiveParameter;

Expand All @@ -17,6 +17,8 @@

final class Driver extends AbstractSQLiteDriver
{
use PDOConnect;

/**
* {@inheritDoc}
*/
Expand All @@ -31,7 +33,7 @@ public function connect(
}

try {
$pdo = new PDO(
$pdo = $this->doConnect(
$this->constructPdoDsn(array_intersect_key($params, ['path' => true, 'memory' => true])),
$params['user'] ?? '',
$params['password'] ?? '',
Expand Down
43 changes: 43 additions & 0 deletions tests/Functional/Driver/PDO/PDOSubclassTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace Doctrine\DBAL\Tests\Functional\Driver\PDO;

use Doctrine\DBAL\Tests\FunctionalTestCase;
use Doctrine\DBAL\Tests\TestUtil;
use Pdo\Mysql;
use Pdo\Pgsql;
use Pdo\Sqlite;
use PHPUnit\Framework\Attributes\RequiresPhp;

#[RequiresPhp('8.4')]
final class PDOSubclassTest extends FunctionalTestCase
{
public function testMySQLSubclass(): void
{
if (! TestUtil::isDriverOneOf('pdo_mysql')) {
self::markTestSkipped('This test requires the pdo_mysql driver.');
}

self::assertInstanceOf(Mysql::class, $this->connection->getNativeConnection());
}

public function testPgSQLSubclass(): void
{
if (! TestUtil::isDriverOneOf('pdo_pgsql')) {
self::markTestSkipped('This test requires the pdo_pgsql driver.');
}

self::assertInstanceOf(Pgsql::class, $this->connection->getNativeConnection());
}

public function testSQLiteSubclass(): void
{
if (! TestUtil::isDriverOneOf('pdo_sqlite')) {
self::markTestSkipped('This test requires the pdo_sqlite driver.');
}

self::assertInstanceOf(Sqlite::class, $this->connection->getNativeConnection());
}
}

0 comments on commit 54be50e

Please sign in to comment.