From 76707f63636a447e047552b984d985ad24b3ab5a Mon Sep 17 00:00:00 2001 From: David Maicher Date: Tue, 17 Oct 2023 12:23:10 +0200 Subject: [PATCH 1/5] run CI with DBAL 4 --- .github/workflows/continuous-integration.yml | 6 ++++++ composer.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 970ad3cd..ae0ae2c4 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -54,6 +54,12 @@ jobs: stability: "stable" remove-orm: true + # DBAL 4 + - php-version: "8.2" + dependencies: "highest" + stability: "dev" + remove-orm: true + # Bleeding edge - php-version: "8.2" dependencies: "highest" diff --git a/composer.json b/composer.json index b211702a..2286e54b 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ "require": { "php": "^7.4 || ^8.0", "doctrine/cache": "^1.11 || ^2.0", - "doctrine/dbal": "^3.7.0", + "doctrine/dbal": "^3.7.0 || ^4.0", "doctrine/persistence": "^2.2 || ^3", "doctrine/sql-formatter": "^1.0.1", "symfony/cache": "^5.4 || ^6.0 || ^7.0", From 293fe8f0eeaba6868be71d4818e823cb8d760abb Mon Sep 17 00:00:00 2001 From: David Maicher Date: Wed, 18 Oct 2023 20:47:47 +0200 Subject: [PATCH 2/5] initial work for DBAL 4 compatibility --- ConnectionFactory.php | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/ConnectionFactory.php b/ConnectionFactory.php index f8d65029..f3769b16 100644 --- a/ConnectionFactory.php +++ b/ConnectionFactory.php @@ -6,17 +6,19 @@ use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; -use Doctrine\DBAL\Exception; use Doctrine\DBAL\Exception as DBALException; use Doctrine\DBAL\Exception\DriverException; -use Doctrine\DBAL\Exception\MalformedDsnException; +use Doctrine\DBAL\Exception\DriverRequired; +use Doctrine\DBAL\Exception\InvalidWrapperClass; use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Tools\DsnParser; use Doctrine\DBAL\Types\Type; use Doctrine\Deprecations\Deprecation; +use RuntimeException; use function array_merge; +use function class_exists; use function is_subclass_of; use function trigger_deprecation; @@ -92,6 +94,10 @@ public function createConnection(array $params, ?Configuration $config = null, ? if (isset($params['wrapperClass'])) { if (! is_subclass_of($params['wrapperClass'], Connection::class)) { + if (class_exists(InvalidWrapperClass::class)) { + throw InvalidWrapperClass::new($params['wrapperClass']); + } + throw DBALException::invalidWrapperClass($params['wrapperClass']); } @@ -102,7 +108,11 @@ public function createConnection(array $params, ?Configuration $config = null, ? $connection = DriverManager::getConnection($params, $config, $eventManager); $params = $this->addDatabaseSuffix(array_merge($connection->getParams(), $overriddenOptions)); $driver = $connection->getDriver(); - $platform = $driver->getDatabasePlatform(); + if (class_exists(Connection\StaticServerVersionProvider::class)) { + $platform = $driver->getDatabasePlatform(new Connection\StaticServerVersionProvider($params['serverVersion'] ?? '')); + } else { + $platform = $driver->getDatabasePlatform(); + } if (! isset($params['charset'])) { if ($platform instanceof AbstractMySQLPlatform) { @@ -162,7 +172,10 @@ private function getDatabasePlatform(Connection $connection): AbstractPlatform try { return $connection->getDatabasePlatform(); } catch (DriverException $driverException) { - throw new DBALException( + //TODO: what more specific exception class should we throw with DBAL 4? + $class = class_exists(DBALException::class) ? DBALException::class : RuntimeException::class; + + throw new $class( 'An exception occurred while establishing a connection to figure out your platform version.' . PHP_EOL . "You can circumvent this by setting a 'server_version' configuration value" . PHP_EOL . PHP_EOL . 'For further information have a look at:' . PHP_EOL . @@ -226,7 +239,7 @@ private function addDatabaseSuffix(array $params): array * URL extracted into individual parameter parts. * @psalm-return Params * - * @throws Exception + * @throws DBALException */ private function parseDatabaseUrl(array $params): array { @@ -234,11 +247,7 @@ private function parseDatabaseUrl(array $params): array return $params; } - try { - $parsedParams = $this->dsnParser->parse($params['url']); - } catch (MalformedDsnException $e) { - throw new Exception('Malformed parameter "url".', 0, $e); - } + $parsedParams = $this->dsnParser->parse($params['url']); if (isset($parsedParams['driver'])) { // The requested driver from the URL scheme takes precedence @@ -248,10 +257,14 @@ private function parseDatabaseUrl(array $params): array $params = array_merge($params, $parsedParams); - // If a schemeless connection URL is given, we require a default driver or default custom driver + // If a schemaless connection URL is given, we require a default driver or default custom driver // as connection parameter. if (! isset($params['driverClass']) && ! isset($params['driver'])) { - throw Exception::driverRequired($params['url']); + if (class_exists(DriverRequired::class)) { + throw DriverRequired::new($params['url']); + } + + throw DBALException::driverRequired($params['url']); } unset($params['url']); From ddacc7994fe3d398118b4c79cca3f18d4b724c29 Mon Sep 17 00:00:00 2001 From: David Maicher Date: Sat, 21 Oct 2023 21:32:33 +0200 Subject: [PATCH 3/5] review feedback + handle missing EventManager arg --- ConnectionFactory.php | 29 ++++++++++++++++------- DependencyInjection/DoctrineExtension.php | 4 +++- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/ConnectionFactory.php b/ConnectionFactory.php index f3769b16..6f1af3ab 100644 --- a/ConnectionFactory.php +++ b/ConnectionFactory.php @@ -5,20 +5,24 @@ use Doctrine\Common\EventManager; use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Connection\StaticServerVersionProvider; +use Doctrine\DBAL\ConnectionException; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Exception as DBALException; use Doctrine\DBAL\Exception\DriverException; use Doctrine\DBAL\Exception\DriverRequired; use Doctrine\DBAL\Exception\InvalidWrapperClass; +use Doctrine\DBAL\Exception\MalformedDsnException; use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Tools\DsnParser; use Doctrine\DBAL\Types\Type; use Doctrine\Deprecations\Deprecation; -use RuntimeException; +use InvalidArgumentException; use function array_merge; use function class_exists; +use function interface_exists; use function is_subclass_of; use function trigger_deprecation; @@ -65,6 +69,10 @@ public function __construct(array $typesConfig, ?DsnParser $dsnParser = null) */ public function createConnection(array $params, ?Configuration $config = null, ?EventManager $eventManager = null, array $mappingTypes = []) { + if (interface_exists(DBALException::class) && $eventManager !== null) { + throw new InvalidArgumentException('Passing an EventManager instance is not supported with DBAL 4'); + } + if (! $this->initialized) { $this->initializeTypes(); } @@ -105,11 +113,11 @@ public function createConnection(array $params, ?Configuration $config = null, ? $params['wrapperClass'] = null; } - $connection = DriverManager::getConnection($params, $config, $eventManager); + $connection = DriverManager::getConnection(...array_merge([$params, $config], $eventManager ? [$eventManager] : [])); $params = $this->addDatabaseSuffix(array_merge($connection->getParams(), $overriddenOptions)); $driver = $connection->getDriver(); - if (class_exists(Connection\StaticServerVersionProvider::class)) { - $platform = $driver->getDatabasePlatform(new Connection\StaticServerVersionProvider($params['serverVersion'] ?? '')); + if (class_exists(StaticServerVersionProvider::class)) { + $platform = $driver->getDatabasePlatform(new StaticServerVersionProvider($params['serverVersion'] ?? '')); } else { $platform = $driver->getDatabasePlatform(); } @@ -144,7 +152,7 @@ public function createConnection(array $params, ?Configuration $config = null, ? $connection = new $wrapperClass($params, $driver, $config, $eventManager); } else { - $connection = DriverManager::getConnection($params, $config, $eventManager); + $connection = DriverManager::getConnection(...array_merge([$params, $config], $eventManager ? [$eventManager] : [])); } if (! empty($mappingTypes)) { @@ -172,8 +180,7 @@ private function getDatabasePlatform(Connection $connection): AbstractPlatform try { return $connection->getDatabasePlatform(); } catch (DriverException $driverException) { - //TODO: what more specific exception class should we throw with DBAL 4? - $class = class_exists(DBALException::class) ? DBALException::class : RuntimeException::class; + $class = class_exists(DBALException::class) ? DBALException::class : ConnectionException::class; throw new $class( 'An exception occurred while establishing a connection to figure out your platform version.' . PHP_EOL . @@ -247,7 +254,11 @@ private function parseDatabaseUrl(array $params): array return $params; } - $parsedParams = $this->dsnParser->parse($params['url']); + try { + $parsedParams = $this->dsnParser->parse($params['url']); + } catch (MalformedDsnException $e) { + throw new MalformedDsnException('Malformed parameter "url".', 0, $e); + } if (isset($parsedParams['driver'])) { // The requested driver from the URL scheme takes precedence @@ -257,7 +268,7 @@ private function parseDatabaseUrl(array $params): array $params = array_merge($params, $parsedParams); - // If a schemaless connection URL is given, we require a default driver or default custom driver + // If a schemeless connection URL is given, we require a default driver or default custom driver // as connection parameter. if (! isset($params['driverClass']) && ! isset($params['driver'])) { if (class_exists(DriverRequired::class)) { diff --git a/DependencyInjection/DoctrineExtension.php b/DependencyInjection/DoctrineExtension.php index ec6dc79f..b2b06260 100644 --- a/DependencyInjection/DoctrineExtension.php +++ b/DependencyInjection/DoctrineExtension.php @@ -17,6 +17,7 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection; use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface; +use Doctrine\DBAL\Exception as DBALException; use Doctrine\DBAL\Schema\LegacySchemaManagerFactory; use Doctrine\ORM\Configuration as OrmConfiguration; use Doctrine\ORM\EntityManager; @@ -278,7 +279,8 @@ protected function loadDbalConnection($name, array $connection, ContainerBuilder ->setArguments([ $options, new Reference(sprintf('doctrine.dbal.%s_connection.configuration', $name)), - new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)), + // event manager is only supported on DBAL < 4 + class_exists(DBALException::class) ? new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)) : null, $connection['mapping_types'], ]); From ad85762157fdb74285413031ec3a0ec07acd96fe Mon Sep 17 00:00:00 2001 From: David Maicher Date: Sun, 22 Oct 2023 11:22:33 +0200 Subject: [PATCH 4/5] improve DBAL 4 detection for EventManager --- ConnectionFactory.php | 6 +++--- DependencyInjection/DoctrineExtension.php | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ConnectionFactory.php b/ConnectionFactory.php index 6f1af3ab..74d2af61 100644 --- a/ConnectionFactory.php +++ b/ConnectionFactory.php @@ -22,8 +22,8 @@ use function array_merge; use function class_exists; -use function interface_exists; use function is_subclass_of; +use function method_exists; use function trigger_deprecation; use const PHP_EOL; @@ -69,8 +69,8 @@ public function __construct(array $typesConfig, ?DsnParser $dsnParser = null) */ public function createConnection(array $params, ?Configuration $config = null, ?EventManager $eventManager = null, array $mappingTypes = []) { - if (interface_exists(DBALException::class) && $eventManager !== null) { - throw new InvalidArgumentException('Passing an EventManager instance is not supported with DBAL 4'); + if (! method_exists(Connection::class, 'getEventManager') && $eventManager !== null) { + throw new InvalidArgumentException('Passing an EventManager instance is not supported with DBAL > 3'); } if (! $this->initialized) { diff --git a/DependencyInjection/DoctrineExtension.php b/DependencyInjection/DoctrineExtension.php index b2b06260..0fe693f2 100644 --- a/DependencyInjection/DoctrineExtension.php +++ b/DependencyInjection/DoctrineExtension.php @@ -17,7 +17,6 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection; use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface; -use Doctrine\DBAL\Exception as DBALException; use Doctrine\DBAL\Schema\LegacySchemaManagerFactory; use Doctrine\ORM\Configuration as OrmConfiguration; use Doctrine\ORM\EntityManager; @@ -280,7 +279,7 @@ protected function loadDbalConnection($name, array $connection, ContainerBuilder $options, new Reference(sprintf('doctrine.dbal.%s_connection.configuration', $name)), // event manager is only supported on DBAL < 4 - class_exists(DBALException::class) ? new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)) : null, + method_exists(Connection::class, 'getEventManager') ? new Reference(sprintf('doctrine.dbal.%s_connection.event_manager', $name)) : null, $connection['mapping_types'], ]); From 843e348374f721c982ab14f28b01e61d14afcd0b Mon Sep 17 00:00:00 2001 From: David Maicher Date: Mon, 23 Oct 2023 10:37:45 +0200 Subject: [PATCH 5/5] refactor getDatabasePlatform call --- ConnectionFactory.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ConnectionFactory.php b/ConnectionFactory.php index 74d2af61..158fecc6 100644 --- a/ConnectionFactory.php +++ b/ConnectionFactory.php @@ -116,11 +116,9 @@ public function createConnection(array $params, ?Configuration $config = null, ? $connection = DriverManager::getConnection(...array_merge([$params, $config], $eventManager ? [$eventManager] : [])); $params = $this->addDatabaseSuffix(array_merge($connection->getParams(), $overriddenOptions)); $driver = $connection->getDriver(); - if (class_exists(StaticServerVersionProvider::class)) { - $platform = $driver->getDatabasePlatform(new StaticServerVersionProvider($params['serverVersion'] ?? '')); - } else { - $platform = $driver->getDatabasePlatform(); - } + $platform = $driver->getDatabasePlatform( + ...(class_exists(StaticServerVersionProvider::class) ? [new StaticServerVersionProvider($params['serverVersion'] ?? '')] : []) + ); if (! isset($params['charset'])) { if ($platform instanceof AbstractMySQLPlatform) {