function ModuleInstaller::install

Same name in this branch
  1. 11.x core/lib/Drupal/Core/ProxyClass/Extension/ModuleInstaller.php \Drupal\Core\ProxyClass\Extension\ModuleInstaller::install()
Same name in other branches
  1. 9 core/lib/Drupal/Core/ProxyClass/Extension/ModuleInstaller.php \Drupal\Core\ProxyClass\Extension\ModuleInstaller::install()
  2. 9 core/lib/Drupal/Core/Extension/ModuleInstaller.php \Drupal\Core\Extension\ModuleInstaller::install()
  3. 8.9.x core/lib/Drupal/Core/ProxyClass/Extension/ModuleInstaller.php \Drupal\Core\ProxyClass\Extension\ModuleInstaller::install()
  4. 8.9.x core/lib/Drupal/Core/Extension/ModuleInstaller.php \Drupal\Core\Extension\ModuleInstaller::install()
  5. 10 core/lib/Drupal/Core/ProxyClass/Extension/ModuleInstaller.php \Drupal\Core\ProxyClass\Extension\ModuleInstaller::install()
  6. 10 core/lib/Drupal/Core/Extension/ModuleInstaller.php \Drupal\Core\Extension\ModuleInstaller::install()

Overrides ModuleInstallerInterface::install

File

core/lib/Drupal/Core/Extension/ModuleInstaller.php, line 122

Class

ModuleInstaller
Default implementation of the module installer.

Namespace

Drupal\Core\Extension

Code

public function install(array $module_list, $enable_dependencies = TRUE) {
    $extension_config = \Drupal::configFactory()->getEditable('core.extension');
    // Remove any modules that are already installed.
    $installed_modules = $extension_config->get('module') ?: [];
    // Only process currently uninstalled modules.
    $module_list = array_diff($module_list, array_keys($installed_modules));
    if (empty($module_list)) {
        // Nothing to do. All modules already installed.
        return TRUE;
    }
    // Get all module data so we can find dependencies and sort and find the
    // core requirements. The module list needs to be reset so that it can
    // re-scan and include any new modules that may have been added directly
    // into the filesystem.
    $module_data = \Drupal::service('extension.list.module')->reset()
        ->getList();
    foreach ($module_list as $module) {
        if (!empty($module_data[$module]->info['core_incompatible'])) {
            throw new MissingDependencyException("Unable to install modules: module '{$module}' is incompatible with this version of Drupal core.");
        }
        if ($module_data[$module]->info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::OBSOLETE) {
            throw new ObsoleteExtensionException("Unable to install modules: module '{$module}' is obsolete.");
        }
        if ($module_data[$module]->info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::DEPRECATED) {
            // phpcs:ignore Drupal.Semantics.FunctionTriggerError
            @trigger_error("The module '{$module}' is deprecated. See " . $module_data[$module]->info['lifecycle_link'], E_USER_DEPRECATED);
        }
    }
    if ($enable_dependencies) {
        $module_list = $module_list ? array_combine($module_list, $module_list) : [];
        if ($missing_modules = array_diff_key($module_list, $module_data)) {
            // One or more of the given modules doesn't exist.
            throw new MissingDependencyException(sprintf('Unable to install modules %s due to missing modules %s.', implode(', ', $module_list), implode(', ', $missing_modules)));
        }
        // Add dependencies to the list. The new modules will be processed as
        // the foreach loop continues.
        foreach ($module_list as $module => $value) {
            foreach (array_keys($module_data[$module]->requires) as $dependency) {
                if (!isset($module_data[$dependency])) {
                    // The dependency does not exist.
                    throw new MissingDependencyException("Unable to install modules: module '{$module}' is missing its dependency module {$dependency}.");
                }
                // Skip already installed modules.
                if (!isset($module_list[$dependency]) && !isset($installed_modules[$dependency])) {
                    if ($module_data[$dependency]->info['core_incompatible']) {
                        throw new MissingDependencyException("Unable to install modules: module '{$module}'. Its dependency module '{$dependency}' is incompatible with this version of Drupal core.");
                    }
                    $module_list[$dependency] = $dependency;
                }
            }
        }
        // Set the actual module weights.
        $module_list = array_map(function ($module) use ($module_data) {
            return $module_data[$module]->sort;
        }, $module_list);
        // Sort the module list by their weights (reverse).
        arsort($module_list);
        $module_list = array_keys($module_list);
    }
    // Required for module installation checks.
    include_once $this->root . '/core/includes/install.inc';
    
    /** @var \Drupal\Core\Config\ConfigInstaller $config_installer */
    $config_installer = \Drupal::service('config.installer');
    $sync_status = $config_installer->isSyncing();
    foreach ($module_list as $module) {
        // Throw an exception if the module name is too long.
        if (strlen($module) > DRUPAL_EXTENSION_NAME_MAX_LENGTH) {
            throw new ExtensionNameLengthException("Module name '{$module}' is over the maximum allowed length of " . DRUPAL_EXTENSION_NAME_MAX_LENGTH . ' characters');
        }
        // Throw an exception if a theme with the same name is enabled.
        $installed_themes = $extension_config->get('theme') ?: [];
        if (isset($installed_themes[$module])) {
            throw new ExtensionNameReservedException("Module name {$module} is already in use by an installed theme.");
        }
    }
    // Check the validity of the default configuration. This will throw
    // exceptions if the configuration is not valid.
    $config_installer->checkConfigurationToInstall('module', $module_list);
    // Some modules require a container rebuild before and after install.
    // Group modules such that as many are installed together as possible until
    // one needs a container rebuild.
    $module_groups = [];
    $index = 0;
    foreach ($module_list as $module) {
        // Ensure the container is rebuilt both before and after a module that
        // requires a container rebuild is installed.
        $container_rebuild_required = !empty($module_data[$module]->info['container_rebuild_required']);
        if ($container_rebuild_required && !empty($module_groups[$index])) {
            $index++;
        }
        $module_groups[$index][] = $module;
        if ($container_rebuild_required) {
            $index++;
        }
    }
    foreach ($module_groups as $modules) {
        $this->doInstall($modules, $installed_modules, $sync_status);
        // Refresh the installed modules list from configuration to preserve
        // module weight.
        $extension_config = \Drupal::configFactory()->getEditable('core.extension');
        $installed_modules = $extension_config->get('module') ?: [];
    }
    if (!InstallerKernel::installationAttempted()) {
        // If the container was rebuilt during hook_install() it might not have
        // the 'router.route_provider.old' service.
        if (\Drupal::hasService('router.route_provider.old')) {
            \Drupal::getContainer()->set('router.route_provider', \Drupal::service('router.route_provider.old'));
        }
        if (!\Drupal::service('router.route_provider.lazy_builder')->hasRebuilt()) {
            // Rebuild routes after installing module. This is done here on top of
            // \Drupal\Core\Routing\RouteBuilder::destruct to not run into errors on
            // fastCGI which executes ::destruct() after the module installation
            // page was sent already.
            \Drupal::service('router.builder')->rebuild();
        }
        else {
            // Rebuild the router immediately if it is marked as needing a rebuild.
            // @todo Work this through a bit more. This fixes
            //   \Drupal\Tests\standard\Functional\StandardTest::testStandard()
            //   after separately out the optional configuration install.
            \Drupal::service('router.builder')->rebuildIfNeeded();
        }
    }
    $this->moduleHandler
        ->invokeAll('modules_installed', [
        $module_list,
        $sync_status,
    ]);
    return TRUE;
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.