You are here

class ConfigInstaller in Drupal 9

Same name in this branch
  1. 9 core/lib/Drupal/Core/Config/ConfigInstaller.php \Drupal\Core\Config\ConfigInstaller
  2. 9 core/lib/Drupal/Core/ProxyClass/Config/ConfigInstaller.php \Drupal\Core\ProxyClass\Config\ConfigInstaller
Same name and namespace in other branches
  1. 8 core/lib/Drupal/Core/Config/ConfigInstaller.php \Drupal\Core\Config\ConfigInstaller

Hierarchy

Expanded class hierarchy of ConfigInstaller

1 string reference to 'ConfigInstaller'
core.services.yml in core/core.services.yml
core/core.services.yml
1 service uses ConfigInstaller
config.installer in core/core.services.yml
Drupal\Core\Config\ConfigInstaller

File

core/lib/Drupal/Core/Config/ConfigInstaller.php, line 11

Namespace

Drupal\Core\Config
View source
class ConfigInstaller implements ConfigInstallerInterface {

  /**
   * The configuration factory.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * The active configuration storages, keyed by collection.
   *
   * @var \Drupal\Core\Config\StorageInterface[]
   */
  protected $activeStorages;

  /**
   * The typed configuration manager.
   *
   * @var \Drupal\Core\Config\TypedConfigManagerInterface
   */
  protected $typedConfig;

  /**
   * The configuration manager.
   *
   * @var \Drupal\Core\Config\ConfigManagerInterface
   */
  protected $configManager;

  /**
   * The event dispatcher.
   *
   * @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface
   */
  protected $eventDispatcher;

  /**
   * The configuration storage that provides the default configuration.
   *
   * @var \Drupal\Core\Config\StorageInterface
   */
  protected $sourceStorage;

  /**
   * Is configuration being created as part of a configuration sync.
   *
   * @var bool
   */
  protected $isSyncing = FALSE;

  /**
   * The name of the currently active installation profile.
   *
   * @var string
   */
  protected $installProfile;

  /**
   * The extension path resolver.
   *
   * @var \Drupal\Core\Extension\ExtensionPathResolver
   */
  protected $extensionPathResolver;

  /**
   * Constructs the configuration installer.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The configuration factory.
   * @param \Drupal\Core\Config\StorageInterface $active_storage
   *   The active configuration storage.
   * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
   *   The typed configuration manager.
   * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager
   *   The configuration manager.
   * @param \Symfony\Contracts\EventDispatcher\EventDispatcherInterface $event_dispatcher
   *   The event dispatcher.
   * @param string $install_profile
   *   The name of the currently active installation profile.
   * @param \Drupal\Core\Extension\ExtensionPathResolver $extension_path_resolver
   *   The extension path resolver.
   */
  public function __construct(ConfigFactoryInterface $config_factory, StorageInterface $active_storage, TypedConfigManagerInterface $typed_config, ConfigManagerInterface $config_manager, EventDispatcherInterface $event_dispatcher, $install_profile, ExtensionPathResolver $extension_path_resolver = NULL) {
    $this->configFactory = $config_factory;
    $this->activeStorages[$active_storage
      ->getCollectionName()] = $active_storage;
    $this->typedConfig = $typed_config;
    $this->configManager = $config_manager;
    $this->eventDispatcher = $event_dispatcher;
    $this->installProfile = $install_profile;
    if (!$extension_path_resolver) {
      @trigger_error('Calling ConfigInstaller::__construct() without the $extension_path_resolver argument is deprecated in drupal:9.3.0 and is removed from drupal:10.0.0. See https://www.drupal.org/node/2940438', E_USER_DEPRECATED);
      $extension_path_resolver = \Drupal::service('extension.path.resolver');
    }
    $this->extensionPathResolver = $extension_path_resolver;
  }

  /**
   * {@inheritdoc}
   */
  public function installDefaultConfig($type, $name) {
    $extension_path = $this->extensionPathResolver
      ->getPath($type, $name);

    // Refresh the schema cache if the extension provides configuration schema
    // or is a theme.
    if (is_dir($extension_path . '/' . InstallStorage::CONFIG_SCHEMA_DIRECTORY) || $type == 'theme') {
      $this->typedConfig
        ->clearCachedDefinitions();
    }
    $default_install_path = $this
      ->getDefaultConfigDirectory($type, $name);
    if (is_dir($default_install_path)) {
      if (!$this
        ->isSyncing()) {
        $storage = new FileStorage($default_install_path, StorageInterface::DEFAULT_COLLECTION);
        $prefix = '';
      }
      else {

        // The configuration importer sets the source storage on the config
        // installer. The configuration importer handles all of the
        // configuration entity imports. We only need to ensure that simple
        // configuration is created when the extension is installed.
        $storage = $this
          ->getSourceStorage();
        $prefix = $name . '.';
      }

      // Gets profile storages to search for overrides if necessary.
      $profile_storages = $this
        ->getProfileStorages($name);

      // Gather information about all the supported collections.
      $collection_info = $this->configManager
        ->getConfigCollectionInfo();
      foreach ($collection_info
        ->getCollectionNames() as $collection) {
        $config_to_create = $this
          ->getConfigToCreate($storage, $collection, $prefix, $profile_storages);
        if ($name == $this
          ->drupalGetProfile()) {

          // If we're installing a profile ensure simple configuration that
          // already exists is excluded as it will have already been written.
          // This means that if the configuration is changed by something else
          // during the install it will not be overwritten again.
          $existing_configuration = array_filter($this
            ->getActiveStorages($collection)
            ->listAll(), function ($config_name) {
            return !$this->configManager
              ->getEntityTypeIdByName($config_name);
          });
          $config_to_create = array_diff_key($config_to_create, array_flip($existing_configuration));
        }
        if (!empty($config_to_create)) {
          $this
            ->createConfiguration($collection, $config_to_create);
        }
      }
    }

    // During a drupal installation optional configuration is installed at the
    // end of the installation process. Once the install profile is installed
    // optional configuration should be installed as usual.
    // @see install_install_profile()
    $profile_installed = in_array($this
      ->drupalGetProfile(), $this
      ->getEnabledExtensions(), TRUE);
    if (!$this
      ->isSyncing() && (!InstallerKernel::installationAttempted() || $profile_installed)) {
      $optional_install_path = $extension_path . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;
      if (is_dir($optional_install_path)) {

        // Install any optional config the module provides.
        $storage = new FileStorage($optional_install_path, StorageInterface::DEFAULT_COLLECTION);
        $this
          ->installOptionalConfig($storage, '');
      }

      // Install any optional configuration entities whose dependencies can now
      // be met. This searches all the installed modules config/optional
      // directories.
      $storage = new ExtensionInstallStorage($this
        ->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, FALSE, $this->installProfile);
      $this
        ->installOptionalConfig($storage, [
        $type => $name,
      ]);
    }

    // Reset all the static caches and list caches.
    $this->configFactory
      ->reset();
  }

  /**
   * {@inheritdoc}
   */
  public function installOptionalConfig(StorageInterface $storage = NULL, $dependency = []) {
    $profile = $this
      ->drupalGetProfile();
    $enabled_extensions = $this
      ->getEnabledExtensions();
    $existing_config = $this
      ->getActiveStorages()
      ->listAll();

    // Create the storages to read configuration from.
    if (!$storage) {

      // Search the install profile's optional configuration too.
      $storage = new ExtensionInstallStorage($this
        ->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, TRUE, $this->installProfile);

      // The extension install storage ensures that overrides are used.
      $profile_storage = NULL;
    }
    elseif (!empty($profile)) {

      // Creates a profile storage to search for overrides.
      $profile_install_path = $this->extensionPathResolver
        ->getPath('module', $profile) . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;
      $profile_storage = new FileStorage($profile_install_path, StorageInterface::DEFAULT_COLLECTION);
    }
    else {

      // Profile has not been set yet. For example during the first steps of the
      // installer or during unit tests.
      $profile_storage = NULL;
    }

    // Build the list of possible configuration to create.
    $list = $storage
      ->listAll();
    if ($profile_storage && !empty($dependency)) {

      // Only add the optional profile configuration into the list if we are
      // have a dependency to check. This ensures that optional profile
      // configuration is not unexpectedly re-created after being deleted.
      $list = array_unique(array_merge($list, $profile_storage
        ->listAll()));
    }

    // Filter the list of configuration to only include configuration that
    // should be created.
    $list = array_filter($list, function ($config_name) use ($existing_config) {

      // Only list configuration that:
      // - does not already exist
      // - is a configuration entity (this also excludes config that has an
      //   implicit dependency on modules that are not yet installed)
      return !in_array($config_name, $existing_config) && $this->configManager
        ->getEntityTypeIdByName($config_name);
    });
    $all_config = array_merge($existing_config, $list);
    $all_config = array_combine($all_config, $all_config);
    $config_to_create = $storage
      ->readMultiple($list);

    // Check to see if the corresponding override storage has any overrides or
    // new configuration that can be installed.
    if ($profile_storage) {
      $config_to_create = $profile_storage
        ->readMultiple($list) + $config_to_create;
    }

    // Sort $config_to_create in the order of the least dependent first.
    $dependency_manager = new ConfigDependencyManager();
    $dependency_manager
      ->setData($config_to_create);
    $config_to_create = array_merge(array_flip($dependency_manager
      ->sortAll()), $config_to_create);
    if (!empty($dependency)) {

      // In order to work out dependencies we need the full config graph.
      $dependency_manager
        ->setData($this
        ->getActiveStorages()
        ->readMultiple($existing_config) + $config_to_create);
      $dependencies = $dependency_manager
        ->getDependentEntities(key($dependency), reset($dependency));
    }
    foreach ($config_to_create as $config_name => $data) {

      // Remove configuration where its dependencies cannot be met.
      $remove = !$this
        ->validateDependencies($config_name, $data, $enabled_extensions, $all_config);

      // Remove configuration that is not dependent on $dependency, if it is
      // defined.
      if (!$remove && !empty($dependency)) {
        $remove = !isset($dependencies[$config_name]);
      }
      if ($remove) {

        // Remove from the list of configuration to create.
        unset($config_to_create[$config_name]);

        // Remove from the list of all configuration. This ensures that any
        // configuration that depends on this configuration is also removed.
        unset($all_config[$config_name]);
      }
    }

    // Create the optional configuration if there is any left after filtering.
    if (!empty($config_to_create)) {
      $this
        ->createConfiguration(StorageInterface::DEFAULT_COLLECTION, $config_to_create, TRUE);
    }
  }

  /**
   * Gets configuration data from the provided storage to create.
   *
   * @param StorageInterface $storage
   *   The configuration storage to read configuration from.
   * @param string $collection
   *   The configuration collection to use.
   * @param string $prefix
   *   (optional) Limit to configuration starting with the provided string.
   * @param \Drupal\Core\Config\StorageInterface[] $profile_storages
   *   An array of storage interfaces containing profile configuration to check
   *   for overrides.
   *
   * @return array
   *   An array of configuration data read from the source storage keyed by the
   *   configuration object name.
   */
  protected function getConfigToCreate(StorageInterface $storage, $collection, $prefix = '', array $profile_storages = []) {
    if ($storage
      ->getCollectionName() != $collection) {
      $storage = $storage
        ->createCollection($collection);
    }
    $data = $storage
      ->readMultiple($storage
      ->listAll($prefix));

    // Check to see if configuration provided by the install profile has any
    // overrides.
    foreach ($profile_storages as $profile_storage) {
      if ($profile_storage
        ->getCollectionName() != $collection) {
        $profile_storage = $profile_storage
          ->createCollection($collection);
      }
      $profile_overrides = $profile_storage
        ->readMultiple(array_keys($data));
      if (InstallerKernel::installationAttempted()) {

        // During installation overrides of simple configuration are applied
        // immediately. Configuration entities that are overridden will be
        // updated when the profile is installed. This allows install profiles
        // to provide configuration entity overrides that have dependencies that
        // cannot be met when the module provided configuration entity is
        // created.
        foreach ($profile_overrides as $name => $override_data) {

          // The only way to determine if they are configuration entities is the
          // presence of a dependencies key.
          if (!isset($override_data['dependencies'])) {
            $data[$name] = $override_data;
          }
        }
      }
      else {

        // Allow install profiles to provide overridden configuration for new
        // extensions that are being enabled after Drupal has already been
        // installed. This allows profiles to ship new extensions in version
        // updates without requiring additional code to apply the overrides.
        $data = $profile_overrides + $data;
      }
    }
    return $data;
  }

  /**
   * Creates configuration in a collection based on the provided list.
   *
   * @param string $collection
   *   The configuration collection.
   * @param array $config_to_create
   *   An array of configuration data to create, keyed by name.
   */
  protected function createConfiguration($collection, array $config_to_create) {

    // Order the configuration to install in the order of dependencies.
    if ($collection == StorageInterface::DEFAULT_COLLECTION) {
      $dependency_manager = new ConfigDependencyManager();
      $config_names = $dependency_manager
        ->setData($config_to_create)
        ->sortAll();
    }
    else {
      $config_names = array_keys($config_to_create);
    }
    foreach ($config_names as $name) {

      // Allow config factory overriders to use a custom configuration object if
      // they are responsible for the collection.
      $overrider = $this->configManager
        ->getConfigCollectionInfo()
        ->getOverrideService($collection);
      if ($overrider) {
        $new_config = $overrider
          ->createConfigObject($name, $collection);
      }
      else {
        $new_config = new Config($name, $this
          ->getActiveStorages($collection), $this->eventDispatcher, $this->typedConfig);
      }
      if ($config_to_create[$name] !== FALSE) {
        $new_config
          ->setData($config_to_create[$name]);

        // Add a hash to configuration created through the installer so it is
        // possible to know if the configuration was created by installing an
        // extension and to track which version of the default config was used.
        if (!$this
          ->isSyncing() && $collection == StorageInterface::DEFAULT_COLLECTION) {
          $new_config
            ->set('_core.default_config_hash', Crypt::hashBase64(serialize($config_to_create[$name])));
        }
      }
      if ($collection == StorageInterface::DEFAULT_COLLECTION && ($entity_type = $this->configManager
        ->getEntityTypeIdByName($name))) {

        // If we are syncing do not create configuration entities. Pluggable
        // configuration entities can have dependencies on modules that are
        // not yet enabled. This approach means that any code that expects
        // default configuration entities to exist will be unstable after the
        // module has been enabled and before the config entity has been
        // imported.
        if ($this
          ->isSyncing()) {
          continue;
        }

        /** @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $entity_storage */
        $entity_storage = $this->configManager
          ->getEntityTypeManager()
          ->getStorage($entity_type);
        $id = $entity_storage
          ->getIDFromConfigName($name, $entity_storage
          ->getEntityType()
          ->getConfigPrefix());

        // It is possible that secondary writes can occur during configuration
        // creation. Updates of such configuration are allowed.
        if ($this
          ->getActiveStorages($collection)
          ->exists($name)) {
          $entity = $entity_storage
            ->load($id);
          $entity = $entity_storage
            ->updateFromStorageRecord($entity, $new_config
            ->get());
        }
        else {
          $entity = $entity_storage
            ->createFromStorageRecord($new_config
            ->get());
        }
        if ($entity
          ->isInstallable()) {
          $entity
            ->trustData()
            ->save();
          if ($id !== $entity
            ->id()) {
            trigger_error(sprintf('The configuration name "%s" does not match the ID "%s"', $name, $entity
              ->id()), E_USER_WARNING);
          }
        }
      }
      else {
        $new_config
          ->save(TRUE);
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function installCollectionDefaultConfig($collection) {
    $storage = new ExtensionInstallStorage($this
      ->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection, InstallerKernel::installationAttempted(), $this->installProfile);

    // Only install configuration for enabled extensions.
    $enabled_extensions = $this
      ->getEnabledExtensions();
    $config_to_install = array_filter($storage
      ->listAll(), function ($config_name) use ($enabled_extensions) {
      $provider = mb_substr($config_name, 0, strpos($config_name, '.'));
      return in_array($provider, $enabled_extensions);
    });
    if (!empty($config_to_install)) {
      $this
        ->createConfiguration($collection, $storage
        ->readMultiple($config_to_install));

      // Reset all the static caches and list caches.
      $this->configFactory
        ->reset();
    }
  }

  /**
   * {@inheritdoc}
   */
  public function setSourceStorage(StorageInterface $storage) {
    $this->sourceStorage = $storage;
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getSourceStorage() {
    return $this->sourceStorage;
  }

  /**
   * Gets the configuration storage that provides the active configuration.
   *
   * @param string $collection
   *   (optional) The configuration collection. Defaults to the default
   *   collection.
   *
   * @return \Drupal\Core\Config\StorageInterface
   *   The configuration storage that provides the default configuration.
   */
  protected function getActiveStorages($collection = StorageInterface::DEFAULT_COLLECTION) {
    if (!isset($this->activeStorages[$collection])) {
      $this->activeStorages[$collection] = reset($this->activeStorages)
        ->createCollection($collection);
    }
    return $this->activeStorages[$collection];
  }

  /**
   * {@inheritdoc}
   */
  public function setSyncing($status) {
    if (!$status) {
      $this->sourceStorage = NULL;
    }
    $this->isSyncing = $status;
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function isSyncing() {
    return $this->isSyncing;
  }

  /**
   * Finds pre-existing configuration objects for the provided extension.
   *
   * Extensions can not be installed if configuration objects exist in the
   * active storage with the same names. This can happen in a number of ways,
   * commonly:
   * - if a user has created configuration with the same name as that provided
   *   by the extension.
   * - if the extension provides default configuration that does not depend on
   *   it and the extension has been uninstalled and is about to the
   *   reinstalled.
   *
   * @return array
   *   Array of configuration object names that already exist keyed by
   *   collection.
   */
  protected function findPreExistingConfiguration(StorageInterface $storage) {
    $existing_configuration = [];

    // Gather information about all the supported collections.
    $collection_info = $this->configManager
      ->getConfigCollectionInfo();
    foreach ($collection_info
      ->getCollectionNames() as $collection) {
      $config_to_create = array_keys($this
        ->getConfigToCreate($storage, $collection));
      $active_storage = $this
        ->getActiveStorages($collection);
      foreach ($config_to_create as $config_name) {
        if ($active_storage
          ->exists($config_name)) {
          $existing_configuration[$collection][] = $config_name;
        }
      }
    }
    return $existing_configuration;
  }

  /**
   * {@inheritdoc}
   */
  public function checkConfigurationToInstall($type, $name) {
    if ($this
      ->isSyncing()) {

      // Configuration is assumed to already be checked by the config importer
      // validation events.
      return;
    }
    $config_install_path = $this
      ->getDefaultConfigDirectory($type, $name);
    if (!is_dir($config_install_path)) {
      return;
    }
    $storage = new FileStorage($config_install_path, StorageInterface::DEFAULT_COLLECTION);
    $enabled_extensions = $this
      ->getEnabledExtensions();

    // Add the extension that will be enabled to the list of enabled extensions.
    $enabled_extensions[] = $name;

    // Gets profile storages to search for overrides if necessary.
    $profile_storages = $this
      ->getProfileStorages($name);

    // Check the dependencies of configuration provided by the module.
    list($invalid_default_config, $missing_dependencies) = $this
      ->findDefaultConfigWithUnmetDependencies($storage, $enabled_extensions, $profile_storages);
    if (!empty($invalid_default_config)) {
      throw UnmetDependenciesException::create($name, array_unique($missing_dependencies, SORT_REGULAR));
    }

    // Install profiles can not have config clashes. Configuration that
    // has the same name as a module's configuration will be used instead.
    if ($name != $this
      ->drupalGetProfile()) {

      // Throw an exception if the module being installed contains configuration
      // that already exists. Additionally, can not continue installing more
      // modules because those may depend on the current module being installed.
      $existing_configuration = $this
        ->findPreExistingConfiguration($storage);
      if (!empty($existing_configuration)) {
        throw PreExistingConfigException::create($name, $existing_configuration);
      }
    }
  }

  /**
   * Finds default configuration with unmet dependencies.
   *
   * @param \Drupal\Core\Config\StorageInterface $storage
   *   The storage containing the default configuration.
   * @param array $enabled_extensions
   *   A list of all the currently enabled modules and themes.
   * @param \Drupal\Core\Config\StorageInterface[] $profile_storages
   *   An array of storage interfaces containing profile configuration to check
   *   for overrides.
   *
   * @return array
   *   An array containing:
   *     - A list of configuration that has unmet dependencies.
   *     - An array that will be filled with the missing dependency names, keyed
   *       by the dependents' names.
   */
  protected function findDefaultConfigWithUnmetDependencies(StorageInterface $storage, array $enabled_extensions, array $profile_storages = []) {
    $missing_dependencies = [];
    $config_to_create = $this
      ->getConfigToCreate($storage, StorageInterface::DEFAULT_COLLECTION, '', $profile_storages);
    $all_config = array_merge($this->configFactory
      ->listAll(), array_keys($config_to_create));
    foreach ($config_to_create as $config_name => $config) {
      if ($missing = $this
        ->getMissingDependencies($config_name, $config, $enabled_extensions, $all_config)) {
        $missing_dependencies[$config_name] = $missing;
      }
    }
    return [
      array_intersect_key($config_to_create, $missing_dependencies),
      $missing_dependencies,
    ];
  }

  /**
   * Validates an array of config data that contains dependency information.
   *
   * @param string $config_name
   *   The name of the configuration object that is being validated.
   * @param array $data
   *   Configuration data.
   * @param array $enabled_extensions
   *   A list of all the currently enabled modules and themes.
   * @param array $all_config
   *   A list of all the active configuration names.
   *
   * @return bool
   *   TRUE if all dependencies are present, FALSE otherwise.
   */
  protected function validateDependencies($config_name, array $data, array $enabled_extensions, array $all_config) {
    if (!isset($data['dependencies'])) {

      // Simple config or a config entity without dependencies.
      list($provider) = explode('.', $config_name, 2);
      return in_array($provider, $enabled_extensions, TRUE);
    }
    $missing = $this
      ->getMissingDependencies($config_name, $data, $enabled_extensions, $all_config);
    return empty($missing);
  }

  /**
   * Returns an array of missing dependencies for a config object.
   *
   * @param string $config_name
   *   The name of the configuration object that is being validated.
   * @param array $data
   *   Configuration data.
   * @param array $enabled_extensions
   *   A list of all the currently enabled modules and themes.
   * @param array $all_config
   *   A list of all the active configuration names.
   *
   * @return array
   *   A list of missing config dependencies.
   */
  protected function getMissingDependencies($config_name, array $data, array $enabled_extensions, array $all_config) {
    $missing = [];
    if (isset($data['dependencies'])) {
      list($provider) = explode('.', $config_name, 2);
      $all_dependencies = $data['dependencies'];

      // Ensure enforced dependencies are included.
      if (isset($all_dependencies['enforced'])) {
        $all_dependencies = array_merge($all_dependencies, $data['dependencies']['enforced']);
        unset($all_dependencies['enforced']);
      }

      // Ensure the configuration entity type provider is in the list of
      // dependencies.
      if (!isset($all_dependencies['module']) || !in_array($provider, $all_dependencies['module'])) {
        $all_dependencies['module'][] = $provider;
      }
      foreach ($all_dependencies as $type => $dependencies) {
        $list_to_check = [];
        switch ($type) {
          case 'module':
          case 'theme':
            $list_to_check = $enabled_extensions;
            break;
          case 'config':
            $list_to_check = $all_config;
            break;
        }
        if (!empty($list_to_check)) {
          $missing = array_merge($missing, array_diff($dependencies, $list_to_check));
        }
      }
    }
    return $missing;
  }

  /**
   * Gets the list of enabled extensions including both modules and themes.
   *
   * @return array
   *   A list of enabled extensions which includes both modules and themes.
   */
  protected function getEnabledExtensions() {

    // Read enabled extensions directly from configuration to avoid circular
    // dependencies on ModuleHandler and ThemeHandler.
    $extension_config = $this->configFactory
      ->get('core.extension');
    $enabled_extensions = (array) $extension_config
      ->get('module');
    $enabled_extensions += (array) $extension_config
      ->get('theme');

    // Core can provide configuration.
    $enabled_extensions['core'] = 'core';
    return array_keys($enabled_extensions);
  }

  /**
   * Gets the profile storage to use to check for profile overrides.
   *
   * The install profile can override module configuration during a module
   * install. Both the install and optional directories are checked for matching
   * configuration. This allows profiles to override default configuration for
   * modules they do not depend on.
   *
   * @param string $installing_name
   *   (optional) The name of the extension currently being installed.
   *
   * @return \Drupal\Core\Config\StorageInterface[]|null
   *   Storages to access configuration from the installation profile. If we're
   *   installing the profile itself, then it will return an empty array as the
   *   profile storage should not be used.
   */
  protected function getProfileStorages($installing_name = '') {
    $profile = $this
      ->drupalGetProfile();
    $profile_storages = [];
    if ($profile && $profile != $installing_name) {
      $profile_path = $this->extensionPathResolver
        ->getPath('module', $profile);
      foreach ([
        InstallStorage::CONFIG_INSTALL_DIRECTORY,
        InstallStorage::CONFIG_OPTIONAL_DIRECTORY,
      ] as $directory) {
        if (is_dir($profile_path . '/' . $directory)) {
          $profile_storages[] = new FileStorage($profile_path . '/' . $directory, StorageInterface::DEFAULT_COLLECTION);
        }
      }
    }
    return $profile_storages;
  }

  /**
   * Gets an extension's default configuration directory.
   *
   * @param string $type
   *   Type of extension to install.
   * @param string $name
   *   Name of extension to install.
   *
   * @return string
   *   The extension's default configuration directory.
   */
  protected function getDefaultConfigDirectory($type, $name) {
    return $this->extensionPathResolver
      ->getPath($type, $name) . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY;
  }

  /**
   * Wrapper for drupal_get_path().
   *
   * @param $type
   *   The type of the item; one of 'core', 'profile', 'module', 'theme', or
   *   'theme_engine'.
   * @param $name
   *   The name of the item for which the path is requested. Ignored for
   *   $type 'core'.
   *
   * @return string
   *   The path to the requested item or an empty string if the item is not
   *   found.
   *
   * @deprecated in drupal:9.3.0 and is removed from drupal:10.0.0. Use
   *   \Drupal\Core\Extension\ExtensionList::getPath() instead.
   *
   * @see https://www.drupal.org/node/2940438
   */
  protected function drupalGetPath($type, $name) {
    @trigger_error(__METHOD__ . ' is deprecated in drupal:9.3.0 and is removed from drupal:10.0.0. Use \\Drupal\\Core\\Extension\\ExtensionPathResolver::getPath() instead. See https://www.drupal.org/node/2940438', E_USER_DEPRECATED);
    return $this->extensionPathResolver
      ->getPath($type, $name);
  }

  /**
   * Gets the install profile from settings.
   *
   * @return string|null
   *   The name of the installation profile or NULL if no installation profile
   *   is currently active. This is the case for example during the first steps
   *   of the installer or during unit tests.
   */
  protected function drupalGetProfile() {
    return $this->installProfile;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ConfigInstaller::$activeStorages protected property The active configuration storages, keyed by collection.
ConfigInstaller::$configFactory protected property The configuration factory.
ConfigInstaller::$configManager protected property The configuration manager.
ConfigInstaller::$eventDispatcher protected property The event dispatcher.
ConfigInstaller::$extensionPathResolver protected property The extension path resolver.
ConfigInstaller::$installProfile protected property The name of the currently active installation profile.
ConfigInstaller::$isSyncing protected property Is configuration being created as part of a configuration sync.
ConfigInstaller::$sourceStorage protected property The configuration storage that provides the default configuration.
ConfigInstaller::$typedConfig protected property The typed configuration manager.
ConfigInstaller::checkConfigurationToInstall public function Checks the configuration that will be installed for an extension. Overrides ConfigInstallerInterface::checkConfigurationToInstall
ConfigInstaller::createConfiguration protected function Creates configuration in a collection based on the provided list.
ConfigInstaller::drupalGetPath Deprecated protected function Wrapper for drupal_get_path().
ConfigInstaller::drupalGetProfile protected function Gets the install profile from settings.
ConfigInstaller::findDefaultConfigWithUnmetDependencies protected function Finds default configuration with unmet dependencies.
ConfigInstaller::findPreExistingConfiguration protected function Finds pre-existing configuration objects for the provided extension.
ConfigInstaller::getActiveStorages protected function Gets the configuration storage that provides the active configuration.
ConfigInstaller::getConfigToCreate protected function Gets configuration data from the provided storage to create.
ConfigInstaller::getDefaultConfigDirectory protected function Gets an extension's default configuration directory.
ConfigInstaller::getEnabledExtensions protected function Gets the list of enabled extensions including both modules and themes.
ConfigInstaller::getMissingDependencies protected function Returns an array of missing dependencies for a config object.
ConfigInstaller::getProfileStorages protected function Gets the profile storage to use to check for profile overrides.
ConfigInstaller::getSourceStorage public function Gets the configuration storage that provides the default configuration. Overrides ConfigInstallerInterface::getSourceStorage
ConfigInstaller::installCollectionDefaultConfig public function Installs all default configuration in the specified collection. Overrides ConfigInstallerInterface::installCollectionDefaultConfig
ConfigInstaller::installDefaultConfig public function Installs the default configuration of a given extension. Overrides ConfigInstallerInterface::installDefaultConfig
ConfigInstaller::installOptionalConfig public function Installs optional configuration. Overrides ConfigInstallerInterface::installOptionalConfig
ConfigInstaller::isSyncing public function Gets the syncing state. Overrides ConfigInstallerInterface::isSyncing
ConfigInstaller::setSourceStorage public function Sets the configuration storage that provides the default configuration. Overrides ConfigInstallerInterface::setSourceStorage
ConfigInstaller::setSyncing public function Sets the status of the isSyncing flag. Overrides ConfigInstallerInterface::setSyncing
ConfigInstaller::validateDependencies protected function Validates an array of config data that contains dependency information.
ConfigInstaller::__construct public function Constructs the configuration installer.