1<?php 2 3namespace Drupal\Core\Config; 4 5use Drupal\Component\Utility\Crypt; 6use Drupal\Core\Config\Entity\ConfigDependencyManager; 7use Drupal\Core\Installer\InstallerKernel; 8use Symfony\Component\EventDispatcher\EventDispatcherInterface; 9 10class ConfigInstaller implements ConfigInstallerInterface { 11 12 /** 13 * The configuration factory. 14 * 15 * @var \Drupal\Core\Config\ConfigFactoryInterface 16 */ 17 protected $configFactory; 18 19 /** 20 * The active configuration storages, keyed by collection. 21 * 22 * @var \Drupal\Core\Config\StorageInterface[] 23 */ 24 protected $activeStorages; 25 26 /** 27 * The typed configuration manager. 28 * 29 * @var \Drupal\Core\Config\TypedConfigManagerInterface 30 */ 31 protected $typedConfig; 32 33 /** 34 * The configuration manager. 35 * 36 * @var \Drupal\Core\Config\ConfigManagerInterface 37 */ 38 protected $configManager; 39 40 /** 41 * The event dispatcher. 42 * 43 * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface 44 */ 45 protected $eventDispatcher; 46 47 /** 48 * The configuration storage that provides the default configuration. 49 * 50 * @var \Drupal\Core\Config\StorageInterface 51 */ 52 protected $sourceStorage; 53 54 /** 55 * Is configuration being created as part of a configuration sync. 56 * 57 * @var bool 58 */ 59 protected $isSyncing = FALSE; 60 61 /** 62 * The name of the currently active installation profile. 63 * 64 * @var string 65 */ 66 protected $installProfile; 67 68 /** 69 * Constructs the configuration installer. 70 * 71 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory 72 * The configuration factory. 73 * @param \Drupal\Core\Config\StorageInterface $active_storage 74 * The active configuration storage. 75 * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config 76 * The typed configuration manager. 77 * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager 78 * The configuration manager. 79 * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher 80 * The event dispatcher. 81 * @param string $install_profile 82 * The name of the currently active installation profile. 83 */ 84 public function __construct(ConfigFactoryInterface $config_factory, StorageInterface $active_storage, TypedConfigManagerInterface $typed_config, ConfigManagerInterface $config_manager, EventDispatcherInterface $event_dispatcher, $install_profile) { 85 $this->configFactory = $config_factory; 86 $this->activeStorages[$active_storage->getCollectionName()] = $active_storage; 87 $this->typedConfig = $typed_config; 88 $this->configManager = $config_manager; 89 $this->eventDispatcher = $event_dispatcher; 90 $this->installProfile = $install_profile; 91 } 92 93 /** 94 * {@inheritdoc} 95 */ 96 public function installDefaultConfig($type, $name) { 97 $extension_path = $this->drupalGetPath($type, $name); 98 // Refresh the schema cache if the extension provides configuration schema 99 // or is a theme. 100 if (is_dir($extension_path . '/' . InstallStorage::CONFIG_SCHEMA_DIRECTORY) || $type == 'theme') { 101 $this->typedConfig->clearCachedDefinitions(); 102 } 103 104 $default_install_path = $this->getDefaultConfigDirectory($type, $name); 105 if (is_dir($default_install_path)) { 106 if (!$this->isSyncing()) { 107 $storage = new FileStorage($default_install_path, StorageInterface::DEFAULT_COLLECTION); 108 $prefix = ''; 109 } 110 else { 111 // The configuration importer sets the source storage on the config 112 // installer. The configuration importer handles all of the 113 // configuration entity imports. We only need to ensure that simple 114 // configuration is created when the extension is installed. 115 $storage = $this->getSourceStorage(); 116 $prefix = $name . '.'; 117 } 118 119 // Gets profile storages to search for overrides if necessary. 120 $profile_storages = $this->getProfileStorages($name); 121 122 // Gather information about all the supported collections. 123 $collection_info = $this->configManager->getConfigCollectionInfo(); 124 foreach ($collection_info->getCollectionNames() as $collection) { 125 $config_to_create = $this->getConfigToCreate($storage, $collection, $prefix, $profile_storages); 126 if ($name == $this->drupalGetProfile()) { 127 // If we're installing a profile ensure simple configuration that 128 // already exists is excluded as it will have already been written. 129 // This means that if the configuration is changed by something else 130 // during the install it will not be overwritten again. 131 $existing_configuration = array_filter($this->getActiveStorages($collection)->listAll(), function ($config_name) { 132 return !$this->configManager->getEntityTypeIdByName($config_name); 133 }); 134 $config_to_create = array_diff_key($config_to_create, array_flip($existing_configuration)); 135 } 136 if (!empty($config_to_create)) { 137 $this->createConfiguration($collection, $config_to_create); 138 } 139 } 140 } 141 142 // During a drupal installation optional configuration is installed at the 143 // end of the installation process. 144 // @see install_install_profile() 145 if (!$this->isSyncing() && !InstallerKernel::installationAttempted()) { 146 $optional_install_path = $extension_path . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY; 147 if (is_dir($optional_install_path)) { 148 // Install any optional config the module provides. 149 $storage = new FileStorage($optional_install_path, StorageInterface::DEFAULT_COLLECTION); 150 $this->installOptionalConfig($storage, ''); 151 } 152 // Install any optional configuration entities whose dependencies can now 153 // be met. This searches all the installed modules config/optional 154 // directories. 155 $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, FALSE, $this->installProfile); 156 $this->installOptionalConfig($storage, [$type => $name]); 157 } 158 159 // Reset all the static caches and list caches. 160 $this->configFactory->reset(); 161 } 162 163 /** 164 * {@inheritdoc} 165 */ 166 public function installOptionalConfig(StorageInterface $storage = NULL, $dependency = []) { 167 $profile = $this->drupalGetProfile(); 168 $enabled_extensions = $this->getEnabledExtensions(); 169 $existing_config = $this->getActiveStorages()->listAll(); 170 171 // Create the storages to read configuration from. 172 if (!$storage) { 173 // Search the install profile's optional configuration too. 174 $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, TRUE, $this->installProfile); 175 // The extension install storage ensures that overrides are used. 176 $profile_storage = NULL; 177 } 178 elseif (!empty($profile)) { 179 // Creates a profile storage to search for overrides. 180 $profile_install_path = $this->drupalGetPath('module', $profile) . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY; 181 $profile_storage = new FileStorage($profile_install_path, StorageInterface::DEFAULT_COLLECTION); 182 } 183 else { 184 // Profile has not been set yet. For example during the first steps of the 185 // installer or during unit tests. 186 $profile_storage = NULL; 187 } 188 189 // Build the list of possible configuration to create. 190 $list = $storage->listAll(); 191 if ($profile_storage && !empty($dependency)) { 192 // Only add the optional profile configuration into the list if we are 193 // have a dependency to check. This ensures that optional profile 194 // configuration is not unexpectedly re-created after being deleted. 195 $list = array_unique(array_merge($list, $profile_storage->listAll())); 196 } 197 198 // Filter the list of configuration to only include configuration that 199 // should be created. 200 $list = array_filter($list, function ($config_name) use ($existing_config) { 201 // Only list configuration that: 202 // - does not already exist 203 // - is a configuration entity (this also excludes config that has an 204 // implicit dependency on modules that are not yet installed) 205 return !in_array($config_name, $existing_config) && $this->configManager->getEntityTypeIdByName($config_name); 206 }); 207 208 $all_config = array_merge($existing_config, $list); 209 $all_config = array_combine($all_config, $all_config); 210 $config_to_create = $storage->readMultiple($list); 211 // Check to see if the corresponding override storage has any overrides or 212 // new configuration that can be installed. 213 if ($profile_storage) { 214 $config_to_create = $profile_storage->readMultiple($list) + $config_to_create; 215 } 216 // Sort $config_to_create in the order of the least dependent first. 217 $dependency_manager = new ConfigDependencyManager(); 218 $dependency_manager->setData($config_to_create); 219 $config_to_create = array_merge(array_flip($dependency_manager->sortAll()), $config_to_create); 220 if (!empty($dependency)) { 221 // In order to work out dependencies we need the full config graph. 222 $dependency_manager->setData($this->getActiveStorages()->readMultiple($existing_config) + $config_to_create); 223 $dependencies = $dependency_manager->getDependentEntities(key($dependency), reset($dependency)); 224 } 225 226 foreach ($config_to_create as $config_name => $data) { 227 // Remove configuration where its dependencies cannot be met. 228 $remove = !$this->validateDependencies($config_name, $data, $enabled_extensions, $all_config); 229 // Remove configuration that is not dependent on $dependency, if it is 230 // defined. 231 if (!$remove && !empty($dependency)) { 232 $remove = !isset($dependencies[$config_name]); 233 } 234 235 if ($remove) { 236 // Remove from the list of configuration to create. 237 unset($config_to_create[$config_name]); 238 // Remove from the list of all configuration. This ensures that any 239 // configuration that depends on this configuration is also removed. 240 unset($all_config[$config_name]); 241 } 242 } 243 244 // Create the optional configuration if there is any left after filtering. 245 if (!empty($config_to_create)) { 246 $this->createConfiguration(StorageInterface::DEFAULT_COLLECTION, $config_to_create, TRUE); 247 } 248 } 249 250 /** 251 * Gets configuration data from the provided storage to create. 252 * 253 * @param StorageInterface $storage 254 * The configuration storage to read configuration from. 255 * @param string $collection 256 * The configuration collection to use. 257 * @param string $prefix 258 * (optional) Limit to configuration starting with the provided string. 259 * @param \Drupal\Core\Config\StorageInterface[] $profile_storages 260 * An array of storage interfaces containing profile configuration to check 261 * for overrides. 262 * 263 * @return array 264 * An array of configuration data read from the source storage keyed by the 265 * configuration object name. 266 */ 267 protected function getConfigToCreate(StorageInterface $storage, $collection, $prefix = '', array $profile_storages = []) { 268 if ($storage->getCollectionName() != $collection) { 269 $storage = $storage->createCollection($collection); 270 } 271 $data = $storage->readMultiple($storage->listAll($prefix)); 272 273 // Check to see if configuration provided by the install profile has any 274 // overrides. 275 foreach ($profile_storages as $profile_storage) { 276 if ($profile_storage->getCollectionName() != $collection) { 277 $profile_storage = $profile_storage->createCollection($collection); 278 } 279 $profile_overrides = $profile_storage->readMultiple(array_keys($data)); 280 if (InstallerKernel::installationAttempted()) { 281 // During installation overrides of simple configuration are applied 282 // immediately. Configuration entities that are overridden will be 283 // updated when the profile is installed. This allows install profiles 284 // to provide configuration entity overrides that have dependencies that 285 // cannot be met when the module provided configuration entity is 286 // created. 287 foreach ($profile_overrides as $name => $override_data) { 288 // The only way to determine if they are configuration entities is the 289 // presence of a dependencies key. 290 if (!isset($override_data['dependencies'])) { 291 $data[$name] = $override_data; 292 } 293 } 294 } 295 else { 296 // Allow install profiles to provide overridden configuration for new 297 // extensions that are being enabled after Drupal has already been 298 // installed. This allows profiles to ship new extensions in version 299 // updates without requiring additional code to apply the overrides. 300 $data = $profile_overrides + $data; 301 } 302 } 303 return $data; 304 } 305 306 /** 307 * Creates configuration in a collection based on the provided list. 308 * 309 * @param string $collection 310 * The configuration collection. 311 * @param array $config_to_create 312 * An array of configuration data to create, keyed by name. 313 */ 314 protected function createConfiguration($collection, array $config_to_create) { 315 // Order the configuration to install in the order of dependencies. 316 if ($collection == StorageInterface::DEFAULT_COLLECTION) { 317 $dependency_manager = new ConfigDependencyManager(); 318 $config_names = $dependency_manager 319 ->setData($config_to_create) 320 ->sortAll(); 321 } 322 else { 323 $config_names = array_keys($config_to_create); 324 } 325 326 foreach ($config_names as $name) { 327 // Allow config factory overriders to use a custom configuration object if 328 // they are responsible for the collection. 329 $overrider = $this->configManager->getConfigCollectionInfo()->getOverrideService($collection); 330 if ($overrider) { 331 $new_config = $overrider->createConfigObject($name, $collection); 332 } 333 else { 334 $new_config = new Config($name, $this->getActiveStorages($collection), $this->eventDispatcher, $this->typedConfig); 335 } 336 if ($config_to_create[$name] !== FALSE) { 337 $new_config->setData($config_to_create[$name]); 338 // Add a hash to configuration created through the installer so it is 339 // possible to know if the configuration was created by installing an 340 // extension and to track which version of the default config was used. 341 if (!$this->isSyncing() && $collection == StorageInterface::DEFAULT_COLLECTION) { 342 $new_config->set('_core.default_config_hash', Crypt::hashBase64(serialize($config_to_create[$name]))); 343 } 344 } 345 if ($collection == StorageInterface::DEFAULT_COLLECTION && $entity_type = $this->configManager->getEntityTypeIdByName($name)) { 346 // If we are syncing do not create configuration entities. Pluggable 347 // configuration entities can have dependencies on modules that are 348 // not yet enabled. This approach means that any code that expects 349 // default configuration entities to exist will be unstable after the 350 // module has been enabled and before the config entity has been 351 // imported. 352 if ($this->isSyncing()) { 353 continue; 354 } 355 /** @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $entity_storage */ 356 $entity_storage = $this->configManager 357 ->getEntityTypeManager() 358 ->getStorage($entity_type); 359 360 $id = $entity_storage->getIDFromConfigName($name, $entity_storage->getEntityType()->getConfigPrefix()); 361 // It is possible that secondary writes can occur during configuration 362 // creation. Updates of such configuration are allowed. 363 if ($this->getActiveStorages($collection)->exists($name)) { 364 $entity = $entity_storage->load($id); 365 $entity = $entity_storage->updateFromStorageRecord($entity, $new_config->get()); 366 } 367 else { 368 $entity = $entity_storage->createFromStorageRecord($new_config->get()); 369 } 370 if ($entity->isInstallable()) { 371 $entity->trustData()->save(); 372 if ($id !== $entity->id()) { 373 trigger_error(sprintf('The configuration name "%s" does not match the ID "%s"', $name, $entity->id()), E_USER_WARNING); 374 } 375 } 376 } 377 else { 378 $new_config->save(TRUE); 379 } 380 } 381 } 382 383 /** 384 * {@inheritdoc} 385 */ 386 public function installCollectionDefaultConfig($collection) { 387 $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection, InstallerKernel::installationAttempted(), $this->installProfile); 388 // Only install configuration for enabled extensions. 389 $enabled_extensions = $this->getEnabledExtensions(); 390 $config_to_install = array_filter($storage->listAll(), function ($config_name) use ($enabled_extensions) { 391 $provider = mb_substr($config_name, 0, strpos($config_name, '.')); 392 return in_array($provider, $enabled_extensions); 393 }); 394 if (!empty($config_to_install)) { 395 $this->createConfiguration($collection, $storage->readMultiple($config_to_install)); 396 // Reset all the static caches and list caches. 397 $this->configFactory->reset(); 398 } 399 } 400 401 /** 402 * {@inheritdoc} 403 */ 404 public function setSourceStorage(StorageInterface $storage) { 405 $this->sourceStorage = $storage; 406 return $this; 407 } 408 409 /** 410 * {@inheritdoc} 411 */ 412 public function getSourceStorage() { 413 return $this->sourceStorage; 414 } 415 416 /** 417 * Gets the configuration storage that provides the active configuration. 418 * 419 * @param string $collection 420 * (optional) The configuration collection. Defaults to the default 421 * collection. 422 * 423 * @return \Drupal\Core\Config\StorageInterface 424 * The configuration storage that provides the default configuration. 425 */ 426 protected function getActiveStorages($collection = StorageInterface::DEFAULT_COLLECTION) { 427 if (!isset($this->activeStorages[$collection])) { 428 $this->activeStorages[$collection] = reset($this->activeStorages)->createCollection($collection); 429 } 430 return $this->activeStorages[$collection]; 431 } 432 433 /** 434 * {@inheritdoc} 435 */ 436 public function setSyncing($status) { 437 if (!$status) { 438 $this->sourceStorage = NULL; 439 } 440 $this->isSyncing = $status; 441 return $this; 442 } 443 444 /** 445 * {@inheritdoc} 446 */ 447 public function isSyncing() { 448 return $this->isSyncing; 449 } 450 451 /** 452 * Finds pre-existing configuration objects for the provided extension. 453 * 454 * Extensions can not be installed if configuration objects exist in the 455 * active storage with the same names. This can happen in a number of ways, 456 * commonly: 457 * - if a user has created configuration with the same name as that provided 458 * by the extension. 459 * - if the extension provides default configuration that does not depend on 460 * it and the extension has been uninstalled and is about to the 461 * reinstalled. 462 * 463 * @return array 464 * Array of configuration object names that already exist keyed by 465 * collection. 466 */ 467 protected function findPreExistingConfiguration(StorageInterface $storage) { 468 $existing_configuration = []; 469 // Gather information about all the supported collections. 470 $collection_info = $this->configManager->getConfigCollectionInfo(); 471 472 foreach ($collection_info->getCollectionNames() as $collection) { 473 $config_to_create = array_keys($this->getConfigToCreate($storage, $collection)); 474 $active_storage = $this->getActiveStorages($collection); 475 foreach ($config_to_create as $config_name) { 476 if ($active_storage->exists($config_name)) { 477 $existing_configuration[$collection][] = $config_name; 478 } 479 } 480 } 481 return $existing_configuration; 482 } 483 484 /** 485 * {@inheritdoc} 486 */ 487 public function checkConfigurationToInstall($type, $name) { 488 if ($this->isSyncing()) { 489 // Configuration is assumed to already be checked by the config importer 490 // validation events. 491 return; 492 } 493 $config_install_path = $this->getDefaultConfigDirectory($type, $name); 494 if (!is_dir($config_install_path)) { 495 return; 496 } 497 498 $storage = new FileStorage($config_install_path, StorageInterface::DEFAULT_COLLECTION); 499 500 $enabled_extensions = $this->getEnabledExtensions(); 501 // Add the extension that will be enabled to the list of enabled extensions. 502 $enabled_extensions[] = $name; 503 // Gets profile storages to search for overrides if necessary. 504 $profile_storages = $this->getProfileStorages($name); 505 506 // Check the dependencies of configuration provided by the module. 507 list($invalid_default_config, $missing_dependencies) = $this->findDefaultConfigWithUnmetDependencies($storage, $enabled_extensions, $profile_storages); 508 if (!empty($invalid_default_config)) { 509 throw UnmetDependenciesException::create($name, array_unique($missing_dependencies, SORT_REGULAR)); 510 } 511 512 // Install profiles can not have config clashes. Configuration that 513 // has the same name as a module's configuration will be used instead. 514 if ($name != $this->drupalGetProfile()) { 515 // Throw an exception if the module being installed contains configuration 516 // that already exists. Additionally, can not continue installing more 517 // modules because those may depend on the current module being installed. 518 $existing_configuration = $this->findPreExistingConfiguration($storage); 519 if (!empty($existing_configuration)) { 520 throw PreExistingConfigException::create($name, $existing_configuration); 521 } 522 } 523 } 524 525 /** 526 * Finds default configuration with unmet dependencies. 527 * 528 * @param \Drupal\Core\Config\StorageInterface $storage 529 * The storage containing the default configuration. 530 * @param array $enabled_extensions 531 * A list of all the currently enabled modules and themes. 532 * @param \Drupal\Core\Config\StorageInterface[] $profile_storages 533 * An array of storage interfaces containing profile configuration to check 534 * for overrides. 535 * 536 * @return array 537 * An array containing: 538 * - A list of configuration that has unmet dependencies. 539 * - An array that will be filled with the missing dependency names, keyed 540 * by the dependents' names. 541 */ 542 protected function findDefaultConfigWithUnmetDependencies(StorageInterface $storage, array $enabled_extensions, array $profile_storages = []) { 543 $missing_dependencies = []; 544 $config_to_create = $this->getConfigToCreate($storage, StorageInterface::DEFAULT_COLLECTION, '', $profile_storages); 545 $all_config = array_merge($this->configFactory->listAll(), array_keys($config_to_create)); 546 foreach ($config_to_create as $config_name => $config) { 547 if ($missing = $this->getMissingDependencies($config_name, $config, $enabled_extensions, $all_config)) { 548 $missing_dependencies[$config_name] = $missing; 549 } 550 } 551 return [ 552 array_intersect_key($config_to_create, $missing_dependencies), 553 $missing_dependencies, 554 ]; 555 } 556 557 /** 558 * Validates an array of config data that contains dependency information. 559 * 560 * @param string $config_name 561 * The name of the configuration object that is being validated. 562 * @param array $data 563 * Configuration data. 564 * @param array $enabled_extensions 565 * A list of all the currently enabled modules and themes. 566 * @param array $all_config 567 * A list of all the active configuration names. 568 * 569 * @return bool 570 * TRUE if all dependencies are present, FALSE otherwise. 571 */ 572 protected function validateDependencies($config_name, array $data, array $enabled_extensions, array $all_config) { 573 if (!isset($data['dependencies'])) { 574 // Simple config or a config entity without dependencies. 575 list($provider) = explode('.', $config_name, 2); 576 return in_array($provider, $enabled_extensions, TRUE); 577 } 578 579 $missing = $this->getMissingDependencies($config_name, $data, $enabled_extensions, $all_config); 580 return empty($missing); 581 } 582 583 /** 584 * Returns an array of missing dependencies for a config object. 585 * 586 * @param string $config_name 587 * The name of the configuration object that is being validated. 588 * @param array $data 589 * Configuration data. 590 * @param array $enabled_extensions 591 * A list of all the currently enabled modules and themes. 592 * @param array $all_config 593 * A list of all the active configuration names. 594 * 595 * @return array 596 * A list of missing config dependencies. 597 */ 598 protected function getMissingDependencies($config_name, array $data, array $enabled_extensions, array $all_config) { 599 $missing = []; 600 if (isset($data['dependencies'])) { 601 list($provider) = explode('.', $config_name, 2); 602 $all_dependencies = $data['dependencies']; 603 604 // Ensure enforced dependencies are included. 605 if (isset($all_dependencies['enforced'])) { 606 $all_dependencies = array_merge($all_dependencies, $data['dependencies']['enforced']); 607 unset($all_dependencies['enforced']); 608 } 609 // Ensure the configuration entity type provider is in the list of 610 // dependencies. 611 if (!isset($all_dependencies['module']) || !in_array($provider, $all_dependencies['module'])) { 612 $all_dependencies['module'][] = $provider; 613 } 614 615 foreach ($all_dependencies as $type => $dependencies) { 616 $list_to_check = []; 617 switch ($type) { 618 case 'module': 619 case 'theme': 620 $list_to_check = $enabled_extensions; 621 break; 622 623 case 'config': 624 $list_to_check = $all_config; 625 break; 626 } 627 if (!empty($list_to_check)) { 628 $missing = array_merge($missing, array_diff($dependencies, $list_to_check)); 629 } 630 } 631 } 632 633 return $missing; 634 } 635 636 /** 637 * Gets the list of enabled extensions including both modules and themes. 638 * 639 * @return array 640 * A list of enabled extensions which includes both modules and themes. 641 */ 642 protected function getEnabledExtensions() { 643 // Read enabled extensions directly from configuration to avoid circular 644 // dependencies on ModuleHandler and ThemeHandler. 645 $extension_config = $this->configFactory->get('core.extension'); 646 $enabled_extensions = (array) $extension_config->get('module'); 647 $enabled_extensions += (array) $extension_config->get('theme'); 648 // Core can provide configuration. 649 $enabled_extensions['core'] = 'core'; 650 return array_keys($enabled_extensions); 651 } 652 653 /** 654 * Gets the profile storage to use to check for profile overrides. 655 * 656 * The install profile can override module configuration during a module 657 * install. Both the install and optional directories are checked for matching 658 * configuration. This allows profiles to override default configuration for 659 * modules they do not depend on. 660 * 661 * @param string $installing_name 662 * (optional) The name of the extension currently being installed. 663 * 664 * @return \Drupal\Core\Config\StorageInterface[]|null 665 * Storages to access configuration from the installation profile. If we're 666 * installing the profile itself, then it will return an empty array as the 667 * profile storage should not be used. 668 */ 669 protected function getProfileStorages($installing_name = '') { 670 $profile = $this->drupalGetProfile(); 671 $profile_storages = []; 672 if ($profile && $profile != $installing_name) { 673 $profile_path = $this->drupalGetPath('module', $profile); 674 foreach ([InstallStorage::CONFIG_INSTALL_DIRECTORY, InstallStorage::CONFIG_OPTIONAL_DIRECTORY] as $directory) { 675 if (is_dir($profile_path . '/' . $directory)) { 676 $profile_storages[] = new FileStorage($profile_path . '/' . $directory, StorageInterface::DEFAULT_COLLECTION); 677 } 678 } 679 } 680 return $profile_storages; 681 } 682 683 /** 684 * Gets an extension's default configuration directory. 685 * 686 * @param string $type 687 * Type of extension to install. 688 * @param string $name 689 * Name of extension to install. 690 * 691 * @return string 692 * The extension's default configuration directory. 693 */ 694 protected function getDefaultConfigDirectory($type, $name) { 695 return $this->drupalGetPath($type, $name) . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY; 696 } 697 698 /** 699 * Wrapper for drupal_get_path(). 700 * 701 * @param $type 702 * The type of the item; one of 'core', 'profile', 'module', 'theme', or 703 * 'theme_engine'. 704 * @param $name 705 * The name of the item for which the path is requested. Ignored for 706 * $type 'core'. 707 * 708 * @return string 709 * The path to the requested item or an empty string if the item is not 710 * found. 711 */ 712 protected function drupalGetPath($type, $name) { 713 return drupal_get_path($type, $name); 714 } 715 716 /** 717 * Gets the install profile from settings. 718 * 719 * @return string|null 720 * The name of the installation profile or NULL if no installation profile 721 * is currently active. This is the case for example during the first steps 722 * of the installer or during unit tests. 723 */ 724 protected function drupalGetProfile() { 725 return $this->installProfile; 726 } 727 728 /** 729 * Wrapper for drupal_installation_attempted(). 730 * 731 * @return bool 732 * TRUE if a Drupal installation is currently being attempted. 733 * 734 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. 735 * Use \Drupal\Core\Installer\InstallerKernel::installationAttempted() 736 * instead. 737 * 738 * @see https://www.drupal.org/node/3052704 739 * @see \Drupal\Core\Installer\InstallerKernel::installationAttempted() 740 */ 741 protected function drupalInstallationAttempted() { 742 @trigger_error(__METHOD__ . '() is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Installer\InstallerKernel::installationAttempted() instead. See https://www.drupal.org/node/3052704', E_USER_DEPRECATED); 743 return InstallerKernel::installationAttempted(); 744 } 745 746} 747