From 3d251fef981336cfe89502285a558fc02ccea117 Mon Sep 17 00:00:00 2001
From: Bram Goffings <bramgoffings@gmail.com>
Date: Fri, 10 Aug 2012 15:35:31 +0200
Subject: [PATCH] config override

---
 core/includes/config.inc                           |  9 +--
 core/lib/Drupal/Core/Config/Config.php             | 81 ++++++++++++++++----
 core/lib/Drupal/Core/Config/ConfigFactory.php      | 11 ++-
 .../lib/Drupal/config/Tests/ConfOverrideTest.php   | 38 ----------
 .../lib/Drupal/config/Tests/ConfigOverrideTest.php | 88 ++++++++++++++++++++++
 5 files changed, 166 insertions(+), 61 deletions(-)
 delete mode 100644 core/modules/config/lib/Drupal/config/Tests/ConfOverrideTest.php
 create mode 100644 core/modules/config/lib/Drupal/config/Tests/ConfigOverrideTest.php

diff --git a/core/includes/config.inc b/core/includes/config.inc
index f2fcd39..347bf85 100644
--- a/core/includes/config.inc
+++ b/core/includes/config.inc
@@ -194,14 +194,11 @@ function config_import_invoke_owner(array $config_changes, StorageInterface $sou
       // handle the configuration change.
       $handled_by_module = FALSE;
       if (module_hook($module, 'config_import_' . $op)) {
-        $old_config = new Config($target_storage);
-        $old_config
-          ->setName($name)
-          ->load();
+        $old_config = new Config($name, $target_storage);
+        $old_config->load();
 
         $data = $source_storage->read($name);
-        $new_config = new Config($target_storage);
-        $new_config->setName($name);
+        $new_config = new Config($name, $target_storage);
         if ($data !== FALSE) {
           $new_config->setData($data);
         }
diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php
index 2f4d14a..10bf925 100644
--- a/core/lib/Drupal/Core/Config/Config.php
+++ b/core/lib/Drupal/Core/Config/Config.php
@@ -33,7 +33,21 @@ class Config {
    *
    * @var array
    */
-  protected $data = array();
+  protected $data;
+
+  /**
+   * The overridden data of the configuration object.
+   *
+   * @var array
+   */
+  protected $overrides;
+
+  /**
+   * The current runtime data ($data + $overrides).
+   *
+   * @var array
+   */
+  protected $overriddenData;
 
   /**
    * The storage used for reading and writing.
@@ -45,11 +59,14 @@ class Config {
   /**
    * Constructs a configuration object.
    *
+   * @param string $name
+   *   The name of the configuration object being constructed.
    * @param Drupal\Core\Config\StorageInterface $storage
    *   A storage controller object to use for reading and writing the
    *   configuration data.
    */
-  public function __construct(StorageInterface $storage) {
+  public function __construct($name, StorageInterface $storage) {
+    $this->name = $name;
     $this->storage = $storage;
   }
 
@@ -103,27 +120,19 @@ class Config {
    *   The data that was requested.
    */
   public function get($key = '') {
-    global $conf;
-
-    $name = $this->getName();
-    if (isset($conf[$name])) {
-      $merged_data = NestedArray::mergeDeepArray(array($this->data, $conf[$name]));
-    }
-    else {
-      $merged_data = $this->data;
+    if (!isset($this->overriddenData)) {
+      $this->setOverriddenData();
     }
-
     if (empty($key)) {
-      return $merged_data;
+      return $this->overriddenData;
     }
     else {
       $parts = explode('.', $key);
       if (count($parts) == 1) {
-        return isset($merged_data[$key]) ? $merged_data[$key] : NULL;
+        return isset($this->overriddenData[$key]) ? $this->overriddenData[$key] : NULL;
       }
       else {
-        $key_exists = NULL;
-        $value = NestedArray::getValue($merged_data, $parts, $key_exists);
+        $value = NestedArray::getValue($this->overriddenData, $parts, $key_exists);
         return $key_exists ? $value : NULL;
       }
     }
@@ -137,6 +146,45 @@ class Config {
    */
   public function setData(array $data) {
     $this->data = $data;
+    $this->resetOverriddenData();
+    return $this;
+  }
+
+  /**
+   * Sets overridden data for this configuration object.
+   *
+   * The overridden data only applies to this configuration object.
+   *
+   * @param array $data
+   *   The overridden values of the configuration data.
+   */
+  public function setOverride(array $data) {
+    $this->overrides = $data;
+    $this->resetOverriddenData();
+    return $this;
+  }
+
+  /**
+   * Sets the current data for this configuration object.
+   *
+   * Merges overridden configuration data into the original data.
+   */
+  protected function setOverriddenData() {
+    $this->overriddenData = $this->data;
+    if (!empty($this->overrides)) {
+      $this->overriddenData = NestedArray::mergeDeepArray(array($this->overriddenData, $this->overrides));
+    }
+    return $this;
+  }
+
+  /**
+   * Resets the current data, so overrides are re-applied.
+   *
+   * This method should be called after the original data or the overridden data
+   * has been changed.
+   */
+  protected function resetOverriddenData() {
+    unset($this->overriddenData);
     return $this;
   }
 
@@ -161,6 +209,7 @@ class Config {
     else {
       NestedArray::setValue($this->data, $parts, $value);
     }
+    $this->resetOverriddenData();
     return $this;
   }
 
@@ -217,6 +266,7 @@ class Config {
     else {
       NestedArray::unsetValue($this->data, $parts);
     }
+    $this->resetOverriddenData();
     return $this;
   }
 
@@ -273,6 +323,7 @@ class Config {
     $this->data = array();
     $this->storage->delete($this->name);
     $this->isNew = TRUE;
+    $this->resetOverriddenData();
     return $this;
   }
 }
diff --git a/core/lib/Drupal/Core/Config/ConfigFactory.php b/core/lib/Drupal/Core/Config/ConfigFactory.php
index 7c26b6e..4bf5b62 100644
--- a/core/lib/Drupal/Core/Config/ConfigFactory.php
+++ b/core/lib/Drupal/Core/Config/ConfigFactory.php
@@ -50,6 +50,8 @@ class ConfigFactory {
    *   A configuration object with the given $name.
    */
   public function get($name) {
+    global $conf;
+
     // @todo Caching the instantiated objects per name might cut off a fair
     //   amount of CPU time and memory. Only the data within the configuration
     //   object changes, so the additional cost of instantiating duplicate
@@ -68,8 +70,13 @@ class ConfigFactory {
     // @todo The decrease of CPU time is interesting, since that means that
     //   ContainerBuilder involves plenty of function calls (which are known to
     //   be slow in PHP).
-    $config = new Config($this->storage);
-    return $config->setName($name);
+    $config = new Config($name, $this->storage);
+
+    // Set overridden values from global $conf, if any.
+    if (isset($conf[$name])) {
+      $config->setOverride($conf[$name]);
+    }
+    return $config;
   }
 
 }
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfOverrideTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfOverrideTest.php
deleted file mode 100644
index 104af8e..0000000
--- a/core/modules/config/lib/Drupal/config/Tests/ConfOverrideTest.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\config\Tests\ConfOverrideTest.
- */
-
-namespace Drupal\config\Tests;
-
-use Drupal\simpletest\WebTestBase;
-
-/**
- * Tests configuration overriding from settings.php.
- */
-class ConfOverrideTest extends WebTestBase {
-  protected $testContent = 'Good morning, Denver!';
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Configuration overrides',
-      'description' => 'Tests configuration overrides through settings.php.',
-      'group' => 'Configuration',
-    );
-  }
-
-  /**
-   * Test configuration override.
-   */
-  function testConfigurationOverride() {
-    global $conf;
-    $config = config('system.performance');
-    $this->assertNotEqual($config->get('cache'), $this->testContent);
-
-    $conf['system.performance']['cache'] = $this->testContent;
-    $config = config('system.performance');
-    $this->assertEqual($config->get('cache'), $conf['system.performance']['cache']);
-  }
-}
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigOverrideTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigOverrideTest.php
new file mode 100644
index 0000000..4e0d8f7
--- /dev/null
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigOverrideTest.php
@@ -0,0 +1,88 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\config\Tests\ConfigOverrideTest.
+ */
+
+namespace Drupal\config\Tests;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests configuration overrides via $conf in settings.php.
+ */
+class ConfigOverrideTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('config_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Configuration overrides',
+      'description' => 'Tests configuration overrides via $conf in settings.php.',
+      'group' => 'Configuration',
+    );
+  }
+
+  /**
+   * Tests configuration override.
+   */
+  function testConfOverride() {
+    global $conf;
+    $expected_original_data = array(
+      'foo' => 'bar',
+      'baz' => NULL,
+    );
+
+    // Verify that the original configuration data exists.
+    $config = config('config_test.system');
+    $this->assertIdentical($config->get('foo'), $expected_original_data['foo']);
+    $this->assertIdentical($config->get('baz'), $expected_original_data['baz']);
+
+    // Apply the overridden data.
+    $conf['config_test.system']['foo'] = 'overridden';
+    $conf['config_test.system']['baz'] = 'injected';
+
+    // Verify that the in-memory configuration object still contains the
+    // original data.
+    $this->assertIdentical($config->get('foo'), $expected_original_data['foo']);
+    $this->assertIdentical($config->get('baz'), $expected_original_data['baz']);
+
+    // Reload the configuration object.
+    $config = config('config_test.system');
+
+    // Verify that it contains the overridden data from $conf.
+    $this->assertIdentical($config->get('foo'), $conf['config_test.system']['foo']);
+    $this->assertIdentical($config->get('baz'), $conf['config_test.system']['baz']);
+
+    // Set the value for 'baz' (on the original data).
+    $expected_original_data['baz'] = 'original baz';
+    $config->set('baz', $expected_original_data['baz']);
+
+    // Verify that it still contains the overridden data from $conf.
+    $this->assertIdentical($config->get('foo'), $conf['config_test.system']['foo']);
+    $this->assertIdentical($config->get('baz'), $conf['config_test.system']['baz']);
+
+    // Save the configuration object (having overrides applied).
+    $config->save();
+
+    // Reload it and verify that it still contains overridden data from $conf.
+    $config = config('config_test.system');
+    $this->assertIdentical($config->get('foo'), $conf['config_test.system']['foo']);
+    $this->assertIdentical($config->get('baz'), $conf['config_test.system']['baz']);
+
+    // Remove the $conf overrides.
+    unset($conf['config_test.system']);
+
+    // Reload it and verify that it still contains the original data.
+    $config = config('config_test.system');
+    $this->assertIdentical($config->get('foo'), $expected_original_data['foo']);
+    $this->assertIdentical($config->get('baz'), $expected_original_data['baz']);
+  }
+
+}
-- 
1.7.11.msysgit.1

