diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 9c1daf9..da29fe0 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -2424,20 +2424,24 @@ function drupal_get_bootstrap_phase() { * * @see Drupal\Core\DrupalKernel * - * @param $reset - * A new container instance to reset the Drupal container to. + * @param Symfony\Component\DependencyInjection\Container $new_container + * A new container instance to replace the current. + * @param bool $reset + * (optional) Internal use only. Whether to enforce a reset of the statically + * cached container. Pass NULL for $new_container to recreate a new Container + * from scratch in a subsequent call to this function. Used by tests. * * @return Symfony\Component\DependencyInjection\Container * The instance of the Container used to set up and maintain object * instances. */ -function drupal_container(Container $reset = NULL) { +function drupal_container(Container $new_container = NULL, $reset = FALSE) { // We do not use drupal_static() here because we do not have a mechanism by // which to reinitialize the stored objects, so a drupal_static_reset() call // would leave Drupal in a nonfunctional state. static $container = NULL; - if (isset($reset)) { - $container = $reset; + if (isset($new_container) || $reset) { + $container = $new_container; } elseif (!isset($container)) { // Return a ContainerBuilder instance with the bare essentials needed for any @@ -2453,16 +2457,13 @@ function drupal_container(Container $reset = NULL) { // lowest of low level configuration. $container->setParameter('config.storage.options', array( 'Drupal\Core\Config\FileStorage' => array( - 'directory' => config_get_config_directory(), + 'directory' => config_get_config_directory() . '/active', ), 'Drupal\Core\Config\CacheStorage' => array( 'backend' => 'Drupal\Core\Cache\DatabaseBackend', - // @todo Add and replace with 'config' default bin. - 'bin' => 'cache', + 'bin' => 'config', ), )); - // @todo Use this for exporting your current config in the DB... ;) - #$container->register('config.storage', 'Drupal\Core\Config\MultiStorage') $container->register('config.storage', 'Drupal\Core\Config\CachedFileStorage') ->addArgument('%config.storage.options%'); @@ -2472,11 +2473,9 @@ function drupal_container(Container $reset = NULL) { // Register configuration state. $container->setParameter('config.state.options', array( - 'connection' => 'default', - 'target' => 'default', - 'table' => 'config', + 'directory' => config_get_config_directory() . '/import', )); - $container->register('config.state', 'Drupal\Core\Config\DatabaseStorage') + $container->register('config.state', 'Drupal\Core\Config\FileStorage') ->addArgument('%config.state.options%'); } return $container; diff --git a/core/includes/install.inc b/core/includes/install.inc index 4c9c60c..f53c54e 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -296,7 +296,12 @@ function install_ensure_config_directory() { // directories that the installer creates. else { $config_directory = config_get_config_directory(); - return file_prepare_directory($config_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + $success = file_prepare_directory($config_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + foreach (array('active', 'import') as $subdir) { + $subdir = $config_directory . '/' . $subdir; + $success = $success && file_prepare_directory($subdir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + } + return $success; } } diff --git a/core/lib/Drupal/Core/Config/CacheStorage.php b/core/lib/Drupal/Core/Config/CacheStorage.php index 6e77ae2..1f69c87 100644 --- a/core/lib/Drupal/Core/Config/CacheStorage.php +++ b/core/lib/Drupal/Core/Config/CacheStorage.php @@ -9,6 +9,22 @@ /** * Defines the Cache storage controller. + * + * This storage controller essentially just bridges the configuration system + * storage to a backend of the cache system. All StorageInterface method calls + * are forwarded to the corresponding CacheBackendInterface methods. + * + * @see Drupal\Core\Cache\CacheBackendInterface + * + * While this causes a level of indirection and the functionality could + * technically be embedded into a configuration storage controller directly, + * a separate bridge is architecturally cleaner and also allows for alternative + * setups. + * + * By default, the configuration system uses this controller in the + * CachedFileStorage implementation. + * + * @see Drupal\Core\Config\CachedFileStorage */ class CacheStorage implements StorageInterface { @@ -62,7 +78,11 @@ public function exists($name) { */ public function read($name) { if ($cache = $this->getBackend()->get($name)) { - return $cache->data; + // The cache backend supports primitive data types, but only an array + // represents valid config object data. + if (is_array($cache->data)) { + return $cache->data; + } } return FALSE; } diff --git a/core/lib/Drupal/Core/Config/CachedFileStorage.php b/core/lib/Drupal/Core/Config/CachedFileStorage.php index 7fbaef9..74eb99e 100644 --- a/core/lib/Drupal/Core/Config/CachedFileStorage.php +++ b/core/lib/Drupal/Core/Config/CachedFileStorage.php @@ -11,7 +11,7 @@ /** * Storage controller that uses FileStorage as canonical storage and - * DatabaseStorage as a cache (active store). + * CacheStorage as a cache. */ class CachedFileStorage implements StorageInterface { @@ -46,8 +46,8 @@ public function __construct(array $options = array()) { $this->storages['file'] = new FileStorage($options['Drupal\Core\Config\FileStorage']); unset($options['Drupal\Core\Config\FileStorage']); - list($active_class, $active_options) = each($options); - $this->storages['active'] = new $active_class($active_options); + list($cache_class, $cache_options) = each($options); + $this->storages['cache'] = new $cache_class($cache_options); } /** @@ -64,13 +64,13 @@ public function exists($name) { */ public function read($name) { // Check the cache. - $data = $this->storages['active']->read($name); + $data = $this->storages['cache']->read($name); // If the cache returns no result, check the file storage. if ($data === FALSE) { $data = $this->storages['file']->read($name); // @todo Should the config object be cached if it does not exist? if ($data !== FALSE) { - $this->storages['active']->write($name, $data); + $this->storages['cache']->write($name, $data); } } return $data; @@ -81,7 +81,7 @@ public function read($name) { */ public function write($name, array $data) { $success = $this->storages['file']->write($name, $data); - $this->storages['active']->delete($name); + $this->storages['cache']->delete($name); return $success; } diff --git a/core/lib/Drupal/Core/Config/MultiStorage.php b/core/lib/Drupal/Core/Config/MultiStorage.php deleted file mode 100644 index 484501a..0000000 --- a/core/lib/Drupal/Core/Config/MultiStorage.php +++ /dev/null @@ -1,117 +0,0 @@ - $storage_options) { - $this->storages[] = new $storage_controller($storage_options); - } - $this->options = $options; - } - - /** - * Implements Drupal\Core\Config\StorageInterface::exists(). - */ - public function exists($name) { - return $this->storages[0]->exists($name); - } - - /** - * Implements Drupal\Core\Config\StorageInterface::read(). - */ - public function read($name) { - return $this->storages[0]->read($name); - } - - /** - * Implements Drupal\Core\Config\StorageInterface::write(). - */ - public function write($name, array $data) { - $success = TRUE; - foreach ($this->storages as $storage) { - if (!$storage->write($name, $data)) { - $success = FALSE; - } - } - return $success; - } - - /** - * Implements Drupal\Core\Config\StorageInterface::delete(). - */ - public function delete($name) { - $success = TRUE; - foreach ($this->storages as $storage) { - if (!$storage->delete($name)) { - $success = FALSE; - } - } - return $success; - } - - /** - * Implements Drupal\Core\Config\StorageInterface::encode(). - * - * @todo Remove encode() from StorageInterface. - */ - public static function encode($data) { - return $this->storages[0]->encode($data); - } - - /** - * Implements Drupal\Core\Config\StorageInterface::decode(). - * - * @todo Remove decode() from StorageInterface. - */ - public static function decode($raw) { - return $this->storages[0]->decode($raw); - } - - /** - * Implements Drupal\Core\Config\StorageInterface::listAll(). - */ - public function listAll($prefix = '') { - return $this->storages[0]->listAll($prefix); - } -} diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php index 0007284..f834b72 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php @@ -7,8 +7,6 @@ namespace Drupal\config\Tests; -use Drupal\Core\Config\DatabaseStorage; -use Drupal\Core\Config\FileStorage; use Drupal\simpletest\WebTestBase; /** @@ -37,6 +35,8 @@ public static function getInfo() { function testDeleted() { $name = 'config_test.system'; $dynamic_name = 'config_test.dynamic.default'; + $storage = $this->container->get('config.storage'); + $state = $this->container->get('config.state'); // Verify the default configuration values exist. $config = config($name); @@ -48,17 +48,15 @@ function testDeleted() { config_export(); // Delete the configuration objects. - $file_storage = new FileStorage(); - $file_storage->delete($name); - $file_storage->delete($dynamic_name); + $state->delete($name); + $state->delete($dynamic_name); // Import. config_import(); // Verify the values have disappeared. - $database_storage = new DatabaseStorage(); - $this->assertIdentical($database_storage->read($name), FALSE); - $this->assertIdentical($database_storage->read($dynamic_name), FALSE); + $this->assertIdentical($storage->read($name), FALSE); + $this->assertIdentical($storage->read($dynamic_name), FALSE); $config = config($name); $this->assertIdentical($config->get('foo'), NULL); @@ -72,31 +70,31 @@ function testDeleted() { function testNew() { $name = 'config_test.new'; $dynamic_name = 'config_test.dynamic.new'; + $storage = $this->container->get('config.storage'); + $state = $this->container->get('config.state'); // Export. config_export(); // Verify the configuration to create does not exist yet. - $database_storage = new DatabaseStorage(); - $this->assertIdentical($database_storage->exists($name), FALSE, $name . ' not found.'); - $this->assertIdentical($database_storage->exists($dynamic_name), FALSE, $dynamic_name . ' not found.'); + $this->assertIdentical($storage->exists($name), FALSE, $name . ' not found.'); + $this->assertIdentical($storage->exists($dynamic_name), FALSE, $dynamic_name . ' not found.'); - $file_storage = new FileStorage(); - $this->assertIdentical($file_storage->exists($name), FALSE, $name . ' not found.'); - $this->assertIdentical($file_storage->exists($dynamic_name), FALSE, $dynamic_name . ' not found.'); + $this->assertIdentical($state->exists($name), FALSE, $name . ' not found.'); + $this->assertIdentical($state->exists($dynamic_name), FALSE, $dynamic_name . ' not found.'); // Create new configuration objects. $original_name_data = array( 'add_me' => 'new value', ); - $file_storage->write($name, $original_name_data); + $state->write($name, $original_name_data); $original_dynamic_data = array( 'id' => 'new', 'label' => 'New', ); - $file_storage->write($dynamic_name, $original_dynamic_data); - $this->assertIdentical($file_storage->exists($name), TRUE, $name . ' found.'); - $this->assertIdentical($file_storage->exists($dynamic_name), TRUE, $dynamic_name . ' found.'); + $state->write($dynamic_name, $original_dynamic_data); + $this->assertIdentical($state->exists($name), TRUE, $name . ' found.'); + $this->assertIdentical($state->exists($dynamic_name), TRUE, $dynamic_name . ' found.'); // Import. config_import(); @@ -114,29 +112,29 @@ function testNew() { function testUpdated() { $name = 'config_test.system'; $dynamic_name = 'config_test.dynamic.default'; + $storage = $this->container->get('config.storage'); + $state = $this->container->get('config.state'); // Export. config_export(); // Verify that the configuration objects to import exist. - $database_storage = new DatabaseStorage(); - $this->assertIdentical($database_storage->exists($name), TRUE, $name . ' found.'); - $this->assertIdentical($database_storage->exists($dynamic_name), TRUE, $dynamic_name . ' found.'); + $this->assertIdentical($storage->exists($name), TRUE, $name . ' found.'); + $this->assertIdentical($storage->exists($dynamic_name), TRUE, $dynamic_name . ' found.'); - $file_storage = new FileStorage(); - $this->assertIdentical($file_storage->exists($name), TRUE, $name . ' found.'); - $this->assertIdentical($file_storage->exists($dynamic_name), TRUE, $dynamic_name . ' found.'); + $this->assertIdentical($state->exists($name), TRUE, $name . ' found.'); + $this->assertIdentical($state->exists($dynamic_name), TRUE, $dynamic_name . ' found.'); // Replace the file content of the existing configuration objects. $original_name_data = array( 'foo' => 'beer', ); - $file_storage->write($name, $original_name_data); + $state->write($name, $original_name_data); $original_dynamic_data = array( 'id' => 'default', 'label' => 'Updated', ); - $file_storage->write($dynamic_name, $original_dynamic_data); + $state->write($dynamic_name, $original_dynamic_data); // Verify the active store still returns the default values. $config = config($name); @@ -154,7 +152,7 @@ function testUpdated() { $this->assertIdentical($config->get('label'), 'Updated'); // Verify that the original file content is still the same. - $this->assertIdentical($file_storage->read($name), $original_name_data); - $this->assertIdentical($file_storage->read($dynamic_name), $original_dynamic_data); + $this->assertIdentical($state->read($name), $original_name_data); + $this->assertIdentical($state->read($dynamic_name), $original_dynamic_data); } } diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php index 3a9acdd..de0ccb2 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImportUITest.php @@ -7,13 +7,15 @@ namespace Drupal\config\Tests; -use Drupal\Core\Config\FileStorage; use Drupal\simpletest\WebTestBase; /** * Tests importing configuration from files into active store. */ class ConfigImportUITest extends WebTestBase { + + public static $modules = array('config', 'config_test'); + public static function getInfo() { return array( 'name' => 'Import/Export UI', @@ -23,7 +25,7 @@ public static function getInfo() { } function setUp() { - parent::setUp(array('config', 'config_test')); + parent::setUp(); $this->web_user = $this->drupalCreateUser(array('synchronize configuration')); $this->drupalLogin($this->web_user); @@ -67,11 +69,14 @@ function testExport() { function testImport() { $name = 'config_test.new'; $dynamic_name = 'config_test.dynamic.new'; + $storage = $this->container->get('config.storage'); + $state = $this->container->get('config.state'); // Verify the configuration to create does not exist yet. - $file_storage = new FileStorage(); - $this->assertIdentical($file_storage->exists($name), FALSE, $name . ' not found.'); - $this->assertIdentical($file_storage->exists($dynamic_name), FALSE, $dynamic_name . ' not found.'); + $this->assertIdentical($storage->exists($name), FALSE, $name . ' not found.'); + $this->assertIdentical($storage->exists($dynamic_name), FALSE, $dynamic_name . ' not found.'); + $this->assertIdentical($state->exists($name), FALSE, $name . ' not found.'); + $this->assertIdentical($state->exists($dynamic_name), FALSE, $dynamic_name . ' not found.'); // Verify that the import UI does not allow to import without exported // configuration. @@ -83,15 +88,17 @@ function testImport() { $this->drupalPost(NULL, array(), t('Export')); // Create new configuration objects. - $file_storage->write($name, array( + $original_name_data = array( 'add_me' => 'new value', - )); - $file_storage->write($dynamic_name, array( + ); + $state->write($name, $original_name_data); + $original_dynamic_data = array( 'id' => 'new', 'label' => 'New', - )); - $this->assertIdentical($file_storage->exists($name), TRUE, $name . ' found.'); - $this->assertIdentical($file_storage->exists($dynamic_name), TRUE, $dynamic_name . ' found.'); + ); + $state->write($dynamic_name, $original_dynamic_data); + $this->assertIdentical($state->exists($name), TRUE, $name . ' found.'); + $this->assertIdentical($state->exists($dynamic_name), TRUE, $dynamic_name . ' found.'); // Verify that both appear as new. $this->drupalGet('admin/config/development/sync'); @@ -117,10 +124,10 @@ function testImport() { */ function testImportLock() { $name = 'config_test.new'; + $state = $this->container->get('config.state'); // Write a configuration object to import. - $file_storage = new FileStorage(); - $file_storage->write($name, array( + $state->write($name, array( 'add_me' => 'new value', )); diff --git a/core/modules/config/tests/config_test/config/config_test.delete.yml b/core/modules/config/tests/config_test/config/config_test.delete.yml deleted file mode 100644 index b8ccb67..0000000 --- a/core/modules/config/tests/config_test/config/config_test.delete.yml +++ /dev/null @@ -1 +0,0 @@ -delete_me: bar diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index f58e1de..8f1fbd6 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -716,13 +716,22 @@ protected function prepareEnvironment() { file_prepare_directory($this->temp_files_directory, FILE_CREATE_DIRECTORY); $this->generatedTestFiles = FALSE; - // Create and set a new configuration directory and signature key. + // Set a new configuration directory name. // The child site automatically adjusts the global $config_directory_name to // a test-prefix-specific directory within the public files directory. // @see config_get_config_directory() $GLOBALS['config_directory_name'] = 'simpletest/' . substr($this->databasePrefix, 10) . '/config'; $this->configFileDirectory = $this->originalFileDirectory . '/' . $GLOBALS['config_directory_name']; - file_prepare_directory($this->configFileDirectory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + + // Reset and create a new service container. + drupal_container(NULL, TRUE); + $this->container = drupal_container(); + + // Create the new configuration directories. + include_once DRUPAL_ROOT . '/core/includes/install.inc'; + if (!install_ensure_config_directory()) { + return FALSE; + } // Log fatal errors. ini_set('log_errors', 1); diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 0e42214..2c0b3d4 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -723,6 +723,8 @@ function system_schema() { ); $schema['cache_bootstrap'] = $schema['cache']; $schema['cache_bootstrap']['description'] = 'Cache table for data required to bootstrap Drupal, may be routed to a shared memory cache.'; + $schema['cache_config'] = $schema['cache']; + $schema['cache_config']['description'] = 'Cache table for configuration data.'; $schema['cache_form'] = $schema['cache']; $schema['cache_form']['description'] = 'Cache table for the form system to store recently built forms and their storage data, to be used in subsequent page requests.'; $schema['cache_page'] = $schema['cache']; @@ -732,28 +734,6 @@ function system_schema() { $schema['cache_path'] = $schema['cache']; $schema['cache_path']['description'] = 'Cache table for path alias lookup.'; - $schema['config'] = array( - 'description' => 'Default active store for the configuration system.', - 'fields' => array( - 'name' => array( - 'description' => 'The identifier for the configuration entry, such as module.example (the name of the file, minus the file extension).', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'data' => array( - 'description' => 'The raw data for this configuration entry.', - 'type' => 'blob', - 'not null' => TRUE, - 'size' => 'big', - 'translatable' => TRUE, - ), - ), - 'primary key' => array('name'), - ); - - $schema['date_format_type'] = array( 'description' => 'Stores configured date format types.', 'fields' => array( diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 983c317..fe6e5f36 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -3398,7 +3398,7 @@ function system_cron() { */ function system_cache_flush() { // Do NOT flush the 'form' cache bin to retain in-progress form submissions. - return array('bootstrap', 'cache', 'page', 'path'); + return array('bootstrap', 'config', 'cache', 'page', 'path'); } /**