bug #42011 [Cache] Support decorated Dbal drivers in PdoAdapter (Jeroeny)

This PR was squashed before being merged into the 4.4 branch.

Discussion
----------

[Cache] Support decorated Dbal drivers in PdoAdapter

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | Explanation in this PR
| License       | MIT

Doctrine v3 supports middleware for Drivers. Upon creating the Connection, `middleware->wrap(Driver): Driver` [is called](https://github.com/doctrine/dbal/blob/3.1.x/src/DriverManager.php#L210), in which the middleware can wrap/decorate the Driver class. So that it can perform tracing [for example](https://github.com/getsentry/sentry-symfony/blob/master/src/Tracing/Doctrine/DBAL/TracingDriverMiddleware.php#L37).

When this happens, the Driver class inside the Connection is no longer one of Doctrine's well known Driver classes. The `PdoAdapter ` uses this class to determine the database platform. Which breaks once the Driver is decorated and no longer one of the classes [listed](https://github.com/symfony/symfony/blob/5.4/src/Symfony/Component/Cache/Adapter/PdoAdapter.php#L452) in the `PdoAdapter`.

Since Dbal exposes this middleware as a feature, I think it would be nice for the `PdoAdapter` to support this.

To solve this, the `getDatabasePlatform` can be used. This returns a `Doctrine\DBAL\Platforms\AbstractPlatform` which defines the abstract method `getName`. This returns a value very similar to the list in the `PdoAdapter`. The names don't match exactly, so therefor a small mapping is done to get right the name used in the adapter. As far as a I know, there'd be no other implications with this change.

Related: https://github.com/getsentry/sentry-symfony/issues/530

Commits
-------

58d74e30be [Cache] Support decorated Dbal drivers in PdoAdapter
This commit is contained in:
Tobias Schultze 2021-07-14 22:18:05 +02:00
commit e5c96c4051
4 changed files with 86 additions and 0 deletions

View File

@ -25,6 +25,7 @@ foreach ($loader->getClassMap() as $class => $file) {
case false !== strpos($file = realpath($file), '/vendor/'):
case false !== strpos($file, '/src/Symfony/Bridge/PhpUnit/'):
case false !== strpos($file, '/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Validation/Article.php'):
case false !== strpos($file, '/src/Symfony/Component/Cache/Tests/Fixtures/DriverWrapper.php'):
case false !== strpos($file, '/src/Symfony/Component/Config/Tests/Fixtures/BadFileName.php'):
case false !== strpos($file, '/src/Symfony/Component/Config/Tests/Fixtures/BadParent.php'):
case false !== strpos($file, '/src/Symfony/Component/Config/Tests/Fixtures/ParseError.php'):

View File

@ -11,10 +11,13 @@
namespace Symfony\Component\Cache\Tests\Adapter;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Driver\Middleware;
use Doctrine\DBAL\DriverManager;
use PHPUnit\Framework\SkippedTestSuiteError;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\Adapter\PdoAdapter;
use Symfony\Component\Cache\Tests\Fixtures\DriverWrapper;
/**
* @group time-sensitive
@ -43,4 +46,29 @@ class PdoDbalAdapterTest extends AdapterTestCase
{
return new PdoAdapter(DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile]), '', $defaultLifetime);
}
public function testConfigureSchemaDecoratedDbalDriver()
{
$connection = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile]);
if (!interface_exists(Middleware::class)) {
$this->markTestSkipped('doctrine/dbal v2 does not support custom drivers using middleware');
}
$middleware = $this->createMock(Middleware::class);
$middleware
->method('wrap')
->willReturn(new DriverWrapper($connection->getDriver()));
$config = new Configuration();
$config->setMiddlewares([$middleware]);
$connection = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile], $config);
$adapter = new PdoAdapter($connection);
$adapter->createTable();
$item = $adapter->getItem('key');
$item->set('value');
$this->assertTrue($adapter->save($item));
}
}

View File

@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Cache\Tests\Fixtures;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
class DriverWrapper implements Driver
{
/** @var Driver */
private $driver;
public function __construct(Driver $driver)
{
$this->driver = $driver;
}
public function connect(array $params, $username = null, $password = null, array $driverOptions = []): Driver\Connection
{
return $this->driver->connect($params, $username, $password, $driverOptions);
}
public function getDatabasePlatform(): AbstractPlatform
{
return $this->driver->getDatabasePlatform();
}
public function getSchemaManager(Connection $conn, AbstractPlatform $platform): AbstractSchemaManager
{
return $this->driver->getSchemaManager($conn, $platform);
}
public function getExceptionConverter(): Driver\API\ExceptionConverter
{
return $this->driver->getExceptionConverter();
}
}

View File

@ -448,6 +448,15 @@ trait PdoTrait
case $driver instanceof \Doctrine\DBAL\Driver\PDO\SQLSrv\Driver:
$this->driver = 'sqlsrv';
break;
case $driver instanceof \Doctrine\DBAL\Driver:
$this->driver = [
'mssql' => 'sqlsrv',
'oracle' => 'oci',
'postgresql' => 'pgsql',
'sqlite' => 'sqlite',
'mysql' => 'mysql',
][$driver->getDatabasePlatform()->getName()] ?? \get_class($driver);
break;
default:
$this->driver = \get_class($driver);
break;