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 and namespace 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()

Installs a given list of modules.

Order of events:

  • Gather and add module dependencies to $module_list (if applicable).
  • For each module that is being installed:
  • Invoke hook_modules_installed().

To install test modules add

$settings['extension_discovery_scan_tests'] = TRUE;

to your settings.php.

Parameters

string[] $module_list: An array of module names.

bool $enable_dependencies: (optional) If TRUE, dependencies will automatically be installed in the correct order. This incurs a significant performance cost, so use FALSE if you know $module_list is already complete.

Return value

bool TRUE if the modules were successfully installed.

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.