ModuleInstaller::install

public ModuleInstaller::install(array $module_list, $enable_dependencies = TRUE)

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

1
$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.

Throws

\Drupal\Core\Extension\MissingDependencyException Thrown when a requested module, or a dependency of one, can not be found.

Overrides ModuleInstallerInterface::install

See also

hook_module_preinstall()

hook_install()

hook_modules_installed()

File

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

Class

ModuleInstaller
Default implementation of the module installer.

Namespace

Drupal\Core\Extension

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
public function install(array $module_list, $enable_dependencies = TRUE) {
  $extension_config = \Drupal::configFactory()->getEditable('core.extension');
  if ($enable_dependencies) {
    // Get all module data so we can find dependencies and sort.
    $module_data = system_rebuild_module_data();
    $module_list = $module_list ? array_combine($module_list, $module_list) : array();
    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)));
    }
 
    // Only process currently uninstalled modules.
    $installed_modules = $extension_config->get('module') ? : array();
    if (!$module_list = array_diff_key($module_list, $installed_modules)) {
      // Nothing to do. All modules already installed.
      return TRUE;
    }
 
    // Add dependencies to the list. The new modules will be processed as
    // the while loop continues.
    while (list($module) = each($module_list)) {
      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])) {
          $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();
  if ($sync_status) {
    $source_storage = $config_installer->getSourceStorage();
  }
  $modules_installed = array();
  foreach ($module_list as $module) {
    $enabled = $extension_config->get("module.$module") !== NULL;
    if (!$enabled) {
      // 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');
      }
 
      // Check the validity of the default configuration. This will throw
      // exceptions if the configuration is not valid.
      $config_installer->checkConfigurationToInstall('module', $module);
 
      // Save this data without checking schema. This is a performance
      // improvement for module installation.
      $extension_config
      ->set("module.$module", 0)
        ->set('module', module_config_sort($extension_config->get('module')))
        ->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 = array();
      foreach ($current_modules as $name => $weight) {
        if (isset($current_module_filenames[$name])) {
          $module_filenames[$name] = $current_module_filenames[$name];
        }
        else {
          $module_path = drupal_get_path('module', $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 load the module's code.
      // This allows the module to participate in hooks and its existence to
      // be discovered by other modules.
      // The current ModuleHandler instance is obsolete with the kernel
      // rebuild below.
      $this->moduleHandler->setModuleList($module_filenames);
      $this->moduleHandler->load($module);
      module_load_install($module);
 
      // Clear the static cache of system_rebuild_module_data() to pick up the
      // new module, since it merges the installation status of modules into
      // its statically cached list.
      drupal_static_reset('system_rebuild_module_data');
 
      // Update the kernel to include it.
      $this->updateKernel($module_filenames);
 
      // Allow modules to react prior to the installation of a module.
      $this->moduleHandler->invokeAll('module_preinstall', array($module));
 
      // Now install the module's schema if necessary.
      drupal_install_schema($module);
 
      // Clear plugin manager caches.
      \Drupal::getContainer()->get('plugin.cache_clearer')->clearCachedDefinitions();
 
      // 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 = drupal_get_schema_versions($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_manager = \Drupal::entityManager();
      $update_manager = \Drupal::entityDefinitionUpdateManager();
      foreach ($entity_manager->getDefinitions() as $entity_type) {
        if ($entity_type->getProvider() == $module) {
          $update_manager->installEntityType($entity_type);
        }
        elseif ($entity_type->isSubclassOf(FieldableEntityInterface::CLASS)) {
          // 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_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) {
                watchdog_exception('system', $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');
      if ($sync_status) {
        $config_installer
        ->setSyncing(TRUE)
          ->setSourceStorage($source_storage);
      }
      \Drupal::service('config.installer')->installDefaultConfig('module', $module);
 
      // 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->moduleHandler->invoke($module, 'update_last_removed')) {
        $version = max($version, $last_removed);
      }
      drupal_set_installed_schema_version($module, $version);
 
      // Ensure that all post_update functions are registered already.
      /** @var \Drupal\Core\Update\UpdateRegistry $post_update_registry */
      $post_update_registry = \Drupal::service('update.post_update_registry');
      $post_update_registry->registerInvokedUpdates($post_update_registry->getModuleUpdateFunctions($module));
 
      // Record the fact that it was installed.
      $modules_installed[] = $module;
 
      // 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_theme_rebuild();
 
      // Modules can alter theme info, so refresh theme data.
      // @todo ThemeHandler cannot be injected into ModuleHandler, since that
      //   causes a circular service dependency.
      \Drupal::service('theme_handler')->refreshInfo();
 
      // In order to make uninstalling transactional if anything uses routes.
      \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'));
 
      // Allow the module to perform install tasks.
      $this->moduleHandler->invoke($module, 'install');
 
      // Record the fact that it was installed.
      \Drupal::logger('system')->info('%module module installed.', array('%module' => $module));
    }
  }
 
  // If any modules were newly installed, invoke hook_modules_installed().
  if (!empty($modules_installed)) {
    \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();
    }
 
    $this->moduleHandler->invokeAll('modules_installed', array($modules_installed));
  }
 
  return TRUE;
}
doc_Drupal
2025-01-10 15:47:30
Comments
Leave a Comment

Please login to continue.