From 145af574f3ea882324c5390796cc44d31f82bb14 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Fri, 10 Jun 2022 00:10:16 +0100
Subject: [PATCH] Issue #3284502 by effulgentsia, alexpott: [regression] Drupal
 9.4 breaks BC via incorrect autoloading of \Drupal\Driver\* overrides of core
 db drivers

---
 core/lib/Drupal/Core/Site/Settings.php        | 30 ++++---
 .../Drupal/Tests/Core/Site/SettingsTest.php   | 80 +++++++++++++++++++
 2 files changed, 98 insertions(+), 12 deletions(-)

diff --git a/core/lib/Drupal/Core/Site/Settings.php b/core/lib/Drupal/Core/Site/Settings.php
index 3998719e8bc9..9e7993ec1e8a 100644
--- a/core/lib/Drupal/Core/Site/Settings.php
+++ b/core/lib/Drupal/Core/Site/Settings.php
@@ -163,38 +163,44 @@ public static function initialize($app_root, $site_path, &$class_loader) {
     foreach ($databases as $key => $targets) {
       foreach ($targets as $target => $info) {
         // 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'])) {
+        // 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['autoload'] = 'core/modules/mysql/src/Driver/Database/mysql/';
+              $info['namespace'] = 'Drupal\\mysql\\Driver\\Database\\mysql';
               break;
 
             case 'pgsql':
-              $info['autoload'] = 'core/modules/pgsql/src/Driver/Database/pgsql/';
+              $info['namespace'] = 'Drupal\\pgsql\\Driver\\Database\\pgsql';
               break;
 
             case 'sqlite':
-              $info['autoload'] = 'core/modules/sqlite/src/Driver/Database/sqlite/';
+              $info['namespace'] = 'Drupal\\sqlite\\Driver\\Database\\sqlite';
               break;
           }
         }
         // 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)) {
+        // arrays. Those do not have the 'autoload' key set for core database
+        // drivers.
+        if (empty($info['autoload'])) {
           switch (strtolower($info['driver'])) {
             case 'mysql':
-              $info['namespace'] = 'Drupal\\mysql\\Driver\\Database\\mysql';
+              if (trim($info['namespace'], '\\') === "Drupal\\mysql\\Driver\\Database\\mysql") {
+                $info['autoload'] = "core/modules/mysql/src/Driver/Database/mysql/";
+              }
               break;
 
             case 'pgsql':
-              $info['namespace'] = 'Drupal\\pgsql\\Driver\\Database\\pgsql';
+              if (trim($info['namespace'], '\\') === "Drupal\\pgsql\\Driver\\Database\\pgsql") {
+                $info['autoload'] = "core/modules/pgsql/src/Driver/Database/pgsql/";
+              }
               break;
 
             case 'sqlite':
-              $info['namespace'] = 'Drupal\\sqlite\\Driver\\Database\\sqlite';
+              if (trim($info['namespace'], '\\') === "Drupal\\sqlite\\Driver\\Database\\sqlite") {
+                $info['autoload'] = "core/modules/sqlite/src/Driver/Database/sqlite/";
+              }
               break;
           }
         }
diff --git a/core/tests/Drupal/Tests/Core/Site/SettingsTest.php b/core/tests/Drupal/Tests/Core/Site/SettingsTest.php
index c192a0199e60..bcc2eda6c9d1 100644
--- a/core/tests/Drupal/Tests/Core/Site/SettingsTest.php
+++ b/core/tests/Drupal/Tests/Core/Site/SettingsTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\Core\Site;
 
+use Composer\Autoload\ClassLoader;
+use Drupal\Core\Database\Database;
 use Drupal\Core\Site\Settings;
 use Drupal\Tests\UnitTestCase;
 use org\bovigo\vfs\vfsStream;
@@ -323,4 +325,82 @@ public function providerTestRealDeprecatedSettings(): array {
     ];
   }
 
+  /**
+   * Tests initialization performed for the $databases variable.
+   *
+   * @dataProvider providerTestDatabaseInfoInitialization
+   */
+  public function testDatabaseInfoInitialization(string $driver, ?string $namespace, ?string $autoload, string $expected_namespace, ?string $expected_autoload): void {
+    $databases['mock'][$driver] = [
+      'driver' => $driver,
+      'prefix' => '',
+    ];
+    if (!is_null($namespace)) {
+      $databases['mock'][$driver]['namespace'] = $namespace;
+    }
+    if (!is_null($autoload)) {
+      $databases['mock'][$driver]['autoload'] = $autoload;
+    }
+    $settings_file_content = "<?php\n\$databases = " . var_export($databases, TRUE) . ";\n";
+
+    $vfs_root = vfsStream::setup('root');
+    $sites_directory = vfsStream::newDirectory('sites')->at($vfs_root);
+    vfsStream::newFile('settings.php')
+      ->at($sites_directory)
+      ->setContent($settings_file_content);
+
+    $class_loader = $this->createMock(ClassLoader::class);
+    if (!empty($expected_autoload)) {
+      $class_loader->expects($this->once())
+        ->method('addPsr4')
+        ->with($expected_namespace . '\\', $expected_autoload);
+    }
+    else {
+      $class_loader->expects($this->never())
+        ->method('addPsr4');
+    }
+
+    Settings::initialize(vfsStream::url('root'), 'sites', $class_loader);
+
+    $expected = [
+      $driver => [
+        'driver' => $driver,
+        'namespace' => $expected_namespace,
+        'prefix' => '',
+      ],
+    ];
+    if (!is_null($expected_autoload)) {
+      $expected[$driver]['autoload'] = $expected_autoload;
+    }
+    $this->assertEquals($expected, Database::getConnectionInfo('mock'));
+  }
+
+  /**
+   * Provides data for testDatabaseInfoInitialization().
+   */
+  public function providerTestDatabaseInfoInitialization(): array {
+    return [
+      ['mysql', NULL, NULL, 'Drupal\\mysql\\Driver\\Database\\mysql', 'core/modules/mysql/src/Driver/Database/mysql/'],
+      ['mysql', '', NULL, 'Drupal\\mysql\\Driver\\Database\\mysql', 'core/modules/mysql/src/Driver/Database/mysql/'],
+      ['mysql', 'Drupal\\Core\\Database\\Driver\\mysql', NULL, 'Drupal\\mysql\\Driver\\Database\\mysql', 'core/modules/mysql/src/Driver/Database/mysql/'],
+      ['mysql', 'Drupal\\mysql\\Driver\\Database\\mysql', NULL, 'Drupal\\mysql\\Driver\\Database\\mysql', 'core/modules/mysql/src/Driver/Database/mysql/'],
+      ['mysql', 'Drupal\\Driver\\Database\\mysql', NULL, 'Drupal\\Driver\\Database\\mysql', NULL],
+      ['mysql', 'Drupal\\mysql\\Driver\\Database\\mysql', 'modules/custom/mysql/src/Driver/Database/mysql/', 'Drupal\\mysql\\Driver\\Database\\mysql', 'modules/custom/mysql/src/Driver/Database/mysql/'],
+
+      ['pgsql', NULL, NULL, 'Drupal\\pgsql\\Driver\\Database\\pgsql', 'core/modules/pgsql/src/Driver/Database/pgsql/'],
+      ['pgsql', '', NULL, 'Drupal\\pgsql\\Driver\\Database\\pgsql', 'core/modules/pgsql/src/Driver/Database/pgsql/'],
+      ['pgsql', 'Drupal\\Core\\Database\\Driver\\pgsql', NULL, 'Drupal\\pgsql\\Driver\\Database\\pgsql', 'core/modules/pgsql/src/Driver/Database/pgsql/'],
+      ['pgsql', 'Drupal\\pgsql\\Driver\\Database\\pgsql', NULL, 'Drupal\\pgsql\\Driver\\Database\\pgsql', 'core/modules/pgsql/src/Driver/Database/pgsql/'],
+      ['pgsql', 'Drupal\\Driver\\Database\\pgsql', NULL, 'Drupal\\Driver\\Database\\pgsql', NULL],
+      ['pgsql', 'Drupal\\pgsql\\Driver\\Database\\pgsql', 'modules/custom/pgsql/src/Driver/Database/pgsql/', 'Drupal\\pgsql\\Driver\\Database\\pgsql', 'modules/custom/pgsql/src/Driver/Database/pgsql/'],
+
+      ['sqlite', NULL, NULL, 'Drupal\\sqlite\\Driver\\Database\\sqlite', 'core/modules/sqlite/src/Driver/Database/sqlite/'],
+      ['sqlite', '', NULL, 'Drupal\\sqlite\\Driver\\Database\\sqlite', 'core/modules/sqlite/src/Driver/Database/sqlite/'],
+      ['sqlite', 'Drupal\\Core\\Database\\Driver\\sqlite', NULL, 'Drupal\\sqlite\\Driver\\Database\\sqlite', 'core/modules/sqlite/src/Driver/Database/sqlite/'],
+      ['sqlite', 'Drupal\\sqlite\\Driver\\Database\\sqlite', NULL, 'Drupal\\sqlite\\Driver\\Database\\sqlite', 'core/modules/sqlite/src/Driver/Database/sqlite/'],
+      ['sqlite', 'Drupal\\Driver\\Database\\sqlite', NULL, 'Drupal\\Driver\\Database\\sqlite', NULL],
+      ['sqlite', 'Drupal\\sqlite\\Driver\\Database\\sqlite', 'modules/custom/sqlite/src/Driver/Database/sqlite/', 'Drupal\\sqlite\\Driver\\Database\\sqlite', 'modules/custom/sqlite/src/Driver/Database/sqlite/'],
+    ];
+  }
+
 }
-- 
GitLab