Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add ability to override url with dsn params #1290

Merged
merged 3 commits into from
Mar 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions ConnectionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,12 @@ public function createConnection(array $params, Configuration $config = null, Ev
$this->initializeTypes();
}

if (! isset($params['pdo']) && ! isset($params['charset'])) {
$overriddenOptions = $params['connection_override_options'] ?? [];
unset($params['connection_override_options']);

if (! isset($params['pdo']) && (! isset($params['charset']) || $overriddenOptions)) {
$wrapperClass = null;

if (isset($params['wrapperClass'])) {
if (! is_subclass_of($params['wrapperClass'], Connection::class)) {
if (class_exists(DBALException::class)) {
Expand All @@ -61,7 +65,7 @@ public function createConnection(array $params, Configuration $config = null, Ev
}

$connection = DriverManager::getConnection($params, $config, $eventManager);
$params = $connection->getParams();
$params = array_merge($connection->getParams(), $overriddenOptions);
$driver = $connection->getDriver();

if ($driver instanceof AbstractMySQLDriver) {
Expand Down
19 changes: 15 additions & 4 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,11 @@ private function configureDbalDriverNode(ArrayNodeDefinition $node): void
->children()
->scalarNode('url')->info('A URL with connection information; any parameter value parsed from this string will override explicitly set parameters')->end()
->scalarNode('dbname')->end()
->scalarNode('host')->defaultValue('localhost')->end()
->scalarNode('port')->defaultNull()->end()
->scalarNode('user')->defaultValue('root')->end()
->scalarNode('password')->defaultNull()->end()
->scalarNode('host')->info('Defaults to "localhost" at runtime.')->end()
->scalarNode('port')->info('Defaults to null at runtime.')->end()
->scalarNode('user')->info('Defaults to "root" at runtime.')->end()
->scalarNode('password')->info('Defaults to null at runtime.')->end()
->booleanNode('override_url')->defaultValue(false)->info('Allows overriding parts of the "url" parameter with dbname, host, port, user, and/or password parameters.')->end()
->scalarNode('application_name')->end()
->scalarNode('charset')->end()
->scalarNode('path')->end()
Expand Down Expand Up @@ -313,6 +314,16 @@ private function configureDbalDriverNode(ArrayNodeDefinition $node): void
$v['MultipleActiveResultSets'] = $v['multiple_active_result_sets'];
unset($v['multiple_active_result_sets']);

return $v;
})
->end()
->beforeNormalization()
->ifTrue(static function ($v) {
return empty($v['override_url']) && isset($v['url']);
})
->then(static function ($v) {
@trigger_error('Not setting doctrine.dbal.override_url to true is deprecated. True is the only value that will be supported in doctrine-bundle 3.0.', E_USER_DEPRECATED);

return $v;
})
->end();
Expand Down
23 changes: 22 additions & 1 deletion DependencyInjection/DoctrineExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,28 @@ protected function loadDbalConnection($name, array $connection, ContainerBuilder

protected function getConnectionOptions($connection)
{
$options = $connection;
$options = ['connection_override_options' => []] + $connection;

$connectionDefaults = [
'host' => 'localhost',
'port' => null,
'user' => 'root',
'password' => null,
];

if ($options['override_url']) {
$options['connection_override_options'] = array_intersect_key($options, ['dbname' => null] + $connectionDefaults);
}

unset($options['override_url']);

$options += $connectionDefaults;

foreach (['shards', 'replicas', 'slaves'] as $connectionKey) {
foreach (array_keys($options[$connectionKey]) as $name) {
$options[$connectionKey][$name] += $connectionDefaults;
}
}

if (isset($options['platform_service'])) {
$options['platform'] = new Reference($options['platform_service']);
Expand Down
1 change: 1 addition & 0 deletions Resources/config/schema/doctrine-1.0.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
<xsd:attribute name="port" type="xsd:string" />
<xsd:attribute name="user" type="xsd:string" />
<xsd:attribute name="password" type="xsd:string" />
<xsd:attribute name="override-url" type="xsd:boolean" />
<xsd:attribute name="application-name" type="xsd:string" />
<xsd:attribute name="path" type="xsd:string" />
<xsd:attribute name="unix-socket" type="xsd:string" />
Expand Down
13 changes: 9 additions & 4 deletions Resources/doc/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ Configuration Reference
connections:
# A collection of different named connections (e.g. default, conn2, etc)
default:
# If true, allows overriding url parameters with explicitly set parameters.
# "dbname", "host", "port", "user", and/or "password" can be overridden.
override_url: ~
dbname: ~
host: localhost
port: ~
Expand Down Expand Up @@ -914,10 +917,11 @@ Doctrine DBAL Configuration
.. note::

When specifying a ``url`` parameter, any information extracted from that
URL will override explicitly set parameters. An example database URL
would be ``mysql://snoopy:redbaron@localhost/baseball``, and any explicitly
set driver, user, password and dbname parameter would be overridden by
this URL. See the Doctrine `DBAL documentation`_ for more information.
URL will override explicitly set parameters unless ``override_url`` is set
to ``true``. An example database URL would be
``mysql://snoopy:redbaron@localhost/baseball``, and any explicitly set driver,
user, password and dbname parameter would be overridden by this URL.
See the Doctrine `DBAL documentation`_ for more information.

Besides default Doctrine options, there are some Symfony-related ones that you
can configure. The following block shows all possible configuration keys:
Expand All @@ -928,6 +932,7 @@ can configure. The following block shows all possible configuration keys:

doctrine:
dbal:
override_url: true
url: mysql://user:secret@localhost:1234/otherdatabase # this would override the values below
dbname: database
host: localhost
Expand Down
18 changes: 18 additions & 0 deletions Tests/ConnectionFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,24 @@ public function testDefaultCharsetMySql(): void

$this->assertSame('utf8mb4', $connection->getParams()['charset']);
}

public function testConnectionOverrideOptions(): void
{
$params = [
'dbname' => 'main_test',
'host' => 'db_test',
'port' => 5432,
'user' => 'tester',
'password' => 'wordpass',
];

$connection = (new ConnectionFactory([]))->createConnection([
'url' => 'mysql://root:password@database:3306/main?serverVersion=mariadb-10.5.8',
'connection_override_options' => $params,
]);

$this->assertEquals($params, array_intersect_key($connection->getParams(), $params));
}
}

/**
Expand Down
58 changes: 49 additions & 9 deletions Tests/DependencyInjection/AbstractDoctrineExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,7 @@ public function testDbalLoadFromXmlMultipleConnections(): void
public function testDbalLoadFromXmlSingleConnections(): void
{
$container = $this->loadContainer('dbal_service_single_connection');

// doctrine.dbal.mysql_connection
$config = $container->getDefinition('doctrine.dbal.default_connection')->getArgument(0);
$config = $container->getDefinition('doctrine.dbal.default_connection')->getArgument(0);

$this->assertEquals('mysql_s3cr3t', $config['password']);
$this->assertEquals('mysql_user', $config['user']);
Expand All @@ -113,12 +111,49 @@ public function testDbalLoadFromXmlSingleConnections(): void
$this->assertEquals('5.6.20', $config['serverVersion']);
}

public function testDbalLoadUrlOverride(): void
{
$container = $this->loadContainer('dbal_allow_url_override');
$config = $container->getDefinition('doctrine.dbal.default_connection')->getArgument(0);

$this->assertSame('mysql://root:password@database:3306/main?serverVersion=mariadb-10.5.8', $config['url']);

$expectedOverrides = [
'dbname' => 'main_test',
'user' => 'tester',
'password' => 'wordpass',
'host' => 'localhost',
'port' => 4321,
];

$this->assertEquals($expectedOverrides, array_intersect_key($config, $expectedOverrides));
$this->assertSame($expectedOverrides, $config['connection_override_options']);
$this->assertFalse(isset($config['override_url']));
}

public function testDbalLoadPartialUrlOverrideSetsDefaults(): void
{
$container = $this->loadContainer('dbal_allow_partial_url_override');
$config = $container->getDefinition('doctrine.dbal.default_connection')->getArgument(0);

$expectedDefaults = [
'host' => 'localhost',
'user' => 'root',
'password' => null,
'port' => null,
];

$this->assertEquals($expectedDefaults, array_intersect_key($config, $expectedDefaults));
$this->assertSame('mysql://root:password@database:3306/main?serverVersion=mariadb-10.5.8', $config['url']);
$this->assertCount(1, $config['connection_override_options']);
$this->assertSame('main_test', $config['connection_override_options']['dbname']);
$this->assertFalse(isset($config['override_url']));
}

public function testDbalLoadSingleMasterSlaveConnection(): void
{
$container = $this->loadContainer('dbal_service_single_master_slave_connection');

// doctrine.dbal.mysql_connection
$param = $container->getDefinition('doctrine.dbal.default_connection')->getArgument(0);
$param = $container->getDefinition('doctrine.dbal.default_connection')->getArgument(0);

$this->assertEquals(
class_exists(PrimaryReadReplicaConnection::class) ?
Expand All @@ -135,6 +170,7 @@ class_exists(PrimaryReadReplicaConnection::class) ?
'dbname' => 'mysql_db',
'host' => 'localhost',
'unix_socket' => '/path/to/mysqld.sock',
'connection_override_options' => [],
],
$param['primary'] ?? $param['master'] // TODO: Remove 'master' support here when we require dbal >= 2.11
);
Expand All @@ -146,6 +182,7 @@ class_exists(PrimaryReadReplicaConnection::class) ?
'dbname' => 'replica_db',
'host' => 'localhost',
'unix_socket' => '/path/to/mysqld_replica.sock',
'override_url' => false,
],
$param['replica']['replica1']
);
Expand All @@ -155,9 +192,7 @@ class_exists(PrimaryReadReplicaConnection::class) ?
public function testDbalLoadPoolShardingConnection(): void
{
$container = $this->loadContainer('dbal_service_pool_sharding_connection');

// doctrine.dbal.mysql_connection
$param = $container->getDefinition('doctrine.dbal.default_connection')->getArgument(0);
$param = $container->getDefinition('doctrine.dbal.default_connection')->getArgument(0);

$this->assertEquals('Doctrine\\DBAL\\Sharding\\PoolingShardConnection', $param['wrapperClass']);
$this->assertEquals(new Reference('foo.shard_choser'), $param['shardChoser']);
Expand All @@ -169,6 +204,7 @@ public function testDbalLoadPoolShardingConnection(): void
'dbname' => 'mysql_db',
'host' => 'localhost',
'unix_socket' => '/path/to/mysqld.sock',
'connection_override_options' => [],
],
$param['global']
);
Expand All @@ -181,6 +217,7 @@ public function testDbalLoadPoolShardingConnection(): void
'host' => 'localhost',
'unix_socket' => '/path/to/mysqld_shard.sock',
'id' => 1,
'override_url' => false,
],
$param['shards'][0]
);
Expand Down Expand Up @@ -223,6 +260,7 @@ public function testLoadSimpleSingleConnection(): void
'driver' => 'pdo_mysql',
'driverOptions' => [],
'defaultTableOptions' => [],
'connection_override_options' => [],
],
new Reference('doctrine.dbal.default_connection.configuration'),
new Reference('doctrine.dbal.default_connection.event_manager'),
Expand Down Expand Up @@ -262,6 +300,7 @@ public function testLoadSimpleSingleConnectionWithoutDbName(): void
'driver' => 'pdo_mysql',
'driverOptions' => [],
'defaultTableOptions' => [],
'connection_override_options' => [],
],
new Reference('doctrine.dbal.default_connection.configuration'),
new Reference('doctrine.dbal.default_connection.event_manager'),
Expand Down Expand Up @@ -302,6 +341,7 @@ public function testLoadSingleConnection(): void
'dbname' => 'sqlite_db',
'memory' => true,
'defaultTableOptions' => [],
'connection_override_options' => [],
],
new Reference('doctrine.dbal.default_connection.configuration'),
new Reference('doctrine.dbal.default_connection.event_manager'),
Expand Down
11 changes: 8 additions & 3 deletions Tests/DependencyInjection/Fixtures/DbalTestKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@

class DbalTestKernel extends Kernel
{
/** @var array<string, mixed> */
private $dbalConfig;

/** @var string|null */
private $projectDir;

public function __construct()
public function __construct(array $dbalConfig = ['driver' => 'pdo_sqlite'])
{
$this->dbalConfig = $dbalConfig;

parent::__construct('test', true);
}

Expand All @@ -30,11 +35,11 @@ public function registerBundles(): iterable

public function registerContainerConfiguration(LoaderInterface $loader): void
{
$loader->load(static function (ContainerBuilder $container): void {
$loader->load(function (ContainerBuilder $container): void {
$container->loadFromExtension('framework', ['secret' => 'F00']);

$container->loadFromExtension('doctrine', [
'dbal' => ['driver' => 'pdo_sqlite'],
'dbal' => $this->dbalConfig,
]);

// Register a NullLogger to avoid getting the stderr default logger of FrameworkBundle
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" ?>

<srv:container xmlns="http://symfony.com/schema/dic/doctrine"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:srv="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/doctrine http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd">

<config>
<dbal override-url="true" dbname="main_test" url="mysql://root:password@database:3306/main?serverVersion=mariadb-10.5.8" />
</config>
</srv:container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" ?>

<srv:container xmlns="http://symfony.com/schema/dic/doctrine"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:srv="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/doctrine http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd">

<config>
<dbal override-url="true" dbname="main_test" user="tester" password="wordpass" host="localhost" port="4321" url="mysql://root:password@database:3306/main?serverVersion=mariadb-10.5.8" />
</config>
</srv:container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
doctrine:
dbal:
override_url: true
url: 'mysql://root:password@database:3306/main?serverVersion=mariadb-10.5.8'
dbname: main_test
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
doctrine:
dbal:
override_url: true
url: 'mysql://root:password@database:3306/main?serverVersion=mariadb-10.5.8'
dbname: main_test
user: tester
password: wordpass
host: localhost
port: 4321
Loading