X-Git-Url: http://aleph1.co.uk/gitweb/?a=blobdiff_plain;f=web%2Fcore%2Flib%2FDrupal%2FCore%2FExtension%2FModuleInstaller.php;h=65466a2d657542847a5f8429c5138631ee59bd6b;hb=refs%2Fheads%2Fd864;hp=f9500a5fc7db9c005b131c9511a92e1f0f8ab408;hpb=a2bd1bf0c2c1f1a17d188f4dc0726a45494cefae;p=yaffs-website diff --git a/web/core/lib/Drupal/Core/Extension/ModuleInstaller.php b/web/core/lib/Drupal/Core/Extension/ModuleInstaller.php index f9500a5fc..65466a2d6 100644 --- a/web/core/lib/Drupal/Core/Extension/ModuleInstaller.php +++ b/web/core/lib/Drupal/Core/Extension/ModuleInstaller.php @@ -14,6 +14,11 @@ use Drupal\Core\Serialization\Yaml; * * It registers the module in config, installs its own configuration, * installs the schema, updates the Drupal kernel and more. + * + * We don't inject dependencies yet, as we would need to reload them after + * each installation or uninstallation of a module. + * https://www.drupal.org/project/drupal/issues/2350111 for example tries to + * solve this dilemma. */ class ModuleInstaller implements ModuleInstallerInterface { @@ -93,8 +98,8 @@ class ModuleInstaller implements ModuleInstallerInterface { } // Add dependencies to the list. The new modules will be processed as - // the while loop continues. - while (list($module) = each($module_list)) { + // 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. @@ -136,6 +141,10 @@ class ModuleInstaller implements ModuleInstallerInterface { throw new ExtensionNameLengthException("Module name '$module' is over the maximum allowed length of " . DRUPAL_EXTENSION_NAME_MAX_LENGTH . ' characters'); } + // Load a new config object for each iteration, otherwise changes made + // in hook_install() are not reflected in $extension_config. + $extension_config = \Drupal::configFactory()->getEditable('core.extension'); + // Check the validity of the default configuration. This will throw // exceptions if the configuration is not valid. $config_installer->checkConfigurationToInstall('module', $module); @@ -166,7 +175,7 @@ class ModuleInstaller implements ModuleInstallerInterface { $module_filenames[$name] = $current_module_filenames[$name]; } else { - $module_path = drupal_get_path('module', $name); + $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); @@ -182,14 +191,25 @@ class ModuleInstaller implements ModuleInstallerInterface { $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'); + // 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); + // Replace the route provider service with a version that will rebuild + // if routes 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')); + // Allow modules to react prior to the installation of a module. $this->moduleHandler->invokeAll('module_preinstall', [$module]); @@ -279,10 +299,6 @@ class ModuleInstaller implements ModuleInstallerInterface { // @see https://www.drupal.org/node/2208429 \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'); @@ -293,7 +309,11 @@ class ModuleInstaller implements ModuleInstallerInterface { // 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 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 @@ -329,9 +349,8 @@ class ModuleInstaller implements ModuleInstallerInterface { if ($uninstall_dependents) { // Add dependent modules to the list. The new modules will be processed as - // the while loop continues. - $profile = drupal_get_profile(); - while (list($module) = each($module_list)) { + // the foreach loop continues. + foreach ($module_list as $module => $value) { foreach (array_keys($module_data[$module]->required_by) as $dependent) { if (!isset($module_data[$dependent])) { // The dependent module does not exist. @@ -339,7 +358,7 @@ class ModuleInstaller implements ModuleInstallerInterface { } // Skip already uninstalled modules. - if (isset($installed_modules[$dependent]) && !isset($module_list[$dependent]) && $dependent != $profile) { + if (isset($installed_modules[$dependent]) && !isset($module_list[$dependent])) { $module_list[$dependent] = $dependent; } } @@ -404,16 +423,11 @@ class ModuleInstaller implements ModuleInstallerInterface { $update_manager->uninstallEntityType($entity_type); } elseif ($entity_type->entityClassImplements(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. - $entity_type_id = $entity_type->id(); - /** @var \Drupal\Core\Entity\FieldableEntityStorageInterface $storage */ - $storage = $entity_manager->getStorage($entity_type_id); - foreach ($entity_manager->getFieldStorageDefinitions($entity_type_id) as $storage_definition) { - // @todo We need to trigger field purging here. - // See https://www.drupal.org/node/2282119. - if ($storage_definition->getProvider() == $module && !$storage->countFieldData($storage_definition, TRUE)) { + // The module being uninstalled might have added new fields to + // existing entity types. This will add them to the deleted fields + // repository so their data will be purged on cron. + foreach ($entity_manager->getFieldStorageDefinitions($entity_type->id()) as $storage_definition) { + if ($storage_definition->getProvider() == $module) { $update_manager->uninstallFieldStorageDefinition($storage_definition); } } @@ -437,10 +451,10 @@ class ModuleInstaller implements ModuleInstallerInterface { // Remove any potential cache bins provided by the module. $this->removeCacheBins($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'); + // 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(); // Clear plugin manager caches. \Drupal::getContainer()->get('plugin.cache_clearer')->clearCachedDefinitions(); @@ -494,34 +508,30 @@ class ModuleInstaller implements ModuleInstallerInterface { * The name of the module for which to remove all registered cache bins. */ protected function removeCacheBins($module) { - // Remove any cache bins defined by a module. $service_yaml_file = drupal_get_path('module', $module) . "/$module.services.yml"; - if (file_exists($service_yaml_file)) { - $definitions = Yaml::decode(file_get_contents($service_yaml_file)); - if (isset($definitions['services'])) { - foreach ($definitions['services'] as $id => $definition) { - if (isset($definition['tags'])) { - foreach ($definition['tags'] as $tag) { - // This works for the default cache registration and even in some - // cases when a non-default "super" factory is used. That should - // be extremely rare. - if ($tag['name'] == 'cache.bin' && isset($definition['factory_service']) && isset($definition['factory_method']) && !empty($definition['arguments'])) { - try { - $factory = \Drupal::service($definition['factory_service']); - if (method_exists($factory, $definition['factory_method'])) { - $backend = call_user_func_array([$factory, $definition['factory_method']], $definition['arguments']); - if ($backend instanceof CacheBackendInterface) { - $backend->removeBin(); - } - } - } - catch (\Exception $e) { - watchdog_exception('system', $e, 'Failed to remove cache bin defined by the service %id.', ['%id' => $id]); - } - } - } + if (!file_exists($service_yaml_file)) { + return; + } + + $definitions = Yaml::decode(file_get_contents($service_yaml_file)); + + $cache_bin_services = array_filter( + isset($definitions['services']) ? $definitions['services'] : [], + function ($definition) { + $tags = isset($definition['tags']) ? $definition['tags'] : []; + foreach ($tags as $tag) { + if (isset($tag['name']) && ($tag['name'] == 'cache.bin')) { + return TRUE; } } + return FALSE; + } + ); + + foreach (array_keys($cache_bin_services) as $service_id) { + $backend = $this->kernel->getContainer()->get($service_id); + if ($backend instanceof CacheBackendInterface) { + $backend->removeBin(); } } }