diff --git a/core/includes/config.inc b/core/includes/config.inc index 00c7fa6..c658a80 100644 --- a/core/includes/config.inc +++ b/core/includes/config.inc @@ -102,7 +102,7 @@ function config_get_storage_names_with_prefix($prefix = '') { * A configuration object. */ function config($name) { - return drupal_container()->get('config.factory')->get($name)->load(); + return drupal_container()->get('config.factory')->get($name); } /** @@ -262,6 +262,7 @@ function config_import_invoke_owner(array $config_changes, StorageInterface $sou $data = $source_storage->read($name); $new_config = new Config($name, $target_storage); + $new_config->noLoad(); 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 6fc2a87..a993cf4 100644 --- a/core/lib/Drupal/Core/Config/Config.php +++ b/core/lib/Drupal/Core/Config/Config.php @@ -65,6 +65,13 @@ class Config { protected $eventDispatcher; /** + * Whether the config object has already been loaded. + * + * @var bool + */ + protected $isLoaded = FALSE; + + /** * Constructs a configuration object. * * @param string $name @@ -151,6 +158,9 @@ public function isNew() { * The data that was requested. */ public function get($key = '') { + if (!$this->isLoaded) { + $this->load(); + } if (!isset($this->overriddenData)) { $this->setOverriddenData(); } @@ -243,6 +253,9 @@ protected function resetOverriddenData() { * The configuration object. */ public function set($key, $value) { + if (!$this->isLoaded) { + $this->load(); + } // Type-cast value into a string. $value = $this->castValue($value); @@ -309,6 +322,9 @@ public function castValue($value) { * The configuration object. */ public function clear($key) { + if (!$this->isLoaded) { + $this->load(); + } $parts = explode('.', $key); if (count($parts) == 1) { unset($this->data[$key]); @@ -337,6 +353,7 @@ public function load() { $this->setData($data); } $this->notify('load'); + $this->isLoaded = TRUE; return $this; } @@ -347,6 +364,9 @@ public function load() { * The configuration object. */ public function save() { + if (!$this->isLoaded) { + $this->load(); + } $this->storage->write($this->name, $this->data); $this->isNew = FALSE; $this->notify('save'); @@ -412,8 +432,21 @@ protected function notify($config_event_name) { * The configuration object. */ public function merge(array $data_to_merge) { + if (!$this->isLoaded) { + $this->load(); + } // Preserve integer keys so that config keys are not changed. $this->data = NestedArray::mergeDeepArray(array($this->data, $data_to_merge), TRUE); return $this; } + + /** + * Supress loading for this config object. Internal use only. + * + * @return Config + */ + public function noLoad() { + $this->isLoaded = TRUE; + return $this; + } } diff --git a/core/lib/Drupal/Core/Config/ConfigFactory.php b/core/lib/Drupal/Core/Config/ConfigFactory.php index ca36ce7..9137ce2 100644 --- a/core/lib/Drupal/Core/Config/ConfigFactory.php +++ b/core/lib/Drupal/Core/Config/ConfigFactory.php @@ -38,6 +38,8 @@ class ConfigFactory { */ protected $eventDispatcher; + protected $configs = array(); + /** * Constructs the Config factory. * @@ -64,26 +66,15 @@ public function __construct(StorageInterface $storage, EventDispatcher $event_di 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 - // objects could possibly be avoided. It is not uncommon for a - // configuration object to be retrieved many times during a single - // request; e.g., 'system.performance' alone is retrieved around 10-20 - // times within a single page request. Sub-requests via HttpKernel will - // most likely only increase these counts. - // @todo Benchmarks were performed with a script that essentially retained - // all instantiated configuration objects in memory until script execution - // ended. A variant of that script called config() within a helper - // function only, which inherently meant that PHP destroyed all - // configuration objects after leaving the function. Consequently, - // benchmark results looked entirely different. Profiling should probably - // redone under more realistic conditions; e.g., actual HTTP requests. + if (isset($this->configs[$name])) { + return $this->configs[$name]; + } + // @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($name, $this->storage, $this->eventDispatcher); - return $config->init(); + $this->configs[$name] = new Config($name, $this->storage, $this->eventDispatcher); + return $this->configs[$name]->init(); } }