You are here

public function ModuleInstaller::uninstall in Zircon Profile 8

Same name in this branch
  1. 8 core/lib/Drupal/Core/Extension/ModuleInstaller.php \Drupal\Core\Extension\ModuleInstaller::uninstall()
  2. 8 core/lib/Drupal/Core/ProxyClass/Extension/ModuleInstaller.php \Drupal\Core\ProxyClass\Extension\ModuleInstaller::uninstall()
Same name and namespace in other branches
  1. 8.0 core/lib/Drupal/Core/Extension/ModuleInstaller.php \Drupal\Core\Extension\ModuleInstaller::uninstall()

Uninstalls a given list of modules.

Parameters

string[] $module_list: The modules to uninstall.

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

Return value

bool FALSE if one or more dependencies are missing, TRUE otherwise.

Overrides ModuleInstallerInterface::uninstall

See also

hook_module_preuninstall()

hook_uninstall()

hook_modules_uninstalled()

File

core/lib/Drupal/Core/Extension/ModuleInstaller.php, line 307
Contains \Drupal\Core\Extension\ModuleInstaller.

Class

ModuleInstaller
Default implementation of the module installer.

Namespace

Drupal\Core\Extension

Code

public function uninstall(array $module_list, $uninstall_dependents = TRUE) {

  // 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 (array_diff_key($module_list, $module_data)) {

    // One or more of the given modules doesn't exist.
    return FALSE;
  }
  $extension_config = \Drupal::configFactory()
    ->getEditable('core.extension');
  $installed_modules = $extension_config
    ->get('module') ?: array();
  if (!($module_list = array_intersect_key($module_list, $installed_modules))) {

    // Nothing to do. All modules already uninstalled.
    return TRUE;
  }
  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)) {
      foreach (array_keys($module_data[$module]->required_by) as $dependent) {
        if (!isset($module_data[$dependent])) {

          // The dependent module does not exist.
          return FALSE;
        }

        // Skip already uninstalled modules.
        if (isset($installed_modules[$dependent]) && !isset($module_list[$dependent]) && $dependent != $profile) {
          $module_list[$dependent] = $dependent;
        }
      }
    }
  }

  // Use the validators and throw an exception with the reasons.
  if ($reasons = $this
    ->validateUninstall($module_list)) {
    foreach ($reasons as $reason) {
      $reason_message[] = implode(', ', $reason);
    }
    throw new ModuleUninstallValidatorException('The following reasons prevents the modules from being uninstalled: ' . implode('; ', $reason_message));
  }

  // 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.
  asort($module_list);
  $module_list = array_keys($module_list);

  // Only process modules that are enabled. A module is only enabled if it is
  // configured as enabled. Custom or overridden module handlers might contain
  // the module already, which means that it might be loaded, but not
  // necessarily installed.
  foreach ($module_list as $module) {

    // Clean up all entity bundles (including fields) of every entity type
    // provided by the module that is being uninstalled.
    // @todo Clean this up in https://www.drupal.org/node/2350111.
    $entity_manager = \Drupal::entityManager();
    foreach ($entity_manager
      ->getDefinitions() as $entity_type_id => $entity_type) {
      if ($entity_type
        ->getProvider() == $module) {
        foreach (array_keys($entity_manager
          ->getBundleInfo($entity_type_id)) as $bundle) {
          $entity_manager
            ->onBundleDelete($bundle, $entity_type_id);
        }
      }
    }

    // Allow modules to react prior to the uninstallation of a module.
    $this->moduleHandler
      ->invokeAll('module_preuninstall', array(
      $module,
    ));

    // Uninstall the module.
    module_load_install($module);
    $this->moduleHandler
      ->invoke($module, 'uninstall');

    // Remove all configuration belonging to the module.
    \Drupal::service('config.manager')
      ->uninstall('module', $module);

    // Notify interested components that this module's entity types are being
    // deleted. For example, a SQL-based storage handler can use this as an
    // opportunity to drop the corresponding database tables.
    // @todo Clean this up in https://www.drupal.org/node/2350111.
    $update_manager = \Drupal::entityDefinitionUpdateManager();
    foreach ($entity_manager
      ->getDefinitions() as $entity_type) {
      if ($entity_type
        ->getProvider() == $module) {
        $update_manager
          ->uninstallEntityType($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.
        $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)) {
            $update_manager
              ->uninstallFieldStorageDefinition($storage_definition);
          }
        }
      }
    }

    // Remove the schema.
    drupal_uninstall_schema($module);

    // Remove the module's entry from the config. Don't check schema when
    // uninstalling a module since we are only clearing a key.
    \Drupal::configFactory()
      ->getEditable('core.extension')
      ->clear("module.{$module}")
      ->save(TRUE);

    // Update the module handler to remove the module.
    // The current ModuleHandler instance is obsolete with the kernel rebuild
    // below.
    $module_filenames = $this->moduleHandler
      ->getModuleList();
    unset($module_filenames[$module]);
    $this->moduleHandler
      ->setModuleList($module_filenames);

    // 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 plugin manager caches.
    \Drupal::getContainer()
      ->get('plugin.cache_clearer')
      ->clearCachedDefinitions();

    // Update the kernel to exclude the uninstalled modules.
    $this
      ->updateKernel($module_filenames);

    // Update the theme registry to remove the newly uninstalled module.
    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.
    // @see https://www.drupal.org/node/2208429
    \Drupal::service('theme_handler')
      ->refreshInfo();
    \Drupal::logger('system')
      ->info('%module module uninstalled.', array(
      '%module' => $module,
    ));
    $schema_store = \Drupal::keyValue('system.schema');
    $schema_store
      ->delete($module);

    /** @var \Drupal\Core\Update\UpdateRegistry $post_update_registry */
    $post_update_registry = \Drupal::service('update.post_update_registry');
    $post_update_registry
      ->filterOutInvokedUpdatesByModule($module);
  }
  \Drupal::service('router.builder')
    ->setRebuildNeeded();
  drupal_get_installed_schema_version(NULL, TRUE);

  // Let other modules react.
  $this->moduleHandler
    ->invokeAll('modules_uninstalled', array(
    $module_list,
  ));

  // Flush all persistent caches.
  // Any cache entry might implicitly depend on the uninstalled modules,
  // so clear all of them explicitly.
  $this->moduleHandler
    ->invokeAll('cache_flush');
  foreach (Cache::getBins() as $service_id => $cache_backend) {
    $cache_backend
      ->deleteAll();
  }
  return TRUE;
}