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() { ...@@ -2624,6 +2624,8 @@ function drupal_language_initialize() {
// Allow modules to react on language system initialization in multilingual // Allow modules to react on language system initialization in multilingual
// environments. // environments.
bootstrap_invoke_all('language_init'); 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 = '') { ...@@ -102,7 +102,7 @@ function config_get_storage_names_with_prefix($prefix = '') {
* A configuration object. * A configuration object.
*/ */
function config($name) { 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 ...@@ -141,11 +141,11 @@ function config_sync_get_changes(StorageInterface $source_storage, StorageInterf
'delete' => array(), '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']; $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']; $config_changes['create'][] = $value['name'];
} }
...@@ -179,6 +179,7 @@ function config_sync_get_changes(StorageInterface $source_storage, StorageInterf ...@@ -179,6 +179,7 @@ function config_sync_get_changes(StorageInterface $source_storage, StorageInterf
* The storage to synchronize configuration to. * The storage to synchronize configuration to.
*/ */
function config_sync_changes(array $config_changes, StorageInterface $source_storage, StorageInterface $target_storage) { 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 (array('delete', 'create', 'change') as $op) {
foreach ($config_changes[$op] as $name) { foreach ($config_changes[$op] as $name) {
if ($op == 'delete') { if ($op == 'delete') {
...@@ -188,6 +189,7 @@ function config_sync_changes(array $config_changes, StorageInterface $source_sto ...@@ -188,6 +189,7 @@ function config_sync_changes(array $config_changes, StorageInterface $source_sto
$data = $source_storage->read($name); $data = $source_storage->read($name);
$target_storage->write($name, $data); $target_storage->write($name, $data);
} }
$factory->reset($name);
} }
} }
} }
......
...@@ -64,6 +64,13 @@ class Config { ...@@ -64,6 +64,13 @@ class Config {
*/ */
protected $eventDispatcher; protected $eventDispatcher;
/**
* Whether the config object has already been loaded.
*
* @var bool
*/
protected $isLoaded = FALSE;
/** /**
* Constructs a configuration object. * Constructs a configuration object.
* *
...@@ -88,6 +95,8 @@ public function __construct($name, StorageInterface $storage, EventDispatcher $e ...@@ -88,6 +95,8 @@ public function __construct($name, StorageInterface $storage, EventDispatcher $e
* The configuration object. * The configuration object.
*/ */
public function init() { public function init() {
$this->isLoaded = FALSE;
$this->overrides = array();
$this->notify('init'); $this->notify('init');
return $this; return $this;
} }
...@@ -120,6 +129,9 @@ public function setName($name) { ...@@ -120,6 +129,9 @@ public function setName($name) {
* TRUE if this config object does not exist in storage. * TRUE if this config object does not exist in storage.
*/ */
public function isNew() { public function isNew() {
if (!$this->isLoaded) {
$this->load();
}
return $this->isNew; return $this->isNew;
} }
...@@ -151,6 +163,9 @@ public function isNew() { ...@@ -151,6 +163,9 @@ public function isNew() {
* The data that was requested. * The data that was requested.
*/ */
public function get($key = '') { public function get($key = '') {
if (!$this->isLoaded) {
$this->load();
}
if (!isset($this->overriddenData)) { if (!isset($this->overriddenData)) {
$this->setOverriddenData(); $this->setOverriddenData();
} }
...@@ -179,6 +194,26 @@ public function get($key = '') { ...@@ -179,6 +194,26 @@ public function get($key = '') {
* The configuration object. * The configuration object.
*/ */
public function setData(array $data) { 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->data = $data;
$this->resetOverriddenData(); $this->resetOverriddenData();
return $this; return $this;
...@@ -243,6 +278,9 @@ protected function resetOverriddenData() { ...@@ -243,6 +278,9 @@ protected function resetOverriddenData() {
* The configuration object. * The configuration object.
*/ */
public function set($key, $value) { public function set($key, $value) {
if (!$this->isLoaded) {
$this->load();
}
// Type-cast value into a string. // Type-cast value into a string.
$value = $this->castValue($value); $value = $this->castValue($value);
...@@ -309,6 +347,9 @@ public function castValue($value) { ...@@ -309,6 +347,9 @@ public function castValue($value) {
* The configuration object. * The configuration object.
*/ */
public function clear($key) { public function clear($key) {
if (!$this->isLoaded) {
$this->load();
}
$parts = explode('.', $key); $parts = explode('.', $key);
if (count($parts) == 1) { if (count($parts) == 1) {
unset($this->data[$key]); unset($this->data[$key]);
...@@ -327,16 +368,18 @@ public function clear($key) { ...@@ -327,16 +368,18 @@ public function clear($key) {
* The configuration object. * The configuration object.
*/ */
public function load() { public function load() {
$this->isLoaded = FALSE;
$data = $this->storage->read($this->name); $data = $this->storage->read($this->name);
if ($data === FALSE) { if ($data === FALSE) {
$this->isNew = TRUE; $this->isNew = TRUE;
$this->setData(array()); $this->replaceData(array());
} }
else { else {
$this->isNew = FALSE; $this->isNew = FALSE;
$this->setData($data); $this->replaceData($data);
} }
$this->notify('load'); $this->notify('load');
$this->isLoaded = TRUE;
return $this; return $this;
} }
...@@ -347,6 +390,9 @@ public function load() { ...@@ -347,6 +390,9 @@ public function load() {
* The configuration object. * The configuration object.
*/ */
public function save() { public function save() {
if (!$this->isLoaded) {
$this->load();
}
$this->storage->write($this->name, $this->data); $this->storage->write($this->name, $this->data);
$this->isNew = FALSE; $this->isNew = FALSE;
$this->notify('save'); $this->notify('save');
...@@ -412,8 +458,11 @@ protected function notify($config_event_name) { ...@@ -412,8 +458,11 @@ protected function notify($config_event_name) {
* The configuration object. * The configuration object.
*/ */
public function merge(array $data_to_merge) { public function merge(array $data_to_merge) {
if (!$this->isLoaded) {
$this->load();
}
// Preserve integer keys so that config keys are not changed. // 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; return $this;
} }
} }
...@@ -38,6 +38,13 @@ class ConfigFactory { ...@@ -38,6 +38,13 @@ class ConfigFactory {
*/ */
protected $eventDispatcher; protected $eventDispatcher;
/**
* Cached configuration objects.
*
* @var array
*/
protected $cache = array();
/** /**
* Constructs the Config factory. * Constructs the Config factory.
* *
...@@ -64,26 +71,53 @@ public function __construct(StorageInterface $storage, EventDispatcher $event_di ...@@ -64,26 +71,53 @@ public function __construct(StorageInterface $storage, EventDispatcher $event_di
public function get($name) { public function get($name) {
global $conf; global $conf;
// @todo Caching the instantiated objects per name might cut off a fair if (isset($this->cache[$name])) {
// amount of CPU time and memory. Only the data within the configuration return $this->cache[$name];
// 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 $this->cache[$name] = new Config($name, $this->storage, $this->eventDispatcher);
// request; e.g., 'system.performance' alone is retrieved around 10-20 return $this->cache[$name]->init();
// 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();
} }
/**
* 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) { ...@@ -297,9 +297,17 @@ public function save(EntityInterface $entity) {
$id = $entity->getOriginalID(); $id = $entity->getOriginalID();
} }
$config = config($prefix . $id); $config = config($prefix . $id);
$config->setName($prefix . $entity->id()); $is_new = $config->isNew();
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 (!$config->isNew() && !isset($entity->original)) { if (!$is_new && !isset($entity->original)) {
$this->resetCache(array($id)); $this->resetCache(array($id));
$result = $this->load(array($id)); $result = $this->load(array($id));
$entity->original = reset($result); $entity->original = reset($result);
...@@ -313,7 +321,7 @@ public function save(EntityInterface $entity) { ...@@ -313,7 +321,7 @@ public function save(EntityInterface $entity) {
$config->set($key, $value); $config->set($key, $value);
} }
if (!$config->isNew()) { if (!$is_new) {
$return = SAVED_UPDATED; $return = SAVED_UPDATED;
$config->save(); $config->save();
$this->postSave($entity, TRUE); $this->postSave($entity, TRUE);
......
...@@ -55,7 +55,8 @@ public function build(ContainerBuilder $container) { ...@@ -55,7 +55,8 @@ public function build(ContainerBuilder $container) {
->addMethodCall('addSubscriber', array(new Reference('config.subscriber.globalconf'))); ->addMethodCall('addSubscriber', array(new Reference('config.subscriber.globalconf')));
$container->register('config.factory', 'Drupal\Core\Config\ConfigFactory') $container->register('config.factory', 'Drupal\Core\Config\ConfigFactory')
->addArgument(new Reference('config.storage')) ->addArgument(new Reference('config.storage'))
->addArgument(new Reference('dispatcher')); ->addArgument(new Reference('dispatcher'))
->addTag('persist');
// Register staging configuration storage. // Register staging configuration storage.
$container $container
......
...@@ -160,6 +160,7 @@ public function registerBundles() { ...@@ -160,6 +160,7 @@ public function registerBundles() {
$bundles = array( $bundles = array(
new CoreBundle(), new CoreBundle(),
); );
$this->bundleClasses = array('Drupal\Core\CoreBundle');
// Ensure we know what modules are enabled and that their namespaces are // Ensure we know what modules are enabled and that their namespaces are
// registered. // registered.
...@@ -259,6 +260,12 @@ protected function getClassName() { ...@@ -259,6 +260,12 @@ protected function getClassName() {
* Initializes the service container. * Initializes the service container.
*/ */
protected function initializeContainer() { 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; $this->container = NULL;
$class = $this->getClassName(); $class = $this->getClassName();
$cache_file = $class . '.php'; $cache_file = $class . '.php';
...@@ -285,6 +292,9 @@ protected function initializeContainer() { ...@@ -285,6 +292,9 @@ protected function initializeContainer() {
// Second, check if some other request -- for example on another web // Second, check if some other request -- for example on another web
// frontend or during the installer -- changed the list of enabled modules. // frontend or during the installer -- changed the list of enabled modules.
if (isset($this->container)) { 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 // All namespaces must be registered before we attempt to use any service
// from the container. // from the container.
$container_modules = $this->container->getParameter('container.modules'); $container_modules = $this->container->getParameter('container.modules');
...@@ -309,6 +319,9 @@ protected function initializeContainer() { ...@@ -309,6 +319,9 @@ protected function initializeContainer() {
if (!isset($this->container)) { if (!isset($this->container)) {
$this->container = $this->buildContainer(); $this->container = $this->buildContainer();
foreach ($persist as $id => $object) {
$this->container->set($id, $object);
}
if ($this->allowDumping) { if ($this->allowDumping) {
$this->containerNeedsDumping = TRUE; $this->containerNeedsDumping = TRUE;
} }
...@@ -336,6 +349,7 @@ protected function buildContainer() { ...@@ -336,6 +349,7 @@ protected function buildContainer() {
foreach ($this->bundles as $bundle) { foreach ($this->bundles as $bundle) {
$bundle->build($container); $bundle->build($container);
} }
$container->setParameter('persistIds', array_keys($container->findTaggedServiceIds('persist')));
$container->compile(); $container->compile();
return $container; return $container;
} }
......
...@@ -65,14 +65,33 @@ public function __construct(DiscoveryInterface $decorated, $cache_key, $cache_bi ...@@ -65,14 +65,33 @@ public function __construct(DiscoveryInterface $decorated, $cache_key, $cache_bi
* Implements Drupal\Component\Plugin\Discovery\DicoveryInterface::getDefinition(). * Implements Drupal\Component\Plugin\Discovery\DicoveryInterface::getDefinition().
*/ */
public function getDefinition($plugin_id) { 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(); $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(). * Implements Drupal\Component\Plugin\Discovery\DicoveryInterface::getDefinitions().
*/ */
public function 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(); $definitions = $this->getCachedDefinitions();
if (!isset($definitions)) { if (!isset($definitions)) {
$definitions = $this->decorated->getDefinitions(); $definitions = $this->decorated->getDefinitions();
......
...@@ -64,7 +64,7 @@ function testConfOverride() { ...@@ -64,7 +64,7 @@ function testConfOverride() {
$this->assertIdentical($config->get('404'), $expected_original_data['404']); $this->assertIdentical($config->get('404'), $expected_original_data['404']);
// Reload the configuration object. // Reload the configuration object.
$config = config('config_test.system'); $config->init();
// Verify that it contains the overridden data from $conf. // Verify that it contains the overridden data from $conf.
$this->assertIdentical($config->get('foo'), $conf['config_test.system']['foo']); $this->assertIdentical($config->get('foo'), $conf['config_test.system']['foo']);
...@@ -97,7 +97,7 @@ function testConfOverride() { ...@@ -97,7 +97,7 @@ function testConfOverride() {
unset($conf['config_test.system']); unset($conf['config_test.system']);
// Reload it and verify that it still contains the original data. // 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('foo'), $expected_original_data['foo']);
$this->assertIdentical($config->get('baz'), $expected_original_data['baz']); $this->assertIdentical($config->get('baz'), $expected_original_data['baz']);
$this->assertIdentical($config->get('404'), $expected_original_data['404']); $this->assertIdentical($config->get('404'), $expected_original_data['404']);
......
...@@ -37,7 +37,6 @@ function testLocaleConfigOverride() { ...@@ -37,7 +37,6 @@ function testLocaleConfigOverride() {
// Spoof multilingual. // Spoof multilingual.
$GLOBALS['conf']['language_count'] = 2; $GLOBALS['conf']['language_count'] = 2;
drupal_language_initialize(); drupal_language_initialize();
$config = config($name);
$this->assertIdentical($config->get('foo'), 'en bar'); $this->assertIdentical($config->get('foo'), 'en bar');
} }
} }
...@@ -298,3 +298,16 @@ function field_test_field_extra_fields_alter(&$info) { ...@@ -298,3 +298,16 @@ function field_test_field_extra_fields_alter(&$info) {
// Remove all extra fields from the 'no_fields' content type; // Remove all extra fields from the 'no_fields' content type;
unset($info['node']['no_fields']); 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() { ...@@ -808,6 +808,7 @@ protected function refreshVariables() {
global $conf; global $conf;
cache('bootstrap')->delete('variables'); cache('bootstrap')->delete('variables');
$conf = variable_initialize(); $conf = variable_initialize();
drupal_container()->get('config.factory')->reset();
} }
/**