Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
5.00% |
1 / 20 |
CRAP | |
7.50% |
9 / 120 |
| StorageComparer | |
0.00% |
0 / 1 |
|
5.00% |
1 / 20 |
1949.28 | |
7.50% |
9 / 120 |
| __construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 9 |
|||
| getSourceStorage | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 5 |
|||
| getTargetStorage | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 5 |
|||
| getEmptyChangelist | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| getChangelist | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 3 |
|||
| addChangeList | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 8 |
|||
| createChangelist | |
100.00% |
1 / 1 |
3 | |
100.00% |
9 / 9 |
|||
| addChangelistDelete | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
| addChangelistCreate | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
| addChangelistUpdate | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 12 |
|||
| addChangelistRename | |
0.00% |
0 / 1 |
110 | |
0.00% |
0 / 20 |
|||
| removeFromChangelist | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 4 |
|||
| moveRenameToUpdate | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 4 |
|||
| reset | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 5 |
|||
| hasChanges | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 5 |
|||
| validateSiteUuid | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
| getAndSortConfigData | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 13 |
|||
| createRenameName | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| extractRenameNames | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
| getAllCollectionNames | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 4 |
|||
| <?php | |
| /** | |
| * @file | |
| * Contains \Drupal\Core\Config\StorageComparer. | |
| */ | |
| namespace Drupal\Core\Config; | |
| use Drupal\Core\Cache\MemoryBackend; | |
| use Drupal\Core\Config\Entity\ConfigDependencyManager; | |
| use Drupal\Core\DependencyInjection\DependencySerializationTrait; | |
| /** | |
| * Defines a config storage comparer. | |
| */ | |
| class StorageComparer implements StorageComparerInterface { | |
| use DependencySerializationTrait; | |
| /** | |
| * The source storage used to discover configuration changes. | |
| * | |
| * @var \Drupal\Core\Config\StorageInterface | |
| */ | |
| protected $sourceStorage; | |
| /** | |
| * The source storages keyed by collection. | |
| * | |
| * @var \Drupal\Core\Config\StorageInterface[] | |
| */ | |
| protected $sourceStorages; | |
| /** | |
| * The target storage used to write configuration changes. | |
| * | |
| * @var \Drupal\Core\Config\StorageInterface | |
| */ | |
| protected $targetStorage; | |
| /** | |
| * The target storages keyed by collection. | |
| * | |
| * @var \Drupal\Core\Config\StorageInterface[] | |
| */ | |
| protected $targetStorages; | |
| /** | |
| * The configuration manager. | |
| * | |
| * @var \Drupal\Core\Config\ConfigManagerInterface | |
| */ | |
| protected $configManager; | |
| /** | |
| * List of changes to between the source storage and the target storage. | |
| * | |
| * The list is keyed by storage collection name. | |
| * | |
| * @var array | |
| */ | |
| protected $changelist; | |
| /** | |
| * Sorted list of all the configuration object names in the source storage. | |
| * | |
| * The list is keyed by storage collection name. | |
| * | |
| * @var array | |
| */ | |
| protected $sourceNames = array(); | |
| /** | |
| * Sorted list of all the configuration object names in the target storage. | |
| * | |
| * The list is keyed by storage collection name. | |
| * | |
| * @var array | |
| */ | |
| protected $targetNames = array(); | |
| /** | |
| * A memory cache backend to statically cache source configuration data. | |
| * | |
| * @var \Drupal\Core\Cache\MemoryBackend | |
| */ | |
| protected $sourceCacheStorage; | |
| /** | |
| * A memory cache backend to statically cache target configuration data. | |
| * | |
| * @var \Drupal\Core\Cache\MemoryBackend | |
| */ | |
| protected $targetCacheStorage; | |
| /** | |
| * Constructs the Configuration storage comparer. | |
| * | |
| * @param \Drupal\Core\Config\StorageInterface $source_storage | |
| * Storage object used to read configuration. | |
| * @param \Drupal\Core\Config\StorageInterface $target_storage | |
| * Storage object used to write configuration. | |
| * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager | |
| * The configuration manager. | |
| */ | |
| public function __construct(StorageInterface $source_storage, StorageInterface $target_storage, ConfigManagerInterface $config_manager) { | |
| // Wrap the storages in a static cache so that multiple reads of the same | |
| // raw configuration object are not costly. | |
| $this->sourceCacheStorage = new MemoryBackend(__CLASS__ . '::source'); | |
| $this->sourceStorage = new CachedStorage( | |
| $source_storage, | |
| $this->sourceCacheStorage | |
| ); | |
| $this->targetCacheStorage = new MemoryBackend(__CLASS__ . '::target'); | |
| $this->targetStorage = new CachedStorage( | |
| $target_storage, | |
| $this->targetCacheStorage | |
| ); | |
| $this->configManager = $config_manager; | |
| $this->changelist[StorageInterface::DEFAULT_COLLECTION] = $this->getEmptyChangelist(); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getSourceStorage($collection = StorageInterface::DEFAULT_COLLECTION) { | |
| if (!isset($this->sourceStorages[$collection])) { | |
| if ($collection == StorageInterface::DEFAULT_COLLECTION) { | |
| $this->sourceStorages[$collection] = $this->sourceStorage; | |
| } | |
| else { | |
| $this->sourceStorages[$collection] = $this->sourceStorage->createCollection($collection); | |
| } | |
| } | |
| return $this->sourceStorages[$collection]; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getTargetStorage($collection = StorageInterface::DEFAULT_COLLECTION) { | |
| if (!isset($this->targetStorages[$collection])) { | |
| if ($collection == StorageInterface::DEFAULT_COLLECTION) { | |
| $this->targetStorages[$collection] = $this->targetStorage; | |
| } | |
| else { | |
| $this->targetStorages[$collection] = $this->targetStorage->createCollection($collection); | |
| } | |
| } | |
| return $this->targetStorages[$collection]; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getEmptyChangelist() { | |
| return array( | |
| 'create' => array(), | |
| 'update' => array(), | |
| 'delete' => array(), | |
| 'rename' => array(), | |
| ); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getChangelist($op = NULL, $collection = StorageInterface::DEFAULT_COLLECTION) { | |
| if ($op) { | |
| return $this->changelist[$collection][$op]; | |
| } | |
| return $this->changelist[$collection]; | |
| } | |
| /** | |
| * Adds changes to the changelist. | |
| * | |
| * @param string $collection | |
| * The storage collection to add changes for. | |
| * @param string $op | |
| * The change operation performed. Either delete, create, rename, or update. | |
| * @param array $changes | |
| * Array of changes to add to the changelist. | |
| * @param array $sort_order | |
| * Array to sort that can be used to sort the changelist. This array must | |
| * contain all the items that are in the change list. | |
| */ | |
| protected function addChangeList($collection, $op, array $changes, array $sort_order = NULL) { | |
| // Only add changes that aren't already listed. | |
| $changes = array_diff($changes, $this->changelist[$collection][$op]); | |
| $this->changelist[$collection][$op] = array_merge($this->changelist[$collection][$op], $changes); | |
| if (isset($sort_order)) { | |
| $count = count($this->changelist[$collection][$op]); | |
| // Sort the changelist in the same order as the $sort_order array and | |
| // ensure the array is keyed from 0. | |
| $this->changelist[$collection][$op] = array_values(array_intersect($sort_order, $this->changelist[$collection][$op])); | |
| if ($count != count($this->changelist[$collection][$op])) { | |
| throw new \InvalidArgumentException("Sorting the $op changelist should not change its length."); | |
| } | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function createChangelist() { | |
| foreach ($this->getAllCollectionNames() as $collection) { | |
| $this->changelist[$collection] = $this->getEmptyChangelist(); | |
| $this->getAndSortConfigData($collection); | |
| $this->addChangelistCreate($collection); | |
| $this->addChangelistUpdate($collection); | |
| $this->addChangelistDelete($collection); | |
| // Only collections that support configuration entities can have renames. | |
| if ($collection == StorageInterface::DEFAULT_COLLECTION) { | |
| $this->addChangelistRename($collection); | |
| } | |
| } | |
| return $this; | |
| } | |
| /** | |
| * Creates the delete changelist. | |
| * | |
| * The list of deletes is sorted so that dependencies are deleted after | |
| * configuration entities that depend on them. For example, fields should be | |
| * deleted after field storages. | |
| * | |
| * @param string $collection | |
| * The storage collection to operate on. | |
| */ | |
| protected function addChangelistDelete($collection) { | |
| $deletes = array_diff(array_reverse($this->targetNames[$collection]), $this->sourceNames[$collection]); | |
| $this->addChangeList($collection, 'delete', $deletes); | |
| } | |
| /** | |
| * Creates the create changelist. | |
| * | |
| * The list of creates is sorted so that dependencies are created before | |
| * configuration entities that depend on them. For example, field storages | |
| * should be created before fields. | |
| * | |
| * @param string $collection | |
| * The storage collection to operate on. | |
| */ | |
| protected function addChangelistCreate($collection) { | |
| $creates = array_diff($this->sourceNames[$collection], $this->targetNames[$collection]); | |
| $this->addChangeList($collection, 'create', $creates); | |
| } | |
| /** | |
| * Creates the update changelist. | |
| * | |
| * The list of updates is sorted so that dependencies are created before | |
| * configuration entities that depend on them. For example, field storages | |
| * should be updated before fields. | |
| * | |
| * @param string $collection | |
| * The storage collection to operate on. | |
| */ | |
| protected function addChangelistUpdate($collection) { | |
| $recreates = array(); | |
| foreach (array_intersect($this->sourceNames[$collection], $this->targetNames[$collection]) as $name) { | |
| $source_data = $this->getSourceStorage($collection)->read($name); | |
| $target_data = $this->getTargetStorage($collection)->read($name); | |
| if ($source_data !== $target_data) { | |
| if (isset($source_data['uuid']) && $source_data['uuid'] !== $target_data['uuid']) { | |
| // The entity has the same file as an existing entity but the UUIDs do | |
| // not match. This means that the entity has been recreated so config | |
| // synchronization should do the same. | |
| $recreates[] = $name; | |
| } | |
| else { | |
| $this->addChangeList($collection, 'update', array($name)); | |
| } | |
| } | |
| } | |
| if (!empty($recreates)) { | |
| // Recreates should become deletes and creates. Deletes should be ordered | |
| // so that dependencies are deleted first. | |
| $this->addChangeList($collection, 'create', $recreates, $this->sourceNames[$collection]); | |
| $this->addChangeList($collection, 'delete', $recreates, array_reverse($this->targetNames[$collection])); | |
| } | |
| } | |
| /** | |
| * Creates the rename changelist. | |
| * | |
| * The list of renames is created from the different source and target names | |
| * with same UUID. These changes will be removed from the create and delete | |
| * lists. | |
| * | |
| * @param string $collection | |
| * The storage collection to operate on. | |
| */ | |
| protected function addChangelistRename($collection) { | |
| // Renames will be present in both the create and delete lists. | |
| $create_list = $this->getChangelist('create', $collection); | |
| $delete_list = $this->getChangelist('delete', $collection); | |
| if (empty($create_list) || empty($delete_list)) { | |
| return; | |
| } | |
| $create_uuids = array(); | |
| foreach ($this->sourceNames[$collection] as $name) { | |
| $data = $this->getSourceStorage($collection)->read($name); | |
| if (isset($data['uuid']) && in_array($name, $create_list)) { | |
| $create_uuids[$data['uuid']] = $name; | |
| } | |
| } | |
| if (empty($create_uuids)) { | |
| return; | |
| } | |
| $renames = array(); | |
| // Renames should be ordered so that dependencies are renamed last. This | |
| // ensures that if there is logic in the configuration entity class to keep | |
| // names in sync it will still work. $this->targetNames is in the desired | |
| // order due to the use of configuration dependencies in | |
| // \Drupal\Core\Config\StorageComparer::getAndSortConfigData(). | |
| // Node type is a good example of a configuration entity that renames other | |
| // configuration when it is renamed. | |
| // @see \Drupal\node\Entity\NodeType::postSave() | |
| foreach ($this->targetNames[$collection] as $name) { | |
| $data = $this->getTargetStorage($collection)->read($name); | |
| if (isset($data['uuid']) && isset($create_uuids[$data['uuid']])) { | |
| // Remove the item from the create list. | |
| $this->removeFromChangelist($collection, 'create', $create_uuids[$data['uuid']]); | |
| // Remove the item from the delete list. | |
| $this->removeFromChangelist($collection, 'delete', $name); | |
| // Create the rename name. | |
| $renames[] = $this->createRenameName($name, $create_uuids[$data['uuid']]); | |
| } | |
| } | |
| $this->addChangeList($collection, 'rename', $renames); | |
| } | |
| /** | |
| * Removes the entry from the given operation changelist for the given name. | |
| * | |
| * @param string $collection | |
| * The storage collection to operate on. | |
| * @param string $op | |
| * The changelist to act on. Either delete, create, rename or update. | |
| * @param string $name | |
| * The name of the configuration to remove. | |
| */ | |
| protected function removeFromChangelist($collection, $op, $name) { | |
| $key = array_search($name, $this->changelist[$collection][$op]); | |
| if ($key !== FALSE) { | |
| unset($this->changelist[$collection][$op][$key]); | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function moveRenameToUpdate($rename, $collection = StorageInterface::DEFAULT_COLLECTION) { | |
| $names = $this->extractRenameNames($rename); | |
| $this->removeFromChangelist($collection, 'rename', $rename); | |
| $this->addChangeList($collection, 'update', array($names['new_name']), $this->sourceNames[$collection]); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function reset() { | |
| $this->changelist = array(StorageInterface::DEFAULT_COLLECTION => $this->getEmptyChangelist()); | |
| $this->sourceNames = $this->targetNames = array(); | |
| // Reset the static configuration data caches. | |
| $this->sourceCacheStorage->deleteAll(); | |
| $this->targetCacheStorage->deleteAll(); | |
| return $this->createChangelist(); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function hasChanges() { | |
| foreach ($this->getAllCollectionNames() as $collection) { | |
| foreach (array('delete', 'create', 'update', 'rename') as $op) { | |
| if (!empty($this->changelist[$collection][$op])) { | |
| return TRUE; | |
| } | |
| } | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function validateSiteUuid() { | |
| $source = $this->sourceStorage->read('system.site'); | |
| $target = $this->targetStorage->read('system.site'); | |
| return $source['uuid'] === $target['uuid']; | |
| } | |
| /** | |
| * Gets and sorts configuration data from the source and target storages. | |
| */ | |
| protected function getAndSortConfigData($collection) { | |
| $source_storage = $this->getSourceStorage($collection); | |
| $target_storage = $this->getTargetStorage($collection); | |
| $target_names = $target_storage->listAll(); | |
| $source_names = $source_storage->listAll(); | |
| // Prime the static caches by reading all the configuration in the source | |
| // and target storages. | |
| $target_data = $target_storage->readMultiple($target_names); | |
| $source_data = $source_storage->readMultiple($source_names); | |
| // If the collection only supports simple configuration do not use | |
| // configuration dependencies. | |
| if ($collection == StorageInterface::DEFAULT_COLLECTION) { | |
| $dependency_manager = new ConfigDependencyManager(); | |
| $this->targetNames[$collection] = $dependency_manager->setData($target_data)->sortAll(); | |
| $this->sourceNames[$collection] = $dependency_manager->setData($source_data)->sortAll(); | |
| } | |
| else { | |
| $this->targetNames[$collection] = $target_names; | |
| $this->sourceNames[$collection] = $source_names; | |
| } | |
| } | |
| /** | |
| * Creates a rename name from the old and new names for the object. | |
| * | |
| * @param string $old_name | |
| * The old configuration object name. | |
| * @param string $new_name | |
| * The new configuration object name. | |
| * | |
| * @return string | |
| * The configuration change name that encodes both the old and the new name. | |
| * | |
| * @see \Drupal\Core\Config\StorageComparerInterface::extractRenameNames() | |
| */ | |
| protected function createRenameName($name1, $name2) { | |
| return $name1 . '::' . $name2; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function extractRenameNames($name) { | |
| $names = explode('::', $name, 2); | |
| return array( | |
| 'old_name' => $names[0], | |
| 'new_name' => $names[1], | |
| ); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getAllCollectionNames($include_default = TRUE) { | |
| $collections = array_unique(array_merge($this->sourceStorage->getAllCollectionNames(), $this->targetStorage->getAllCollectionNames())); | |
| if ($include_default) { | |
| array_unshift($collections, StorageInterface::DEFAULT_COLLECTION); | |
| } | |
| return $collections; | |
| } | |
| } |