diff --git a/core/core.services.yml b/core/core.services.yml index 0b8be53..a2f7575 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -67,16 +67,9 @@ services: factory_method: get factory_service: cache_factory arguments: [data] - config.cachedstorage.storage: - class: Drupal\Core\Config\FileStorage - factory_class: Drupal\Core\Config\FileStorageFactory - factory_method: getActive config.manager: class: Drupal\Core\Config\ConfigManager arguments: ['@entity.manager', '@config.factory', '@config.typed', '@string_translation', '@config.storage'] - config.storage: - class: Drupal\Core\Config\CachedStorage - arguments: ['@config.cachedstorage.storage', '@cache.config'] config.factory: class: Drupal\Core\Config\ConfigFactory tags: @@ -85,6 +78,10 @@ services: config.installer: class: Drupal\Core\Config\ConfigInstaller arguments: ['@config.factory', '@config.storage', '@config.typed', '@config.manager', '@event_dispatcher'] + config.storage: "@config.storage.uncached" + config.storage.uncached: + class: Drupal\Core\Config\DatabaseStorage + arguments: ['@database', 'config'] config.storage.staging: class: Drupal\Core\Config\FileStorage factory_class: Drupal\Core\Config\FileStorageFactory diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index a2ef2c1..71ee819 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -440,7 +440,7 @@ function install_begin_request(&$install_state) { // Ensure that the active configuration directory is empty before installation // starts. if ($install_state['config_verified'] && empty($task)) { - $config = glob(config_get_config_directory(CONFIG_ACTIVE_DIRECTORY) . '/*.' . FileStorage::getFileExtension()); + $config = Database::getConnection()->schema()->tableExists('config'); if (!empty($config)) { $task = NULL; throw new AlreadyInstalledException($container->get('string_translation')); diff --git a/core/lib/Drupal/Core/Config/BootstrapConfigStorageFactory.php b/core/lib/Drupal/Core/Config/BootstrapConfigStorageFactory.php index e075261..bf65660 100644 --- a/core/lib/Drupal/Core/Config/BootstrapConfigStorageFactory.php +++ b/core/lib/Drupal/Core/Config/BootstrapConfigStorageFactory.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Config; use Drupal\Component\Utility\Settings; +use Drupal\Core\Database\Database; /** * Defines a factory for retrieving the config storage used pre-kernel. @@ -26,7 +27,8 @@ public static function get() { return call_user_func($drupal_bootstrap_config_storage); } else { - return new FileStorage(config_get_config_directory(CONFIG_ACTIVE_DIRECTORY)); + $storage = new DatabaseStorage(Database::getConnection(), 'config'); + return $storage->createStorage(); } } diff --git a/core/lib/Drupal/Core/Config/CachedStorage.php b/core/lib/Drupal/Core/Config/CachedStorage.php index 1ff4046..f10b794 100644 --- a/core/lib/Drupal/Core/Config/CachedStorage.php +++ b/core/lib/Drupal/Core/Config/CachedStorage.php @@ -54,6 +54,14 @@ public function __construct(StorageInterface $storage, CacheBackendInterface $ca } /** + * {@inheritdoc} + */ + public function createStorage() { + $this->storage->createStorage(); + return $this; + } + + /** * Implements Drupal\Core\Config\StorageInterface::exists(). */ public function exists($name) { diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php index 19899a5..0ba01b4 100644 --- a/core/lib/Drupal/Core/Config/Config.php +++ b/core/lib/Drupal/Core/Config/Config.php @@ -2,7 +2,7 @@ /** * @file - * Definition of Drupal\Core\Config\Config. + * Contains \Drupal\Core\Config\Config. */ namespace Drupal\Core\Config; @@ -215,6 +215,11 @@ public function save() { $this->data[$key] = $this->castValue($key, $value); } } + else { + foreach ($this->data as $key => $value) { + $this->validateValue($key, $value); + } + } $this->storage->write($this->name, $this->data); $this->isNew = FALSE; diff --git a/core/lib/Drupal/Core/Config/DatabaseStorage.php b/core/lib/Drupal/Core/Config/DatabaseStorage.php index fd8f2ee..fa8ad15 100644 --- a/core/lib/Drupal/Core/Config/DatabaseStorage.php +++ b/core/lib/Drupal/Core/Config/DatabaseStorage.php @@ -2,13 +2,14 @@ /** * @file - * Definition of Drupal\Core\Config\DatabaseStorage. + * Contains \Drupal\Core\Config\DatabaseStorage. */ namespace Drupal\Core\Config; use Drupal\Core\Database\Database; use Drupal\Core\Database\Connection; +use Drupal\Core\Database\SchemaObjectExistsException; /** * Defines the Database storage. @@ -45,6 +46,7 @@ class DatabaseStorage implements StorageInterface { * A database table name to store configuration data in. * @param array $options * (optional) Any additional database connection options to use in queries. + * For testing, set 'throw_exception' to TRUE to prevent table creation. */ public function __construct(Connection $connection, $table, array $options = array()) { $this->connection = $connection; @@ -53,27 +55,35 @@ public function __construct(Connection $connection, $table, array $options = arr } /** + * {@inheritdoc} + */ + public function createStorage() { + $this->ensureTableExists(); + return $this; + } + + /** * Implements Drupal\Core\Config\StorageInterface::exists(). */ public function exists($name) { - return (bool) $this->connection->queryRange('SELECT 1 FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name = :name', 0, 1, array( - ':name' => $name, - ), $this->options)->fetchField(); + try { + return (bool) $this->connection->queryRange('SELECT 1 FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name = :name', 0, 1, array( + ':name' => $name, + ), $this->options)->fetchField(); + } + catch (\Exception $e) { + // There are situations, like in the installer, where we may attempt a + // read without actually having the database available. In this case, + // catch the exception and just return FALSE so the caller can handle it. + return FALSE; + } } /** - * Implements Drupal\Core\Config\StorageInterface::read(). - * - * @throws PDOException - * @throws \Drupal\Core\Database\DatabaseExceptionWrapper - * Only thrown in case $this->options['throw_exception'] is TRUE. + * {@inheritdoc} */ public function read($name) { $data = FALSE; - // There are situations, like in the installer, where we may attempt a - // read without actually having the database available. In this case, - // catch the exception and just return an empty array so the caller can - // handle it if need be. try { $raw = $this->connection->query('SELECT data FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name = :name', array(':name' => $name), $this->options)->fetchField(); if ($raw !== FALSE) { @@ -81,6 +91,9 @@ public function read($name) { } } catch (\Exception $e) { + // There are situations, like in the installer, where we may attempt a + // read without actually having the database available. In this case, + // catch the exception and just return FALSE so the caller can handle it. } return $data; } @@ -89,10 +102,6 @@ public function read($name) { * {@inheritdoc} */ public function readMultiple(array $names) { - // There are situations, like in the installer, where we may attempt a - // read without actually having the database available. In this case, - // catch the exception and just return an empty array so the caller can - // handle it if need be. $list = array(); try { $list = $this->connection->query('SELECT name, data FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name IN (:names)', array(':names' => $names), $this->options)->fetchAllKeyed(); @@ -100,7 +109,11 @@ public function readMultiple(array $names) { $data = $this->decode($data); } } - catch (Exception $e) { + catch (\Exception $e) { + // There are situations, like in the installer, where we may attempt a + // read without actually having the database available. In this case, + // catch the exception and just return an empty array so the caller can + // handle it if need be. } return $list; } @@ -113,15 +126,68 @@ public function readMultiple(array $names) { * @todo Ignore slave targets for data manipulation operations. */ public function write($name, array $data) { - $data = $this->encode($data); $options = array('return' => Database::RETURN_AFFECTED) + $this->options; return (bool) $this->connection->merge($this->table, $options) ->key('name', $name) - ->fields(array('data' => $data)) + ->fields(array('data' => $this->encode($data))) ->execute(); } /** + * Check if the config table exists and create it if not. + * + * @return bool + * TRUE if the table was created, FALSE otherwise. + * + * @throws \Drupal\Core\Config\StorageException + * If a database error occurs. + */ + protected function ensureTableExists() { + try { + if (!$this->connection->schema()->tableExists($this->table)) { + $this->connection->schema()->createTable($this->table, static::schemaDefinition()); + return TRUE; + } + } + // If another process has already created the config table, attempting to + // recreate it will throw an exception. In this case just catch the + // exception and do nothing. + catch (SchemaObjectExistsException $e) { + return TRUE; + } + catch (\Exception $e) { + throw new StorageException($e->getMessage(), NULL, $e); + } + return FALSE; + } + + /** + * Defines the schema for the configuration table. + */ + public static function schemaDefinition() { + $schema = array( + 'description' => 'The base table for configuration data.', + 'fields' => array( + 'name' => array( + 'description' => 'Primary Key: Unique config object name.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'data' => array( + 'description' => 'A serialized configuration object data.', + 'type' => 'blob', + 'not null' => FALSE, + 'size' => 'big', + ), + ), + 'primary key' => array('name'), + ); + return $schema; + } + + /** * Implements Drupal\Core\Config\StorageInterface::delete(). * * @throws PDOException @@ -168,29 +234,31 @@ public function decode($raw) { } /** - * Implements Drupal\Core\Config\StorageInterface::listAll(). - * - * @throws PDOException - * @throws \Drupal\Core\Database\DatabaseExceptionWrapper - * Only thrown in case $this->options['throw_exception'] is TRUE. + * {@inheritdoc} */ public function listAll($prefix = '') { - return $this->connection->query('SELECT name FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name LIKE :name', array( - ':name' => db_like($prefix) . '%', - ), $this->options)->fetchCol(); + try { + return $this->connection->query('SELECT name FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name LIKE :name', array( + ':name' => $this->connection->escapeLike($prefix) . '%', + ), $this->options)->fetchCol(); + } + catch (\Exception $e) { + return array(); + } } /** - * Implements Drupal\Core\Config\StorageInterface::deleteAll(). - * - * @throws PDOException - * @throws \Drupal\Core\Database\DatabaseExceptionWrapper - * Only thrown in case $this->options['throw_exception'] is TRUE. + * {@inheritdoc} */ public function deleteAll($prefix = '') { - $options = array('return' => Database::RETURN_AFFECTED) + $this->options; - return (bool) $this->connection->delete($this->table, $options) - ->condition('name', $prefix . '%', 'LIKE') - ->execute(); + try { + $options = array('return' => Database::RETURN_AFFECTED) + $this->options; + return (bool) $this->connection->delete($this->table, $options) + ->condition('name', $prefix . '%', 'LIKE') + ->execute(); + } + catch (\Exception $e) { + return FALSE; + } } } diff --git a/core/lib/Drupal/Core/Config/FileStorage.php b/core/lib/Drupal/Core/Config/FileStorage.php index 14268e9..26da8d2 100644 --- a/core/lib/Drupal/Core/Config/FileStorage.php +++ b/core/lib/Drupal/Core/Config/FileStorage.php @@ -69,6 +69,19 @@ public static function getFileExtension() { } /** + * {@inheritdoc} + */ + public function createStorage() { + // @todo: Use this method during install. + $success = file_prepare_directory($this->directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + $success = $success && file_save_htaccess($this->directory, TRUE, TRUE); + if (!$success) { + throw new StorageException("Failed to create config directory {$this->directory}"); + } + return $this; + } + + /** * Implements Drupal\Core\Config\StorageInterface::exists(). */ public function exists($name) { @@ -213,7 +226,7 @@ public function listAll($prefix = '') { // glob() silently ignores the error of a non-existing search directory, // even with the GLOB_ERR flag. if (!file_exists($this->directory)) { - throw new StorageException($this->directory . '/ not found.'); + return array(); } $extension = '.' . static::getFileExtension(); // \GlobIterator on Windows requires an absolute path. diff --git a/core/lib/Drupal/Core/Config/InstallStorage.php b/core/lib/Drupal/Core/Config/InstallStorage.php index 124748a..89d478c 100644 --- a/core/lib/Drupal/Core/Config/InstallStorage.php +++ b/core/lib/Drupal/Core/Config/InstallStorage.php @@ -65,6 +65,13 @@ public function getFilePath($name) { } /** + * {@inheritdoc} + */ + public function createStorage() { + return $this; + } + + /** * Overrides Drupal\Core\Config\FileStorage::write(). * * @throws \Drupal\Core\Config\StorageException diff --git a/core/lib/Drupal/Core/Config/NullStorage.php b/core/lib/Drupal/Core/Config/NullStorage.php index 2bf121d..5ab1fa8 100644 --- a/core/lib/Drupal/Core/Config/NullStorage.php +++ b/core/lib/Drupal/Core/Config/NullStorage.php @@ -24,6 +24,13 @@ class NullStorage implements StorageInterface { /** + * {@inheritdoc} + */ + public function createStorage() { + return $this; + } + + /** * Implements Drupal\Core\Config\StorageInterface::exists(). */ public function exists($name) { diff --git a/core/lib/Drupal/Core/Config/StorableConfigBase.php b/core/lib/Drupal/Core/Config/StorableConfigBase.php index 8aa292b..18c0f07 100644 --- a/core/lib/Drupal/Core/Config/StorableConfigBase.php +++ b/core/lib/Drupal/Core/Config/StorableConfigBase.php @@ -132,6 +132,27 @@ protected function getSchemaWrapper() { } /** + * Validate the values are allowed data types. + * + * @throws UnsupportedDataTypeConfigException + * If there is any invalid value. + */ + protected function validateValue($key, $value) { + // Minimal validation. Should not try to serialize resources or non-arrays. + if (is_array($value)) { + foreach ($value as $nested_value_key => $nested_value) { + $this->validateValue($key . '.' . $nested_value_key, $nested_value); + } + } + elseif ($value !== NULL && !is_scalar($value)) { + throw new UnsupportedDataTypeConfigException(String::format('Invalid data type for config element @name:@key', array( + '@name' => $this->getName(), + '@key' => $key, + ))); + } + } + + /** * Casts the value to correct data type using the configuration schema. * * @param string $key diff --git a/core/lib/Drupal/Core/Config/StorageInterface.php b/core/lib/Drupal/Core/Config/StorageInterface.php index 9b45fa8..929ed33 100644 --- a/core/lib/Drupal/Core/Config/StorageInterface.php +++ b/core/lib/Drupal/Core/Config/StorageInterface.php @@ -16,6 +16,17 @@ interface StorageInterface { /** + * Ensure that the back-end for this storage exists (idempotent). + * + * @return \Drupal\Core\Config\StorageInterface + * The called object. + * + * @throws \Drupal\Core\Config\StorageException + * If the back-end storage does not exist and cannot be created. + */ + public function createStorage(); + + /** * Returns whether a configuration object exists. * * @param string $name @@ -41,7 +52,7 @@ public function read($name); /** * Reads configuration data from the storage. * - * @param array $name + * @param array $names * List of names of the configuration objects to load. * * @return array diff --git a/core/lib/Drupal/Core/DependencyInjection/UpdateServiceProvider.php b/core/lib/Drupal/Core/DependencyInjection/UpdateServiceProvider.php index aa3c709..7098bf8 100644 --- a/core/lib/Drupal/Core/DependencyInjection/UpdateServiceProvider.php +++ b/core/lib/Drupal/Core/DependencyInjection/UpdateServiceProvider.php @@ -28,11 +28,10 @@ public function register(ContainerBuilder $container) { $container ->register('lock', 'Drupal\Core\Lock\NullLockBackend'); - // Prevent config from accessing {cache_config}. - // @see $conf['cache_classes'], update_prepare_d8_bootstrap() - $container - ->register('config.storage', 'Drupal\Core\Config\FileStorage') - ->addArgument(config_get_config_directory(CONFIG_ACTIVE_DIRECTORY)); + // Prevent config from being access via a cache wrapper by removing + // any existing definition and adding an alias to the un-cached service. + $container->removeDefinition('config.storage'); + $container->setAlias('config.storage', 'config.storage.uncached'); $container->register('module_handler', 'Drupal\Core\Extension\UpdateModuleHandler') ->addArgument('%container.modules%'); $container diff --git a/core/modules/config/lib/Drupal/config/Controller/ConfigController.php b/core/modules/config/lib/Drupal/config/Controller/ConfigController.php index 18f6287..c79b8cb 100644 --- a/core/modules/config/lib/Drupal/config/Controller/ConfigController.php +++ b/core/modules/config/lib/Drupal/config/Controller/ConfigController.php @@ -12,6 +12,7 @@ use Drupal\Core\Config\StorageInterface; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\system\FileDownloadController; +use Symfony\Component\Yaml\Dumper; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; @@ -81,13 +82,15 @@ public function __construct(StorageInterface $target_storage, StorageInterface $ * Downloads a tarball of the site configuration. */ public function downloadExport() { + file_unmanaged_delete(file_directory_temp() . '/config.tar.gz'); + + $dumper = new Dumper(); + $dumper->setIndentation(2); + $archiver = new ArchiveTar(file_directory_temp() . '/config.tar.gz', 'gz'); - $config_dir = config_get_config_directory(); - $config_files = array(); - foreach (\Drupal::service('config.storage')->listAll() as $config_name) { - $config_files[] = $config_dir . '/' . $config_name . '.yml'; + foreach (\Drupal::service('config.storage')->listAll() as $name) { + $archiver->addString("$name.yml", $dumper->dump(\Drupal::config($name)->get(), PHP_INT_MAX, 0, TRUE)); } - $archiver->createModify($config_files, '', config_get_config_directory()); $request = new Request(array('file' => 'config.tar.gz')); return $this->fileDownloadController->download($request, 'temporary'); diff --git a/core/modules/config/lib/Drupal/config/Form/ConfigSingleExportForm.php b/core/modules/config/lib/Drupal/config/Form/ConfigSingleExportForm.php index 101dacb..4b2b6f7 100644 --- a/core/modules/config/lib/Drupal/config/Form/ConfigSingleExportForm.php +++ b/core/modules/config/lib/Drupal/config/Form/ConfigSingleExportForm.php @@ -12,6 +12,7 @@ use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Form\FormBase; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Yaml\Dumper; /** * Provides a form for exporting a single configuration file. @@ -33,6 +34,13 @@ class ConfigSingleExportForm extends FormBase { protected $configStorage; /** + * The YAML dumper. + * + * @var \Symfony\Component\Yaml\Dumper + */ + protected $dumper; + + /** * Tracks the valid config entity type definitions. * * @var \Drupal\Core\Entity\EntityTypeInterface[] @@ -46,10 +54,14 @@ class ConfigSingleExportForm extends FormBase { * The entity manager. * @param \Drupal\Core\Config\StorageInterface $config_storage * The config storage. + * @param \Symfony\Component\Yaml\Dumper $dumper + * The yaml dumper. */ - public function __construct(EntityManagerInterface $entity_manager, StorageInterface $config_storage) { + public function __construct(EntityManagerInterface $entity_manager, StorageInterface $config_storage, Dumper $dumper) { $this->entityManager = $entity_manager; $this->configStorage = $config_storage; + $this->dumper = $dumper; + $this->dumper->setIndentation(2); } /** @@ -58,7 +70,8 @@ public function __construct(EntityManagerInterface $entity_manager, StorageInter public static function create(ContainerInterface $container) { return new static( $container->get('entity.manager'), - $container->get('config.storage') + $container->get('config.storage'), + new Dumper() ); } @@ -151,8 +164,7 @@ public function updateExport($form, &$form_state) { $name = $form_state['values']['config_name']; } // Read the raw data for this config name, encode it, and display it. - $data = $this->configStorage->read($name); - $form['export']['#value'] = $this->configStorage->encode($data); + $form['export']['#value'] = $this->dumper->dump($this->configStorage->read($name), PHP_INT_MAX, 0, TRUE); $form['export']['#description'] = $this->t('The filename is %name.', array('%name' => $name . '.yml')); return $form['export']; } diff --git a/core/modules/config/lib/Drupal/config/Form/ConfigSingleImportForm.php b/core/modules/config/lib/Drupal/config/Form/ConfigSingleImportForm.php index 1295862..e20afde 100644 --- a/core/modules/config/lib/Drupal/config/Form/ConfigSingleImportForm.php +++ b/core/modules/config/lib/Drupal/config/Form/ConfigSingleImportForm.php @@ -11,6 +11,7 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Form\ConfirmFormBase; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Yaml\Yaml; /** * Provides a form for importing a single configuration file. @@ -32,6 +33,13 @@ class ConfigSingleImportForm extends ConfirmFormBase { protected $configStorage; /** + * The YAML component. + * + * @var \Symfony\Component\Yaml\Yaml + */ + protected $yaml; + + /** * If the config exists, this is that object. Otherwise, FALSE. * * @var \Drupal\Core\Config\Config|\Drupal\Core\Config\Entity\ConfigEntityInterface|bool @@ -52,10 +60,13 @@ class ConfigSingleImportForm extends ConfirmFormBase { * The entity manager. * @param \Drupal\Core\Config\StorageInterface $config_storage * The config storage. + * @param \Symfony\Component\Yaml\Yaml $yaml + * The YAML component. */ - public function __construct(EntityManagerInterface $entity_manager, StorageInterface $config_storage) { + public function __construct(EntityManagerInterface $entity_manager, StorageInterface $config_storage, Yaml $yaml) { $this->entityManager = $entity_manager; $this->configStorage = $config_storage; + $this->yaml = $yaml; } /** @@ -64,7 +75,8 @@ public function __construct(EntityManagerInterface $entity_manager, StorageInter public static function create(ContainerInterface $container) { return new static( $container->get('entity.manager'), - $container->get('config.storage') + $container->get('config.storage'), + new Yaml() ); } @@ -184,7 +196,7 @@ public function validateForm(array &$form, array &$form_state) { } // Decode the submitted import. - $data = $this->configStorage->decode($form_state['values']['import']); + $data = $this->yaml->parse($form_state['values']['import']); // Validate for config entities. if ($form_state['values']['config_type'] !== 'system.simple') { diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php index 7576163..f10a120 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php @@ -11,6 +11,7 @@ use Drupal\Core\Config\ConfigNameException; use Drupal\simpletest\DrupalUnitTestBase; use Drupal\Core\Config\FileStorage; +use Drupal\Core\Config\DatabaseStorage; use Drupal\Core\Config\UnsupportedDataTypeConfigException; /** @@ -192,10 +193,10 @@ function testNameValidation() { */ public function testDataTypes() { \Drupal::moduleHandler()->install(array('config_test')); - $storage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]); + $storage = new DatabaseStorage($this->container->get('database'), 'config'); $name = 'config_test.types'; $config = $this->container->get('config.factory')->get($name); - $original_content = file_get_contents($storage->getFilePath($name)); + $original_content = file_get_contents(drupal_get_path('module', 'config_test') . "/config/$name.yml"); $this->verbose('
' . $original_content . "\n" . var_export($storage->read($name), TRUE));
 
     // Verify variable data types are intact.
@@ -220,7 +221,7 @@ public function testDataTypes() {
     $this->assertIdentical($config->get(), $data);
     // Assert the data against the file storage.
     $this->assertIdentical($storage->read($name), $data);
-    $this->verbose('
' . file_get_contents($storage->getFilePath($name)) . var_export($storage->read($name), TRUE));
+    $this->verbose('
' . $name . var_export($storage->read($name), TRUE));
 
     // Set data using config::setData().
     $config->setData($data)->save();
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigSingleImportExportTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigSingleImportExportTest.php
index 46a14d4..5f1a416 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigSingleImportExportTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigSingleImportExportTest.php
@@ -130,6 +130,7 @@ public function testImportSimpleConfiguration() {
    */
   public function testExport() {
     $this->drupalLogin($this->drupalCreateUser(array('export configuration')));
+    $yaml = new Yaml();
 
     $this->drupalGet('admin/config/development/configuration/single/export/system.simple');
     $this->assertFieldByXPath('//select[@name="config_type"]//option[@selected="selected"]', t('Simple configuration'), 'The simple configuration option is selected when specified in the URL.');
@@ -152,7 +153,7 @@ public function testExport() {
     $this->assertFieldByXPath('//select[@name="config_name"]//option[@selected="selected"]', t('Fallback date format'), 'The fallback date format config entity is selected when specified in the URL.');
 
     $fallback_date = \Drupal::entityManager()->getStorage('date_format')->load('fallback');
-    $data = \Drupal::service('config.storage')->encode($fallback_date->toArray());
+    $data = $yaml->dump($fallback_date->toArray(), 2, 2);
     $this->assertFieldByXPath('//textarea[@name="export"]', $data, 'The fallback date format config entity export code is displayed.');
   }
 
diff --git a/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php b/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php
index 140b50a..74df325 100644
--- a/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php
+++ b/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php
@@ -95,6 +95,10 @@ function testCRUD() {
     $result = $this->invalidStorage->read($name);
     $this->assertIdentical($result, FALSE);
 
+    // Listing on a non-existing storage bin returns an empty array.
+    $result = $this->invalidStorage->listAll();
+    $this->assertIdentical($result, array());
+
     // Deleting all names with prefix deletes the appropriate data and returns
     // TRUE.
     $files = array(
@@ -131,16 +135,6 @@ function testCRUD() {
       $this->pass($class . ' thrown upon deleting from a non-existing storage bin.');
     }
 
-    // Listing on a non-existing storage bin throws an exception.
-    try {
-      $this->invalidStorage->listAll();
-      $this->fail('Exception not thrown upon listing from a non-existing storage bin.');
-    }
-    catch (\Exception $e) {
-      $class = get_class($e);
-      $this->pass($class . ' thrown upon listing from a non-existing storage bin.');
-    }
-
     // Test renaming an object that does not exist throws an exception.
     try {
       $this->storage->rename('config_test.storage_does_not_exist', 'config_test.storage_does_not_exist_rename');
diff --git a/core/modules/config/lib/Drupal/config/Tests/Storage/DatabaseStorageTest.php b/core/modules/config/lib/Drupal/config/Tests/Storage/DatabaseStorageTest.php
index 516bb9d..beb24da 100644
--- a/core/modules/config/lib/Drupal/config/Tests/Storage/DatabaseStorageTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/Storage/DatabaseStorageTest.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Definition of Drupal\config\Tests\Storage\DatabaseStorageTest.
+ * Contains \Drupal\config\Tests\Storage\DatabaseStorageTest.
  */
 
 namespace Drupal\config\Tests\Storage;
@@ -24,29 +24,8 @@ public static function getInfo() {
   function setUp() {
     parent::setUp();
 
-    $schema['config'] = array(
-      'description' => 'Database storage 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'),
-    );
-    db_create_table('config', $schema['config']);
-
     $this->storage = new DatabaseStorage($this->container->get('database'), 'config');
+    $this->storage->createStorage();
     $this->invalidStorage = new DatabaseStorage($this->container->get('database'), 'invalid');
 
     // ::listAll() verifications require other configuration data to exist.
diff --git a/core/modules/config/lib/Drupal/config/Tests/Storage/FileStorageTest.php b/core/modules/config/lib/Drupal/config/Tests/Storage/FileStorageTest.php
index d430bd3..4331e99 100644
--- a/core/modules/config/lib/Drupal/config/Tests/Storage/FileStorageTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/Storage/FileStorageTest.php
@@ -25,10 +25,12 @@ public static function getInfo() {
   function setUp() {
     parent::setUp();
     $this->storage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
+    $this->storage->createStorage();
     $this->invalidStorage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY] . '/nonexisting');
 
     // FileStorage::listAll() requires other configuration data to exist.
     $this->storage->write('system.performance', \Drupal::config('system.performance')->get());
+    $this->storage->write('core.extension', array('module' => array()));
   }
 
   protected function read($name) {
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationUiTest.php b/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationUiTest.php
index 405aaf5..cb07cd4 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationUiTest.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationUiTest.php
@@ -368,7 +368,6 @@ public function testContactConfigEntityTranslation() {
    */
   public function testDateFormatTranslation() {
     $this->drupalLogin($this->admin_user);
-    $file_storage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
 
     $this->drupalGet('admin/config/regional/date-time');
 
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php
index a50a2d4..6523b10 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php
@@ -225,8 +225,9 @@ public function containerBuild(ContainerBuilder $container) {
     $container->register('cache_factory', 'Drupal\Core\Cache\MemoryBackendFactory');
 
     $container
-      ->register('config.storage', 'Drupal\Core\Config\FileStorage')
-      ->addArgument($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
+      ->register('config.storage.uncached', 'Drupal\Core\Config\DatabaseStorage')
+      ->addArgument(Database::getConnection())
+      ->addArgument('config');
 
     $this->settingsSet('keyvalue_default', 'keyvalue.memory');
     $container->set('keyvalue.memory', $this->keyValueFactory);