function ModuleInstaller::doInstall
Installs a set of modules.
Parameters
array $module_list: The list of modules to install.
array $installed_modules: An array of the already installed modules.
bool $sync_status: The config sync status.
1 call to ModuleInstaller::doInstall()
- ModuleInstaller::install in core/
lib/ Drupal/ Core/ Extension/ ModuleInstaller.php - Installs a given list of modules.
File
-
core/
lib/ Drupal/ Core/ Extension/ ModuleInstaller.php, line 271
Class
- ModuleInstaller
- Default implementation of the module installer.
Namespace
Drupal\Core\ExtensionCode
private function doInstall(array $module_list, array $installed_modules, bool $sync_status) : void {
$extension_config = \Drupal::configFactory()->getEditable('core.extension');
// Save this data without checking schema. This is a performance
// improvement for module installation.
$extension_config->set('module', module_config_sort(array_merge(array_fill_keys($module_list, 0), $installed_modules)))
->save(TRUE);
// Prepare the new module list, sorted by weight, including filenames.
// This list is used for both the ModuleHandler and DrupalKernel. It
// needs to be kept in sync between both. A DrupalKernel reboot or
// rebuild will automatically re-instantiate a new ModuleHandler that
// uses the new module list of the kernel. However, DrupalKernel does
// not cause any modules to be loaded.
// Furthermore, the currently active (fixed) module list can be
// different from the configured list of enabled modules. For all active
// modules not contained in the configured enabled modules, we assume a
// weight of 0.
$current_module_filenames = $this->moduleHandler
->getModuleList();
$current_modules = array_fill_keys(array_keys($current_module_filenames), 0);
$current_modules = module_config_sort(array_merge($current_modules, $extension_config->get('module')));
$module_filenames = [];
foreach ($current_modules as $name => $weight) {
if (isset($current_module_filenames[$name])) {
$module_filenames[$name] = $current_module_filenames[$name];
}
else {
$module_path = \Drupal::service('extension.list.module')->getPath($name);
$pathname = "{$module_path}/{$name}.info.yml";
$filename = file_exists($module_path . "/{$name}.module") ? "{$name}.module" : NULL;
$module_filenames[$name] = new Extension($this->root, 'module', $pathname, $filename);
}
}
// Update the module handler in order to have the correct module list
// for the kernel update.
$this->moduleHandler
->setModuleList($module_filenames);
// Clear the static cache of the "extension.list.module" service to pick
// up the new module, since it merges the installation status of modules
// into its statically cached list.
\Drupal::service('extension.list.module')->reset();
// Update the kernel to include it.
$this->updateKernel($module_filenames);
if (!InstallerKernel::installationAttempted()) {
// Replace the route provider service with a version that will rebuild
// if routes are used during installation. This ensures that a module's
// routes are available during installation. This has to occur before
// any services that depend on it are instantiated otherwise those
// services will have the old route provider injected. Note that, since
// the container is rebuilt by updating the kernel, the route provider
// service is the regular one even though we are in a loop and might
// have replaced it before.
\Drupal::getContainer()->set('router.route_provider.old', \Drupal::service('router.route_provider'));
\Drupal::getContainer()->set('router.route_provider', \Drupal::service('router.route_provider.lazy_builder'));
}
foreach ($module_list as $module) {
// Load the module's .module and .install files. Do this for all modules
// prior to calling hook_module_preinstall() in order to not pollute the
// cache.
$this->moduleHandler
->load($module);
$this->moduleHandler
->loadInclude($module, 'install');
}
foreach ($module_list as $module) {
// Allow modules to react prior to the installation of a module.
$this->moduleHandler
->invokeAll('module_preinstall', [
$module,
$sync_status,
]);
// Now install the module's schema if necessary.
$this->installSchema($module);
}
// Clear plugin manager caches.
// @todo should this be in the loop?
\Drupal::getContainer()->get('plugin.cache_clearer')
->clearCachedDefinitions();
foreach ($module_list as $module) {
// Set the schema version to the number of the last update provided by
// the module, or the minimum core schema version.
$version = \Drupal::CORE_MINIMUM_SCHEMA_VERSION;
$versions = $this->updateRegistry
->getAvailableUpdates($module);
if ($versions) {
$version = max(max($versions), $version);
}
// Notify interested components that this module's entity types and
// field storage definitions are new. For example, a SQL-based storage
// handler can use this as an opportunity to create the necessary
// database tables.
// @todo Clean this up in https://www.drupal.org/node/2350111.
$entity_type_manager = \Drupal::entityTypeManager();
$update_manager = \Drupal::entityDefinitionUpdateManager();
/** @var \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager */
$entity_field_manager = \Drupal::service('entity_field.manager');
foreach ($entity_type_manager->getDefinitions() as $entity_type) {
$is_fieldable_entity_type = $entity_type->entityClassImplements(FieldableEntityInterface::class);
if ($entity_type->getProvider() == $module) {
if ($is_fieldable_entity_type) {
$update_manager->installFieldableEntityType($entity_type, $entity_field_manager->getFieldStorageDefinitions($entity_type->id()));
}
else {
$update_manager->installEntityType($entity_type);
}
}
elseif ($is_fieldable_entity_type) {
// The module being installed may be adding new fields to existing
// entity types. Field definitions for any entity type defined by
// the module are handled in the if branch.
foreach ($entity_field_manager->getFieldStorageDefinitions($entity_type->id()) as $storage_definition) {
if ($storage_definition->getProvider() == $module) {
// If the module being installed is also defining a storage key
// for the entity type, the entity schema may not exist yet. It
// will be created later in that case.
try {
$update_manager->installFieldStorageDefinition($storage_definition->getName(), $entity_type->id(), $module, $storage_definition);
} catch (EntityStorageException $e) {
Error::logException($this->logger, $e, 'An error occurred while notifying the creation of the @name field storage definition: "@message" in %function (line %line of %file).', [
'@name' => $storage_definition->getName(),
]);
}
}
}
}
}
// Install default configuration of the module.
$config_installer = \Drupal::service('config.installer');
$config_installer->installDefaultConfig('module', $module, DefaultConfigMode::InstallSimple);
// If the module has no current updates, but has some that were
// previously removed, set the version to the value of
// hook_update_last_removed().
if ($last_removed = $this->invoke($module, 'update_last_removed')) {
$version = max($version, $last_removed);
}
$this->updateRegistry
->setInstalledVersion($module, $version);
}
// Drupal's stream wrappers needs to be re-registered in case a
// module-provided stream wrapper is used later in the same request. In
// particular, this happens when installing Drupal via Drush, as the
// 'translations' stream wrapper is provided by Interface Translation
// module and is later used to import translations.
\Drupal::service('stream_wrapper_manager')->register();
// Update the theme registry to include it.
\Drupal::service('theme.registry')->reset();
// Modules can alter theme info, so refresh theme data.
// @todo ThemeHandler cannot be injected into ModuleHandler, since that
// causes a circular service dependency.
// @see https://www.drupal.org/node/2208429
\Drupal::service('theme_handler')->refreshInfo();
// Modules may provide single directory components which are added to
// the core library definitions rather than the module itself, this
// requires the library discovery cache to be rebuilt.
\Drupal::service('library.discovery')->clear();
$config_installer = \Drupal::service('config.installer');
foreach ($module_list as $module) {
// Create config entities a module has in the /install directory.
$config_installer->installDefaultConfig('module', $module, DefaultConfigMode::InstallEntities);
// Allow the module to perform install tasks.
$this->invoke($module, 'install', [
$sync_status,
]);
// Record the fact that it was installed.
\Drupal::logger('system')->info('%module module installed.', [
'%module' => $module,
]);
}
// Install optional configuration from modules once all the modules have
// been properly installed. This is often where soft dependencies lie.
// @todo This code fixes \Drupal\Tests\help\Functional\HelpTest::testHelp().
foreach ($module_list as $module) {
$config_installer->installDefaultConfig('module', $module, DefaultConfigMode::Optional);
}
// Install optional configuration from other modules once all the modules
// have been properly installed. This is often where soft dependencies lie.
// @todo This code fixes
// \Drupal\Tests\forum\Functional\Module\DependencyTest::testUninstallDependents().
foreach ($module_list as $module) {
$config_installer->installDefaultConfig('module', $module, DefaultConfigMode::SiteOptional);
}
if (count($module_list) > 1) {
// Reset the container so static caches are rebuilt. This prevents static
// caches like those in \Drupal\views\ViewsData() from having stale data.
// @todo Adding this code fixed
// \Drupal\KernelTests\Config\DefaultConfigTest::testModuleConfig().
// \Drupal\Component\DependencyInjection\Container::reset() seems to
// offer a way to do this but was broken for the following reasons:
// 1. Needs to set itself to 'service_container' like the constructor.
// 2. Needs to persist services, user and session like
// DrupalKernel::initializeContainer()
// 3. Needs to work out how to work with things like
// KernelTestBase::register() which set synthetic like services.
$this->updateKernel([]);
// Refresh anything cached with core.extension. This prevents caches in
// things like \Drupal\views\ViewsData() from having stale data.
// @todo This fixes \Drupal\Tests\views\Functional\ViewsFormAlterTest().
Cache::invalidateTags([
'config:core.extension',
]);
}
}
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.