Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 15 |
CRAP | |
0.00% |
0 / 220 |
| ConfigManager | |
0.00% |
0 / 1 |
|
6.25% |
1 / 16 |
3782 | |
0.00% |
0 / 220 |
| __construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 7 |
|||
| getEntityTypeIdByName | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 4 |
|||
| anonymous function | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
| loadConfigEntityByName | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 8 |
|||
| getEntityManager | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| getConfigFactory | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| diff | |
0.00% |
0 / 1 |
30 | |
0.00% |
0 / 17 |
|||
| createSnapshot | |
0.00% |
0 / 1 |
30 | |
0.00% |
0 / 16 |
|||
| uninstall | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 21 |
|||
| getConfigDependencyManager | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 10 |
|||
| findConfigEntityDependents | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 9 |
|||
| findConfigEntityDependentsAsEntities | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 17 |
|||
| getConfigEntitiesToChangeOnDependencyRemoval | |
0.00% |
0 / 1 |
90 | |
0.00% |
0 / 42 |
|||
| getConfigCollectionInfo | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 6 |
|||
| callOnDependencyRemoval | |
0.00% |
0 / 1 |
110 | |
0.00% |
0 / 32 |
|||
| findMissingContentDependencies | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 22 |
|||
| <?php | |
| /** | |
| * @file | |
| * Contains \Drupal\Core\Config\ConfigManager. | |
| */ | |
| namespace Drupal\Core\Config; | |
| use Drupal\Component\Diff\Diff; | |
| use Drupal\Component\Serialization\Yaml; | |
| use Drupal\Core\Config\Entity\ConfigDependencyManager; | |
| use Drupal\Core\Config\Entity\ConfigEntityInterface; | |
| use Drupal\Core\Config\Entity\ConfigEntityTypeInterface; | |
| use Drupal\Core\Entity\EntityManagerInterface; | |
| use Drupal\Core\Entity\EntityTypeInterface; | |
| use Drupal\Core\StringTranslation\StringTranslationTrait; | |
| use Drupal\Core\StringTranslation\TranslationInterface; | |
| use Symfony\Component\EventDispatcher\EventDispatcherInterface; | |
| /** | |
| * The ConfigManager provides helper functions for the configuration system. | |
| */ | |
| class ConfigManager implements ConfigManagerInterface { | |
| use StringTranslationTrait; | |
| /** | |
| * The entity manager. | |
| * | |
| * @var \Drupal\Core\Entity\EntityManagerInterface | |
| */ | |
| protected $entityManager; | |
| /** | |
| * The configuration factory. | |
| * | |
| * @var \Drupal\Core\Config\ConfigFactoryInterface | |
| */ | |
| protected $configFactory; | |
| /** | |
| * The typed config manager. | |
| * | |
| * @var \Drupal\Core\Config\TypedConfigManagerInterface | |
| */ | |
| protected $typedConfigManager; | |
| /** | |
| * The active configuration storage. | |
| * | |
| * @var \Drupal\Core\Config\StorageInterface | |
| */ | |
| protected $activeStorage; | |
| /** | |
| * The event dispatcher. | |
| * | |
| * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface | |
| */ | |
| protected $eventDispatcher; | |
| /** | |
| * The configuration collection info. | |
| * | |
| * @var \Drupal\Core\Config\ConfigCollectionInfo | |
| */ | |
| protected $configCollectionInfo; | |
| /** | |
| * The configuration storages keyed by collection name. | |
| * | |
| * @var \Drupal\Core\Config\StorageInterface[] | |
| */ | |
| protected $storages; | |
| /** | |
| * Creates ConfigManager objects. | |
| * | |
| * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager | |
| * The entity manager. | |
| * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory | |
| * The configuration factory. | |
| * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager | |
| * The typed config manager. | |
| * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation | |
| * The string translation service. | |
| * @param \Drupal\Core\Config\StorageInterface $active_storage | |
| * The active configuration storage. | |
| * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher | |
| * The event dispatcher. | |
| */ | |
| public function __construct(EntityManagerInterface $entity_manager, ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config_manager, TranslationInterface $string_translation, StorageInterface $active_storage, EventDispatcherInterface $event_dispatcher) { | |
| $this->entityManager = $entity_manager; | |
| $this->configFactory = $config_factory; | |
| $this->typedConfigManager = $typed_config_manager; | |
| $this->stringTranslation = $string_translation; | |
| $this->activeStorage = $active_storage; | |
| $this->eventDispatcher = $event_dispatcher; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getEntityTypeIdByName($name) { | |
| $entities = array_filter($this->entityManager->getDefinitions(), function (EntityTypeInterface $entity_type) use ($name) { | |
| return ($entity_type instanceof ConfigEntityTypeInterface && $config_prefix = $entity_type->getConfigPrefix()) && strpos($name, $config_prefix . '.') === 0; | |
| }); | |
| return key($entities); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function loadConfigEntityByName($name) { | |
| $entity_type_id = $this->getEntityTypeIdByName($name); | |
| if ($entity_type_id) { | |
| $entity_type = $this->entityManager->getDefinition($entity_type_id); | |
| $id = substr($name, strlen($entity_type->getConfigPrefix()) + 1); | |
| return $this->entityManager->getStorage($entity_type_id)->load($id); | |
| } | |
| return NULL; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getEntityManager() { | |
| return $this->entityManager; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getConfigFactory() { | |
| return $this->configFactory; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function diff(StorageInterface $source_storage, StorageInterface $target_storage, $source_name, $target_name = NULL, $collection = StorageInterface::DEFAULT_COLLECTION) { | |
| if ($collection != StorageInterface::DEFAULT_COLLECTION) { | |
| $source_storage = $source_storage->createCollection($collection); | |
| $target_storage = $target_storage->createCollection($collection); | |
| } | |
| if (!isset($target_name)) { | |
| $target_name = $source_name; | |
| } | |
| // The output should show configuration object differences formatted as YAML. | |
| // But the configuration is not necessarily stored in files. Therefore, they | |
| // need to be read and parsed, and lastly, dumped into YAML strings. | |
| $source_data = explode("\n", Yaml::encode($source_storage->read($source_name))); | |
| $target_data = explode("\n", Yaml::encode($target_storage->read($target_name))); | |
| // Check for new or removed files. | |
| if ($source_data === array('false')) { | |
| // Added file. | |
| // Cast the result of t() to a string, as the diff engine doesn't know | |
| // about objects. | |
| $source_data = array((string) $this->t('File added')); | |
| } | |
| if ($target_data === array('false')) { | |
| // Deleted file. | |
| // Cast the result of t() to a string, as the diff engine doesn't know | |
| // about objects. | |
| $target_data = array((string) $this->t('File removed')); | |
| } | |
| return new Diff($source_data, $target_data); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function createSnapshot(StorageInterface $source_storage, StorageInterface $snapshot_storage) { | |
| // Empty the snapshot of all configuration. | |
| $snapshot_storage->deleteAll(); | |
| foreach ($snapshot_storage->getAllCollectionNames() as $collection) { | |
| $snapshot_collection = $snapshot_storage->createCollection($collection); | |
| $snapshot_collection->deleteAll(); | |
| } | |
| foreach ($source_storage->listAll() as $name) { | |
| $snapshot_storage->write($name, $source_storage->read($name)); | |
| } | |
| // Copy collections as well. | |
| foreach ($source_storage->getAllCollectionNames() as $collection) { | |
| $source_collection = $source_storage->createCollection($collection); | |
| $snapshot_collection = $snapshot_storage->createCollection($collection); | |
| foreach ($source_collection->listAll() as $name) { | |
| $snapshot_collection->write($name, $source_collection->read($name)); | |
| } | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function uninstall($type, $name) { | |
| $entities = $this->getConfigEntitiesToChangeOnDependencyRemoval($type, [$name], FALSE); | |
| // Fix all dependent configuration entities. | |
| /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $entity */ | |
| foreach ($entities['update'] as $entity) { | |
| $entity->save(); | |
| } | |
| // Remove all dependent configuration entities. | |
| foreach ($entities['delete'] as $entity) { | |
| $entity->setUninstalling(TRUE); | |
| $entity->delete(); | |
| } | |
| $config_names = $this->configFactory->listAll($name . '.'); | |
| foreach ($config_names as $config_name) { | |
| $this->configFactory->getEditable($config_name)->delete(); | |
| } | |
| // Remove any matching configuration from collections. | |
| foreach ($this->activeStorage->getAllCollectionNames() as $collection) { | |
| $collection_storage = $this->activeStorage->createCollection($collection); | |
| $collection_storage->deleteAll($name . '.'); | |
| } | |
| $schema_dir = drupal_get_path($type, $name) . '/' . InstallStorage::CONFIG_SCHEMA_DIRECTORY; | |
| if (is_dir($schema_dir)) { | |
| // Refresh the schema cache if uninstalling an extension that provides | |
| // configuration schema. | |
| $this->typedConfigManager->clearCachedDefinitions(); | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getConfigDependencyManager() { | |
| $dependency_manager = new ConfigDependencyManager(); | |
| // Read all configuration using the factory. This ensures that multiple | |
| // deletes during the same request benefit from the static cache. Using the | |
| // factory also ensures configuration entity dependency discovery has no | |
| // dependencies on the config entity classes. Assume data with UUID is a | |
| // config entity. Only configuration entities can be depended on so we can | |
| // ignore everything else. | |
| $data = array_map(function($config) { | |
| $data = $config->get(); | |
| if (isset($data['uuid'])) { | |
| return $data; | |
| } | |
| return FALSE; | |
| }, $this->configFactory->loadMultiple($this->activeStorage->listAll())); | |
| $dependency_manager->setData(array_filter($data)); | |
| return $dependency_manager; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function findConfigEntityDependents($type, array $names, ConfigDependencyManager $dependency_manager = NULL) { | |
| if (!$dependency_manager) { | |
| $dependency_manager = $this->getConfigDependencyManager(); | |
| } | |
| $dependencies = array(); | |
| foreach ($names as $name) { | |
| $dependencies = array_merge($dependencies, $dependency_manager->getDependentEntities($type, $name)); | |
| } | |
| return $dependencies; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function findConfigEntityDependentsAsEntities($type, array $names, ConfigDependencyManager $dependency_manager = NULL) { | |
| $dependencies = $this->findConfigEntityDependents($type, $names, $dependency_manager); | |
| $entities = array(); | |
| $definitions = $this->entityManager->getDefinitions(); | |
| foreach ($dependencies as $config_name => $dependency) { | |
| // Group by entity type to efficient load entities using | |
| // \Drupal\Core\Entity\EntityStorageInterface::loadMultiple(). | |
| $entity_type_id = $this->getEntityTypeIdByName($config_name); | |
| // It is possible that a non-configuration entity will be returned if a | |
| // simple configuration object has a UUID key. This would occur if the | |
| // dependents of the system module are calculated since system.site has | |
| // a UUID key. | |
| if ($entity_type_id) { | |
| $id = substr($config_name, strlen($definitions[$entity_type_id]->getConfigPrefix()) + 1); | |
| $entities[$entity_type_id][] = $id; | |
| } | |
| } | |
| $entities_to_return = array(); | |
| foreach ($entities as $entity_type_id => $entities_to_load) { | |
| $storage = $this->entityManager->getStorage($entity_type_id); | |
| // Remove the keys since there are potential ID clashes from different | |
| // configuration entity types. | |
| $entities_to_return = array_merge($entities_to_return, array_values($storage->loadMultiple($entities_to_load))); | |
| } | |
| return $entities_to_return; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getConfigEntitiesToChangeOnDependencyRemoval($type, array $names, $dry_run = TRUE) { | |
| // Determine the current list of dependent configuration entities and set up | |
| // initial values. | |
| $dependency_manager = $this->getConfigDependencyManager(); | |
| $dependents = $this->findConfigEntityDependentsAsEntities($type, $names, $dependency_manager); | |
| $original_dependencies = $dependents; | |
| $delete_uuids = $update_uuids = []; | |
| $return = [ | |
| 'update' => [], | |
| 'delete' => [], | |
| 'unchanged' => [], | |
| ]; | |
| // Try to fix any dependencies and find out what will happen to the | |
| // dependency graph. Entities are processed in the order of most dependent | |
| // first. For example, this ensures that fields are removed before | |
| // field storages. | |
| while ($dependent = array_pop($dependents)) { | |
| /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $dependent */ | |
| if ($dry_run) { | |
| // Clone the entity so any changes do not change any static caches. | |
| $dependent = clone $dependent; | |
| } | |
| $fixed = FALSE; | |
| if ($this->callOnDependencyRemoval($dependent, $original_dependencies, $type, $names)) { | |
| // Recalculate dependencies and update the dependency graph data. | |
| $dependent->calculateDependencies(); | |
| $dependency_manager->updateData($dependent->getConfigDependencyName(), $dependent->getDependencies()); | |
| // Based on the updated data rebuild the list of dependents. This will | |
| // remove entities that are no longer dependent after the recalculation. | |
| $dependents = $this->findConfigEntityDependentsAsEntities($type, $names, $dependency_manager); | |
| // Remove any entities that we've already marked for deletion. | |
| $dependents = array_filter($dependents, function ($dependent) use ($delete_uuids) { | |
| return !in_array($dependent->uuid(), $delete_uuids); | |
| }); | |
| // Ensure that the dependency has actually been fixed. It is possible | |
| // that the dependent has multiple dependencies that cause it to be in | |
| // the dependency chain. | |
| $fixed = TRUE; | |
| foreach ($dependents as $key => $entity) { | |
| if ($entity->uuid() == $dependent->uuid()) { | |
| $fixed = FALSE; | |
| unset($dependents[$key]); | |
| break; | |
| } | |
| } | |
| if ($fixed) { | |
| $return['update'][] = $dependent; | |
| $update_uuids[] = $dependent->uuid(); | |
| } | |
| } | |
| // If the entity cannot be fixed then it has to be deleted. | |
| if (!$fixed) { | |
| $delete_uuids[] = $dependent->uuid(); | |
| $return['delete'][] = $dependent; | |
| } | |
| } | |
| // Use the lists of UUIDs to filter the original list to work out which | |
| // configuration entities are unchanged. | |
| $return['unchanged'] = array_filter($original_dependencies, function ($dependent) use ($delete_uuids, $update_uuids) { | |
| return !(in_array($dependent->uuid(), $delete_uuids) || in_array($dependent->uuid(), $update_uuids)); | |
| }); | |
| return $return; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getConfigCollectionInfo() { | |
| if (!isset($this->configCollectionInfo)) { | |
| $this->configCollectionInfo = new ConfigCollectionInfo(); | |
| $this->eventDispatcher->dispatch(ConfigEvents::COLLECTION_INFO, $this->configCollectionInfo); | |
| } | |
| return $this->configCollectionInfo; | |
| } | |
| /** | |
| * Calls an entity's onDependencyRemoval() method. | |
| * | |
| * A helper method to call onDependencyRemoval() with the correct list of | |
| * affected entities. This list should only contain dependencies on the | |
| * entity. Configuration and content entity dependencies will be converted | |
| * into entity objects. | |
| * | |
| * @param \Drupal\Core\Config\Entity\ConfigEntityInterface $entity | |
| * The entity to call onDependencyRemoval() on. | |
| * @param \Drupal\Core\Config\Entity\ConfigEntityInterface[] $dependent_entities | |
| * The list of dependent configuration entities. | |
| * @param string $type | |
| * The type of dependency being checked. Either 'module', 'theme', 'config' | |
| * or 'content'. | |
| * @param array $names | |
| * The specific names to check. If $type equals 'module' or 'theme' then it | |
| * should be a list of module names or theme names. In the case of 'config' | |
| * or 'content' it should be a list of configuration dependency names. | |
| * | |
| * @return bool | |
| * TRUE if the entity has changed as a result of calling the | |
| * onDependencyRemoval() method, FALSE if not. | |
| */ | |
| protected function callOnDependencyRemoval(ConfigEntityInterface $entity, array $dependent_entities, $type, array $names) { | |
| $entity_dependencies = $entity->getDependencies(); | |
| if (empty($entity_dependencies)) { | |
| // No dependent entities nothing to do. | |
| return FALSE; | |
| } | |
| $affected_dependencies = array( | |
| 'config' => array(), | |
| 'content' => array(), | |
| 'module' => array(), | |
| 'theme' => array(), | |
| ); | |
| // Work out if any of the entity's dependencies are going to be affected. | |
| if (isset($entity_dependencies[$type])) { | |
| // Work out which dependencies the entity has in common with the provided | |
| // $type and $names. | |
| $affected_dependencies[$type] = array_intersect($entity_dependencies[$type], $names); | |
| // If the dependencies are entities we need to convert them into objects. | |
| if ($type == 'config' || $type == 'content') { | |
| $affected_dependencies[$type] = array_map(function ($name) use ($type) { | |
| if ($type == 'config') { | |
| return $this->loadConfigEntityByName($name); | |
| } | |
| else { | |
| // Ignore the bundle. | |
| list($entity_type_id,, $uuid) = explode(':', $name); | |
| return $this->entityManager->loadEntityByConfigTarget($entity_type_id, $uuid); | |
| } | |
| }, $affected_dependencies[$type]); | |
| } | |
| } | |
| // Merge any other configuration entities into the list of affected | |
| // dependencies if necessary. | |
| if (isset($entity_dependencies['config'])) { | |
| foreach ($dependent_entities as $dependent_entity) { | |
| if (in_array($dependent_entity->getConfigDependencyName(), $entity_dependencies['config'])) { | |
| $affected_dependencies['config'][] = $dependent_entity; | |
| } | |
| } | |
| } | |
| // Key the entity arrays by config dependency name to make searching easy. | |
| foreach (['config', 'content'] as $dependency_type) { | |
| $affected_dependencies[$dependency_type] = array_combine( | |
| array_map(function ($entity) { return $entity->getConfigDependencyName(); }, $affected_dependencies[$dependency_type]), | |
| $affected_dependencies[$dependency_type] | |
| ); | |
| } | |
| // Inform the entity. | |
| return $entity->onDependencyRemoval($affected_dependencies); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function findMissingContentDependencies() { | |
| $content_dependencies = array(); | |
| $missing_dependencies = array(); | |
| foreach ($this->activeStorage->readMultiple($this->activeStorage->listAll()) as $config_data) { | |
| if (isset($config_data['dependencies']['content'])) { | |
| $content_dependencies = array_merge($content_dependencies, $config_data['dependencies']['content']); | |
| } | |
| if (isset($config_data['dependencies']['enforced']['content'])) { | |
| $content_dependencies = array_merge($content_dependencies, $config_data['dependencies']['enforced']['content']); | |
| } | |
| } | |
| foreach (array_unique($content_dependencies) as $content_dependency) { | |
| // Format of the dependency is entity_type:bundle:uuid. | |
| list($entity_type, $bundle, $uuid) = explode(':', $content_dependency, 3); | |
| if (!$this->entityManager->loadEntityByUuid($entity_type, $uuid)) { | |
| $missing_dependencies[$uuid] = array( | |
| 'entity_type' => $entity_type, | |
| 'bundle' => $bundle, | |
| 'uuid' => $uuid, | |
| ); | |
| } | |
| } | |
| return $missing_dependencies; | |
| } | |
| } |