Drupal's database abstraction layer uses the concept of a "connection array" (the one that is commonly defined in the settings.php file) to establish connection to the database. However, in order to pass database information between installs (and specifically, during test runs to pass this information to child sites) a URL version of this connection array is used.
The conversion between the "connection URL" and the "connection array" was not centralized neither encapsulated in any way.
Some methods on the database layer have now been implemented to assist and isolate these conversions, that also now rely on the database drivers themselves to provide any custom or database specific alterations if needed:
\Drupal\Core\Database::convertDbUrlToConnectionInfo($url, $root)
: to convert a connection URL into a connection array.\Drupal\Core\Database::convertConnectionInfoToUrl(array $db_info)
: to convert a connection array to a connection URL.
Custom/contrib database drivers
Custom/contrib drivers can extend/override the default implementations in order to add/retrieve to/from the URL additional connection elements they may require.
In the example below, the SQLite driver's Connection
class implements overrides for Connection::createConnectionOptionsFromUrl
and for Connection::createUrlFromConnectionOptions
, in order to adjust the results:
/**
* {@inheritdoc}
*/
public static function createConnectionOptionsFromUrl($url, $root) {
$database = parent::createConnectionOptionsFromUrl($url, $root);
// A SQLite database path with two leading slashes indicates a system path.
// Otherwise the path is relative to the Drupal root.
$url_components = parse_url($url);
if ($url_components['path'][0] === '/') {
$url_components['path'] = substr($url_components['path'], 1);
}
if ($url_components['path'][0] === '/') {
$database['database'] = $url_components['path'];
}
else {
$database['database'] = $root . '/' . $url_components['path'];
}
// User credentials and system port are irrelevant for SQLite.
unset(
$database['username'],
$database['password'],
$database['port']
);
return $database;
}
/**
* {@inheritdoc}
*/
public static function createUrlFromConnectionOptions(array $connection_options) {
if (!isset($connection_options['driver'], $connection_options['database'])) {
throw new \InvalidArgumentException("As a minimum, the connection options array must contain at least the 'driver' and 'database' keys");
}
$db_url = 'sqlite://localhost/' . $connection_options['database'];
if (isset($connection_options['prefix']['default']) && $connection_options['prefix']['default'] !== NULL && $connection_options['prefix']['default'] !== '') {
$db_url .= '#' . $connection_options['prefix']['default'];
}
return $db_url;
}
Specifying the database URL in env variables or in phpunit.xml
for test runs
Contrib database drivers will specify the driver name in the schema of the URL, for example:
dbal://username:password@host:port/database?dbal_driver=pdo_mysql#prefix
Note that if you use the URL in the phpunit.xml
file to set the db environment for testing, and the driver adds multiple parts to the querystring, you have to manually convert the querystring separators &
to HTML entities counterparts &
, to comply to XML syntax.
So, if you get something like
mydriver://username:password@host:port/database?my_driver=xxxx&my_param=yyyy#prefix
from call to \Drupal\Core\Database\Database::getConnectionInfoAsUrl
, your phpunit.xml
entry should be
<env name="SIMPLETEST_DB" value="mydriver://username:password@host:port/database?my_driver=xxxx&my_param=yyyy#prefix"/>