Commit 4eaa775b authored by catch's avatar catch

Issue #1890784 by alexpott, YesCT, heyrocker, tim.plunkett, EllaTheHarpy,...

Issue #1890784 by alexpott, YesCT, heyrocker, tim.plunkett, EllaTheHarpy, beejeebus: Refactor configuration import and sync functions.
parent 3a8e8c60
......@@ -356,6 +356,15 @@ services:
class: Drupal\Core\EventSubscriber\ConfigGlobalOverrideSubscriber
tags:
- { name: event_subscriber }
config_import_subscriber:
class: Drupal\Core\EventSubscriber\ConfigImportSubscriber
tags:
- { name: event_subscriber }
config_snapshot_subscriber:
class: Drupal\Core\EventSubscriber\ConfigSnapshotSubscriber
tags:
- { name: event_subscriber }
arguments: ['@config.storage', '@config.storage.snapshot']
language_request_subscriber:
class: Drupal\Core\EventSubscriber\LanguageRequestSubscriber
tags:
......
This diff is collapsed.
......@@ -96,15 +96,13 @@ public function get($name) {
*/
public function reset($name = NULL) {
if ($name) {
// Reinitialize the configuration object in all contexts.
// Clear the cached configuration object in all contexts.
foreach ($this->getCacheKeys($name) as $cache_key) {
$this->cache[$cache_key]->init();
unset($this->cache[$cache_key]);
}
}
else {
foreach ($this->cache as $config) {
$config->init();
}
$this->cache = array();
}
return $this;
}
......
This diff is collapsed.
<?php
/**
* @file
* Contains \Drupal\Core\Config\ConfigImporterEvent.
*/
namespace Drupal\Core\Config;
use Symfony\Component\EventDispatcher\Event;
class ConfigImporterEvent extends Event {
/**
* Configuration import object.
*
* @var \Drupal\Core\Config\ConfigImporter
*/
protected $configImporter;
/**
* Constructs ConfigImporterEvent.
*
* @param \Drupal\Core\Config\ConfigImporter $config_importer
* A config import object to notify listeners about.
*/
public function __construct(ConfigImporter $config_importer) {
$this->configImporter = $config_importer;
}
/**
* Gets the config import object.
*
* @return \Drupal\Core\Config\ConfigImporter
* The ConfigImporter object.
*/
public function getConfigImporter() {
return $this->configImporter;
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Config\ConfigImporterException.
*/
namespace Drupal\Core\Config;
/**
* Exception thrown when a config import fails.
*/
class ConfigImporterException extends ConfigException {}
<?php
/**
* @file
* Contains \Drupal\Core\Config\ConfigInstaller.
*/
namespace Drupal\Core\Config;
/**
* Defines a configuration installer.
*
* A config installer imports the changes into the configuration system during
* module installs.
*
* The ConfigInstaller has a identifier which is used to construct event names.
* The events fired during an import are:
* - 'config.installer.validate': Events listening can throw a
* \Drupal\Core\Config\ConfigImporterException to prevent an import from
* occurring.
* @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber
* - 'config.installer.import': Events listening can react to a successful import.
*
* @see \Drupal\Core\Config\ConfigImporter
*/
class ConfigInstaller extends ConfigImporter {
/**
* The name used to identify events and the lock.
*/
const ID = 'config.installer';
}
......@@ -521,7 +521,7 @@ public function importCreate($name, Config $new_config, Config $old_config) {
}
/**
* Update configuration upon synchronizing configuration changes.
* Updates configuration upon synchronizing configuration changes.
*
* This callback is invoked when configuration is synchronized between storages
* and allows a module to take over the synchronization of configuration data.
......@@ -533,7 +533,7 @@ public function importCreate($name, Config $new_config, Config $old_config) {
* @param \Drupal\Core\Config\Config $old_config
* A configuration object containing the old configuration data.
*/
public function importChange($name, Config $new_config, Config $old_config) {
public function importUpdate($name, Config $new_config, Config $old_config) {
$id = static::getIDFromConfigName($name, $this->entityInfo['config_prefix']);
$entities = $this->load(array($id));
$entity = $entities[$id];
......
<?php
/**
* @file
* Contains \Drupal\Core\Config\StorageComparer.
*/
namespace Drupal\Core\Config;
/**
* Defines a config storage comparer.
*/
class StorageComparer implements StorageComparerInterface {
/**
* The source storage used to discover configuration changes.
*
* @var \Drupal\Core\Config\StorageInterface
*/
protected $sourceStorage;
/**
* The target storage used to write configuration changes.
*
* @var \Drupal\Core\Config\StorageInterface
*/
protected $targetStorage;
/**
* List of changes to between the source storage and the target storage.
*
* @var array
*/
protected $changelist;
/**
* Lists all the configuration object names in the source storage.
*
* @see \Drupal\Core\Config\StorageComparer::getSourceNames()
*
* @var array
*/
protected $sourceNames = array();
/**
* Lists all the configuration object names in the target storage.
*
* @see \Drupal\Core\Config\StorageComparer::getTargetNames()
*
* @var array
*/
protected $targetNames = array();
/**
* Constructs the Configuration storage comparer.
*
* @param \Drupal\Core\Config\StorageInterface $source_storage
* Storage controller object used to read configuration.
* @param \Drupal\Core\Config\StorageInterface $target_storage
* Storage controller object used to write configuration.
*/
public function __construct(StorageInterface $source_storage, StorageInterface $target_storage) {
$this->sourceStorage = $source_storage;
$this->targetStorage = $target_storage;
$this->changelist = $this->getEmptyChangelist();
}
/**
* {@inheritdoc}
*/
public function getSourceStorage() {
return $this->sourceStorage;
}
/**
* {@inheritdoc}
*/
public function getTargetStorage() {
return $this->targetStorage;
}
/**
* {@inheritdoc}
*/
public function getEmptyChangelist() {
return array(
'create' => array(),
'update' => array(),
'delete' => array(),
);
}
/**
* {@inheritdoc}
*/
public function getChangelist($op = NULL) {
if ($op) {
return $this->changelist[$op];
}
return $this->changelist;
}
/**
* {@inheritdoc}
*/
public function addChangeList($op, array $changes) {
// Only add changes that aren't already listed.
$changes = array_diff($changes, $this->changelist[$op]);
$this->changelist[$op] = array_merge($this->changelist[$op], $changes);
return $this;
}
/**
* {@inheritdoc}
*/
public function createChangelist() {
return $this
->addChangelistCreate()
->addChangelistUpdate()
->addChangelistDelete();
}
/**
* {@inheritdoc}
*/
public function addChangelistDelete() {
return $this->addChangeList('delete', array_diff($this->getTargetNames(), $this->getSourceNames()));
}
/**
* {@inheritdoc}
*/
public function addChangelistCreate() {
return $this->addChangeList('create', array_diff($this->getSourceNames(), $this->getTargetNames()));
}
/**
* {@inheritdoc}
*/
public function addChangelistUpdate() {
foreach (array_intersect($this->getSourceNames(), $this->getTargetNames()) as $name) {
// Ignore manifest files.
if (substr($name, 0, 9) != 'manifest.') {
$source_config_data = $this->sourceStorage->read($name);
$target_config_data = $this->targetStorage->read($name);
if ($source_config_data !== $target_config_data) {
$this->addChangeList('update', array($name));
}
}
}
return $this;
}
/**
* {@inheritdoc}
*/
public function reset() {
$this->changelist = $this->getEmptyChangelist();
$this->sourceNames = $this->targetNames = array();
return $this->createChangelist();
}
/**
* {@inheritdoc}
*/
public function hasChanges($ops = array('delete', 'create', 'update')) {
foreach ($ops as $op) {
if (!empty($this->changelist[$op])) {
return TRUE;
}
}
return FALSE;
}
/**
* Gets all the configuration names in the source storage.
*
* @return array
* List of all the configuration names in the source storage.
*/
protected function getSourceNames() {
if (empty($this->sourceNames)) {
$this->sourceNames = $this->sourceStorage->listAll();
}
return $this->sourceNames;
}
/**
* Gets all the configuration names in the target storage.
*
* @return array
* List of all the configuration names in the target storage.
*/
protected function getTargetNames() {
if (empty($this->targetNames)) {
$this->targetNames = $this->targetStorage->listAll();
}
return $this->targetNames;
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Config\StorageComparerInterface.
*/
namespace Drupal\Core\Config;
/**
* Defines an interface for comparison of configuration storage objects.
*/
interface StorageComparerInterface {
/**
* Gets the configuration source storage.
*
* @return \Drupal\Core\Config\StorageInterface
* Storage controller object used to read configuration.
*/
public function getSourceStorage();
/**
* Gets the configuration target storage.
*
* @return \Drupal\Core\Config\StorageInterface
* Storage controller object used to write configuration.
*/
public function getTargetStorage();
/**
* Gets an empty changelist.
*
* @return array
* An empty changelist array.
*/
public function getEmptyChangelist();
/**
* Gets the list of differences to import.
*
* @param string $op
* (optional) A change operation. Either delete, create or update. If
* supplied the returned list will be limited to this operation.
*
* @return array
* An array of config changes that are yet to be imported.
*/
public function getChangelist($op = NULL);
/**
* Adds changes to the changelist.
*
* @param string $op
* The change operation performed. Either delete, create or update.
* @param array $changes
* Array of changes to add the changelist.
*
* @return \Drupal\Core\Config\StorageComparerInterface
* An object which implements the StorageComparerInterface.
*/
public function addChangeList($op, array $changes);
/**
* Add differences between source and target configuration storage to changelist.
*
* @return \Drupal\Core\Config\StorageComparerInterface
* An object which implements the StorageComparerInterface.
*/
public function createChangelist();
/**
* Creates the delete changelist.
*
* @return \Drupal\Core\Config\StorageComparerInterface
* An object which implements the StorageComparerInterface.
*/
public function addChangelistDelete();
/**
* Creates the create changelist.
*
* @return \Drupal\Core\Config\StorageComparerInterface
* An object which implements the StorageComparerInterface.
*/
public function addChangelistCreate();
/**
* Creates the update changelist.
*
* @return \Drupal\Core\Config\StorageComparerInterface
* An object which implements the StorageComparerInterface.
*/
public function addChangelistUpdate();
/**
* Recalculates the differences.
*
* @return \Drupal\Core\Config\StorageComparerInterface
* An object which implements the StorageComparerInterface.
*/
public function reset();
/**
* Checks if there are any operations with changes to process.
*
* Until the changelist has been calculated this will always be FALSE.
*
* @see \Drupal\Core\Config\StorageComparerInterface::createChangelist().
*
* @param array $ops
* The operations to check for changes. Defaults to all operations, i.e.
* array('delete', 'create', 'update').
*
* @return bool
* TRUE if there are changes to process and FALSE if not.
*/
public function hasChanges($ops = array('delete', 'create', 'update'));
}
<?php
/**
* @file
* Contains \Drupal\Core\Config\StorageComparerManifest.
*/
namespace Drupal\Core\Config;
/**
* Defines a config storage comparer that uses config entity manifests.
*
* Config entities maintain 'manifest' files that list the objects they are
* currently handling. Each file is a simple indexed array of config object
* names. In order to generate a list of objects that have been created or
* deleted we need to open these files in both the source and target storage,
* generate an array of the objects, and compare them.
*/
class StorageComparerManifest extends StorageComparer {
/**
* List of config entities managed by manifests in the source storage.
*
* @see \Drupal\Core\Config\StorageComparerManifest::getSourceManifestData()
*
* @var array
*/
protected $sourceManifestData = array();
/**
* List of config entities managed by manifests in the target storage.
*
* @see \Drupal\Core\Config\StorageComparerManifest::getTargetManifestData()
*
* @var array
*/
protected $targetManifestData = array();
/**
* {@inheritdoc}
*/
public function addChangelistDelete() {
foreach (array_diff_key($this->getTargetManifestData(), $this->getSourceManifestData()) as $value) {
$this->addChangeList('delete', array($value['name']));
}
return $this;
}
/**
* {@inheritdoc}
*/
public function addChangelistCreate() {
foreach (array_diff_key($this->getSourceManifestData(), $this->getTargetManifestData()) as $value) {
$this->addChangeList('create', array($value['name']));
}
return $this;
}
/**
* Gets the list of config entities from the source storage's manifest files.
*
* @return array
* The list of config entities in the source storage whose entity type has a
* manifest in the source storage.
*/
protected function getSourceManifestData() {
if (empty($this->sourceManifestData)) {
foreach ($this->getSourceStorage()->listAll('manifest') as $name) {
if ($source_manifest_data = $this->getSourceStorage()->read($name)) {
$this->sourceManifestData = array_merge($this->sourceManifestData, $source_manifest_data);
}
}
}
return $this->sourceManifestData;
}
/**
* Gets the list of config entities from the target storage's manifest files.
*
* @see \Drupal\Core\Config\ConfigImporter::getSourceManifestData()
*
* @return array
* The list of config entities in the target storage whose entity type has a
* manifest in the source storage.
*/
protected function getTargetManifestData() {
if (empty($this->targetManifestData)) {
foreach ($this->getSourceStorage()->listAll('manifest') as $name) {
if ($target_manifest_data = $this->targetStorage->read($name)) {
$this->targetManifestData = array_merge($this->targetManifestData, $target_manifest_data);
}
}
}
return $this->targetManifestData;
}
/**
* {@inheritdoc}
*/
public function reset() {
$this->sourceManifestData = $this->targetManifestData = array();
return parent::reset();
}
}
<?php
/**
* @file
* Contains \Drupal\Core\EventSubscriber\ConfigImportSubscriber.
*/
namespace Drupal\Core\EventSubscriber;
use Drupal\Core\Config\Config;
use Drupal\Core\Config\ConfigImporterEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Config import subscriber for config import events.
*/
class ConfigImportSubscriber implements EventSubscriberInterface {
/**
* Validates the configuration to be imported.
*
* @param \Drupal\Core\Config\ConfigImporterEvent $event
* The Event to process.
*
* @throws \Drupal\Core\Config\ConfigNameException
*/
public function onConfigImporterValidate(ConfigImporterEvent $event) {
foreach (array('delete', 'create', 'update') as $op) {
foreach ($event->getConfigImporter()->getUnprocessed($op) as $name) {
Config::validateName($name);
}
}
}
/**
* Registers the methods in this class that should be listeners.
*
* @return array
* An array of event listener definitions.
*/
static function getSubscribedEvents() {
$events['config.importer.validate'][] = array('onConfigImporterValidate', 40);
$events['config.installer.validate'][] = array('onConfigImporterValidate', 40);
return $events;
}
}
<?php
/**
* @file
* Contains \Drupal\Core\EventSubscriber\ConfigSnapshotSubscriber.
*/
namespace Drupal\Core\EventSubscriber;
use Drupal\Core\Config\Config;
use Drupal\Core\Config\StorageInterface;
use Drupal\Core\Config\ConfigImporterEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Create a snapshot when config is imported.
*/
class ConfigSnapshotSubscriber implements EventSubscriberInterface {
/**
* The source storage used to discover configuration changes.
*
* @var \Drupal\Core\Config\StorageInterface
*/
protected $sourceStorage;
/**
* The snapshot storage used to write configuration changes.
*
* @var \Drupal\Core\Config\StorageInterface
*/
protected $snapshotStorage;
/**
* Constructs the ConfigSnapshotSubscriber object.
*
* @param StorageInterface $source_storage
* The source storage used to discover configuration changes.
* @param StorageInterface $snapshot_storage
* The snapshot storage used to write configuration changes.
*/
public function __construct(StorageInterface $source_storage, StorageInterface $snapshot_storage) {
$this->sourceStorage = $source_storage;
$this->snapshotStorage = $snapshot_storage;