diff --git a/lib/private/DB/ConnectionFactory.php b/lib/private/DB/ConnectionFactory.php index 91ed009f4d45d..8182a07d91327 100644 --- a/lib/private/DB/ConnectionFactory.php +++ b/lib/private/DB/ConnectionFactory.php @@ -210,7 +210,7 @@ public function createConnectionParams(string $configPrefix = '', array $additio //additional driver options, eg. for mysql ssl $driverOptions = $this->config->getValue($configPrefix . 'dbdriveroptions', $this->config->getValue('dbdriveroptions', null)); if ($driverOptions) { - $connectionParams['driverOptions'] = array_merge($connectionParams['driverOptions'], $driverOptions); + $connectionParams['driverOptions'] = $driverOptions + ($connectionParams['driverOptions'] ?? []); } // set default table creation options diff --git a/tests/lib/DB/ConnectionFactoryTest.php b/tests/lib/DB/ConnectionFactoryTest.php index 5e57bb6be139b..42f363fbff859 100644 --- a/tests/lib/DB/ConnectionFactoryTest.php +++ b/tests/lib/DB/ConnectionFactoryTest.php @@ -41,6 +41,53 @@ public function testSplitHostFromPortAndSocket($host, array $expected): void { $this->assertEquals($expected, self::invokePrivate($factory, 'splitHostFromPortAndSocket', [$host])); } + /** + * The numeric value of a PDO MySQL attribute, e.g. `SSL_CA`. + * + * The values are not stable across PHP versions, so they must never be hardcoded. + * Since PHP 8.5 the `PDO::MYSQL_ATTR_*` constants are deprecated in favor of + * `Pdo\Mysql::ATTR_*`, and either only exists with the MySQL driver installed. + */ + private function mysqlAttribute(string $name): int { + if (!extension_loaded('pdo_mysql')) { + $this->markTestSkipped('The pdo_mysql extension is required to resolve the PDO attribute values'); + } + if (PHP_VERSION_ID >= 80500 && class_exists(\Pdo\Mysql::class)) { + return (int)constant('Pdo\Mysql::ATTR_' . $name); + } + return (int)constant('PDO::MYSQL_ATTR_' . $name); + } + + public function testMysqlSslConnection(): void { + /** @var SystemConfig|\PHPUnit\Framework\MockObject\MockObject $config */ + $config = $this->createMock(SystemConfig::class); + $config->method('getValue') + ->willReturnCallback(function ($key, $default) { + return match ($key) { + 'dbdriveroptions' => [ + $this->mysqlAttribute('SSL_CA') => 'rootCA.crt', + $this->mysqlAttribute('SSL_CERT') => 'client.crt', + $this->mysqlAttribute('SSL_KEY') => 'client.key', + $this->mysqlAttribute('SSL_VERIFY_SERVER_CERT') => true, + ], + 'dbtype' => 'mysql', + default => $default, + }; + }); + $factory = new ConnectionFactory($config); + + $params = $factory->createConnectionParams(); + + $this->assertEquals('pdo_mysql', $params['driver']); + $this->assertEquals([ + $this->mysqlAttribute('FOUND_ROWS') => true, + $this->mysqlAttribute('SSL_CA') => 'rootCA.crt', + $this->mysqlAttribute('SSL_CERT') => 'client.crt', + $this->mysqlAttribute('SSL_KEY') => 'client.key', + $this->mysqlAttribute('SSL_VERIFY_SERVER_CERT') => true, + ], $params['driverOptions']); + } + public function testPgsqlSslConnection(): void { /** @var SystemConfig|\PHPUnit\Framework\MockObject\MockObject $config */ $config = $this->createMock(SystemConfig::class); @@ -48,6 +95,10 @@ public function testPgsqlSslConnection(): void { ->willReturnCallback(function ($key, $default) { return match ($key) { 'dbtype' => 'pgsql', + 'dbdriveroptions' => [ + 1 => 'foo', + 3 => 'bar', + ], 'pgsql_ssl' => [ 'mode' => 'verify-full', 'cert' => 'client.crt', @@ -68,5 +119,9 @@ public function testPgsqlSslConnection(): void { $this->assertEquals('client.crt', $params['sslcert']); $this->assertEquals('client.key', $params['sslkey']); $this->assertEquals('client.crl', $params['sslcrl']); + $this->assertEquals([ + 1 => 'foo', + 3 => 'bar', + ], $params['driverOptions']); } }