diff --git a/core/lib/Drupal/Core/Database/Database.php b/core/lib/Drupal/Core/Database/Database.php index 6493c87c5c..3d044127fe 100644 --- a/core/lib/Drupal/Core/Database/Database.php +++ b/core/lib/Drupal/Core/Database/Database.php @@ -238,9 +238,41 @@ abstract class Database { // Prefix information, default to an empty prefix. $info['prefix'] = $info['prefix'] ?? ''; - // Fallback for Drupal 7 settings.php if namespace is not provided. - if (empty($info['namespace'])) { - $info['namespace'] = 'Drupal\\' . $info['driver'] . '\\Driver\\Database\\' . $info['driver']; + // 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; + } } return $info; @@ -268,12 +300,28 @@ abstract class Database { * The database connection information, as defined in settings.php. The * structure of this array depends on the database driver it is connecting * 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 */ - 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])) { - 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']); + } } } @@ -306,11 +354,16 @@ abstract class Database { * @param array $databases * A multi-dimensional array specifying database connection parameters, as * 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 ($targets as $target => $info) { - self::addConnectionInfo($key, $target, $info); + self::addConnectionInfo($key, $target, $info, $class_loader, $app_root); } } } diff --git a/core/lib/Drupal/Core/Site/Settings.php b/core/lib/Drupal/Core/Site/Settings.php index 245bc3537e..aedae4c951 100644 --- a/core/lib/Drupal/Core/Site/Settings.php +++ b/core/lib/Drupal/Core/Site/Settings.php @@ -143,57 +143,7 @@ public static function initialize($app_root, $site_path, &$class_loader) { self::handleDeprecations($settings); // Initialize databases. - foreach ($databases as $key => $targets) { - 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']); - } - } - } + Database::setMultipleConnectionInfo($databases, $class_loader, $app_root); // Initialize Settings. new Settings($settings); diff --git a/core/tests/Drupal/FunctionalTests/ExistingDrupal8StyleDatabaseConnectionInSettingsPhpTest.php b/core/tests/Drupal/FunctionalTests/ExistingDrupal8StyleDatabaseConnectionInSettingsPhpTest.php index 855387f695..3a8a1342d8 100644 --- a/core/tests/Drupal/FunctionalTests/ExistingDrupal8StyleDatabaseConnectionInSettingsPhpTest.php +++ b/core/tests/Drupal/FunctionalTests/ExistingDrupal8StyleDatabaseConnectionInSettingsPhpTest.php @@ -2,6 +2,7 @@ namespace Drupal\FunctionalTests; +use Drupal\Core\Database\Connection; use Drupal\Core\Database\Database; use Drupal\Tests\BrowserTestBase; @@ -35,6 +36,19 @@ protected function setUp(): void { $namespace_search = "'namespace' => 'Drupal\\\\$driver\\\\Driver\\\\Database\\\\$driver',"; $namespace_replace = "'namespace' => 'Drupal\\\\Core\\\\Database\\\\Driver\\\\$driver',"; $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); } @@ -56,4 +70,14 @@ public function testExistingDrupal8StyleDatabaseConnectionInSettingsPhp() { $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); + } + }