public function ModuleInstaller::uninstall

public ModuleInstaller::uninstall(array $module_list, $uninstall_dependents = TRUE)

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 314

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 prevent 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);

    // 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'));

    // 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);
  }
  // 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 uninstallation page
  // was sent already.
  \Drupal::service('router.builder')->rebuild();
  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;
}

© 2001–2016 by the original authors
Licensed under the GNU General Public License, version 2 and later.
Drupal is a registered trademark of Dries Buytaert.
https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Extension!ModuleInstaller.php/function/ModuleInstaller::uninstall/8.1.x