diff --git a/core/includes/config.inc b/core/includes/config.inc index 8d37e13..ff498e4 100644 --- a/core/includes/config.inc +++ b/core/includes/config.inc @@ -4,6 +4,7 @@ use Drupal\Core\Config\ConfigObject; use Drupal\Core\Config\ConfigException; use Drupal\Core\Config\DatabaseStorage; use Drupal\Core\Config\FileStorage; +use Drupal\Core\Config\StorageInterface; /** * @file @@ -86,10 +87,46 @@ function config($name) { } /** - * Synchronizes configuration from FileStorage to DatabaseStorage. + * Returns a list of differences between configuration storages. + * + * @param Drupal\Core\Config\StorageInterface $source_storage + * The storage to synchronize configuration from. + * @param Drupal\Core\Config\StorageInterface $target_storage + * The storage to synchronize configuration to. + * + * @return array|bool + * An assocative array containing the differences between source and target + * storage, or FALSE if there are no differences. */ -function config_sync() { - $config_changes = config_sync_get_changes(); +function config_sync_get_changes(StorageInterface $source_storage, StorageInterface $target_storage) { + $source_names = $source_storage->getNamesWithPrefix(); + $target_names = $target_storage->getNamesWithPrefix(); + $config_changes = array( + 'create' => array_diff($source_names, $target_names), + 'change' => array(), + 'delete' => array_diff($target_names, $source_names), + ); + foreach (array_intersect($source_names, $target_names) as $name) { + $source_config_data = $source_storage->read($name); + $target_config_data = $target_storage->read($name); + if ($source_config_data != $target_config_data) { + $config_changes['change'][] = $name; + } + } + + // Do not trigger subsequent synchronization operations if there are no + // changes in either category. + if (empty($config_changes['create']) && empty($config_changes['change']) && empty($config_changes['delete'])) { + return FALSE; + } + return $config_changes; +} + +/** + * Imports configuration from FileStorage to DatabaseStorage. + */ +function config_import() { + $config_changes = config_import_get_changes(); if (empty($config_changes)) { return; } @@ -105,14 +142,14 @@ function config_sync() { } try { - $remaining_changes = config_sync_invoke_sync_hooks($config_changes); - config_sync_save_changes($remaining_changes); + $remaining_changes = config_import_invoke_sync_hooks($config_changes); + config_import_save_changes($remaining_changes); // Flush all caches and reset static variables after a successful import. drupal_flush_all_caches(); } catch (ConfigException $e) { - watchdog_exception('config_sync', $e); - config_sync_invoke_sync_error_hooks($config_changes); + watchdog_exception('config_import', $e); + config_import_invoke_sync_error_hooks($config_changes); lock_release(__FUNCTION__); return FALSE; } @@ -121,12 +158,25 @@ function config_sync() { } /** + * Returns a list of differences between FileStorage and DatabaseStorage. + * + * @see config_sync_get_changes() + */ +function config_import_get_changes() { + // @todo Leverage DI + config.storage.info. + $source_storage = new FileStorage(); + $target_storage = new DatabaseStorage(); + + return config_sync_get_changes($source_storage, $target_storage); +} + +/** * Writes an array of config file changes to the active store. * * @param array $config_changes * An array of changes to be written. */ -function config_sync_save_changes(array $config_changes) { +function config_import_save_changes(array $config_changes) { // @todo Leverage DI + config.storage.info. $source_storage = new FileStorage(); $target_storage = new DatabaseStorage(); @@ -144,12 +194,12 @@ function config_sync_save_changes(array $config_changes) { } /** - * Invokes hook_config_sync_validate() and hook_config_sync() implementations. + * Invokes hook_config_import_validate() and hook_config_import() implementations. * * @param array $config_changes * An array of changes to be loaded. */ -function config_sync_invoke_sync_hooks(array $config_changes) { +function config_import_invoke_sync_hooks(array $config_changes) { // @todo Leverage DI + config.storage.info. $source_storage = new FileStorage(); $target_storage = new DatabaseStorage(); @@ -159,39 +209,37 @@ function config_sync_invoke_sync_hooks(array $config_changes) { // Note: module_invoke_all() can only be used as long as it does not allow // implementations to take $config_changes by reference, since they are not // supposed to change the configuration that is to be imported. - module_invoke_all('config_sync_validate', $config_changes, $target_storage, $source_storage); + module_invoke_all('config_import_validate', $config_changes, $target_storage, $source_storage); // Allow modules to take over configuration change operations for // higher-level configuration data. // First pass deleted, then new, and lastly changed configuration, in order to // handle dependencies correctly. - $remaining_changes = $config_changes; foreach (array('delete', 'create', 'change') as $op) { - foreach ($remaining_changes[$op] as $key => $name) { + foreach ($config_changes[$op] as $key => $name) { // Extract owner from configuration object name. $module = strtok($name, '.'); - // Check whether the module implements hook_config_sync() and ask it to + // Check whether the module implements hook_config_import() and ask it to // handle the configuration change. $handled_by_module = FALSE; - if (module_hook($module, 'config_sync')) { + if (module_hook($module, 'config_import')) { $old_config = new ConfigObject($storage_manager); $old_config->setName($name)->load(); $data = $source_storage->read($name); $new_config = new ConfigObject($storage_manager); $new_config->setName($name)->setData($data); - $handled_by_module = module_invoke($module, 'config_sync', $op, $name, $new_config, $old_config); + $handled_by_module = module_invoke($module, 'config_import', $op, $name, $new_config, $old_config); } if (!empty($handled_by_module)) { - unset($remaining_changes[$op][$key]); + unset($config_changes[$op][$key]); } } } - - return $remaining_changes; + return $config_changes; } /** - * Invokes hook_config_sync_error() implementations. + * Invokes hook_config_import_error() implementations. * * During a sync run, modules may make changes that cannot be rolled back. * This hook allows modules to react to an error that occurs after they have @@ -201,18 +249,18 @@ function config_sync_invoke_sync_hooks(array $config_changes) { * @param array $config_changes * An array of changes to be loaded. */ -function config_sync_invoke_sync_error_hooks(array $config_changes) { +function config_import_invoke_sync_error_hooks(array $config_changes) { // @todo Leverage DI + config.storage.info. $source_storage = new FileStorage(); $target_storage = new DatabaseStorage(); - foreach (module_implements('config_sync_error') as $module) { - $function = $module . '_config_sync_error'; + foreach (module_implements('config_import_error') as $module) { + $function = $module . '_config_import_error'; try { $function($config_changes, $target_storage, $source_storage); } catch (ConfigException $e) { - watchdog_exception('config_sync', $e); + watchdog_exception('config_import', $e); // Just keep going, because we need to allow all modules to react even if // some of them are behaving badly. } @@ -220,36 +268,50 @@ function config_sync_invoke_sync_error_hooks(array $config_changes) { } /** - * Returns a list of differences between FileStorage and DatabaseStorage. + * Exports configuration from DatabaseStorage to FileStorage. + */ +function config_export() { + $config_changes = config_export_get_changes(); + if (empty($config_changes)) { + return; + } + config_export_save_changes($config_changes); + return TRUE; +} + +/** + * Returns a list of differences between DatabaseStorage and FileStorage. * - * @return array|bool - * The list of files changed on disk compared to the active store, or FALSE if - * there are no differences. + * @see config_sync_get_changes() */ -function config_sync_get_changes() { +function config_export_get_changes() { // @todo Leverage DI + config.storage.info. - $source_storage = new FileStorage(); - $target_storage = new DatabaseStorage(); + $source_storage = new DatabaseStorage(); + $target_storage = new FileStorage(); - $source_names = $source_storage->getNamesWithPrefix(); - $target_names = $target_storage->getNamesWithPrefix(); - $config_changes = array( - 'create' => array_diff($source_names, $target_names), - 'change' => array(), - 'delete' => array_diff($target_names, $source_names), - ); - foreach (array_intersect($source_names, $target_names) as $name) { - $source_config_data = $source_storage->read($name); - $target_config_data = $target_storage->read($name); - if ($source_config_data != $target_config_data) { - $config_changes['change'][] = $name; - } - } + return config_sync_get_changes($source_storage, $target_storage); +} - // Do not trigger subsequent synchronization operations if there are no - // changes in either category. - if (empty($config_changes['create']) && empty($config_changes['change']) && empty($config_changes['delete'])) { - return FALSE; +/** + * Writes an array of configuration changes to FileStorage. + * + * @param array $config_changes + * An array of changes to be written. + */ +function config_export_save_changes(array $config_changes) { + // @todo Leverage DI + config.storage.info. + $source_storage = new DatabaseStorage(); + $target_storage = new FileStorage(); + foreach (array('delete', 'create', 'change') as $op) { + foreach ($config_changes[$op] as $name) { + if ($op == 'delete') { + $target_storage->delete($name); + } + else { + $data = $source_storage->read($name); + $target_storage->write($name, $data); + } + } } - return $config_changes; } + diff --git a/core/modules/config/config.admin.inc b/core/modules/config/config.admin.inc index 2366c5a..6f77bf2 100644 --- a/core/modules/config/config.admin.inc +++ b/core/modules/config/config.admin.inc @@ -11,7 +11,7 @@ * @see config_admin_import_form_submit() */ function config_admin_import_form($form, &$form_state) { - $config_changes = config_sync_get_changes(); + $config_changes = config_import_get_changes(); if (empty($config_changes)) { $form['no_changes'] = array( @@ -48,7 +48,7 @@ function config_admin_import_form($form, &$form_state) { * Form submission handler for config_admin_import_form(). */ function config_admin_import_form_submit($form, &$form_state) { - if (config_sync()) { + if (config_import()) { drupal_set_message(t('The configuration was imported successfully.')); } else { diff --git a/core/modules/config/config.api.php b/core/modules/config/config.api.php index 7e92658..4161e84 100644 --- a/core/modules/config/config.api.php +++ b/core/modules/config/config.api.php @@ -31,7 +31,7 @@ * @throws ConfigException * In case a configuration change cannot be allowed. */ -function hook_config_sync_validate($config_changes, $target_storage, $source_storage) { +function hook_config_import_validate($config_changes, $target_storage, $source_storage) { // Deny changes to our settings. if (isset($config_changes['change']['mymodule.locked'])) { throw new ConfigException('MyModule settings cannot be changed.'); @@ -63,7 +63,7 @@ function hook_config_sync_validate($config_changes, $target_storage, $source_sto * differences were read. Use $target_storage->load() to load a configuration * object. */ -function hook_config_sync($op, $name, $new_config, $old_config) { +function hook_config_import($op, $name, $new_config, $old_config) { // Only image styles require custom handling. Any other module settings can be // synchronized directly. if (strpos($name, 'image.style.') !== 0) { @@ -105,7 +105,7 @@ function hook_config_sync($op, $name, $new_config, $old_config) { * differences were read. Use $target_storage->load() to load a configuration * object. */ -function hook_config_sync_error($config_changes, $target_storage, $source_storage) { +function hook_config_import_error($config_changes, $target_storage, $source_storage) { // @todo Feasability and usage of this hook is still unclear, without having a // backup of $target_storage at hand. } diff --git a/core/modules/config/config_test/config_test.module b/core/modules/config/config_test/config_test.module index 8af386e..3c1c767 100644 --- a/core/modules/config/config_test/config_test.module +++ b/core/modules/config/config_test/config_test.module @@ -3,34 +3,34 @@ use Drupal\Core\Config\ConfigException; /** - * Implements hook_config_sync_validate(). + * Implements hook_config_import_validate(). */ -function config_test_config_sync_validate($config_changes, $target_storage, $source_storage) { - if (!empty($GLOBALS['config_sync_validate_throw_error'])) { +function config_test_config_import_validate($config_changes, $target_storage, $source_storage) { + if (!empty($GLOBALS['config_import_validate_throw_error'])) { throw new ConfigException('Configuration changes are invalid.'); } // Set a global value we can check in test code. - $GLOBALS['hook_config_sync_validate'] = __FUNCTION__; + $GLOBALS['hook_config_import_validate'] = __FUNCTION__; } /** - * Implements hook_config_sync(). + * Implements hook_config_import(). */ -function config_test_config_sync($op, $name, $new_config, $old_config) { - if (!empty($GLOBALS['config_sync_throw_error'])) { +function config_test_config_import($op, $name, $new_config, $old_config) { + if (!empty($GLOBALS['config_import_throw_error'])) { throw new ConfigException('Error during synchronization.'); } // Set a global value we can check in test code. - $GLOBALS['hook_config_sync'] = __FUNCTION__; + $GLOBALS['hook_config_import'] = __FUNCTION__; } /** - * Implements hook_config_sync_error(). + * Implements hook_config_import_error(). */ -function config_test_config_sync_error($config_changes, $target_storage, $source_storage) { +function config_test_config_import_error($config_changes, $target_storage, $source_storage) { // Set a global value we can check in test code. - $GLOBALS['hook_config_sync_error'] = __FUNCTION__; + $GLOBALS['hook_config_import_error'] = __FUNCTION__; } diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php index 569b3c6..634068d 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php @@ -11,7 +11,7 @@ use Drupal\Core\Config\FileStorage; use Drupal\simpletest\WebTestBase; /** - * Tests config_sync() functionality. + * Tests config_import() functionality. */ class ConfigImportTest extends WebTestBase { public static function getInfo() { @@ -37,12 +37,15 @@ class ConfigImportTest extends WebTestBase { $config = config($name); $this->assertIdentical($config->get('foo'), 'bar'); + // Export. + config_export(); + // Delete the configuration object. - $file = new FileStorage(); - $file->delete($name); + $file_storage = new FileStorage(); + $file_storage->delete($name); // Import. - config_sync(); + config_import(); // Verify the value has disappeared. $config = config($name); @@ -56,17 +59,20 @@ class ConfigImportTest extends WebTestBase { $name = 'config_test.new'; // Verify the configuration to create does not exist yet. - $file = new FileStorage(); - $this->assertIdentical($file->exists($name), FALSE, $name . ' not found.'); + $file_storage = new FileStorage(); + $this->assertIdentical($file_storage->exists($name), FALSE, $name . ' not found.'); + + // Export. + config_export(); // Create a new configuration object. - $file->write($name, array( + $file_storage->write($name, array( 'add_me' => 'new value', )); - $this->assertIdentical($file->exists($name), TRUE, $name . ' found.'); + $this->assertIdentical($file_storage->exists($name), TRUE, $name . ' found.'); // Import. - config_sync(); + config_import(); // Verify the value has appeared. $config = config($name); @@ -79,10 +85,13 @@ class ConfigImportTest extends WebTestBase { function testUpdated() { $name = 'config_test.system'; + // Export. + config_export(); + // Replace the file content of the existing configuration object. - $file = new FileStorage(); - $this->assertIdentical($file->exists($name), TRUE, $name . ' found.'); - $file->write($name, array( + $file_storage = new FileStorage(); + $this->assertIdentical($file_storage->exists($name), TRUE, $name . ' found.'); + $file_storage->write($name, array( 'foo' => 'beer', )); @@ -91,7 +100,7 @@ class ConfigImportTest extends WebTestBase { $this->assertIdentical($config->get('foo'), 'bar'); // Import. - config_sync(); + config_import(); // Verify the value was updated. $config = config($name); @@ -99,29 +108,32 @@ class ConfigImportTest extends WebTestBase { } /** - * Tests config_sync() hook invocations. + * Tests config_import() hook invocations. */ function testSyncHooks() { $name = 'config_test.system'; - // Delete a file so that hook_config_sync() hooks are run. - $file = new FileStorage(); - $this->assertIdentical($file->exists($name), TRUE, $name . ' found.'); - $file->delete($name); + // Export. + config_export(); + + // Delete a file so that hook_config_import() hooks are run. + $file_storage = new FileStorage(); + $this->assertIdentical($file_storage->exists($name), TRUE, $name . ' found.'); + $file_storage->delete($name); // Make the test implementation throw an error during synchronization, so - // hook_config_sync_error() is also invoked. - $GLOBALS['config_sync_throw_error'] = TRUE; + // hook_config_import_error() is also invoked. + $GLOBALS['config_import_throw_error'] = TRUE; // Import. - config_sync(); - - // Verify hook_config_sync() was invoked. - $this->assertIdentical($GLOBALS['hook_config_sync'], 'config_test_config_sync'); - // Verify hook_config_sync_error() was invoked. - $this->assertIdentical($GLOBALS['hook_config_sync_validate'], 'config_test_config_sync_validate'); - // Verify hook_config_sync_error() was invoked. - $this->assertIdentical($GLOBALS['hook_config_sync_error'], 'config_test_config_sync_error'); + config_import(); + + // Verify hook_config_import() was invoked. + $this->assertIdentical($GLOBALS['hook_config_import'], 'config_test_config_import'); + // Verify hook_config_import_error() was invoked. + $this->assertIdentical($GLOBALS['hook_config_import_validate'], 'config_test_config_import_validate'); + // Verify hook_config_import_error() was invoked. + $this->assertIdentical($GLOBALS['hook_config_import_error'], 'config_test_config_import_error'); } /** @@ -130,17 +142,20 @@ class ConfigImportTest extends WebTestBase { function testSyncValidationError() { $name = 'config_test.system'; - // Delete a file so that hook_config_sync() hooks are run. - $file = new FileStorage(); - $this->assertIdentical($file->exists($name), TRUE, $name . ' found.'); - $file->delete($name); + // Export. + config_export(); + + // Delete a file so that hook_config_import() hooks are run. + $file_storage = new FileStorage(); + $this->assertIdentical($file_storage->exists($name), TRUE, $name . ' found.'); + $file_storage->delete($name); // Make the test implementation throw a validation error, aborting the // synchronization. - $GLOBALS['config_sync_validate_throw_error'] = TRUE; + $GLOBALS['config_import_validate_throw_error'] = TRUE; // Import. - config_sync(); + config_import(); // Verify the active store was not updated. $config = config($name); diff --git a/core/modules/image/image.module b/core/modules/image/image.module index 7825a18..c726952 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -500,9 +500,9 @@ function image_path_flush($path) { } /** - * Implements hook_config_sync(). + * Implements hook_config_import(). */ -function image_config_sync($op, $name, $new_config, $old_config) { +function image_config_import($op, $name, $new_config, $old_config) { // Only image styles require custom handling. Any other module settings can be // synchronized directly. if (strpos($name, 'image.style.') !== 0) { @@ -514,7 +514,7 @@ function image_config_sync($op, $name, $new_config, $old_config) { // to be used by other modules instead of the deleted style. Good idea. // But squeezing that into a "delete" operation is the worst idea ever. // Regardless of Image module insanity, add a 'replaced' stack to - // config_sync()? And how can that work? If an 'old_ID' key would be a + // config_import()? And how can that work? If an 'old_ID' key would be a // standard, wouldn't this belong into 'changed' instead? $style = $old_config->get(); return image_style_delete($style);