diff --git a/core/assets/scaffold/files/default.settings.php b/core/assets/scaffold/files/default.settings.php index c0b18427ae9..0c1b1155d6e 100644 --- a/core/assets/scaffold/files/default.settings.php +++ b/core/assets/scaffold/files/default.settings.php @@ -222,6 +222,27 @@ $databases = []; * 'prefix' => '', * ]; * @endcode + * + * Sample Database configuration format for a driver that is extending another + * database driver. + * @code + * $databases['default']['default'] = [ + * 'driver' => 'my_driver', + * 'namespace' => 'Drupal\my_module\Driver\Database\my_driver', + * 'autoload' => 'modules/my_module/src/Driver/Database/my_driver/', + * 'database' => 'databasename', + * 'username' => 'sqlusername', + * 'password' => 'sqlpassword', + * 'host' => 'localhost', + * 'prefix' => '', + * 'dependencies' => [ + * 'parent_module' => [ + * 'namespace' => 'Drupal\parent_module', + * 'autoload' => 'core/modules/parent_module/src/', + * ], + * ], + * ]; + * @endcode */ /** diff --git a/core/core.services.yml b/core/core.services.yml index 5ae0bc5ad4f..43244f98dd0 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -608,6 +608,10 @@ services: class: Drupal\Core\Extension\ThemeEngineExtensionList arguments: ['%app.root%', 'theme_engine', '@cache.default', '@info_parser', '@module_handler', '@state', '%install_profile%'] Drupal\Core\Extension\ThemeEngineExtensionList: '@extension.list.theme_engine' + extension.list.database_driver: + class: Drupal\Core\Extension\DatabaseDriverList + arguments: ['%app.root%', 'database_driver', '@cache.default'] + Drupal\Core\Extension\DatabaseDriverList: '@extension.list.database_driver' extension.path.resolver: class: Drupal\Core\Extension\ExtensionPathResolver arguments: ['@extension.list.module', '@extension.list.profile', '@extension.list.theme', '@extension.list.theme_engine'] diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index e477a4685d2..0cf78ba206f 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -7,6 +7,7 @@ use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Batch\BatchBuilder; +use Drupal\Core\Cache\NullBackend; use Drupal\Core\Config\ConfigImporter; use Drupal\Core\Config\ConfigImporterException; use Drupal\Core\Config\Importer\ConfigImporterBatch; @@ -15,6 +16,7 @@ use Drupal\Core\Config\StorageComparer; use Drupal\Core\DrupalKernel; use Drupal\Core\Database\Database; use Drupal\Core\Database\DatabaseExceptionWrapper; +use Drupal\Core\Extension\Exception\UnknownExtensionException; use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormState; use Drupal\Core\Installer\Exception\AlreadyInstalledException; @@ -368,9 +370,16 @@ function install_begin_request($class_loader, &$install_state) { ->addArgument(Settings::getInstance()) ->addArgument((new LoggerChannelFactory())->get('file')); + // Register the database driver extension list provider. + $container + ->register('extension.list.database_driver', 'Drupal\Core\Extension\DatabaseDriverList') + ->addArgument(dirname(__DIR__, 2)) + ->addArgument('database_driver') + ->addArgument(new NullBackend('database_driver')); + // Register the class loader so contrib and custom database drivers can be // autoloaded. - // @see drupal_get_database_types() + // @see \Drupal\Core\Extension\DatabaseDriverList $container->set('class_loader', $class_loader); \Drupal::setContainer($container); @@ -960,7 +969,16 @@ function install_get_form($form_id, array &$install_state) { // values taken from the installation state. $install_form_id = $form_builder->getFormId($form_id, $form_state); if (!empty($install_state['forms'][$install_form_id])) { - $form_state->setValues($install_state['forms'][$install_form_id]); + $values = $install_state['forms'][$install_form_id]; + if ($install_form_id === 'install_settings_form' && !str_contains($values['driver'], "\\")) { + @trigger_error("Passing a database driver name '{$values['driver']}' to " . __FUNCTION__ . '() is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Pass a database driver namespace instead. See https://www.drupal.org/node/3258175', E_USER_DEPRECATED); + $driverExtension = Database::getDriverList()->getFromDriverName($values['driver']); + $tmp = []; + $tmp['driver'] = $driverExtension->getName(); + $tmp[$driverExtension->getName()] = $values[$values['driver']]; + $values = $tmp; + } + $form_state->setValues($values); } $form_builder->submitForm($form_id, $form_state); @@ -1184,15 +1202,10 @@ function install_verify_database_ready() { function install_database_errors($database, $settings_file) { $errors = []; - // Check database type. - $database_types = drupal_get_database_types(); - $driver = $database['driver']; - if (!isset($database_types[$driver])) { - $errors['driver'] = t("In your %settings_file file you have configured @drupal to use a %driver server, however your PHP installation currently does not support this database type.", ['%settings_file' => $settings_file, '@drupal' => drupal_install_profile_distribution_name(), '%driver' => $driver]); - } - else { + try { + $driverExtension = Database::getDriverList()->get($database['namespace']); // Run driver specific validation - $errors += $database_types[$driver]->validateDatabaseSettings($database); + $errors = $driverExtension->getInstallTasks()->validateDatabaseSettings($database); if (!empty($errors)) { // No point to try further. return $errors; @@ -1200,8 +1213,10 @@ function install_database_errors($database, $settings_file) { // Run tasks associated with the database type. Any errors are caught in the // calling function. Database::addConnectionInfo('default', 'default', $database); - $installer_class = $database['namespace'] . "\\Install\\Tasks"; - $errors = (new $installer_class())->runTasks(); + $errors = $driverExtension->getInstallTasks()->runTasks(); + } + catch (UnknownExtensionException $e) { + $errors['driver'] = t("In your %settings_file file you have configured @drupal to use a %driver server, however your PHP installation currently does not support this database type.", ['%settings_file' => $settings_file, '@drupal' => drupal_install_profile_distribution_name(), '%driver' => $database['driver']]); } return $errors; } diff --git a/core/includes/install.inc b/core/includes/install.inc index 588855a3dc9..1bf8c485c03 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -146,8 +146,14 @@ function drupal_install_profile_distribution_version() { * * @return array * An array of database types compiled into PHP. + * + * @deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use + * DatabaseDriverList::getList() instead. + * + * @see https://www.drupal.org/node/3258175 */ function drupal_detect_database_types() { + @trigger_error('drupal_detect_database_types() is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use DatabaseDriverList::getList() instead. See https://www.drupal.org/node/3258175', E_USER_DEPRECATED); $databases = drupal_get_database_types(); foreach ($databases as $driver => $installer) { @@ -162,8 +168,14 @@ function drupal_detect_database_types() { * * @return \Drupal\Core\Database\Install\Tasks[] * An array of available database driver installer objects. + * + * @deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use + * DatabaseDriverList::getList() instead. + * + * @see https://www.drupal.org/node/3258175 */ function drupal_get_database_types() { + @trigger_error('drupal_get_database_types() is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use DatabaseDriverList::getList() instead. See https://www.drupal.org/node/3258175', E_USER_DEPRECATED); $databases = []; $drivers = []; diff --git a/core/lib/Drupal/Core/Database/Database.php b/core/lib/Drupal/Core/Database/Database.php index ac37df511d1..c641ac203de 100644 --- a/core/lib/Drupal/Core/Database/Database.php +++ b/core/lib/Drupal/Core/Database/Database.php @@ -5,7 +5,8 @@ namespace Drupal\Core\Database; use Composer\Autoload\ClassLoader; use Drupal\Core\Database\Event\StatementExecutionEndEvent; use Drupal\Core\Database\Event\StatementExecutionStartEvent; -use Drupal\Core\Extension\ExtensionDiscovery; +use Drupal\Core\Extension\DatabaseDriverList; +use Drupal\Core\Cache\NullBackend; /** * Primary front-controller for the database system. @@ -326,6 +327,18 @@ abstract class Database { // for the driver. if (isset($info['autoload']) && $class_loader && $app_root) { $class_loader->addPsr4($info['namespace'] . '\\', $app_root . '/' . $info['autoload']); + + // When the database driver is extending from other database drivers, + // then add autoload directory for the parent database driver modules + // as well. + if (!empty($info['dependencies'])) { + assert(is_array($info['dependencies'])); + foreach ($info['dependencies'] as $dependency) { + if (isset($dependency['namespace']) && isset($dependency['autoload'])) { + $class_loader->addPsr4($dependency['namespace'] . '\\', $app_root . '/' . $dependency['autoload']); + } + } + } } } } @@ -534,59 +547,79 @@ abstract class Database { if (preg_match('/^(.*):\/\//', $url, $matches) !== 1) { throw new \InvalidArgumentException("Missing scheme in URL '$url'"); } - $driver = $matches[1]; + $driverName = $matches[1]; // Determine if the database driver is provided by a module. // @todo https://www.drupal.org/project/drupal/issues/3250999. Refactor when // all database drivers are provided by modules. - $module = NULL; - $connection_class = NULL; $url_components = parse_url($url); $url_component_query = $url_components['query'] ?? ''; parse_str($url_component_query, $query); // Add the module key for core database drivers when the module key is not // set. - if (!isset($query['module']) && in_array($driver, ['mysql', 'pgsql', 'sqlite'], TRUE)) { - $query['module'] = $driver; + if (!isset($query['module']) && in_array($driverName, ['mysql', 'pgsql', 'sqlite'], TRUE)) { + $query['module'] = $driverName; + } + if (!isset($query['module'])) { + throw new \InvalidArgumentException("Can not convert '$url' to a database connection, the module providing the driver '{$driverName}' is not specified"); } - if (isset($query['module']) && $query['module']) { - $module = $query['module']; - // Set up an additional autoloader. We don't use the main autoloader as - // this method can be called before Drupal is installed and is never - // called during regular runtime. - $namespace = "Drupal\\$module\\Driver\\Database\\$driver"; - $psr4_base_directory = Database::findDriverAutoloadDirectory($namespace, $root, $include_test_drivers); - $additional_class_loader = new ClassLoader(); - $additional_class_loader->addPsr4($namespace . '\\', $psr4_base_directory); - $additional_class_loader->register(TRUE); - $connection_class = $namespace . '\\Connection'; - } + $driverNamespace = "Drupal\\{$query['module']}\\Driver\\Database\\{$driverName}"; - if (!$module) { - // Determine the connection class to use. Discover if the URL has a valid - // driver scheme for a Drupal 8 style custom driver. - // @todo Remove this in Drupal 10. - $connection_class = "Drupal\\Driver\\Database\\{$driver}\\Connection"; - } + /** @var \Drupal\Core\Extension\DatabaseDriver $driver */ + $driver = self::getDriverList() + ->includeTestDrivers($include_test_drivers) + ->get($driverNamespace); + // Set up an additional autoloader. We don't use the main autoloader as + // this method can be called before Drupal is installed and is never + // called during regular runtime. + $additional_class_loader = new ClassLoader(); + $additional_class_loader->addPsr4($driverNamespace . '\\', $driver->getPath()); + $connection_class = $driverNamespace . '\\Connection'; if (!class_exists($connection_class)) { throw new \InvalidArgumentException("Can not convert '$url' to a database connection, class '$connection_class' does not exist"); } + // When the database driver is extending another database driver, then + // add autoload info for the parent database driver as well. + $autoloadInfo = $driver->getAutoloadInfo(); + if (isset($autoloadInfo['dependencies'])) { + foreach ($autoloadInfo['dependencies'] as $dependency) { + $additional_class_loader->addPsr4($dependency['namespace'] . '\\', $dependency['autoload']); + } + } + + $additional_class_loader->register(TRUE); + $options = $connection_class::createConnectionOptionsFromUrl($url, $root); - // If the driver is provided by a module add the necessary information to - // autoload the code. + // Add the necessary information to autoload code. // @see \Drupal\Core\Site\Settings::initialize() - if (isset($psr4_base_directory)) { - $options['autoload'] = $psr4_base_directory; + $options['autoload'] = $driver->getPath() . DIRECTORY_SEPARATOR; + if (isset($autoloadInfo['dependencies'])) { + $options['dependencies'] = $autoloadInfo['dependencies']; } return $options; } + /** + * Returns the list provider for available database drivers. + * + * @return \Drupal\Core\Extension\DatabaseDriverList + * The list provider for available database drivers. + */ + public static function getDriverList(): DatabaseDriverList { + if (\Drupal::hasContainer() && \Drupal::hasService('extension.list.database_driver')) { + return \Drupal::service('extension.list.database_driver'); + } + else { + return new DatabaseDriverList(DRUPAL_ROOT, 'database_driver', new NullBackend('database_driver')); + } + } + /** * Finds the directory to add to the autoloader for the driver's namespace. * @@ -641,33 +674,19 @@ abstract class Database { * * @throws \RuntimeException * Exception thrown when a module provided database driver does not exist. + * + * @deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use + * DatabaseDriverList::getList() instead. + * + * @see https://www.drupal.org/node/3258175 */ public static function findDriverAutoloadDirectory($namespace, $root, ?bool $include_test_drivers = NULL) { - // As explained by this method's documentation, return FALSE if the - // namespace is not a sub-namespace of a Drupal module. - if (!static::isWithinModuleNamespace($namespace)) { - return FALSE; - } - - // Extract the module information from the namespace. - [, $module, $module_relative_namespace] = explode('\\', $namespace, 3); - - // The namespace is within a Drupal module. Find the directory where the - // module is located. - $extension_discovery = new ExtensionDiscovery($root, FALSE, []); - $modules = $extension_discovery->scan('module', $include_test_drivers); - if (!isset($modules[$module])) { - throw new \RuntimeException(sprintf("Cannot find the module '%s' for the database driver namespace '%s'", $module, $namespace)); - } - $module_directory = $modules[$module]->getPath(); - - // All code within the Drupal\MODULE namespace is expected to follow a - // PSR-4 layout within the module's "src" directory. - $driver_directory = $module_directory . '/src/' . str_replace('\\', '/', $module_relative_namespace) . '/'; - if (!is_dir($root . '/' . $driver_directory)) { - throw new \RuntimeException(sprintf("Cannot find the database driver namespace '%s' in module '%s'", $namespace, $module)); - } - return $driver_directory; + @trigger_error(__METHOD__ . '() is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use DatabaseDriverList::getList() instead. See https://www.drupal.org/node/3258175', E_USER_DEPRECATED); + $autoload_info = static::getDriverList() + ->includeTestDrivers($include_test_drivers) + ->get($namespace) + ->getAutoloadInfo(); + return $autoload_info['autoload'] ?? FALSE; } /** @@ -713,9 +732,9 @@ abstract class Database { * TRUE if the passed in namespace is a sub-namespace of a Drupal module's * namespace. * - * @todo https://www.drupal.org/project/drupal/issues/3125476 Remove if we - * add this to the extension API or if - * \Drupal\Core\Database\Database::getConnectionInfoAsUrl() is removed. + * @todo remove in Drupal 11. + * + * @see https://www.drupal.org/node/3256524 */ private static function isWithinModuleNamespace(string $namespace) { [$first, $second] = explode('\\', $namespace, 3); diff --git a/core/lib/Drupal/Core/Extension/DatabaseDriver.php b/core/lib/Drupal/Core/Extension/DatabaseDriver.php new file mode 100644 index 00000000000..4e134c1a7a3 --- /dev/null +++ b/core/lib/Drupal/Core/Extension/DatabaseDriver.php @@ -0,0 +1,242 @@ +root = $root; + $this->type = 'database_driver'; + } + + /** + * Returns the Extension object of the module containing the database driver. + * + * @return \Drupal\Core\Extension\Extension + * The Extension object of the module containing the database driver. + */ + public function getModule(): Extension { + return $this->module; + } + + /** + * Returns the name of the database driver. + * + * @return string + * The name of the database driver. + */ + public function getDriverName(): string { + return $this->driverName; + } + + /** + * Returns the PHP namespace of the database driver. + * + * @return string + * The PHP namespace of the database driver. + */ + public function getNamespace(): string { + return "Drupal\\" . $this->getModule()->getName() . "\\Driver\\Database\\" . $this->getDriverName(); + } + + /** + * {@inheritdoc} + */ + public function getName() { + return $this->getNamespace(); + } + + /** + * {@inheritdoc} + */ + public function getPath() { + return $this->getModule()->getPath() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Driver' . DIRECTORY_SEPARATOR . 'Database' . DIRECTORY_SEPARATOR . $this->getDriverName(); + } + + /** + * {@inheritdoc} + */ + public function load() { + if (!isset($this->classLoader)) { + $this->classLoader = \Drupal::service('class_loader'); + $this->classLoader->addPsr4($this->getNamespace() . '\\', $this->getPath()); + foreach (($this->getAutoloadInfo()['dependencies'] ?? []) as $dependency) { + $this->classLoader->addPsr4($dependency['namespace'] . '\\', $dependency['autoload']); + } + } + return TRUE; + } + + /** + * Returns the install tasks object instance of this database driver. + * + * @return \Drupal\Core\Database\Install\Tasks + * The install tasks object instance. + */ + public function getInstallTasks(): Tasks { + if (!isset($this->installTasks)) { + $this->load(); + $installTasksClass = $this->getNamespace() . "\\Install\\Tasks"; + $this->installTasks = new $installTasksClass(); + } + return $this->installTasks; + } + + // phpcs:disable + /** + * Returns an array with the driver's autoload information. + * + * The module that provides the database driver should add the driver's + * namespace to Composer's autoloader. However, since the database connection + * must be established before Drupal adds the module's entire namespace to the + * autoloader, the database connection info array includes an "autoload" key + * containing the autoload directory for the driver's namespace. For requests + * that connect to the database via a connection info array, the value of the + * "autoload" key is automatically added to the autoloader. + * + * This method can be called to find the default value of that key when the + * database connection info array isn't available. This includes: + * - Console commands and test runners that connect to a database specified + * by a database URL rather than a connection info array. + * - During installation, prior to the connection info array being written to + * settings.php. + * + * This method returns an array with the driver's namespace and autoload + * directory that must be added to the autoloader, as well as those of any + * dependency specified in the driver's module.info.yml file, in the format + * @code + * [ + * 'autoload' => 'path_to_modules/module_a/src/Driver/Database/driver_1/', + * 'namespace' => 'Drupal\\module_a\\Driver\\Database\\driver_1', + * 'dependencies' => [ + * 'module_x' => [ + * 'autoload' => 'path_to_modules/module_x/src/', + * 'namespace' => 'Drupal\\module_x', + * ], + * ], + * ] + * @endcode + * + * @return array{ + * 'autoload': string, + * 'namespace': string, + * 'dependencies': array, + * } + */ + // phpcs:enable + public function getAutoloadInfo(): array { + $this->getModuleInfo(); + + $autoloadInfo = [ + 'namespace' => $this->getNamespace(), + 'autoload' => $this->getPath() . DIRECTORY_SEPARATOR, + ]; + + foreach (($this->info['dependencies'] ?? []) as $dependency) { + $dependencyData = Dependency::createFromString($dependency); + $dependencyName = $dependencyData->getName(); + if (empty($this->discoveredModules[$dependencyName])) { + throw new \RuntimeException(sprintf("Cannot find the module '%s' that is required by module '%s'", $dependencyName, $this->getModule()->getName())); + } + $autoloadInfo['dependencies'][$dependencyName] = [ + 'namespace' => "Drupal\\{$dependencyName}", + 'autoload' => $this->discoveredModules[$dependencyName]->getPath() . '/src/', + ]; + } + + return $autoloadInfo; + } + + /** + * {@inheritdoc} + */ + public function isExperimental(): bool { + $this->getModuleInfo(); + return parent::isExperimental(); + } + + /** + * {@inheritdoc} + */ + public function isObsolete(): bool { + $this->getModuleInfo(); + return parent::isObsolete(); + } + + /** + * Gets the content of the info.yml file of the driver's module, as an array. + * + * The info array is saved in the $info property. + * + * @throws \Drupal\Core\Extension\InfoParserException + * Exception thrown if there is a parsing error or the .info.yml file does + * not contain a required key. + */ + private function getModuleInfo(): void { + if (!isset($this->info)) { + $infoParser = new InfoParser($this->root); + $this->info = $infoParser->parse($this->root . DIRECTORY_SEPARATOR . $this->getModule()->getPathname()); + } + } + + /** + * {@inheritdoc} + */ + public function getPathname() { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + + /** + * {@inheritdoc} + */ + public function getFilename() { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + + /** + * {@inheritdoc} + */ + public function getExtensionPathname() { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + + /** + * {@inheritdoc} + */ + public function getExtensionFilename() { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + +} diff --git a/core/lib/Drupal/Core/Extension/DatabaseDriverList.php b/core/lib/Drupal/Core/Extension/DatabaseDriverList.php new file mode 100644 index 00000000000..3126faf2e35 --- /dev/null +++ b/core/lib/Drupal/Core/Extension/DatabaseDriverList.php @@ -0,0 +1,260 @@ +root = $root; + $this->type = $type; + $this->cache = $cache; + } + + /** + * Determines whether test drivers shall be included in the discovery. + * + * @param bool|null $includeTestDrivers + * Whether to include test extensions. If FALSE, all 'tests' directories + * are excluded in the search. If NULL, it will be determined by the + * 'extension_discovery_scan_tests' setting. + * + * @return $this + */ + public function includeTestDrivers(?bool $includeTestDrivers): static { + $this->includeTestDrivers = $includeTestDrivers; + return $this; + } + + /** + * {@inheritdoc} + */ + protected function getExtensionDiscovery() { + return new ExtensionDiscovery($this->root, FALSE); + } + + /** + * {@inheritdoc} + */ + protected function doScanExtensions() { + return $this->getExtensionDiscovery()->scan('module', $this->includeTestDrivers); + } + + /** + * {@inheritdoc} + */ + protected function doList(): array { + // Determine the modules that contain at least one installable database + // driver. + $discoveredModules = $this->doScanExtensions(); + $drivers = []; + foreach ($discoveredModules as $module) { + $moduleDriverDirectory = $this->root . DIRECTORY_SEPARATOR . $module->getPath() . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Driver' . DIRECTORY_SEPARATOR . 'Database'; + if (is_dir($moduleDriverDirectory)) { + // Use directory iterator to avoid services. + $directoryIterator = new \DirectoryIterator($moduleDriverDirectory); + foreach ($directoryIterator as $fileInfo) { + if ($fileInfo->isDir() && !$fileInfo->isDot() && file_exists($moduleDriverDirectory . DIRECTORY_SEPARATOR . $fileInfo->getFilename() . DIRECTORY_SEPARATOR . 'Install' . DIRECTORY_SEPARATOR . 'Tasks.php')) { + $databaseDriver = new DatabaseDriver($this->root, $module, $fileInfo->getFilename(), $discoveredModules); + $drivers[$databaseDriver->getName()] = $databaseDriver; + } + } + } + } + return $drivers; + } + + /** + * Returns the list of installable database drivers. + * + * @return \Drupal\Core\Extension\DatabaseDriver[] + * An array of installable database driver extension objects. + */ + public function getInstallableList(): array { + $installableDrivers = []; + foreach ($this->getList() as $name => $driver) { + if ($driver->getInstallTasks()->installable()) { + $installableDrivers[$name] = $driver; + } + } + // Usability: unconditionally put core MySQL driver on top. + if (isset($installableDrivers[static::CORE_MYSQL_DRIVER_NAMESPACE])) { + $mysqlDriver = $installableDrivers[static::CORE_MYSQL_DRIVER_NAMESPACE]; + unset($installableDrivers[static::CORE_MYSQL_DRIVER_NAMESPACE]); + $installableDrivers = [static::CORE_MYSQL_DRIVER_NAMESPACE => $mysqlDriver] + $installableDrivers; + } + return $installableDrivers; + } + + /** + * {@inheritdoc} + */ + public function getName($extension_name) { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + + /** + * {@inheritdoc} + */ + public function get($extension_name) { + if (!str_contains($extension_name, "\\")) { + @trigger_error("Passing a database driver name '{$extension_name}' to " . __METHOD__ . '() is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Pass a database driver namespace instead. See https://www.drupal.org/node/3258175', E_USER_DEPRECATED); + return $this->getFromDriverName($extension_name); + } + return parent::get($extension_name); + } + + /** + * Returns the first available driver extension by the driver name. + * + * @param string $driverName + * The database driver name. + * + * @return \Drupal\Core\Extension\DatabaseDriver + * The driver extension. + * + * @throws \Drupal\Core\Extension\Exception\UnknownExtensionException + * When no matching driver extension can be found. + * + * @deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use + * DatabaseDriverList::get() instead, passing a database driver namespace. + * + * @see https://www.drupal.org/node/3258175 + */ + public function getFromDriverName(string $driverName): DatabaseDriver { + @trigger_error(__METHOD__ . '() is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use DatabaseDriverList::get() instead, passing a database driver namespace. See https://www.drupal.org/node/3258175', E_USER_DEPRECATED); + foreach ($this->getList() as $extensionName => $driver) { + $namespaceParts = explode('\\', $extensionName); + if (end($namespaceParts) === $driverName) { + return parent::get($extensionName); + } + } + throw new UnknownExtensionException("Could not find a database driver named '{$driverName}' in any module"); + } + + /** + * {@inheritdoc} + */ + public function getExtensionInfo($extension_name) { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + + /** + * {@inheritdoc} + */ + public function getAllAvailableInfo() { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + + /** + * {@inheritdoc} + */ + protected function getInstalledExtensionNames() { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + + /** + * {@inheritdoc} + */ + public function getAllInstalledInfo() { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + + /** + * {@inheritdoc} + */ + protected function recalculateInfo() { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + + /** + * {@inheritdoc} + */ + public function getPathnames() { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + + /** + * {@inheritdoc} + */ + protected function recalculatePathnames() { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + + /** + * {@inheritdoc} + */ + public function setPathname($extension_name, $pathname) { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + + /** + * {@inheritdoc} + */ + public function getPathname($extension_name) { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + + /** + * {@inheritdoc} + */ + public function getPath($extension_name) { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + + /** + * {@inheritdoc} + */ + protected function createExtensionInfo(Extension $extension) { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + + /** + * {@inheritdoc} + */ + public function checkIncompatibility($name) { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + + /** + * {@inheritdoc} + */ + public static function sortByName(Extension $a, Extension $b): int { + throw new \LogicException(__METHOD__ . '() is not implemented'); + } + +} diff --git a/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php b/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php index 32e20511911..aee13ec2d8e 100644 --- a/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php +++ b/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php @@ -228,6 +228,12 @@ class ExtensionDiscovery { */ public function setProfileDirectoriesFromSettings() { $this->profileDirectories = []; + // This method may be called by the database system early in bootstrap + // before the container is initialized. In that case, the parameter is not + // accessible yet, hence return. + if (!\Drupal::hasContainer() || !\Drupal::getContainer()->hasParameter('install_profile')) { + return $this; + } if ($profile = \Drupal::installProfile()) { $this->profileDirectories[] = \Drupal::service('extension.list.profile')->getPath($profile); } diff --git a/core/lib/Drupal/Core/Extension/ExtensionList.php b/core/lib/Drupal/Core/Extension/ExtensionList.php index 6f9e95bbbe3..f12c9a61f83 100644 --- a/core/lib/Drupal/Core/Extension/ExtensionList.php +++ b/core/lib/Drupal/Core/Extension/ExtensionList.php @@ -21,7 +21,9 @@ use Drupal\Core\State\StateInterface; abstract class ExtensionList { /** - * The type of the extension: "module", "theme" or "profile". + * The type of the extension. + * + * Possible values: "module", "theme", "profile" or "database_driver". * * @var string */ diff --git a/core/lib/Drupal/Core/Installer/Form/SiteSettingsForm.php b/core/lib/Drupal/Core/Installer/Form/SiteSettingsForm.php index 739d6177414..e3555e05b04 100644 --- a/core/lib/Drupal/Core/Installer/Form/SiteSettingsForm.php +++ b/core/lib/Drupal/Core/Installer/Form/SiteSettingsForm.php @@ -4,6 +4,7 @@ namespace Drupal\Core\Installer\Form; use Drupal\Component\Utility\Crypt; use Drupal\Core\Database\Database; +use Drupal\Core\Extension\DatabaseDriverList; use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; @@ -19,31 +20,20 @@ use Symfony\Component\DependencyInjection\ContainerInterface; */ class SiteSettingsForm extends FormBase { - /** - * The site path. - * - * @var string - */ - protected $sitePath; - - /** - * The renderer. - * - * @var \Drupal\Core\Render\RendererInterface - */ - protected $renderer; - /** * Constructs a new SiteSettingsForm. * - * @param string $site_path + * @param string $sitePath * The site path. * @param \Drupal\Core\Render\RendererInterface $renderer * The renderer. + * @param \Drupal\Core\Extension\DatabaseDriverList $databaseDriverList + * The list provider of database drivers. */ - public function __construct($site_path, RendererInterface $renderer) { - $this->sitePath = $site_path; - $this->renderer = $renderer; + public function __construct( + protected string $sitePath, + protected RendererInterface $renderer, + protected DatabaseDriverList $databaseDriverList) { } /** @@ -52,7 +42,8 @@ class SiteSettingsForm extends FormBase { public static function create(ContainerInterface $container) { return new static( $container->getParameter('site.path'), - $container->get('renderer') + $container->get('renderer'), + $container->get('extension.list.database_driver') ); } @@ -73,20 +64,24 @@ class SiteSettingsForm extends FormBase { $form['#title'] = $this->t('Database configuration'); - $drivers = drupal_get_database_types(); + $drivers = $this->databaseDriverList->getInstallableList(); $drivers_keys = array_keys($drivers); // Unless there is input for this form (for a non-interactive installation, // input originates from the $settings array passed into install_drupal()), // check whether database connection settings have been prepared in // settings.php already. + // Since there could potentially be multiple drivers with the same name, + // provided by different modules, we fill the 'driver' form field with the + // driver's namespace, not with the driver name, so to ensure uniqueness of + // the selection. // Note: The installer even executes this form if there is a valid database // connection already, since the submit handler of this form is responsible // for writing all $settings to settings.php (not limited to $databases). $input = &$form_state->getUserInput(); if (!isset($input['driver']) && $database = Database::getConnectionInfo()) { - $input['driver'] = $database['default']['driver']; - $input[$database['default']['driver']] = $database['default']; + $input['driver'] = $database['default']['namespace']; + $input[$database['default']['namespace']] = $database['default']; } if (isset($input['driver'])) { @@ -121,10 +116,9 @@ class SiteSettingsForm extends FormBase { // Add driver specific configuration options. foreach ($drivers as $key => $driver) { - $form['driver']['#options'][$key] = $driver->name(); - - $form['settings'][$key] = $driver->getFormOptions($default_options); - $form['settings'][$key]['#prefix'] = '

' . $this->t('@driver_name settings', ['@driver_name' => $driver->name()]) . '

'; + $form['driver']['#options'][$key] = $driver->getInstallTasks()->name(); + $form['settings'][$key] = $driver->getInstallTasks()->getFormOptions($default_options); + $form['settings'][$key]['#prefix'] = '

' . $this->t('@driver_name settings', ['@driver_name' => $driver->getInstallTasks()->name()]) . '

'; $form['settings'][$key]['#type'] = 'container'; $form['settings'][$key]['#tree'] = TRUE; $form['settings'][$key]['advanced_options']['#parents'] = [$key]; @@ -162,18 +156,12 @@ class SiteSettingsForm extends FormBase { $driver = $form_state->getValue('driver'); $database = $form_state->getValue($driver); - $drivers = drupal_get_database_types(); - $reflection = new \ReflectionClass($drivers[$driver]); - $install_namespace = $reflection->getNamespaceName(); - // Cut the trailing \Install from namespace. - $database['namespace'] = substr($install_namespace, 0, strrpos($install_namespace, '\\')); + $database['driver'] = $driver; - // See default.settings.php for an explanation of the 'autoload' key. - if ($autoload = Database::findDriverAutoloadDirectory($database['namespace'], DRUPAL_ROOT)) { - $database['autoload'] = $autoload; - } + $database = array_merge($database, $this->databaseDriverList->get($driver)->getAutoloadInfo()); $form_state->set('database', $database); + foreach ($this->getDatabaseErrors($database, $form_state->getValue('settings_file')) as $name => $message) { $form_state->setErrorByName($name, $message); } @@ -246,11 +234,17 @@ class SiteSettingsForm extends FormBase { // Update global settings array and save. $settings = []; + + // For BC, just save the database driver name, not the database driver + // extension name which equals the driver's namespace. $database = $form_state->get('database'); + $namespaceParts = explode('\\', $database['driver']); + $database['driver'] = end($namespaceParts); $settings['databases']['default']['default'] = (object) [ 'value' => $database, 'required' => TRUE, ]; + $settings['settings']['hash_salt'] = (object) [ 'value' => Crypt::randomBytesBase64(55), 'required' => TRUE, diff --git a/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php b/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php index 8847857fb50..c4014bf27bd 100644 --- a/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php +++ b/core/lib/Drupal/Core/Test/FunctionalTestSetupTrait.php @@ -503,21 +503,24 @@ trait FunctionalTestSetupTrait { * Array of parameters for use in install_drupal(). */ protected function installParameters() { - $connection_info = Database::getConnectionInfo(); - $driver = $connection_info['default']['driver']; - unset($connection_info['default']['driver']); - unset($connection_info['default']['namespace']); - unset($connection_info['default']['autoload']); - unset($connection_info['default']['pdo']); - unset($connection_info['default']['init_commands']); - unset($connection_info['default']['isolation_level']); + $formInput = Database::getConnectionInfo()['default']; + $driverName = $formInput['driver']; + $driverNamespace = $formInput['namespace']; + + unset($formInput['driver']); + unset($formInput['namespace']); + unset($formInput['autoload']); + unset($formInput['pdo']); + unset($formInput['init_commands']); + unset($formInput['isolation_level']); // Remove database connection info that is not used by SQLite. - if ($driver === 'sqlite') { - unset($connection_info['default']['username']); - unset($connection_info['default']['password']); - unset($connection_info['default']['host']); - unset($connection_info['default']['port']); + if ($driverName === "sqlite") { + unset($formInput['username']); + unset($formInput['password']); + unset($formInput['host']); + unset($formInput['port']); } + $parameters = [ 'interactive' => FALSE, 'parameters' => [ @@ -526,8 +529,8 @@ trait FunctionalTestSetupTrait { ], 'forms' => [ 'install_settings_form' => [ - 'driver' => $driver, - $driver => $connection_info['default'], + 'driver' => $driverNamespace, + $driverNamespace => $formInput, ], 'install_configure_form' => [ 'site_name' => 'Drupal', @@ -550,7 +553,6 @@ trait FunctionalTestSetupTrait { ]; // If we only have one db driver available, we cannot set the driver. - include_once DRUPAL_ROOT . '/core/includes/install.inc'; if (count($this->getDatabaseTypes()) == 1) { unset($parameters['forms']['install_settings_form']['driver']); } @@ -687,7 +689,8 @@ trait FunctionalTestSetupTrait { /** * Returns all supported database driver installer objects. * - * This wraps drupal_get_database_types() for use without a current container. + * This wraps DatabaseDriverList::getInstallableList() for use without a + * current container. * * @return \Drupal\Core\Database\Install\Tasks[] * An array of available database driver installer objects. @@ -696,7 +699,10 @@ trait FunctionalTestSetupTrait { if (isset($this->originalContainer) && $this->originalContainer) { \Drupal::setContainer($this->originalContainer); } - $database_types = drupal_get_database_types(); + $database_types = []; + foreach (Database::getDriverList()->getInstallableList() as $name => $driver) { + $database_types[$name] = $driver->getInstallTasks(); + } if (isset($this->originalContainer) && $this->originalContainer) { \Drupal::unsetContainer(); } diff --git a/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php b/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php index 87e57604bb8..5e70200feab 100644 --- a/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php +++ b/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php @@ -172,7 +172,7 @@ class CredentialForm extends MigrateUpgradeFormBase { ':input[name=driver]' => ['value' => $key], ], ]; - if ($key != 'sqlite') { + if (!str_ends_with($key, '\\sqlite')) { $form['database']['settings'][$key]['username']['#states'] = [ 'required' => [ ':input[name=source_connection]' => ['value' => ''], @@ -395,7 +395,11 @@ class CredentialForm extends MigrateUpgradeFormBase { protected function getDatabaseTypes() { // Make sure the install API is available. include_once DRUPAL_ROOT . '/core/includes/install.inc'; - return drupal_get_database_types(); + $database_types = []; + foreach (Database::getDriverList()->getInstallableList() as $name => $driver) { + $database_types[$name] = $driver->getInstallTasks(); + } + return $database_types; } /** diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/CredentialFormTest.php b/core/modules/migrate_drupal_ui/tests/src/Functional/CredentialFormTest.php index f0183917f1a..275efc0a1a3 100644 --- a/core/modules/migrate_drupal_ui/tests/src/Functional/CredentialFormTest.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/CredentialFormTest.php @@ -4,6 +4,8 @@ namespace Drupal\Tests\migrate_drupal_ui\Functional; use Drupal\Tests\migrate_drupal\Traits\CreateTestContentEntitiesTrait; +// cspell:ignore drupalmysqldriverdatabasemysql + /** * Test the credential form for both Drupal 6 and Drupal 7 sources. * @@ -40,7 +42,7 @@ class CredentialFormTest extends MigrateUpgradeTestBase { $this->submitForm([], 'Continue'); $session->pageTextContains('Provide credentials for the database of the Drupal site you want to upgrade.'); - $session->fieldExists('mysql[host]'); + $session->fieldExists('edit-drupalmysqldriverdatabasemysql-host'); // Ensure submitting the form with invalid database credentials gives us a // nice warning. diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php index 0c19b7e564d..85cfbcfbe61 100644 --- a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php @@ -298,8 +298,8 @@ abstract class MigrateUpgradeTestBase extends BrowserTestBase { // Use the driver connection form to get the correct options out of the // database settings. This supports all of the databases we test against. - $drivers = drupal_get_database_types(); - $form = $drivers[$driver]->getFormOptions($connection_options); + $drivers = Database::getDriverList()->getInstallableList(); + $form = $drivers[$driver]->getInstallTasks()->getFormOptions($connection_options); $connection_options = array_intersect_key($connection_options, $form + $form['advanced_options']); // Remove isolation_level since that option is not configurable in the UI. unset($connection_options['isolation_level']); diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/FilePathTest.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/FilePathTest.php index 332b621dc9a..d4c797086b3 100644 --- a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/FilePathTest.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/FilePathTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\migrate_drupal_ui\Functional\d7; +use Drupal\Core\Database\Database; use Drupal\Core\File\FileSystemInterface; use Drupal\Tests\ExtensionListTestTrait; use Drupal\Tests\migrate_drupal_ui\Functional\MigrateUpgradeTestBase; @@ -124,8 +125,8 @@ class FilePathTest extends MigrateUpgradeTestBase { // Use the driver connection form to get the correct options out of the // database settings. This supports all of the databases we test against. - $drivers = drupal_get_database_types(); - $form = $drivers[$driver]->getFormOptions($connection_options); + $drivers = Database::getDriverList()->getInstallableList(); + $form = $drivers[$driver]->getInstallTasks()->getFormOptions($connection_options); $connection_options = array_intersect_key($connection_options, $form + $form['advanced_options']); // Remove isolation_level since that option is not configurable in the UI. unset($connection_options['isolation_level']); diff --git a/core/modules/migrate_drupal_ui/tests/src/FunctionalJavascript/SettingsTest.php b/core/modules/migrate_drupal_ui/tests/src/FunctionalJavascript/SettingsTest.php index 776f115f7a6..18ec74bb16d 100644 --- a/core/modules/migrate_drupal_ui/tests/src/FunctionalJavascript/SettingsTest.php +++ b/core/modules/migrate_drupal_ui/tests/src/FunctionalJavascript/SettingsTest.php @@ -4,6 +4,8 @@ namespace Drupal\Tests\migrate_drupal_ui\FunctionalJavascript; use Drupal\FunctionalJavascriptTests\WebDriverTestBase; +// cspell:ignore drupalmysqldriverdatabasemysql + /** * Tests migrate upgrade credential form with settings in settings.php. * @@ -86,7 +88,7 @@ class SettingsTest extends WebDriverTestBase { // Enter the values manually if provided. if (!empty($manual)) { $edit = []; - $driver = 'mysql'; + $driver = 'Drupal\\mysql\\Driver\\Database\\mysql'; $edit[$driver]['host'] = $manual['host']; $edit[$driver]['database'] = $manual['database']; $edit[$driver]['username'] = $manual['username']; @@ -123,9 +125,9 @@ class SettingsTest extends WebDriverTestBase { $session->fieldValueEquals('source_connection', $expected_source_connection); } else { - $session->fieldValueEquals('mysql[host]', $manual['host']); - $session->fieldValueEquals('mysql[database]', $manual['database']); - $session->fieldValueEquals('mysql[username]', $manual['username']); + $session->fieldValueEquals('edit-drupalmysqldriverdatabasemysql-host', $manual['host']); + $session->fieldValueEquals('edit-drupalmysqldriverdatabasemysql-database', $manual['database']); + $session->fieldValueEquals('edit-drupalmysqldriverdatabasemysql-username', $manual['username']); } // Confirm the file paths are correct. @@ -164,7 +166,7 @@ class SettingsTest extends WebDriverTestBase { 'prefix' => 'test', 'host' => '172.18.0.3', 'port' => '3307', - 'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql', + 'namespace' => 'Drupal\\mysql\\Driver\\Database\\mysql', 'driver' => 'mysql', ], ], @@ -184,7 +186,7 @@ class SettingsTest extends WebDriverTestBase { 'prefix' => 'test', 'host' => '172.18.0.3', 'port' => '3307', - 'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql', + 'namespace' => 'Drupal\\mysql\\Driver\\Database\\mysql', 'driver' => 'mysql', ], ], @@ -204,7 +206,7 @@ class SettingsTest extends WebDriverTestBase { 'prefix' => 'test', 'host' => '172.18.0.6', 'port' => '3307', - 'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql', + 'namespace' => 'Drupal\\mysql\\Driver\\Database\\mysql', 'driver' => 'mysql', ], ], @@ -224,7 +226,7 @@ class SettingsTest extends WebDriverTestBase { 'prefix' => 'test', 'host' => '172.18.0.3', 'port' => '3307', - 'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql', + 'namespace' => 'Drupal\\mysql\\Driver\\Database\\mysql', 'driver' => 'mysql', ], ], @@ -236,7 +238,7 @@ class SettingsTest extends WebDriverTestBase { 'prefix' => 'test', 'host' => '172.18.0.2', 'port' => '3307', - 'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql', + 'namespace' => 'Drupal\\mysql\\Driver\\Database\\mysql', 'driver' => 'mysql', ], ], @@ -261,7 +263,7 @@ class SettingsTest extends WebDriverTestBase { 'prefix' => 'test', 'host' => '172.18.0.2', 'port' => '3307', - 'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql', + 'namespace' => 'Drupal\\mysql\\Driver\\Database\\mysql', 'driver' => 'mysql', ], ], diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 538b0a6a4e2..cbab5df838e 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -516,8 +516,7 @@ function system_requirements($phase) { } else { // Make sure at least one supported database driver exists. - $drivers = drupal_detect_database_types(); - if (empty($drivers)) { + if (empty(Database::getDriverList()->getInstallableList())) { $database_ok = FALSE; $pdo_message = t('Your web server does not appear to support any common PDO database extensions. Check with your hosting provider to see if they support PDO (PHP Data Objects) and offer any databases that Drupal supports.', [ ':drupal-databases' => 'https://www.drupal.org/docs/system-requirements/database-server-requirements', diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Connection.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Connection.php index a87a0d38695..9de6eda72cb 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Connection.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Connection.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestMysql; -include_once dirname(__DIR__, 8) . '/mysql/src/Driver/Database/mysql/Connection.php'; - use Drupal\mysql\Driver\Database\mysql\Connection as CoreConnection; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Insert.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Insert.php index 8c15b608fe4..5c253174bc0 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Insert.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Insert.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestMysql; -include_once dirname(__DIR__, 8) . '/mysql/src/Driver/Database/mysql/Insert.php'; - use Drupal\mysql\Driver\Database\mysql\Insert as CoreInsert; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Install/Tasks.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Install/Tasks.php index 2b20ba147f9..4a6f247bb9a 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Install/Tasks.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Install/Tasks.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestMysql\Install; -include_once dirname(__DIR__, 9) . '/mysql/src/Driver/Database/mysql/Install/Tasks.php'; - use Drupal\mysql\Driver\Database\mysql\Install\Tasks as CoreTasks; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Schema.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Schema.php index b3a2dc01fef..bc1bf9eec25 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Schema.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Schema.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestMysql; -include_once dirname(__DIR__, 8) . '/mysql/src/Driver/Database/mysql/Schema.php'; - use Drupal\mysql\Driver\Database\mysql\Schema as CoreSchema; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Upsert.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Upsert.php index dd2d71af8c7..d2bd76e6ead 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Upsert.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Upsert.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestMysql; -include_once dirname(__DIR__, 8) . '/mysql/src/Driver/Database/mysql/Upsert.php'; - use Drupal\mysql\Driver\Database\mysql\Upsert as CoreUpsert; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Connection.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Connection.php index 6ef463cb157..447c3acd481 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Connection.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Connection.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestMysqlDeprecatedVersion; -include_once dirname(__DIR__, 8) . '/mysql/src/Driver/Database/mysql/Connection.php'; - use Drupal\mysql\Driver\Database\mysql\Connection as CoreConnection; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Insert.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Insert.php index f1a54e3cacc..f29049d5af1 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Insert.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Insert.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestMysqlDeprecatedVersion; -include_once dirname(__DIR__, 8) . '/mysql/src/Driver/Database/mysql/Insert.php'; - use Drupal\mysql\Driver\Database\mysql\Insert as CoreInsert; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Install/Tasks.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Install/Tasks.php index 647268c7b81..42a7c52d7e2 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Install/Tasks.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Install/Tasks.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestMysqlDeprecatedVersion\Install; -include_once dirname(__DIR__, 9) . '/mysql/src/Driver/Database/mysql/Install/Tasks.php'; - use Drupal\mysql\Driver\Database\mysql\Install\Tasks as CoreTasks; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Schema.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Schema.php index 4e739215848..0b608b4baf4 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Schema.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Schema.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestMysqlDeprecatedVersion; -include_once dirname(__DIR__, 8) . '/mysql/src/Driver/Database/mysql/Schema.php'; - use Drupal\mysql\Driver\Database\mysql\Schema as CoreSchema; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Upsert.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Upsert.php index 2513c7357b4..58a792b6446 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Upsert.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Upsert.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestMysqlDeprecatedVersion; -include_once dirname(__DIR__, 8) . '/mysql/src/Driver/Database/mysql/Upsert.php'; - use Drupal\mysql\Driver\Database\mysql\Upsert as CoreUpsert; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Connection.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Connection.php index e45d48ade94..f9f15f091d3 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Connection.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Connection.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestPgsql; -include_once dirname(__DIR__, 8) . '/pgsql/src/Driver/Database/pgsql/Connection.php'; - use Drupal\pgsql\Driver\Database\pgsql\Connection as CoreConnection; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Delete.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Delete.php index 92081533cba..29840d0dfa8 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Delete.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Delete.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestPgsql; -include_once dirname(__DIR__, 8) . '/pgsql/src/Driver/Database/pgsql/Delete.php'; - use Drupal\pgsql\Driver\Database\pgsql\Delete as CoreDelete; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Insert.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Insert.php index 957373ffc38..a35e74d445b 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Insert.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Insert.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestPgsql; -include_once dirname(__DIR__, 8) . '/pgsql/src/Driver/Database/pgsql/Insert.php'; - use Drupal\pgsql\Driver\Database\pgsql\Insert as CoreInsert; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Install/Tasks.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Install/Tasks.php index a8c096438f2..51fcd279672 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Install/Tasks.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Install/Tasks.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestPgsql\Install; -include_once dirname(__DIR__, 9) . '/pgsql/src/Driver/Database/pgsql/Install/Tasks.php'; - use Drupal\pgsql\Driver\Database\pgsql\Install\Tasks as CoreTasks; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Schema.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Schema.php index df0c45c2220..522311cdc2c 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Schema.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Schema.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestPgsql; -include_once dirname(__DIR__, 8) . '/pgsql/src/Driver/Database/pgsql/Schema.php'; - use Drupal\pgsql\Driver\Database\pgsql\Schema as CoreSchema; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Select.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Select.php index b11fbefe114..12fe909b2e1 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Select.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Select.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestPgsql; -include_once dirname(__DIR__, 8) . '/pgsql/src/Driver/Database/pgsql/Select.php'; - use Drupal\pgsql\Driver\Database\pgsql\Select as CoreSelect; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Truncate.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Truncate.php index 61b58711ff9..416c082563d 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Truncate.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Truncate.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestPgsql; -include_once dirname(__DIR__, 8) . '/pgsql/src/Driver/Database/pgsql/Truncate.php'; - use Drupal\pgsql\Driver\Database\pgsql\Truncate as CoreTruncate; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Update.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Update.php index e30ace4bc8e..48d3f0380ef 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Update.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Update.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestPgsql; -include_once dirname(__DIR__, 8) . '/pgsql/src/Driver/Database/pgsql/Update.php'; - use Drupal\pgsql\Driver\Database\pgsql\Update as CoreUpdate; /** diff --git a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Upsert.php b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Upsert.php index b36b039bf9b..93e51d1ae07 100644 --- a/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Upsert.php +++ b/core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Upsert.php @@ -2,8 +2,6 @@ namespace Drupal\driver_test\Driver\Database\DrivertestPgsql; -include_once dirname(__DIR__, 8) . '/pgsql/src/Driver/Database/pgsql/Upsert.php'; - use Drupal\pgsql\Driver\Database\pgsql\Upsert as CoreUpsert; /** diff --git a/core/modules/system/tests/src/Functional/System/DatabaseDriverProvidedByModuleTest.php b/core/modules/system/tests/src/Functional/System/DatabaseDriverProvidedByModuleTest.php index e81ced70d30..450651c171c 100644 --- a/core/modules/system/tests/src/Functional/System/DatabaseDriverProvidedByModuleTest.php +++ b/core/modules/system/tests/src/Functional/System/DatabaseDriverProvidedByModuleTest.php @@ -50,6 +50,12 @@ class DatabaseDriverProvidedByModuleTest extends BrowserTestBase { 'driver' => 'Drivertest' . ucfirst($driver), 'namespace' => 'Drupal\\driver_test\\Driver\\Database\\Drivertest' . ucfirst($driver), 'autoload' => 'core/modules/system/tests/modules/driver_test/src/Driver/Database/Drivertest' . ucfirst($driver), + 'dependencies' => [ + $driver => [ + 'namespace' => "Drupal\\{$driver}", + 'autoload' => "core/modules/$driver/src/", + ], + ], ]; if (isset($connection_info['default']['port'])) { $database['port'] = $connection_info['default']['port']; diff --git a/core/modules/system/tests/src/Functional/Update/DatabaseVersionCheckUpdateTest.php b/core/modules/system/tests/src/Functional/Update/DatabaseVersionCheckUpdateTest.php index 2202dc000b2..dfbf3ba33c7 100644 --- a/core/modules/system/tests/src/Functional/Update/DatabaseVersionCheckUpdateTest.php +++ b/core/modules/system/tests/src/Functional/Update/DatabaseVersionCheckUpdateTest.php @@ -39,17 +39,22 @@ class DatabaseVersionCheckUpdateTest extends BrowserTestBase { // Use a database driver that reports a fake database version that does // not meet requirements. Only change the necessary settings in the database // settings array so that run-tests.sh continues to work. - $autoload = Database::findDriverAutoloadDirectory('Drupal\driver_test\Driver\Database\DrivertestMysqlDeprecatedVersion', \Drupal::root()); + $driverExtensionName = 'Drupal\\driver_test\\Driver\\Database\\DrivertestMysqlDeprecatedVersion'; + $autoloading = \Drupal::service('extension.list.database_driver')->get($driverExtensionName)->getAutoloadInfo(); $settings['databases']['default']['default']['driver'] = (object) [ 'value' => 'DrivertestMysqlDeprecatedVersion', 'required' => TRUE, ]; $settings['databases']['default']['default']['namespace'] = (object) [ - 'value' => 'Drupal\\driver_test\\Driver\\Database\\DrivertestMysqlDeprecatedVersion', + 'value' => $driverExtensionName, 'required' => TRUE, ]; $settings['databases']['default']['default']['autoload'] = (object) [ - 'value' => $autoload, + 'value' => $autoloading['autoload'], + 'required' => TRUE, + ]; + $settings['databases']['default']['default']['dependencies'] = (object) [ + 'value' => $autoloading['dependencies'], 'required' => TRUE, ]; $settings['settings'] = [ diff --git a/core/tests/Drupal/FunctionalTests/Installer/InstallerDeprecatedDriverNameTest.php b/core/tests/Drupal/FunctionalTests/Installer/InstallerDeprecatedDriverNameTest.php new file mode 100644 index 00000000000..f1c72c8a598 --- /dev/null +++ b/core/tests/Drupal/FunctionalTests/Installer/InstallerDeprecatedDriverNameTest.php @@ -0,0 +1,52 @@ +installParameters(); + // Replace the driver namespace with the driver name in the + // 'install_settings_form' parameter. + $driverNamespace = $parameters['forms']['install_settings_form']['driver']; + $driverName = Database::getDriverList()->get($driverNamespace)->getDriverName(); + $parameters['forms']['install_settings_form']['driver'] = $driverName; + $parameters['forms']['install_settings_form'][$driverName] = $parameters['forms']['install_settings_form'][$driverNamespace]; + unset($parameters['forms']['install_settings_form'][$driverNamespace]); + // Simulate a real install which does not start with the any connections set + // in \Drupal\Core\Database\Database::$connections. + Database::removeConnection('default'); + $this->expectDeprecation("Passing a database driver name '{$driverName}' to install_get_form() is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Pass a database driver namespace instead. See https://www.drupal.org/node/3258175"); + $this->expectDeprecation('Drupal\\Core\\Extension\\DatabaseDriverList::getFromDriverName() is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use DatabaseDriverList::get() instead, passing a database driver namespace. See https://www.drupal.org/node/3258175'); + install_drupal($this->classLoader, $parameters); + } + + /** + * Verifies that installation succeeded. + */ + public function testInstaller() { + $this->assertSession()->addressEquals('/'); + $this->assertSession()->statusCodeEquals(200); + } + +} diff --git a/core/tests/Drupal/FunctionalTests/Installer/InstallerExistingBrokenDatabaseSettingsTest.php b/core/tests/Drupal/FunctionalTests/Installer/InstallerExistingBrokenDatabaseSettingsTest.php index 3a42b319c1c..0e2822cd73f 100644 --- a/core/tests/Drupal/FunctionalTests/Installer/InstallerExistingBrokenDatabaseSettingsTest.php +++ b/core/tests/Drupal/FunctionalTests/Installer/InstallerExistingBrokenDatabaseSettingsTest.php @@ -35,7 +35,9 @@ class InstallerExistingBrokenDatabaseSettingsTest extends InstallerTestBase { $connection_info['default']['driver'] = 'DrivertestMysqlDeprecatedVersion'; $namespace = 'Drupal\\driver_test\\Driver\\Database\\DrivertestMysqlDeprecatedVersion'; $connection_info['default']['namespace'] = $namespace; - $connection_info['default']['autoload'] = Database::findDriverAutoloadDirectory($namespace, \Drupal::root()); + $connection_info['default']['autoload'] = \Drupal::service('extension.list.database_driver') + ->get($namespace) + ->getAutoloadInfo()['autoload']; $this->settings['databases']['default'] = (object) [ 'value' => $connection_info, diff --git a/core/tests/Drupal/FunctionalTests/Installer/InstallerNonDefaultDatabaseDriverTest.php b/core/tests/Drupal/FunctionalTests/Installer/InstallerNonDefaultDatabaseDriverTest.php index c7e4dff8b83..fa995dd677e 100644 --- a/core/tests/Drupal/FunctionalTests/Installer/InstallerNonDefaultDatabaseDriverTest.php +++ b/core/tests/Drupal/FunctionalTests/Installer/InstallerNonDefaultDatabaseDriverTest.php @@ -6,6 +6,9 @@ use Drupal\Core\Database\Database; use Drupal\Core\Extension\Extension; use Drupal\Core\Extension\ModuleUninstallValidatorException; +// cspell:ignore drupaldriver testdriverdatabasedrivertestmysql +// cspell:ignore testdriverdatabasedrivertestpgsql + /** * Tests the interactive installer. * @@ -32,17 +35,19 @@ class InstallerNonDefaultDatabaseDriverTest extends InstallerTestBase { if (!in_array($driver, ['mysql', 'pgsql'])) { $this->markTestSkipped("This test does not support the {$driver} database driver."); } + $driverNamespace = Database::getConnection()->getConnectionOptions()['namespace']; $this->testDriverName = 'Drivertest' . ucfirst($driver); + $testDriverNamespace = "Drupal\\driver_test\\Driver\\Database\\{$this->testDriverName}"; // Assert that we are using the database drivers from the driver_test module. - $this->assertSession()->elementTextEquals('xpath', '//label[@for="edit-driver-drivertestmysql"]', 'MySQL by the driver_test module'); - $this->assertSession()->elementTextEquals('xpath', '//label[@for="edit-driver-drivertestpgsql"]', 'PostgreSQL by the driver_test module'); + $this->assertSession()->elementTextEquals('xpath', '//label[@for="edit-driver-drupaldriver-testdriverdatabasedrivertestmysql"]', 'MySQL by the driver_test module'); + $this->assertSession()->elementTextEquals('xpath', '//label[@for="edit-driver-drupaldriver-testdriverdatabasedrivertestpgsql"]', 'PostgreSQL by the driver_test module'); $settings = $this->parameters['forms']['install_settings_form']; - $settings['driver'] = $this->testDriverName; - $settings[$this->testDriverName] = $settings[$driver]; - unset($settings[$driver]); + $settings['driver'] = $testDriverNamespace; + $settings[$testDriverNamespace] = $settings[$driverNamespace]; + unset($settings[$driverNamespace]); $edit = $this->translatePostValues($settings); $this->submitForm($edit, $this->translations['Save and continue']); } @@ -61,6 +66,21 @@ class InstallerNonDefaultDatabaseDriverTest extends InstallerTestBase { $this->assertStringContainsString("'driver' => '{$this->testDriverName}',", $contents); $this->assertStringContainsString("'autoload' => 'core/modules/system/tests/modules/driver_test/src/Driver/Database/{$this->testDriverName}/',", $contents); + $dependencies = "'dependencies' => " . PHP_EOL . + " array (" . PHP_EOL . + " 'mysql' => " . PHP_EOL . + " array (" . PHP_EOL . + " 'namespace' => 'Drupal\\\\mysql'," . PHP_EOL . + " 'autoload' => 'core/modules/mysql/src/'," . PHP_EOL . + " )," . PHP_EOL . + " 'pgsql' => " . PHP_EOL . + " array (" . PHP_EOL . + " 'namespace' => 'Drupal\\\\pgsql'," . PHP_EOL . + " 'autoload' => 'core/modules/pgsql/src/'," . PHP_EOL . + " )," . PHP_EOL . + " )," . PHP_EOL; + $this->assertStringContainsString($dependencies, $contents); + // Assert that the module "driver_test" has been installed. $this->assertEquals(\Drupal::service('module_handler')->getModule('driver_test'), new Extension($this->root, 'module', 'core/modules/system/tests/modules/driver_test/driver_test.info.yml')); diff --git a/core/tests/Drupal/FunctionalTests/Installer/InstallerTest.php b/core/tests/Drupal/FunctionalTests/Installer/InstallerTest.php index 12a2f0ddcf1..9ff456af9d7 100644 --- a/core/tests/Drupal/FunctionalTests/Installer/InstallerTest.php +++ b/core/tests/Drupal/FunctionalTests/Installer/InstallerTest.php @@ -7,6 +7,8 @@ use Drupal\Core\Routing\RoutingEvents; use Drupal\Core\Test\PerformanceTestRecorder; use Drupal\Core\Extension\ModuleUninstallValidatorException; +// cspell:ignore drupalmysqldriverdatabasemysql drupalpgsqldriverdatabasepgsql + /** * Tests the interactive installer. * @@ -88,8 +90,8 @@ class InstallerTest extends InstallerTestBase { // Assert that we use the by core supported database drivers by default and // not the ones from the driver_test module. - $this->assertSession()->elementTextEquals('xpath', '//label[@for="edit-driver-mysql"]', 'MySQL, MariaDB, Percona Server, or equivalent'); - $this->assertSession()->elementTextEquals('xpath', '//label[@for="edit-driver-pgsql"]', 'PostgreSQL'); + $this->assertSession()->elementTextEquals('xpath', '//label[@for="edit-driver-drupalmysqldriverdatabasemysql"]', 'MySQL, MariaDB, Percona Server, or equivalent'); + $this->assertSession()->elementTextEquals('xpath', '//label[@for="edit-driver-drupalpgsqldriverdatabasepgsql"]', 'PostgreSQL'); parent::setUpSettings(); } diff --git a/core/tests/Drupal/FunctionalTests/Installer/InstallerTestBase.php b/core/tests/Drupal/FunctionalTests/Installer/InstallerTestBase.php index 6ec3919d6a8..7ded3eec27f 100644 --- a/core/tests/Drupal/FunctionalTests/Installer/InstallerTestBase.php +++ b/core/tests/Drupal/FunctionalTests/Installer/InstallerTestBase.php @@ -255,7 +255,10 @@ abstract class InstallerTestBase extends BrowserTestBase { * Installer step: Configure settings. */ protected function setUpSettings() { - $edit = $this->translatePostValues($this->parameters['forms']['install_settings_form']); + $parameters = $this->parameters['forms']['install_settings_form']; + $driver = $parameters['driver']; + unset($parameters[$driver]['dependencies']); + $edit = $this->translatePostValues($parameters); $this->submitForm($edit, $this->translations['Save and continue']); } diff --git a/core/tests/Drupal/KernelTests/Core/Database/DatabaseLegacyTest.php b/core/tests/Drupal/KernelTests/Core/Database/DatabaseLegacyTest.php new file mode 100644 index 00000000000..7c2cec9505a --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/DatabaseLegacyTest.php @@ -0,0 +1,29 @@ +root . '/core/includes/install.inc'; + $this->expectDeprecation('drupal_detect_database_types() is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use DatabaseDriverList::getList() instead. See https://www.drupal.org/node/3258175'); + $this->expectDeprecation('drupal_get_database_types() is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use DatabaseDriverList::getList() instead. See https://www.drupal.org/node/3258175'); + $installableDriverNames = []; + foreach (Database::getDriverList()->getInstallableList() as $driver => $driverExtension) { + $installableDriverNames[$driverExtension->getDriverName()] = $driverExtension->getInstallTasks()->name(); + } + $this->assertEquals($installableDriverNames, drupal_detect_database_types()); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Database/DatabaseTest.php b/core/tests/Drupal/Tests/Core/Database/DatabaseTest.php index abff64ebee7..f3f3ed1cfe5 100644 --- a/core/tests/Drupal/Tests/Core/Database/DatabaseTest.php +++ b/core/tests/Drupal/Tests/Core/Database/DatabaseTest.php @@ -3,7 +3,10 @@ namespace Drupal\Tests\Core\Database; use Composer\Autoload\ClassLoader; +use Drupal\Core\Cache\NullBackend; use Drupal\Core\Database\Database; +use Drupal\Core\Extension\DatabaseDriverList; +use Drupal\Core\Extension\Exception\UnknownExtensionException; use Drupal\Tests\UnitTestCase; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -42,11 +45,18 @@ class DatabaseTest extends UnitTestCase { // Mock the container so we don't need to mock drupal_valid_test_ua(). // @see \Drupal\Core\Extension\ExtensionDiscovery::scan() $this->root = dirname(__DIR__, 6); + $databaseDriverList = new DatabaseDriverList($this->root, 'database_driver', new NullBackend('database_driver')); $container = $this->createMock(ContainerInterface::class); $container->expects($this->any()) ->method('has') - ->with('kernel') - ->willReturn(TRUE); + ->willReturnMap([ + ['kernel', TRUE], + ['extension.list.database_driver', TRUE], + ]); + $container->expects($this->any()) + ->method('get') + ->with('extension.list.database_driver') + ->willReturn($databaseDriverList); $container->expects($this->any()) ->method('getParameter') ->with('site.path') @@ -57,9 +67,18 @@ class DatabaseTest extends UnitTestCase { /** * @covers ::findDriverAutoloadDirectory * @dataProvider providerFindDriverAutoloadDirectory + * @group legacy */ public function testFindDriverAutoloadDirectory($expected, $namespace, $include_test_drivers) { - $this->assertSame($expected, Database::findDriverAutoloadDirectory($namespace, $this->root, $include_test_drivers)); + $this->expectDeprecation('Drupal\Core\Database\Database::findDriverAutoloadDirectory() is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use DatabaseDriverList::getList() instead. See https://www.drupal.org/node/3258175'); + // The only module that provides a driver in core is a test module. + if (!$expected) { + $this->expectException(UnknownExtensionException::class); + Database::findDriverAutoloadDirectory($namespace, $this->root, $include_test_drivers); + } + else { + $this->assertSame($expected, Database::findDriverAutoloadDirectory($namespace, $this->root, $include_test_drivers)); + } } /** @@ -78,9 +97,11 @@ class DatabaseTest extends UnitTestCase { /** * @covers ::findDriverAutoloadDirectory * @dataProvider providerFindDriverAutoloadDirectoryException + * @group legacy */ public function testFindDriverAutoloadDirectoryException($expected_message, $namespace, $include_tests) { - $this->expectException(\RuntimeException::class); + $this->expectDeprecation('Drupal\Core\Database\Database::findDriverAutoloadDirectory() is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use DatabaseDriverList::getList() instead. See https://www.drupal.org/node/3258175'); + $this->expectException(UnknownExtensionException::class); $this->expectExceptionMessage($expected_message); Database::findDriverAutoloadDirectory($namespace, $this->root, $include_tests); } @@ -92,9 +113,21 @@ class DatabaseTest extends UnitTestCase { */ public function providerFindDriverAutoloadDirectoryException() { return [ - 'test module but tests not included' => ["Cannot find the module 'driver_test' for the database driver namespace 'Drupal\driver_test\Driver\Database\DrivertestMysql'", 'Drupal\driver_test\Driver\Database\DrivertestMysql', FALSE], - 'non-existent driver in test module' => ["Cannot find the database driver namespace 'Drupal\driver_test\Driver\Database\sqlite' in module 'driver_test'", 'Drupal\driver_test\Driver\Database\sqlite', TRUE], - 'non-existent module' => ["Cannot find the module 'does_not_exist' for the database driver namespace 'Drupal\does_not_exist\Driver\Database\mysql'", 'Drupal\does_not_exist\Driver\Database\mysql', TRUE], + 'test module but tests not included' => [ + "The database_driver Drupal\driver_test\Driver\Database\DrivertestMysql does not exist.", + 'Drupal\driver_test\Driver\Database\DrivertestMysql', + FALSE, + ], + 'non-existent driver in test module' => [ + "The database_driver Drupal\driver_test\Driver\Database\sqlite does not exist.", + 'Drupal\driver_test\Driver\Database\sqlite', + TRUE, + ], + 'non-existent module' => [ + "The database_driver Drupal\does_not_exist\Driver\Database\mysql does not exist.", + 'Drupal\does_not_exist\Driver\Database\mysql', + TRUE, + ], ]; } diff --git a/core/tests/Drupal/Tests/Core/Database/DriverModuleMissingDependenciesTest.php b/core/tests/Drupal/Tests/Core/Database/DriverModuleMissingDependenciesTest.php new file mode 100644 index 00000000000..7926234ac02 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Database/DriverModuleMissingDependenciesTest.php @@ -0,0 +1,61 @@ +createMock(ContainerInterface::class); + $container->expects($this->any()) + ->method('has') + ->with('kernel') + ->willReturn(TRUE); + $container->expects($this->any()) + ->method('getParameter') + ->with() + ->willReturnMap([ + ['install_profile', ''], + ['site.path', ''], + ]); + $container->expects($this->any()) + ->method('get') + ->with('extension.list.database_driver') + ->willReturn(new DatabaseDriverList($root, 'database_driver', new NullBackend('database_driver'))); + \Drupal::setContainer($container); + + $this->expectException(UnknownExtensionException::class); + $this->expectExceptionMessage("The database_driver a_really_missing_module\dependent_driver does not exist."); + $container->get('extension.list.database_driver') + ->includeTestDrivers(TRUE) + ->get('a_really_missing_module\\dependent_driver') + ->getAutoloadInfo(); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Database/UrlConversionTest.php b/core/tests/Drupal/Tests/Core/Database/UrlConversionTest.php index 091449fd5a7..a4795b2da0b 100644 --- a/core/tests/Drupal/Tests/Core/Database/UrlConversionTest.php +++ b/core/tests/Drupal/Tests/Core/Database/UrlConversionTest.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\Core\Database; use Drupal\Core\Database\Database; +use Drupal\Core\Extension\Exception\UnknownExtensionException; use Drupal\Tests\UnitTestCase; /** @@ -26,18 +27,6 @@ class UrlConversionTest extends UnitTestCase { protected function setUp(): void { parent::setUp(); $this->root = dirname(__FILE__, 7); - // Mock the container so we don't need to mock drupal_valid_test_ua(). - // @see \Drupal\Core\Extension\ExtensionDiscovery::scan() - $container = $this->createMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container->expects($this->any()) - ->method('has') - ->with('kernel') - ->willReturn(TRUE); - $container->expects($this->any()) - ->method('getParameter') - ->with('site.path') - ->willReturn(''); - \Drupal::setContainer($container); } /** @@ -135,6 +124,16 @@ class UrlConversionTest extends UnitTestCase { 'port' => 3306, 'namespace' => 'Drupal\driver_test\Driver\Database\DrivertestMysql', 'autoload' => 'core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/', + 'dependencies' => [ + 'mysql' => [ + 'namespace' => 'Drupal\mysql', + 'autoload' => 'core/modules/mysql/src/', + ], + 'pgsql' => [ + 'namespace' => 'Drupal\pgsql', + 'autoload' => 'core/modules/pgsql/src/', + ], + ], ], TRUE, ], @@ -150,6 +149,16 @@ class UrlConversionTest extends UnitTestCase { 'port' => 3306, 'namespace' => 'Drupal\driver_test\Driver\Database\DrivertestMysql', 'autoload' => 'core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/', + 'dependencies' => [ + 'mysql' => [ + 'namespace' => 'Drupal\mysql', + 'autoload' => 'core/modules/mysql/src/', + ], + 'pgsql' => [ + 'namespace' => 'Drupal\pgsql', + 'autoload' => 'core/modules/pgsql/src/', + ], + ], ], TRUE, ], @@ -164,6 +173,16 @@ class UrlConversionTest extends UnitTestCase { 'port' => 5432, 'namespace' => 'Drupal\driver_test\Driver\Database\DrivertestPgsql', 'autoload' => 'core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/', + 'dependencies' => [ + 'mysql' => [ + 'namespace' => 'Drupal\mysql', + 'autoload' => 'core/modules/mysql/src/', + ], + 'pgsql' => [ + 'namespace' => 'Drupal\pgsql', + 'autoload' => 'core/modules/pgsql/src/', + ], + ], ], TRUE, ], @@ -179,6 +198,16 @@ class UrlConversionTest extends UnitTestCase { 'port' => 5432, 'namespace' => 'Drupal\driver_test\Driver\Database\DrivertestPgsql', 'autoload' => 'core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/', + 'dependencies' => [ + 'mysql' => [ + 'namespace' => 'Drupal\mysql', + 'autoload' => 'core/modules/mysql/src/', + ], + 'pgsql' => [ + 'namespace' => 'Drupal\pgsql', + 'autoload' => 'core/modules/pgsql/src/', + ], + ], ], TRUE, ], @@ -274,11 +303,11 @@ class UrlConversionTest extends UnitTestCase { return [ ['foo', '', "Missing scheme in URL 'foo'"], ['foo', 'bar', "Missing scheme in URL 'foo'"], - ['foo://', 'bar', "Can not convert 'foo://' to a database connection, class 'Drupal\\Driver\\Database\\foo\\Connection' does not exist"], - ['foo://bar', 'baz', "Can not convert 'foo://bar' to a database connection, class 'Drupal\\Driver\\Database\\foo\\Connection' does not exist"], - ['foo://bar:port', 'baz', "Can not convert 'foo://bar:port' to a database connection, class 'Drupal\\Driver\\Database\\foo\\Connection' does not exist"], + ['foo://', 'bar', "Can not convert 'foo://' to a database connection, the module providing the driver 'foo' is not specified"], + ['foo://bar', 'baz', "Can not convert 'foo://bar' to a database connection, the module providing the driver 'foo' is not specified"], + ['foo://bar:port', 'baz', "Can not convert 'foo://bar:port' to a database connection, the module providing the driver 'foo' is not specified"], ['foo/bar/baz', 'bar2', "Missing scheme in URL 'foo/bar/baz'"], - ['foo://bar:baz@test1', 'test2', "Can not convert 'foo://bar:baz@test1' to a database connection, class 'Drupal\\Driver\\Database\\foo\\Connection' does not exist"], + ['foo://bar:baz@test1', 'test2', "Can not convert 'foo://bar:baz@test1' to a database connection, the module providing the driver 'foo' is not specified"], ]; } @@ -390,6 +419,29 @@ class UrlConversionTest extends UnitTestCase { ]; $expected_url8 = 'DrivertestPgsql://test_user:test_pass@test_host:5432/test_database?module=driver_test#pre'; + $info9 = [ + 'database' => 'test_database', + 'username' => 'test_user', + 'password' => 'test_pass', + 'prefix' => '', + 'host' => 'test_host', + 'port' => '3306', + 'driver' => 'DrivertestMysql', + 'namespace' => 'Drupal\\driver_test\\Driver\\Database\\DrivertestMysql', + 'autoload' => 'core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/', + 'dependencies' => [ + 'mysql' => [ + 'namespace' => 'Drupal\mysql', + 'autoload' => 'core/modules/mysql/src/', + ], + 'pgsql' => [ + 'namespace' => 'Drupal\pgsql', + 'autoload' => 'core/modules/pgsql/src/', + ], + ], + ]; + $expected_url9 = 'DrivertestMysql://test_user:test_pass@test_host:3306/test_database?module=driver_test'; + return [ [$info1, $expected_url1], [$info2, $expected_url2], @@ -399,6 +451,7 @@ class UrlConversionTest extends UnitTestCase { [$info6, $expected_url6], [$info7, $expected_url7], [$info8, $expected_url8], + [$info9, $expected_url9], ]; } @@ -447,9 +500,9 @@ class UrlConversionTest extends UnitTestCase { * @covers ::convertDbUrlToConnectionInfo */ public function testDriverModuleDoesNotExist() { - $url = 'mysql://test_user:test_pass@test_host:3306/test_database?module=does_not_exist'; - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage("Cannot find the module 'does_not_exist' for the database driver namespace 'Drupal\does_not_exist\Driver\Database\mysql'"); + $url = 'foo_bar_mysql://test_user:test_pass@test_host:3306/test_database?module=foo_bar'; + $this->expectException(UnknownExtensionException::class); + $this->expectExceptionMessage("The database_driver Drupal\\foo_bar\\Driver\\Database\\foo_bar_mysql does not exist."); Database::convertDbUrlToConnectionInfo($url, $this->root, TRUE); } @@ -457,9 +510,9 @@ class UrlConversionTest extends UnitTestCase { * @covers ::convertDbUrlToConnectionInfo */ public function testModuleDriverDoesNotExist() { - $url = 'mysql://test_user:test_pass@test_host:3306/test_database?module=driver_test'; - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage("Cannot find the database driver namespace 'Drupal\driver_test\Driver\Database\mysql' in module 'driver_test'"); + $url = 'driver_test_mysql://test_user:test_pass@test_host:3306/test_database?module=driver_test'; + $this->expectException(UnknownExtensionException::class); + $this->expectExceptionMessage("The database_driver Drupal\\driver_test\\Driver\\Database\\driver_test_mysql does not exist."); Database::convertDbUrlToConnectionInfo($url, $this->root, TRUE); } diff --git a/core/tests/Drupal/Tests/Core/Database/fixtures/core/modules/driver_missing_dependency_test/driver_missing_dependency_test.info.yml b/core/tests/Drupal/Tests/Core/Database/fixtures/core/modules/driver_missing_dependency_test/driver_missing_dependency_test.info.yml new file mode 100644 index 00000000000..165db40f366 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Database/fixtures/core/modules/driver_missing_dependency_test/driver_missing_dependency_test.info.yml @@ -0,0 +1,7 @@ +name: 'Contrib database driver test with a missing dependency' +type: module +description: 'Support database contrib driver testing.' +package: Testing +version: VERSION +dependencies: + - drupal:a_really_missing_module diff --git a/core/tests/Drupal/Tests/Core/Database/fixtures/core/modules/driver_missing_dependency_test/src/Driver/Database/MissingDependency/Install/Tasks.php b/core/tests/Drupal/Tests/Core/Database/fixtures/core/modules/driver_missing_dependency_test/src/Driver/Database/MissingDependency/Install/Tasks.php new file mode 100644 index 00000000000..43eeee5fa4b --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Database/fixtures/core/modules/driver_missing_dependency_test/src/Driver/Database/MissingDependency/Install/Tasks.php @@ -0,0 +1,19 @@ +includeTestDrivers(TRUE)->get($driverExtensionName); + $this->assertSame($driverExtensionName, $driverExtension->getName()); + $this->assertSame($moduleName, $driverExtension->getModule()->getName()); + $this->assertSame($driverName, $driverExtension->getDriverName()); + } + + /** + * @covers ::get + * @group legacy + * + * @dataProvider providerDatabaseDrivers + */ + public function testLegacyGet(string $driverName, string $moduleName, string $driverExtensionName): void { + $this->expectDeprecation("Passing a database driver name '{$driverName}' to Drupal\\Core\\Extension\\DatabaseDriverList::get() is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Pass a database driver namespace instead. See https://www.drupal.org/node/3258175"); + $this->expectDeprecation('Drupal\\Core\\Extension\\DatabaseDriverList::getFromDriverName() is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use DatabaseDriverList::get() instead, passing a database driver namespace. See https://www.drupal.org/node/3258175'); + $driverExtension = Database::getDriverList()->includeTestDrivers(TRUE)->get($driverName); + $this->assertSame($driverExtensionName, $driverExtension->getName()); + $this->assertSame($moduleName, $driverExtension->getModule()->getName()); + $this->assertSame($driverName, $driverExtension->getDriverName()); + } + + /** + * @covers ::getFromDriverName + * @group legacy + * + * @dataProvider providerDatabaseDrivers + */ + public function testLegacyGetFromDriverName(string $driverName, string $moduleName, string $driverExtensionName): void { + $this->expectDeprecation('Drupal\\Core\\Extension\\DatabaseDriverList::getFromDriverName() is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. Use DatabaseDriverList::get() instead, passing a database driver namespace. See https://www.drupal.org/node/3258175'); + $driverExtension = Database::getDriverList()->includeTestDrivers(TRUE)->getFromDriverName($driverName); + $this->assertSame($driverExtensionName, $driverExtension->getName()); + $this->assertSame($moduleName, $driverExtension->getModule()->getName()); + $this->assertSame($driverName, $driverExtension->getDriverName()); + } + + /** + * Data provider for testLegacyGetFromDriverName(). + */ + public function providerDatabaseDrivers(): array { + return [ + ['mysql', 'mysql', 'Drupal\\mysql\\Driver\\Database\\mysql'], + ['pgsql', 'pgsql', 'Drupal\\pgsql\\Driver\\Database\\pgsql'], + ['sqlite', 'sqlite', 'Drupal\\sqlite\\Driver\\Database\\sqlite'], + ['DrivertestMysql', 'driver_test', 'Drupal\\driver_test\\Driver\\Database\\DrivertestMysql'], + ['DrivertestPgsql', 'driver_test', 'Drupal\\driver_test\\Driver\\Database\\DrivertestPgsql'], + ['DrivertestMysqlDeprecatedVersion', 'driver_test', 'Drupal\\driver_test\\Driver\\Database\\DrivertestMysqlDeprecatedVersion'], + ]; + } + +} diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index c0b18427ae9..0c1b1155d6e 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -222,6 +222,27 @@ $databases = []; * 'prefix' => '', * ]; * @endcode + * + * Sample Database configuration format for a driver that is extending another + * database driver. + * @code + * $databases['default']['default'] = [ + * 'driver' => 'my_driver', + * 'namespace' => 'Drupal\my_module\Driver\Database\my_driver', + * 'autoload' => 'modules/my_module/src/Driver/Database/my_driver/', + * 'database' => 'databasename', + * 'username' => 'sqlusername', + * 'password' => 'sqlpassword', + * 'host' => 'localhost', + * 'prefix' => '', + * 'dependencies' => [ + * 'parent_module' => [ + * 'namespace' => 'Drupal\parent_module', + * 'autoload' => 'core/modules/parent_module/src/', + * ], + * ], + * ]; + * @endcode */ /**