Commit d08da8f8 authored by Dries's avatar Dries
Browse files

Merge branch '8.x' of git.drupal.org:project/drupal into 8.x

parents efe2e462 3cbb4d38
......@@ -2624,6 +2624,8 @@ function drupal_language_initialize() {
// Allow modules to react on language system initialization in multilingual
// environments.
bootstrap_invoke_all('language_init');
// @todo D8: Remove after http://drupal.org/node/1859110.
drupal_container()->get('config.factory')->reset();
}
}
......
......@@ -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);
}
/**
......@@ -141,11 +141,11 @@ function config_sync_get_changes(StorageInterface $source_storage, StorageInterf
'delete' => array(),
);
foreach (array_diff_assoc($target_config_data, $source_config_data) as $name => $value) {
foreach (array_diff_key($target_config_data, $source_config_data) as $name => $value) {
$config_changes['delete'][] = $value['name'];
}
foreach (array_diff_assoc($source_config_data, $target_config_data) as $name => $value) {
foreach (array_diff_key($source_config_data, $target_config_data) as $name => $value) {
$config_changes['create'][] = $value['name'];
}
......@@ -179,6 +179,7 @@ function config_sync_get_changes(StorageInterface $source_storage, StorageInterf
* The storage to synchronize configuration to.
*/
function config_sync_changes(array $config_changes, StorageInterface $source_storage, StorageInterface $target_storage) {
$factory = drupal_container()->get('config.factory');
foreach (array('delete', 'create', 'change') as $op) {
foreach ($config_changes[$op] as $name) {
if ($op == 'delete') {
......@@ -188,6 +189,7 @@ function config_sync_changes(array $config_changes, StorageInterface $source_sto
$data = $source_storage->read($name);
$target_storage->write($name, $data);
}
$factory->reset($name);
}
}
}
......
......@@ -64,6 +64,13 @@ class Config {
*/
protected $eventDispatcher;
/**
* Whether the config object has already been loaded.
*
* @var bool
*/
protected $isLoaded = FALSE;
/**
* Constructs a configuration object.
*
......@@ -88,6 +95,8 @@ public function __construct($name, StorageInterface $storage, EventDispatcher $e
* The configuration object.
*/
public function init() {
$this->isLoaded = FALSE;
$this->overrides = array();
$this->notify('init');
return $this;
}
......@@ -120,6 +129,9 @@ public function setName($name) {
* TRUE if this config object does not exist in storage.
*/
public function isNew() {
if (!$this->isLoaded) {
$this->load();
}
return $this->isNew;
}
......@@ -151,6 +163,9 @@ public function isNew() {
* The data that was requested.
*/
public function get($key = '') {
if (!$this->isLoaded) {
$this->load();
}
if (!isset($this->overriddenData)) {
$this->setOverriddenData();
}
......@@ -179,6 +194,26 @@ public function get($key = '') {
* The configuration object.
*/
public function setData(array $data) {
$this->replaceData($data);
// A load would destroy the data just set (for example on import).
$this->isLoaded = TRUE;
return $this;
}
/**
* Replaces the data of this configuration object.
*
* This function is separate from setData() to avoid load() state tracking.
* A load() would destroy the replaced data (for example on import). Do not
* call set() when inside load().
*
* @param array $data
* The new configuration data.
*
* @return Drupal\Core\Config\Config
* The configuration object.
*/
protected function replaceData(array $data) {
$this->data = $data;
$this->resetOverriddenData();
return $this;
......@@ -243,6 +278,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 +347,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]);
......@@ -327,16 +368,18 @@ public function clear($key) {
* The configuration object.
*/
public function load() {
$this->isLoaded = FALSE;
$data = $this->storage->read($this->name);
if ($data === FALSE) {
$this->isNew = TRUE;
$this->setData(array());
$this->replaceData(array());
}
else {
$this->isNew = FALSE;
$this->setData($data);
$this->replaceData($data);
}
$this->notify('load');
$this->isLoaded = TRUE;
return $this;
}
......@@ -347,6 +390,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 +458,11 @@ 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);
$this->replaceData(NestedArray::mergeDeepArray(array($this->data, $data_to_merge), TRUE));
return $this;
}
}
......@@ -38,6 +38,13 @@ class ConfigFactory {
*/
protected $eventDispatcher;
/**
* Cached configuration objects.
*
* @var array
*/
protected $cache = array();
/**
* Constructs the Config factory.
*
......@@ -64,26 +71,53 @@ 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.
// @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();
if (isset($this->cache[$name])) {
return $this->cache[$name];
}
$this->cache[$name] = new Config($name, $this->storage, $this->eventDispatcher);
return $this->cache[$name]->init();
}
/**
* Resets and re-initializes configuration objects. Internal use only.
*
* @param string $name
* (optional) The name of the configuration object to reset. If omitted, all
* configuration objects are reset.
*/
public function reset($name = NULL) {
if ($name) {
if (isset($this->cache[$name])) {
$this->cache[$name]->init();
}
}
else {
foreach ($this->cache as $config) {
$config->init();
}
}
}
/**
* Renames a configuration object in the cache.
*
* @param string $old_name
* The old name of the configuration object.
* @param string $new_name
* The new name of the configuration object.
*
* @todo D8: Remove after http://drupal.org/node/1865206.
*/
public function rename($old_name, $new_name) {
if (isset($this->cache[$old_name])) {
$config = $this->cache[$old_name];
// Clone the object into the existing slot.
$this->cache[$old_name] = clone $config;
// Change the object's name and re-initialize it.
$config->setName($new_name)->init();
$this->cache[$new_name] = $config;
}
}
}
......@@ -297,9 +297,17 @@ public function save(EntityInterface $entity) {
$id = $entity->getOriginalID();
}
$config = config($prefix . $id);
$config->setName($prefix . $entity->id());
$is_new = $config->isNew();
if (!$config->isNew() && !isset($entity->original)) {
if ($id !== $entity->id()) {
// Renaming a config object needs to cater for:
// - Storage controller needs to access the original object.
// - The object needs to be renamed/copied in ConfigFactory and reloaded.
// - All instances of the object need to be renamed.
drupal_container()->get('config.factory')->rename($prefix . $id, $prefix . $entity->id());
}
if (!$is_new && !isset($entity->original)) {
$this->resetCache(array($id));
$result = $this->load(array($id));
$entity->original = reset($result);
......@@ -313,7 +321,7 @@ public function save(EntityInterface $entity) {
$config->set($key, $value);
}
if (!$config->isNew()) {
if (!$is_new) {
$return = SAVED_UPDATED;
$config->save();
$this->postSave($entity, TRUE);
......
......@@ -55,7 +55,8 @@ public function build(ContainerBuilder $container) {
->addMethodCall('addSubscriber', array(new Reference('config.subscriber.globalconf')));
$container->register('config.factory', 'Drupal\Core\Config\ConfigFactory')
->addArgument(new Reference('config.storage'))
->addArgument(new Reference('dispatcher'));
->addArgument(new Reference('dispatcher'))
->addTag('persist');
// Register staging configuration storage.
$container
......
......@@ -160,6 +160,7 @@ public function registerBundles() {
$bundles = array(
new CoreBundle(),
);
$this->bundleClasses = array('Drupal\Core\CoreBundle');
// Ensure we know what modules are enabled and that their namespaces are
// registered.
......@@ -259,6 +260,12 @@ protected function getClassName() {
* Initializes the service container.
*/
protected function initializeContainer() {
$persist = array();
if (isset($this->container)) {
foreach ($this->container->getParameter('persistIds') as $id) {
$persist[$id] = $this->container->get($id);
}
}
$this->container = NULL;
$class = $this->getClassName();
$cache_file = $class . '.php';
......@@ -285,6 +292,9 @@ protected function initializeContainer() {
// Second, check if some other request -- for example on another web
// frontend or during the installer -- changed the list of enabled modules.
if (isset($this->container)) {
foreach ($persist as $id => $object) {
$this->container->set($id, $object);
}
// All namespaces must be registered before we attempt to use any service
// from the container.
$container_modules = $this->container->getParameter('container.modules');
......@@ -309,6 +319,9 @@ protected function initializeContainer() {
if (!isset($this->container)) {
$this->container = $this->buildContainer();
foreach ($persist as $id => $object) {
$this->container->set($id, $object);
}
if ($this->allowDumping) {
$this->containerNeedsDumping = TRUE;
}
......@@ -336,6 +349,7 @@ protected function buildContainer() {
foreach ($this->bundles as $bundle) {
$bundle->build($container);
}
$container->setParameter('persistIds', array_keys($container->findTaggedServiceIds('persist')));
$container->compile();
return $container;
}
......
......@@ -65,14 +65,33 @@ public function __construct(DiscoveryInterface $decorated, $cache_key, $cache_bi
* Implements Drupal\Component\Plugin\Discovery\DicoveryInterface::getDefinition().
*/
public function getDefinition($plugin_id) {
// Optimize for fast access to definitions if they are already in memory.
if (isset($this->definitions)) {
// Avoid using a ternary that would create a copy of the array.
if (isset($this->definitions[$plugin_id])) {
return $this->definitions[$plugin_id];
}
else {
return;
}
}
$definitions = $this->getDefinitions();
return isset($definitions[$plugin_id]) ? $definitions[$plugin_id] : NULL;
// Avoid using a ternary that would create a copy of the array.
if (isset($definitions[$plugin_id])) {
return $definitions[$plugin_id];
}
}
/**
* Implements Drupal\Component\Plugin\Discovery\DicoveryInterface::getDefinitions().
*/
public function getDefinitions() {
// Optimize for fast access to definitions if they are already in memory.
if (isset($this->definitions)) {
return $this->definitions;
}
$definitions = $this->getCachedDefinitions();
if (!isset($definitions)) {
$definitions = $this->decorated->getDefinitions();
......
......@@ -64,7 +64,7 @@ function testConfOverride() {
$this->assertIdentical($config->get('404'), $expected_original_data['404']);
// Reload the configuration object.
$config = config('config_test.system');
$config->init();
// Verify that it contains the overridden data from $conf.
$this->assertIdentical($config->get('foo'), $conf['config_test.system']['foo']);
......@@ -97,7 +97,7 @@ function testConfOverride() {
unset($conf['config_test.system']);
// Reload it and verify that it still contains the original data.
$config = config('config_test.system');
$config->init();
$this->assertIdentical($config->get('foo'), $expected_original_data['foo']);
$this->assertIdentical($config->get('baz'), $expected_original_data['baz']);
$this->assertIdentical($config->get('404'), $expected_original_data['404']);
......
......@@ -37,7 +37,6 @@ function testLocaleConfigOverride() {
// Spoof multilingual.
$GLOBALS['conf']['language_count'] = 2;
drupal_language_initialize();
$config = config($name);
$this->assertIdentical($config->get('foo'), 'en bar');
}
}
......@@ -298,3 +298,16 @@ function field_test_field_extra_fields_alter(&$info) {
// Remove all extra fields from the 'no_fields' content type;
unset($info['node']['no_fields']);
}
/**
* Implements hook_module_implements_alter().
*/
function field_test_module_implements_alter(&$implementations, $hook) {
if ($hook == 'entity_info_alter' && isset($implementations['field_test']) && isset($implementations['rdf'])) {
foreach (array('field_test', 'rdf') as $module) {
$group = $implementations[$module];
unset($implementations[$module]);
$implementations[$module] = $group;
}
}
}
......@@ -808,6 +808,7 @@ protected function refreshVariables() {
global $conf;
cache('bootstrap')->delete('variables');
$conf = variable_initialize();
drupal_container()->get('config.factory')->reset();
}
/**
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment