Issue #3294695 by daffie, longwave, mkalkbrenner, pandaski: Drupal 8 BC for database driver namespace fails for replicas

merge-requests/2787/head
catch 2022-09-22 12:51:26 +01:00
parent 43c410120f
commit e7ef291666
3 changed files with 85 additions and 58 deletions

View File

@ -256,9 +256,41 @@ abstract class Database {
$info['prefix'] = $prefix; $info['prefix'] = $prefix;
} }
// Fallback for Drupal 7 settings.php if namespace is not provided. // Backwards compatibility layer for Drupal 8 style database connection
if (empty($info['namespace'])) { // arrays. Those have the wrong 'namespace' key set, or not set at all
$info['namespace'] = 'Drupal\\' . $info['driver'] . '\\Driver\\Database\\' . $info['driver']; // for core supported database drivers.
if (empty($info['namespace']) || (strpos($info['namespace'], 'Drupal\\Core\\Database\\Driver\\') === 0)) {
switch (strtolower($info['driver'])) {
case 'mysql':
$info['namespace'] = 'Drupal\\mysql\\Driver\\Database\\mysql';
break;
case 'pgsql':
$info['namespace'] = 'Drupal\\pgsql\\Driver\\Database\\pgsql';
break;
case 'sqlite':
$info['namespace'] = 'Drupal\\sqlite\\Driver\\Database\\sqlite';
break;
}
}
// Backwards compatibility layer for Drupal 8 style database connection
// arrays. Those do not have the 'autoload' key set for core database
// drivers.
if (empty($info['autoload'])) {
switch (trim($info['namespace'], '\\')) {
case "Drupal\\mysql\\Driver\\Database\\mysql":
$info['autoload'] = "core/modules/mysql/src/Driver/Database/mysql/";
break;
case "Drupal\\pgsql\\Driver\\Database\\pgsql":
$info['autoload'] = "core/modules/pgsql/src/Driver/Database/pgsql/";
break;
case "Drupal\\sqlite\\Driver\\Database\\sqlite":
$info['autoload'] = "core/modules/sqlite/src/Driver/Database/sqlite/";
break;
}
} }
return $info; return $info;
@ -286,12 +318,28 @@ abstract class Database {
* The database connection information, as defined in settings.php. The * The database connection information, as defined in settings.php. The
* structure of this array depends on the database driver it is connecting * structure of this array depends on the database driver it is connecting
* to. * to.
* @param \Composer\Autoload\ClassLoader $class_loader
* The class loader. Used for adding the database driver to the autoloader
* if $info['autoload'] is set.
* @param string $app_root
* The app root.
* *
* @see \Drupal\Core\Database\Database::setActiveConnection * @see \Drupal\Core\Database\Database::setActiveConnection
*/ */
final public static function addConnectionInfo($key, $target, array $info) { final public static function addConnectionInfo($key, $target, array $info, $class_loader = NULL, $app_root = NULL) {
if (empty(self::$databaseInfo[$key][$target])) { if (empty(self::$databaseInfo[$key][$target])) {
self::$databaseInfo[$key][$target] = self::parseConnectionInfo($info); $info = self::parseConnectionInfo($info);
self::$databaseInfo[$key][$target] = $info;
// If the database driver is provided by a module, then its code may need
// to be instantiated prior to when the module's root namespace is added
// to the autoloader, because that happens during service container
// initialization but the container definition is likely in the database.
// Therefore, allow the connection info to specify an autoload directory
// for the driver.
if (isset($info['autoload']) && $class_loader && $app_root) {
$class_loader->addPsr4($info['namespace'] . '\\', $app_root . '/' . $info['autoload']);
}
} }
} }
@ -324,11 +372,16 @@ abstract class Database {
* @param array $databases * @param array $databases
* A multi-dimensional array specifying database connection parameters, as * A multi-dimensional array specifying database connection parameters, as
* defined in settings.php. * defined in settings.php.
* @param \Composer\Autoload\ClassLoader $class_loader
* The class loader. Used for adding the database driver(s) to the
* autoloader if $databases[$key][$target]['autoload'] is set.
* @param string $app_root
* The app root.
*/ */
final public static function setMultipleConnectionInfo(array $databases) { final public static function setMultipleConnectionInfo(array $databases, $class_loader = NULL, $app_root = NULL) {
foreach ($databases as $key => $targets) { foreach ($databases as $key => $targets) {
foreach ($targets as $target => $info) { foreach ($targets as $target => $info) {
self::addConnectionInfo($key, $target, $info); self::addConnectionInfo($key, $target, $info, $class_loader, $app_root);
} }
} }
} }

View File

@ -160,57 +160,7 @@ final class Settings {
self::handleDeprecations($settings); self::handleDeprecations($settings);
// Initialize databases. // Initialize databases.
foreach ($databases as $key => $targets) { Database::setMultipleConnectionInfo($databases, $class_loader, $app_root);
foreach ($targets as $target => $info) {
// Backwards compatibility layer for Drupal 8 style database connection
// arrays. Those have the wrong 'namespace' key set, or not set at all
// for core supported database drivers.
if (empty($info['namespace']) || (strpos($info['namespace'], 'Drupal\\Core\\Database\\Driver\\') === 0)) {
switch (strtolower($info['driver'])) {
case 'mysql':
$info['namespace'] = 'Drupal\\mysql\\Driver\\Database\\mysql';
break;
case 'pgsql':
$info['namespace'] = 'Drupal\\pgsql\\Driver\\Database\\pgsql';
break;
case 'sqlite':
$info['namespace'] = 'Drupal\\sqlite\\Driver\\Database\\sqlite';
break;
}
}
// Backwards compatibility layer for Drupal 8 style database connection
// arrays. Those do not have the 'autoload' key set for core database
// drivers.
if (empty($info['autoload'])) {
switch (trim($info['namespace'], '\\')) {
case "Drupal\\mysql\\Driver\\Database\\mysql":
$info['autoload'] = "core/modules/mysql/src/Driver/Database/mysql/";
break;
case "Drupal\\pgsql\\Driver\\Database\\pgsql":
$info['autoload'] = "core/modules/pgsql/src/Driver/Database/pgsql/";
break;
case "Drupal\\sqlite\\Driver\\Database\\sqlite":
$info['autoload'] = "core/modules/sqlite/src/Driver/Database/sqlite/";
break;
}
}
Database::addConnectionInfo($key, $target, $info);
// If the database driver is provided by a module, then its code may
// need to be instantiated prior to when the module's root namespace
// is added to the autoloader, because that happens during service
// container initialization but the container definition is likely in
// the database. Therefore, allow the connection info to specify an
// autoload directory for the driver.
if (isset($info['autoload'])) {
$class_loader->addPsr4($info['namespace'] . '\\', $app_root . '/' . $info['autoload']);
}
}
}
// Initialize Settings. // Initialize Settings.
new Settings($settings); new Settings($settings);

View File

@ -2,6 +2,7 @@
namespace Drupal\FunctionalTests; namespace Drupal\FunctionalTests;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Database; use Drupal\Core\Database\Database;
use Drupal\Tests\BrowserTestBase; use Drupal\Tests\BrowserTestBase;
@ -35,6 +36,19 @@ class ExistingDrupal8StyleDatabaseConnectionInSettingsPhpTest extends BrowserTes
$namespace_search = "'namespace' => 'Drupal\\\\$driver\\\\Driver\\\\Database\\\\$driver',"; $namespace_search = "'namespace' => 'Drupal\\\\$driver\\\\Driver\\\\Database\\\\$driver',";
$namespace_replace = "'namespace' => 'Drupal\\\\Core\\\\Database\\\\Driver\\\\$driver',"; $namespace_replace = "'namespace' => 'Drupal\\\\Core\\\\Database\\\\Driver\\\\$driver',";
$contents = str_replace($namespace_search, $namespace_replace, $contents); $contents = str_replace($namespace_search, $namespace_replace, $contents);
// Add a replica connection to the database settings.
$contents .= "\$databases['default']['replica'][] = array (\n";
$contents .= " 'database' => 'db',\n";
$contents .= " 'username' => 'db',\n";
$contents .= " 'password' => 'db',\n";
$contents .= " 'prefix' => 'test22806835',\n";
$contents .= " 'host' => 'db',\n";
$contents .= " 'port' => 3306,\n";
$contents .= " $namespace_replace\n";
$contents .= " 'driver' => 'mysql',\n";
$contents .= ");\n";
file_put_contents($filename, $contents); file_put_contents($filename, $contents);
} }
@ -56,4 +70,14 @@ class ExistingDrupal8StyleDatabaseConnectionInSettingsPhpTest extends BrowserTes
$this->assertStringNotContainsString("'autoload' => 'core/modules/$driver/src/Driver/Database/$driver/", $contents); $this->assertStringNotContainsString("'autoload' => 'core/modules/$driver/src/Driver/Database/$driver/", $contents);
} }
/**
* Confirms that the replica database connection works.
*/
public function testReplicaDrupal8StyleDatabaseConnectionInSettingsPhp() {
$this->drupalLogin($this->drupalCreateUser());
$replica = Database::getConnection('replica', 'default');
$this->assertInstanceOf(Connection::class, $replica);
}
} }