diff --git a/core/lib/Drupal/Core/Config/ConfigObject.php b/core/lib/Drupal/Core/Config/ConfigObject.php index 0884e9a..583af92 100644 --- a/core/lib/Drupal/Core/Config/ConfigObject.php +++ b/core/lib/Drupal/Core/Config/ConfigObject.php @@ -214,11 +214,32 @@ class ConfigObject { * Saves the configuration object. */ public function save() { + $this->sortByKey($this->data); $this->storageManager->selectStorage('write', $this->name)->write($this->name, $this->data); return $this; } /** + * Sorts all keys in configuration data. + * + * Ensures that re-inserted keys appear in the same location as before, in + * order to ensure an identical order regardless of storage controller. + * A consistent order is important for any storage that allows any kind of + * diff operation. + * + * @param array $data + * An associative array to sort recursively by key name. + */ + public function sortByKey(array &$data) { + ksort($data); + foreach ($data as &$value) { + if (is_array($value)) { + $this->sortByKey($value); + } + } + } + + /** * Deletes the configuration object. */ public function delete() { diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php index 5e5b926..b8848b1 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php @@ -85,4 +85,31 @@ class ConfigCRUDTest extends WebTestBase { // Verify config() returned the existing config instance. $this->assertIdentical($new_config, $config); } + + /** + * Tests Drupal\Core\Config\ConfigObject::sortByKey(). + */ + function testDataKeySort() { + $config = config('config_test.keysort'); + $config->set('new', 'Value to be replaced'); + $config->set('static', 'static'); + $config->save(); + // Clone this ConfigObject, so this test does not rely on any particular + // architecture. + $config = clone $config; + + // Load the configuration data into a new object. + $new_config = config('config_test.keysort'); + // Clear the 'new' key that came first. + $new_config->clear('new'); + // Add a new 'new' key and save. + $new_config->set('new', 'Value to be replaced'); + $new_config->save(); + + // Verify that the data of both objects is in the identical order. + // assertIdentical() is the required essence of this test; it performs a + // strict comparison, which means that keys and values must be identical and + // their order must be identical. + $this->assertIdentical($new_config->get(), $config->get()); + } }