-
ambient.impact authoredambient.impact authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ConfigImporter.php 5.02 KiB
<?php
declare(strict_types=1);
namespace Drupal\config_enforce;
use Drupal\Core\Config\ConfigImporter as CoreConfigImporter;
use Drupal\Core\Config\ConfigException;
use Drupal\Core\Config\InstallStorage;
use Drupal\Core\Config\StorageComparer;
use Drupal\config\StorageReplaceDataWrapper;
/**
* Imports configuration.
*/
class ConfigImporter {
// Use log() method and related traits.
use ConfigEnforceHelperTrait;
// Channel with which to log from this class.
const LOGCHANNEL = 'config_enforce\ConfigImporter';
/**
* Basic constructor.
*/
public function __construct() { }
/**
* Imports a config item from the given filename.
*
* @param string $filename
* The filename to import from with the Drupal-relative path.
*/
public function importConfig($filenames) {
$config_storage = \Drupal::service('config.storage');
$source_storage = new StorageReplaceDataWrapper($config_storage);
foreach ($this->getDataFromConfigFiles($filenames) as $config_name => $data) {
$source_storage->replaceData($config_name, $data);
}
$storage_comparer = new StorageComparer($source_storage, $config_storage);
$storage_comparer->createChangelist();
// @TODO log whether config has changed?
return $this->doImport($storage_comparer);
}
protected function getDataFromConfigFiles($filenames) {
$config_data = [];
foreach ($filenames as $filename) {
if (!is_readable($filename)) {
$message = $this->t('The configuration file at :filename does not exist, or cannot be read.', [
':filename' => $filename,
]);
// @TODO Log this to Drush too.
$this->messenger()->addWarning($message);
$this->log()->warning($message);
continue;
}
$contents = file_get_contents($filename);
$data = (new InstallStorage())->decode($contents);
$config_name = basename($filename, '.yml');
$this->handleSpecialCaseConfig($config_name, $data);
$config_data[$config_name] = $data;
}
return $config_data;
}
// Copied from submitForm() at /core/modules/config/src/Form/ConfigSync.php
protected function doImport($storage_comparer) {
$config_importer = $this->getConfigImporter($storage_comparer);
if ($config_importer->alreadyImporting()) {
$this->log()->warning('Another request may be synchronizing configuration already.');
} else {
try {
// This is the contents of \Drupal\Core\Config\ConfigImporter::import.
// Copied here so we can log progress.
if ($config_importer->hasUnprocessedConfigurationChanges()) {
$sync_steps = $config_importer->initialize();
foreach ($sync_steps as $step) {
$context = [];
do {
$config_importer->doSyncStep($step, $context);
if (isset($context['message'])) {
$this->log()->notice(str_replace('Synchronizing', 'Synchronized', (string)$context['message']));
}
} while ($context['finished'] < 1);
}
// Clear the cache of the active config storage.
\Drupal::service('cache.config')->deleteAll();
}
if ($config_importer->getErrors()) {
throw new ConfigException('Errors occurred during import');
} else {
$this->log()->notice('The configuration was imported successfully.');
}
} catch (ConfigException $e) {
// Return a negative result for UI purposes. We do not differentiate
// between an actual synchronization error and a failed lock, because
// concurrent synchronizations are an edge-case happening only when
// multiple developers or site builders attempt to do it without
// coordinating.
$message = 'The import failed due to the following reasons:' . "\n";
$message .= implode("\n", $config_importer->getErrors());
watchdog_exception('config_import', $e);
throw new \Exception($message);
}
}
}
/**
* Return an instantiated ConfigImporter.
*/
protected function getConfigImporter($storage_comparer) {
// @TODO: simplify this when
// https://www.drupal.org/project/drupal/issues/3123491 is fixed.
return new CoreConfigImporter(
$storage_comparer,
\Drupal::service('event_dispatcher'),
\Drupal::service('config.manager'),
\Drupal::service('lock.persistent'),
\Drupal::service('config.typed'),
\Drupal::service('module_handler'),
\Drupal::service('module_installer'),
\Drupal::service('theme_handler'),
\Drupal::service('string_translation'),
\Drupal::service('extension.list.module')
);
}
/**
* Alter data or otherwise handle config special cases.
*/
protected function handleSpecialCaseConfig($config_name, &$data) {
switch ($config_name) {
case 'system.site':
// Site UUID gets validated, so we need to make sure it'll match.
// @TODO log this?
$data['uuid'] = \Drupal::config('system.site')->get('uuid');
break;
default:
return;
}
}
}