diff --git a/core/lib/Drupal/Core/Config/Schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml similarity index 100% rename from core/lib/Drupal/Core/Config/Schema/core.data_types.schema.yml rename to core/config/schema/core.data_types.schema.yml diff --git a/core/core.services.yml b/core/core.services.yml index 2b31f93..8845772 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -64,18 +64,9 @@ services: factory_method: get factory_service: cache_factory arguments: [path] - 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: - class: Drupal\Core\Config\CachedStorage - arguments: ['@config.cachedstorage.storage', '@cache.config'] - tags: - - { name: persist } config.factory: class: Drupal\Core\Config\ConfigFactory tags: @@ -85,6 +76,15 @@ services: config.installer: class: Drupal\Core\Config\ConfigInstaller arguments: ['@config.factory', '@config.storage', '@config.typed', '@config.manager', '@event_dispatcher'] + config.storage: + class: Drupal\Core\Config\CachedStorage + arguments: ['@config.storage.active', '@cache.config'] + tags: + - { name: persist } + config.storage.active: + class: Drupal\Core\Config\FileStorage + factory_class: Drupal\Core\Config\FileStorageFactory + factory_method: getActive config.storage.staging: class: Drupal\Core\Config\FileStorage factory_class: Drupal\Core\Config\FileStorageFactory @@ -118,7 +118,7 @@ services: arguments: ['@service_container', '@settings'] keyvalue.database: class: Drupal\Core\KeyValueStore\KeyValueDatabaseFactory - arguments: ['@database'] + arguments: ['@database', '@serialization.phpserialize'] keyvalue.expirable: class: Drupal\Core\KeyValueStore\KeyValueExpirableFactory arguments: ['@service_container', '@settings'] @@ -127,6 +127,16 @@ services: tags: - { name: needs_destruction } arguments: ['@database'] + + serialization.json: + class: Drupal\Core\Serialization\Json + serialization.phpserialize: + class: Drupal\Core\Serialization\PhpSerialize + serialization.xml: + class: Drupal\Core\Serialization\Xml + serialization.yaml: + class: Drupal\Core\Serialization\Yaml + settings: class: Drupal\Component\Utility\Settings factory_class: Drupal\Component\Utility\Settings diff --git a/core/lib/Drupal/Core/Config/BootstrapConfigStorageFactory.php b/core/lib/Drupal/Core/Config/BootstrapConfigStorageFactory.php index 054e690..8688ed6 100644 --- a/core/lib/Drupal/Core/Config/BootstrapConfigStorageFactory.php +++ b/core/lib/Drupal/Core/Config/BootstrapConfigStorageFactory.php @@ -26,7 +26,7 @@ public static function get() { return call_user_func($drupal_bootstrap_config_storage); } else { - return new FileStorage(config_get_config_directory(CONFIG_ACTIVE_DIRECTORY)); + return FileStorageFactory::getActive(); } } diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php index e000200..95f724c 100644 --- a/core/lib/Drupal/Core/Config/Config.php +++ b/core/lib/Drupal/Core/Config/Config.php @@ -12,6 +12,7 @@ use Drupal\Core\Config\ConfigNameException; use Drupal\Core\Config\Schema\SchemaIncompleteException; use Drupal\Core\DependencyInjection\DependencySerialization; +use Drupal\Core\Serialization\Exception\InvalidDataTypeException; use Drupal\Core\TypedData\PrimitiveInterface; use Drupal\Core\TypedData\Type\FloatInterface; use Drupal\Core\TypedData\Type\IntegerInterface; @@ -533,7 +534,7 @@ protected function getSchemaWrapper() { * @return mixed * The value cast to the type indicated in the schema. * - * @throws \Drupal\Core\Config\UnsupportedDataTypeConfigException + * @throws \Drupal\Core\Serialization\Exception\InvalidDataTypeException * Exception on unsupported/undefined data type deducted. */ protected function castValue($key, $value) { @@ -572,7 +573,7 @@ protected function castValue($key, $value) { else { // Throw exception on any non-scalar or non-array value. if (!is_array($value)) { - throw new UnsupportedDataTypeConfigException(String::format('Invalid data type for config element @name:@key', array( + throw new InvalidDataTypeException(String::format('Invalid data type for config element @name:@key', array( '@name' => $this->getName(), '@key' => $key, ))); diff --git a/core/lib/Drupal/Core/Config/ConfigInstaller.php b/core/lib/Drupal/Core/Config/ConfigInstaller.php index 2bb06b3..abd52b5 100644 --- a/core/lib/Drupal/Core/Config/ConfigInstaller.php +++ b/core/lib/Drupal/Core/Config/ConfigInstaller.php @@ -86,7 +86,7 @@ public function installDefaultConfig($type, $name) { // extension has a configuration schema directory. $this->typedConfig->clearCachedDefinitions(); } - $default_storage = new FileStorage($config_dir); + $default_storage = FileStorageFactory::getExtension($config_dir); $other_module_config = array_filter($default_storage->listAll(), function ($value) use ($name) { return !preg_match('/^' . $name . '\./', $value); }); diff --git a/core/lib/Drupal/Core/Config/ExtensionInstallStorage.php b/core/lib/Drupal/Core/Config/ExtensionInstallStorage.php index 5bedbbe..d43339a 100644 --- a/core/lib/Drupal/Core/Config/ExtensionInstallStorage.php +++ b/core/lib/Drupal/Core/Config/ExtensionInstallStorage.php @@ -29,8 +29,9 @@ class ExtensionInstallStorage extends InstallStorage { * The active configuration store where the list of enabled modules and * themes is stored. */ - public function __construct(StorageInterface $config_storage) { + public function __construct(StorageInterface $config_storage, $collection = '') { $this->configStorage = $config_storage; + parent::__construct($collection); } /** @@ -57,11 +58,15 @@ protected function getAllFolders() { $this->folders = array(); $modules = $this->configStorage->read('system.module'); if (isset($modules['enabled'])) { - $this->folders += $this->getComponentNames('module', array_keys($modules['enabled'])); + foreach (array_keys($modules['enabled']) as $name) { + $this->folders[$name] = drupal_get_path('module', $name); + } } $themes = $this->configStorage->read('system.theme'); if (isset($themes['enabled'])) { - $this->folders += $this->getComponentNames('theme', array_keys($themes['enabled'])); + foreach (array_keys($themes['enabled']) as $name) { + $this->folders[$name] = drupal_get_path('theme', $name); + } } } return $this->folders; diff --git a/core/lib/Drupal/Core/Config/FileStorage.php b/core/lib/Drupal/Core/Config/FileStorage.php index 2df9fbd..a2f0273 100644 --- a/core/lib/Drupal/Core/Config/FileStorage.php +++ b/core/lib/Drupal/Core/Config/FileStorage.php @@ -7,55 +7,18 @@ namespace Drupal\Core\Config; -use Drupal\Component\Utility\String; -use Symfony\Component\Yaml\Dumper; -use Symfony\Component\Yaml\Exception\DumpException; -use Symfony\Component\Yaml\Parser; +use Drupal\Core\KeyValueStore\FileStorage as KeyValueFileStorage; /** * Defines the file storage controller. + * + * @todo Remove Boolean return values from write/delete methods. + * @todo Change read method return value for missing key from FALSE to NULL. */ -class FileStorage implements StorageInterface { +class FileStorage extends KeyValueFileStorage implements StorageInterface { - /** - * The filesystem path for configuration objects. - * - * @var string - */ - protected $directory = ''; - - /** - * A shared YAML dumper instance. - * - * @var Symfony\Component\Yaml\Dumper - */ - protected $dumper; - - /** - * A shared YAML parser instance. - * - * @var Symfony\Component\Yaml\Parser - */ - protected $parser; - - /** - * Constructs a new FileStorage controller. - * - * @param string $directory - * A directory path to use for reading and writing of configuration files. - */ - public function __construct($directory) { - $this->directory = $directory; - } - - /** - * Returns the path to the configuration file. - * - * @return string - * The path to the configuration file. - */ public function getFilePath($name) { - return $this->directory . '/' . $name . '.' . static::getFileExtension(); + return $this->getPathname($name); } /** @@ -68,177 +31,83 @@ public static function getFileExtension() { return 'yml'; } - /** - * Implements Drupal\Core\Config\StorageInterface::exists(). - */ public function exists($name) { - return file_exists($this->getFilePath($name)); + return $this->has($name); } - /** - * Implements Drupal\Core\Config\StorageInterface::read(). - * - * @throws Symfony\Component\Yaml\Exception\ParseException - */ public function read($name) { - if (!$this->exists($name)) { + $value = $this->get($name, FALSE); + if (!is_array($value)) { return FALSE; } - $data = file_get_contents($this->getFilePath($name)); - // @todo Yaml throws a ParseException on invalid data. Is it expected to be - // caught or not? - $data = $this->decode($data); - return $data; + return $value; } - /** - * {@inheritdoc} - */ public function readMultiple(array $names) { - $list = array(); - foreach ($names as $name) { - if ($data = $this->read($name)) { - $list[$name] = $data; + $values = $this->getMultiple($names); + foreach ($values as $key => $value) { + if (!is_array($value)) { + $values[$key] = FALSE; } } - return $list; + return $values; } /** * Implements Drupal\Core\Config\StorageInterface::write(). * - * @throws \Drupal\Core\Config\UnsupportedDataTypeConfigException - * @throws \Drupal\Core\Config\StorageException + * @throws \Drupal\Core\Serialization\Exception\InvalidDataTypeException */ public function write($name, array $data) { - try { - $data = $this->encode($data); - } - catch(DumpException $e) { - throw new UnsupportedDataTypeConfigException(String::format('Invalid data type for used in config: @name', array('@name' => $name))); - } - - $target = $this->getFilePath($name); - $status = @file_put_contents($target, $data); - if ($status === FALSE) { - throw new StorageException('Failed to write configuration file: ' . $this->getFilePath($name)); - } - else { - drupal_chmod($target); - } + $this->set($name, $data); return TRUE; } - /** - * Implements Drupal\Core\Config\StorageInterface::delete(). - */ - public function delete($name) { - if (!$this->exists($name)) { - if (!file_exists($this->directory)) { - throw new StorageException($this->directory . '/ not found.'); - } - return FALSE; - } - return drupal_unlink($this->getFilePath($name)); - } - - /** - * Implements Drupal\Core\Config\StorageInterface::rename(). - */ public function rename($name, $new_name) { - $status = @rename($this->getFilePath($name), $this->getFilePath($new_name)); - if ($status === FALSE) { - throw new StorageException('Failed to rename configuration file from: ' . $this->getFilePath($name) . ' to: ' . $this->getFilePath($new_name)); + if ($this->has($new_name)) { + return FALSE; } + $value = $this->get($name); + $this->delete($name); + $this->set($new_name, $value); return TRUE; } - /** - * Gets the YAML dumper instance. - * - * @return Symfony\Component\Yaml\Dumper - */ - protected function getDumper() { - if (!isset($this->dumper)) { - $this->dumper = new Dumper(); - // Set Yaml\Dumper's default indentation for nested nodes/collections to - // 2 spaces for consistency with Drupal coding standards. - $this->dumper->setIndentation(2); - } - return $this->dumper; - } - - /** - * Gets the YAML parser instance. - * - * @return Symfony\Component\Yaml\Parser - */ - protected function getParser() { - if (!isset($this->parser)) { - $this->parser = new Parser(); - } - return $this->parser; - } - - /** - * Implements Drupal\Core\Config\StorageInterface::encode(). - * - * @throws Symfony\Component\Yaml\Exception\DumpException - */ public function encode($data) { - // The level where you switch to inline YAML is set to PHP_INT_MAX to ensure - // this does not occur. Also set the exceptionOnInvalidType parameter to - // TRUE, so exceptions are thrown for an invalid data type. - return $this->getDumper()->dump($data, PHP_INT_MAX, 0, TRUE); + return $this->format->encode($data); } - /** - * Implements Drupal\Core\Config\StorageInterface::decode(). - * - * @throws Symfony\Component\Yaml\Exception\ParseException - */ public function decode($raw) { - $data = $this->getParser()->parse($raw); - // A simple string is valid YAML for any reason. - if (!is_array($data)) { - return FALSE; - } - return $data; + return $this->format->decode($raw); } - /** - * Implements Drupal\Core\Config\StorageInterface::listAll(). - */ 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.'); - } - $extension = '.' . static::getFileExtension(); - // \GlobIterator on Windows requires an absolute path. - $files = new \GlobIterator(realpath($this->directory) . '/' . $prefix . '*' . $extension); - + // @todo Add KeyValueStoreInterface::listKeys(). $names = array(); - foreach ($files as $file) { - $names[] = $file->getBasename($extension); + foreach ($this->getIterator($prefix) as $file) { + $names[] = $file->getBasename('.' . $this->format->getFileExtension()); } - return $names; } - /** - * Implements Drupal\Core\Config\StorageInterface::deleteAll(). - */ + public function delete($key) { + parent::delete($key); + return TRUE; + } + + public function deleteMultiple(array $keys) { + parent::deleteMultiple($keys); + return TRUE; + } + public function deleteAll($prefix = '') { - $success = TRUE; - $files = $this->listAll($prefix); - foreach ($files as $name) { - if (!$this->delete($name) && $success) { - $success = FALSE; - } + if ($prefix === '') { + parent::deleteAll(); + return TRUE; } - - return $success; + foreach ($this->listAll($prefix) as $name) { + $this->delete($name); + } + return TRUE; } } diff --git a/core/lib/Drupal/Core/Config/FileStorageFactory.php b/core/lib/Drupal/Core/Config/FileStorageFactory.php index 1768df4..ea50dfb 100644 --- a/core/lib/Drupal/Core/Config/FileStorageFactory.php +++ b/core/lib/Drupal/Core/Config/FileStorageFactory.php @@ -6,6 +6,8 @@ namespace Drupal\Core\Config; +use Drupal\Core\Serialization\Yaml; + /** * Provides a factory for creating config file storage objects. */ @@ -17,7 +19,7 @@ class FileStorageFactory { * @return \Drupal\Core\Config\FileStorage FileStorage */ static function getActive() { - return new FileStorage(config_get_config_directory(CONFIG_ACTIVE_DIRECTORY)); + return new FileStorage('', new Yaml(), config_get_config_directory(CONFIG_ACTIVE_DIRECTORY)); } /** @@ -26,7 +28,19 @@ static function getActive() { * @return \Drupal\Core\Config\FileStorage FileStorage */ static function getStaging() { - return new FileStorage(config_get_config_directory(CONFIG_STAGING_DIRECTORY)); + return new FileStorage('', new Yaml(), config_get_config_directory(CONFIG_STAGING_DIRECTORY)); + } + + /** + * Returns a FileStorage object working with an extension's config directory. + * + * @param string $config_dir + * The filesystem path of the extension's config directory. + * + * @return \Drupal\Core\Config\FileStorage + */ + static function getExtension($config_dir) { + return new FileStorage('', new Yaml(), $config_dir); } } diff --git a/core/lib/Drupal/Core/Config/InstallStorage.php b/core/lib/Drupal/Core/Config/InstallStorage.php index 16bcc1c..882de29 100644 --- a/core/lib/Drupal/Core/Config/InstallStorage.php +++ b/core/lib/Drupal/Core/Config/InstallStorage.php @@ -7,9 +7,20 @@ namespace Drupal\Core\Config; +use Drupal\Core\Serialization\Yaml; + /** * Storage controller used by the Drupal installer. * + * In essence, this is a clustered/sharded key/value store, which reads from + * multiple storage bins. Theoretically, each cluster/bin contains the keys for + * a certain key namespace only. + * + * However, since modules can additionally ship with integration configuration + * for already installed modules, and those keys need to be discovered in the + * cluster/bin namespace of the module to be installed, the otherwise simple + * concept of namespaced key clusters is muddied. + * * @see install_begin_request() */ class InstallStorage extends FileStorage { @@ -22,38 +33,23 @@ class InstallStorage extends FileStorage { protected $folders; /** - * Overrides Drupal\Core\Config\FileStorage::__construct(). + * The subdirectory name to scan in each extension directory. + * + * This does not include the collection. + * + * @var string */ - public function __construct() { - } + protected $subpath; /** - * Overrides Drupal\Core\Config\FileStorage::getFilePath(). - * - * Returns the path to the configuration file. - * - * Determines the owner and path to the default configuration file of a - * requested config object name located in the installation profile, a module, - * or a theme (in this order). - * - * @return string - * The path to the configuration file. - * - * @todo Improve this when figuring out how we want to handle configuration in - * installation profiles. E.g., a config object actually has to be searched - * in the profile first (whereas the profile is never the owner), only - * afterwards check for a corresponding module or theme. + * Overrides Drupal\Core\Config\FileStorage::__construct(). */ - public function getFilePath($name) { - $folders = $this->getAllFolders(); - if (isset($folders[$name])) { - return $folders[$name] . '/' . $name . '.' . $this->getFileExtension(); + public function __construct($collection = '', $subpath = 'config') { + parent::__construct($collection, new Yaml(), 'unused'); + $this->subpath = $subpath; + if ($collection !== '') { + $this->subpath .= '/' . $collection; } - // If any code in the early installer requests a configuration object that - // does not exist anywhere as default config, then that must be mistake. - throw new StorageException(format_string('Missing configuration file: @name', array( - '@name' => $name, - ))); } /** @@ -62,7 +58,7 @@ public function getFilePath($name) { * @throws \Drupal\Core\Config\StorageException */ public function write($name, array $data) { - throw new StorageException('Write operation is not allowed during install.'); + throw new StorageException('Write operation is not allowed.'); } /** @@ -71,7 +67,7 @@ public function write($name, array $data) { * @throws \Drupal\Core\Config\StorageException */ public function delete($name) { - throw new StorageException('Delete operation is not allowed during install.'); + throw new StorageException('Delete operation is not allowed.'); } /** @@ -80,82 +76,51 @@ public function delete($name) { * @throws \Drupal\Core\Config\StorageException */ public function rename($name, $new_name) { - throw new StorageException('Rename operation is not allowed during install.'); + throw new StorageException('Rename operation is not allowed.'); } /** - * Implements Drupal\Core\Config\StorageInterface::listAll(). + * Overrides \Drupal\Core\KeyValueStore\FileStorage::getPathname(). */ - public function listAll($prefix = '') { - $names = array_keys($this->getAllFolders()); - if (!$prefix) { - return $names; - } - else { - $return = array(); - foreach ($names as $index => $name) { - if (strpos($name, $prefix) === 0 ) { - $return[$index] = $names[$index]; - } + protected function getPathname($key) { + foreach ($this->getIterator($key) as $file) { + if ($file->getBasename('.' . $this->format->getFileExtension()) == $key) { + return $file->getPathname(); } - return $return; } } /** - * Returns a map of all config object names and their folders. - * - * @return array - * An array mapping config object names with directories. + * Overrides \Drupal\Core\KeyValueStore\FileStorage::getIterator(). */ - protected function getAllFolders() { - if (!isset($this->folders)) { - $this->folders = $this->getComponentNames('profile', array(drupal_get_profile())); - $this->folders += $this->getComponentNames('module', array_keys(drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0))); - $this->folders += $this->getComponentNames('theme', array_keys(drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info.yml$/', 'themes'))); + protected function getIterator($prefix = '') { + $iterator = new \AppendIterator(); + foreach ($this->getAllFolders() as $extension_path) { + $path = DRUPAL_ROOT . '/' . $extension_path . '/' . $this->subpath; + if (is_dir($path)) { + $git = new \GlobIterator("$path/$prefix*." . $this->format->getFileExtension()); + $iterator->append($git); + } } - return $this->folders; + return $iterator; } /** - * Get all configuration names and folders for a list of modules or themes. - * - * @param string $type - * Type of components: 'module' | 'theme' | 'profile' - * @param array $list - * Array of theme or module names. + * Returns a list of all directory paths to be scanned. * * @return array - * Folders indexed by configuration name. */ - public function getComponentNames($type, array $list) { - $extension = '.' . $this->getFileExtension(); - $folders = array(); - foreach ($list as $name) { - $directory = $this->getComponentFolder($type, $name); - if (file_exists($directory)) { - $files = new \GlobIterator(DRUPAL_ROOT . '/' . $directory . '/*' . $extension); - foreach ($files as $file) { - $folders[$file->getBasename($extension)] = $directory; - } - } + protected function getAllFolders() { + if (!isset($this->folders)) { + $profile = drupal_get_profile(); + $this->folders = array($profile => drupal_get_path('profile', $profile)); + $folders = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules'); + $folders += drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info\.yml$/', 'themes'); + $this->folders += array_map(function ($file) { + return dirname($file->uri); + }, $folders); } - return $folders; - } - - /** - * Get folder inside each component that contains the files. - * - * @param string $type - * Component type: 'module' | 'theme' | 'profile' - * @param string $name - * Component name. - * - * @return string - * The configuration folder name for this component. - */ - protected function getComponentFolder($type, $name) { - return drupal_get_path($type, $name) . '/config'; + return $this->folders; } /** @@ -164,7 +129,7 @@ protected function getComponentFolder($type, $name) { * @throws \Drupal\Core\Config\StorageException */ public function deleteAll($prefix = '') { - throw new StorageException('Delete operation is not allowed during install.'); + throw new StorageException('Delete operation is not allowed.'); } } diff --git a/core/lib/Drupal/Core/Config/Schema/SchemaStorage.php b/core/lib/Drupal/Core/Config/Schema/SchemaStorage.php index 1b84dec..bce4b6f 100644 --- a/core/lib/Drupal/Core/Config/Schema/SchemaStorage.php +++ b/core/lib/Drupal/Core/Config/Schema/SchemaStorage.php @@ -9,51 +9,25 @@ use Drupal\Core\Config\ExtensionInstallStorage; use Drupal\Core\Config\StorageException; +use Drupal\Core\Config\StorageInterface; /** * Defines the file storage controller for metadata files. */ class SchemaStorage extends ExtensionInstallStorage { - /** - * Implements \Drupal\Core\Config\StorageInterface::exists(). - */ - public function exists($name) { - return array_key_exists($name, $this->getAllFolders()); - } - - /** - * Overrides \Drupal\Core\Config\InstallStorage::getComponentFolder(). - */ - protected function getComponentFolder($type, $name) { - return drupal_get_path($type, $name) . '/config/schema'; - } - - /** - * Overrides \Drupal\Core\Config\InstallStorage::write(). - * - * @throws \Drupal\Core\Config\StorageException - */ - public function write($name, array $data) { - throw new StorageException('Write operation is not allowed for config schema storage.'); - } - - /** - * Overrides \Drupal\Core\Config\InstallStorage::delete(). - * - * @throws \Drupal\Core\Config\StorageException - */ - public function delete($name) { - throw new StorageException('Delete operation is not allowed for config schema storage.'); + public function __construct(StorageInterface $config_storage) { + parent::__construct($config_storage, 'schema'); } - /** - * Overrides \Drupal\Core\Config\InstallStorage::rename(). - * - * @throws \Drupal\Core\Config\StorageException - */ - public function rename($name, $new_name) { - throw new StorageException('Rename operation is not allowed for config schema storage.'); + public function readMultiple(array $names) { + $values = parent::readMultiple($names); + foreach ($values as $name => $value) { + if (empty($value)) { + throw new SchemaIncompleteException("Missing configuration schema for $name."); + } + } + return $values; } /** @@ -67,21 +41,9 @@ public function rename($name, $new_name) { protected function getAllFolders() { if (!isset($this->folders)) { parent::getAllFolders(); - $this->folders += $this->getBaseDataTypeSchema(); + $this->folders['core'] = 'core'; } return $this->folders; } - /** - * Gets the base data types for configuration schema. - * - * @return array - * The file containing the base data types for configuration schema. - */ - protected function getBaseDataTypeSchema() { - return array( - 'core.data_types.schema' => 'core/lib/Drupal/Core/Config/Schema' - ); - } - } diff --git a/core/lib/Drupal/Core/Config/UnsupportedConfigDataTypeException.php b/core/lib/Drupal/Core/Config/UnsupportedConfigDataTypeException.php deleted file mode 100644 index ac63c26..0000000 --- a/core/lib/Drupal/Core/Config/UnsupportedConfigDataTypeException.php +++ /dev/null @@ -1,14 +0,0 @@ -listAll() as $config_name) { // If this file already exists, something in the upgrade path went diff --git a/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php b/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php index 4045c48..03fba90 100644 --- a/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php +++ b/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php @@ -9,6 +9,7 @@ use Drupal\Core\Database\Query\Merge; use Drupal\Core\Database\Connection; +use Drupal\Core\Serialization\SerializationInterface; /** * Defines a default key/value store implementation. @@ -37,16 +38,28 @@ class DatabaseStorage extends StorageBase { * * @param string $collection * The name of the collection holding key and value pairs. + * @param \Drupal\Core\Serialization\SerializationInterface $format + * The serialization format to use. * @param string $table * The name of the SQL table to use, defaults to key_value. */ - public function __construct($collection, Connection $connection, $table = 'key_value') { - parent::__construct($collection); + public function __construct($collection, SerializationInterface $format, Connection $connection, $table = 'key_value') { + parent::__construct($collection, $format); $this->connection = $connection; $this->table = $table; } /** + * {@inheritdoc} + */ + public function has($key) { + return (bool) $this->connection->query('SELECT 1 FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection AND name = :key', array( + ':collection' => $this->collection, + ':key' => $key, + ))->fetchField(); + } + + /** * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::getMultiple(). */ public function getMultiple(array $keys) { @@ -55,7 +68,7 @@ public function getMultiple(array $keys) { $result = $this->connection->query('SELECT name, value FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name IN (:keys) AND collection = :collection', array(':keys' => $keys, ':collection' => $this->collection))->fetchAllAssoc('name'); foreach ($keys as $key) { if (isset($result[$key])) { - $values[$key] = unserialize($result[$key]->value); + $values[$key] = $this->format->decode($result[$key]->value); } } } @@ -76,7 +89,7 @@ public function getAll() { foreach ($result as $item) { if ($item) { - $values[$item->name] = unserialize($item->value); + $values[$item->name] = $this->format->decode($item->value); } } return $values; @@ -91,7 +104,7 @@ public function set($key, $value) { 'name' => $key, 'collection' => $this->collection, )) - ->fields(array('value' => serialize($value))) + ->fields(array('value' => $this->format->encode($value))) ->execute(); } @@ -103,7 +116,7 @@ public function setIfNotExists($key, $value) { ->insertFields(array( 'collection' => $this->collection, 'name' => $key, - 'value' => serialize($value), + 'value' => $this->format->encode($value), )) ->condition('collection', $this->collection) ->condition('name', $key) diff --git a/core/lib/Drupal/Core/KeyValueStore/DatabaseStorageExpirable.php b/core/lib/Drupal/Core/KeyValueStore/DatabaseStorageExpirable.php index 02eec8c..145548f 100644 --- a/core/lib/Drupal/Core/KeyValueStore/DatabaseStorageExpirable.php +++ b/core/lib/Drupal/Core/KeyValueStore/DatabaseStorageExpirable.php @@ -10,6 +10,7 @@ use Drupal\Core\DestructableInterface; use Drupal\Core\Database\Connection; use Drupal\Core\Database\Query\Merge; +use Drupal\Core\Serialization\PhpSerialize; /** * Defines a default key/value store implementation for expiring items. @@ -53,7 +54,18 @@ class DatabaseStorageExpirable extends DatabaseStorage implements KeyValueStoreE * key_value_expire. */ public function __construct($collection, Connection $connection, $table = 'key_value_expire') { - parent::__construct($collection, $connection, $table); + parent::__construct($collection, new PhpSerialize(), $connection, $table); + } + + /** + * {@inheritdoc} + */ + public function has($key) { + return (bool) $this->connection->query('SELECT 1 FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection AND name = :key AND expire > :now', array( + ':collection' => $this->collection, + ':key' => $key, + ':now' => REQUEST_TIME, + ))->fetchField(); } /** diff --git a/core/lib/Drupal/Core/KeyValueStore/FileStorage.php b/core/lib/Drupal/Core/KeyValueStore/FileStorage.php new file mode 100644 index 0000000..f8c279f --- /dev/null +++ b/core/lib/Drupal/Core/KeyValueStore/FileStorage.php @@ -0,0 +1,129 @@ +path = $path; + if ($this->collection !== '') { + $this->path .= '/' . $this->collection; + } + } + + /** + * {@inheritdoc} + */ + public function has($key) { + return file_exists($this->getPathname($key)); + } + + public function getMultiple(array $keys) { + $values = array(); + foreach ($keys as $key) { + if (file_exists($pathname = $this->getPathname($key))) { + $values[$key] = $this->format->decode(file_get_contents($pathname)); + } + } + return $values; + } + + /** + * {@inheritdoc} + */ + public function getAll() { + $values = array(); + foreach ($this->getIterator() as $pathname => $file) { + $key = $file->getBasename('.' . $this->format->getFileExtension()); + $values[$key] = $this->format->decode(file_get_contents($pathname)); + } + return $values; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value) { + file_put_contents($this->getPathname($key), $this->format->encode($value)); + } + + /** + * {@inheritdoc} + */ + public function setIfNotExists($key, $value) { + if (!file_exists($this->getPathname($key))) { + $this->set($key, $value); + return TRUE; + } + return FALSE; + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple(array $keys) { + foreach ($keys as $key) { + if (file_exists($pathname = $this->getPathname($key))) { + unlink($pathname); + } + } + } + + /** + * {@inheritdoc} + */ + public function deleteAll() { + foreach ($this->getIterator() as $pathname => $file) { + unlink($pathname); + } + } + + /** + * Returns an iterator for the file storage. + * + * @return \FilesystemIterator + * The filesystem iterator. + */ + protected function getIterator($prefix = '') { + // GlobIterator requires absolute paths on Windows, so we use a filtered + // FilesystemIterator instead. + $flags = \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_FILEINFO; + $flags |= \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS; + $iterator = new \FilesystemIterator($this->path, $flags); + $filter = new FileStorageFilterIterator($iterator, $this->format->getFileExtension(), $prefix); + return $filter; + } + + protected function getPathname($key) { + return $this->path . "/$key." . $this->format->getFileExtension(); + } + +} diff --git a/core/lib/Drupal/Core/KeyValueStore/FileStorageFilterIterator.php b/core/lib/Drupal/Core/KeyValueStore/FileStorageFilterIterator.php new file mode 100644 index 0000000..0f4a12b --- /dev/null +++ b/core/lib/Drupal/Core/KeyValueStore/FileStorageFilterIterator.php @@ -0,0 +1,62 @@ +extension = $extension; + $this->prefix = $prefix; + } + + /** + * Implements \FilterIterator::accept(). + */ + public function accept() { + $file = $this->current(); + if (!$file->isFile()) { + return FALSE; + } + if ($file->getExtension() != $this->extension) { + return FALSE; + } + if ($this->prefix !== '' && strpos($file->getFilename(), $this->prefix) !== 0) { + return FALSE; + } + return TRUE; + } + +} diff --git a/core/lib/Drupal/Core/KeyValueStore/KeyValueDatabaseFactory.php b/core/lib/Drupal/Core/KeyValueStore/KeyValueDatabaseFactory.php index 1a840b3..6df46e6 100644 --- a/core/lib/Drupal/Core/KeyValueStore/KeyValueDatabaseFactory.php +++ b/core/lib/Drupal/Core/KeyValueStore/KeyValueDatabaseFactory.php @@ -6,8 +6,10 @@ */ namespace Drupal\Core\KeyValueStore; + use Drupal\Core\Database\Connection; use Drupal\Core\Database\Database; +use Drupal\Core\Serialization\SerializationInterface; /** * Defines the key/value store factory for the database backend. @@ -15,20 +17,36 @@ class KeyValueDatabaseFactory implements KeyValueFactoryInterface { /** - * Constructs this factory object. + * The database connection to use. + * + * @var \Drupal\Core\Database\Connection + */ + protected $connection; + + /** + * The serialization format to use. * + * @var \Drupal\Core\Serialization\SerializationInterface + */ + protected $format; + + /** + * Constructs this factory object. * * @param \Drupal\Core\Database\Connection $connection * The Connection object containing the key-value tables. + * @param \Drupal\Core\Serialization\SerializationInterface $format + * The serialization format to use. */ - function __construct(Connection $connection) { + function __construct(Connection $connection, SerializationInterface $format) { $this->connection = $connection; + $this->format = $format; } /** * {@inheritdoc} */ public function get($collection) { - return new DatabaseStorage($collection, $this->connection); + return new DatabaseStorage($collection, $this->format, $this->connection); } } diff --git a/core/lib/Drupal/Core/KeyValueStore/KeyValueFileFactory.php b/core/lib/Drupal/Core/KeyValueStore/KeyValueFileFactory.php new file mode 100644 index 0000000..91edf2c --- /dev/null +++ b/core/lib/Drupal/Core/KeyValueStore/KeyValueFileFactory.php @@ -0,0 +1,53 @@ +path = $path; + $this->format = $format; + } + + /** + * {@inheritdoc} + */ + public function get($collection) { + return new FileStorage($collection, $this->format, $this->path); + } + +} diff --git a/core/lib/Drupal/Core/KeyValueStore/KeyValueStoreInterface.php b/core/lib/Drupal/Core/KeyValueStore/KeyValueStoreInterface.php index 60c150c..39d1da0 100644 --- a/core/lib/Drupal/Core/KeyValueStore/KeyValueStoreInterface.php +++ b/core/lib/Drupal/Core/KeyValueStore/KeyValueStoreInterface.php @@ -21,6 +21,17 @@ public function getCollectionName(); /** + * Returns whether a given key exists in the store. + * + * @param string $key + * The key to check. + * + * @return bool + * TRUE if the key exists, FALSE otherwise. + */ + public function has($key); + + /** * Returns the stored value for a given key. * * @param string $key diff --git a/core/lib/Drupal/Core/KeyValueStore/MemoryStorage.php b/core/lib/Drupal/Core/KeyValueStore/MemoryStorage.php index e6e07ef..6e9b0f7 100644 --- a/core/lib/Drupal/Core/KeyValueStore/MemoryStorage.php +++ b/core/lib/Drupal/Core/KeyValueStore/MemoryStorage.php @@ -20,6 +20,13 @@ class MemoryStorage extends StorageBase { protected $data = array(); /** + * {@inheritdoc} + */ + public function has($key) { + return array_key_exists($key, $this->data); + } + + /** * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::get(). */ public function get($key, $default = NULL) { diff --git a/core/lib/Drupal/Core/KeyValueStore/NullStorageExpirable.php b/core/lib/Drupal/Core/KeyValueStore/NullStorageExpirable.php index 29bcde6..a3704eb 100644 --- a/core/lib/Drupal/Core/KeyValueStore/NullStorageExpirable.php +++ b/core/lib/Drupal/Core/KeyValueStore/NullStorageExpirable.php @@ -34,6 +34,13 @@ public function __construct($collection) { } /** + * {@inheritdoc} + */ + public function has($key) { + return FALSE; + } + + /** * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::get(). */ public function get($key, $default = NULL) { diff --git a/core/lib/Drupal/Core/KeyValueStore/StorageBase.php b/core/lib/Drupal/Core/KeyValueStore/StorageBase.php index 4c54271..8dc54e6 100644 --- a/core/lib/Drupal/Core/KeyValueStore/StorageBase.php +++ b/core/lib/Drupal/Core/KeyValueStore/StorageBase.php @@ -7,6 +7,8 @@ namespace Drupal\Core\KeyValueStore; +use Drupal\Core\Serialization\SerializationInterface; + /** * Provides a base class for key/value storage implementations. */ @@ -20,10 +22,18 @@ protected $collection; /** + * The serialization format to use. + * + * @var \Drupal\Core\Serialization\SerializationInterface + */ + protected $format; + + /** * Implements Drupal\Core\KeyValueStore\KeyValueStoreInterface::__construct(). */ - public function __construct($collection) { + public function __construct($collection, SerializationInterface $format = NULL) { $this->collection = $collection; + $this->format = $format; } /** diff --git a/core/lib/Drupal/Core/Serialization/Exception/InvalidDataTypeException.php b/core/lib/Drupal/Core/Serialization/Exception/InvalidDataTypeException.php new file mode 100644 index 0000000..e6b8ce6 --- /dev/null +++ b/core/lib/Drupal/Core/Serialization/Exception/InvalidDataTypeException.php @@ -0,0 +1,14 @@ +'), $data)->asXML(); + } + + /** + * Recursively encodes a value as XML. + * + * @param \SimpleXMLElement $xml + * The \SimpleXMLElement to add to. + * @param mixed $value + * The value to be encoded. + * @param string $key + * (optional) The key in the parent array, if any. + * + * @return \SimpleXMLElement + * The enhanced $xml element. + */ + protected static function encodeXml(\SimpleXMLElement $xml, $value, $key = NULL) { + if (isset($key)) { + $xml = $xml->addChild(gettype($value), is_array($value) ? '' : htmlspecialchars($value)); + $xml->addAttribute('key', $key); + } + if (is_array($value)) { + foreach ($value as $new_key => $new_value) { + static::encodeXml($xml, $new_value, $new_key); + } + } + return $xml; + } + + /** + * {@inheritdoc} + */ + public static function decode($raw) { + return static::decodeXml(new \SimpleXMLElement($raw)); + } + + /** + * Decodes the XML produced by encodeXml(). + * + * @param \SimpleXMLElement $xml + * The \SimpleXMLElement to decode. + * + * @return mixed + * The decoded value. + */ + protected static function decodeXml(\SimpleXMLElement $xml) { + $type = $xml->getName(); + if ($type == 'array') { + $value = array(); + foreach ($xml as $element) { + $value[(string) $element['key']] = static::decodeXml($element); + } + } + else { + $value = (string) $xml; + settype($value, $type); + } + return $value; + } + + /** + * {@inheritdoc} + */ + public static function getFileExtension() { + return 'xml'; + } + +} diff --git a/core/lib/Drupal/Core/Serialization/Yaml.php b/core/lib/Drupal/Core/Serialization/Yaml.php new file mode 100644 index 0000000..25ee001 --- /dev/null +++ b/core/lib/Drupal/Core/Serialization/Yaml.php @@ -0,0 +1,49 @@ +getMessage(), $e->getCode(), $e); + } + } + + /** + * {@inheritdoc} + */ + public static function decode($raw) { + try { + return Symfony::parse($raw, TRUE); + } + catch (\Exception $e) { + throw new InvalidDataTypeException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * {@inheritdoc} + */ + public static function getFileExtension() { + return 'yml'; + } + +} diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php index 7576163..50c85ea 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php @@ -11,7 +11,8 @@ use Drupal\Core\Config\ConfigNameException; use Drupal\simpletest\DrupalUnitTestBase; use Drupal\Core\Config\FileStorage; -use Drupal\Core\Config\UnsupportedDataTypeConfigException; +use Drupal\Core\Serialization\Exception\InvalidDataTypeException; +use Drupal\Core\Serialization\Yaml; /** * Tests CRUD operations on configuration objects. @@ -192,7 +193,7 @@ function testNameValidation() { */ public function testDataTypes() { \Drupal::moduleHandler()->install(array('config_test')); - $storage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]); + $storage = new FileStorage('', new Yaml(), $this->configDirectories[CONFIG_ACTIVE_DIRECTORY]); $name = 'config_test.types'; $config = $this->container->get('config.factory')->get($name); $original_content = file_get_contents($storage->getFilePath($name)); @@ -233,7 +234,7 @@ public function testDataTypes() { $config->set('stream', fopen(__FILE__, 'r'))->save(); $this->fail('No Exception thrown upon saving invalid data type.'); } - catch (UnsupportedDataTypeConfigException $e) { + catch (InvalidDataTypeException $e) { $this->pass(String::format('%class thrown upon saving invalid data type.', array( '%class' => get_class($e), ))); @@ -250,7 +251,7 @@ public function testDataTypes() { $config->set('stream', fopen(__FILE__, 'r'))->save(); $this->fail('No Exception thrown upon saving invalid data type.'); } - catch (UnsupportedDataTypeConfigException $e) { + catch (InvalidDataTypeException $e) { $this->pass(String::format('%class thrown upon saving invalid data type.', array( '%class' => get_class($e), ))); diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigFileContentTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigFileContentTest.php index a73155d..146a97b 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigFileContentTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigFileContentTest.php @@ -8,6 +8,7 @@ namespace Drupal\config\Tests; use Drupal\Core\Config\FileStorage; +use Drupal\Core\Serialization\Yaml; use Drupal\simpletest\DrupalUnitTestBase; /** @@ -206,7 +207,7 @@ function testSerialization() { ); // Encode and write, and reload and decode the configuration data. - $filestorage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]); + $filestorage = new FileStorage('', new Yaml(), $this->configDirectories[CONFIG_ACTIVE_DIRECTORY]); $filestorage->write($name, $config_data); $config_parsed = $filestorage->read($name); diff --git a/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php b/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php index b7424f3..f9d56a0 100644 --- a/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php @@ -70,7 +70,7 @@ public function testDefaultConfig() { // every module, profile and theme. $typed_config = new TypedConfigManager( \Drupal::service('config.storage'), - new TestSchemaStorage(), + new TestSchemaStorage(\Drupal::service('config.storage.active')), \Drupal::service('cache.config') ); 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 d986835..8ed254a 100644 --- a/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php +++ b/core/modules/config/lib/Drupal/config/Tests/Storage/ConfigStorageTestBase.php @@ -87,10 +87,6 @@ function testCRUD() { $result = $this->storage->delete($name); $this->assertIdentical($result, TRUE); - // Deleting a non-existing name returns FALSE. - $result = $this->storage->delete($name); - $this->assertIdentical($result, FALSE); - // Reading from a non-existing storage bin returns FALSE. $result = $this->invalidStorage->read($name); $this->assertIdentical($result, FALSE); 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 6a99605..efdbb9a 100644 --- a/core/modules/config/lib/Drupal/config/Tests/Storage/FileStorageTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/Storage/FileStorageTest.php @@ -8,7 +8,7 @@ namespace Drupal\config\Tests\Storage; use Drupal\Core\Config\FileStorage; -use Symfony\Component\Yaml\Yaml; +use Drupal\Core\Serialization\Yaml; /** * Tests FileStorage controller operations. @@ -24,8 +24,8 @@ public static function getInfo() { function setUp() { parent::setUp(); - $this->storage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]); - $this->invalidStorage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY] . '/nonexisting'); + $this->storage = new FileStorage('', new Yaml(), $this->configDirectories[CONFIG_ACTIVE_DIRECTORY]); + $this->invalidStorage = new FileStorage('', new Yaml(), $this->configDirectories[CONFIG_ACTIVE_DIRECTORY] . '/nonexisting'); // FileStorage::listAll() requires other configuration data to exist. $this->storage->write('system.performance', \Drupal::config('system.performance')->get()); @@ -33,7 +33,7 @@ function setUp() { protected function read($name) { $data = file_get_contents($this->storage->getFilePath($name)); - return Yaml::parse($data); + return Yaml::decode($data); } protected function insert($name, $data) { @@ -62,7 +62,7 @@ protected function testlistAll() { // Initialize FileStorage with absolute file path. $absolute_path = realpath($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]); - $storage_absolute_path = new FileStorage($absolute_path); + $storage_absolute_path = new FileStorage('', new Yaml(), $absolute_path); $config_files = $storage_absolute_path->listAll(); $this->assertIdentical($config_files, $expected_files, 'Absolute path, two config files found.'); } diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/TestInstallStorage.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/TestInstallStorage.php index c940422..d3d6cad 100644 --- a/core/modules/config/tests/config_test/lib/Drupal/config_test/TestInstallStorage.php +++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/TestInstallStorage.php @@ -22,9 +22,12 @@ class TestInstallStorage extends InstallStorage { */ protected function getAllFolders() { if (!isset($this->folders)) { - $this->folders = $this->getComponentNames('profile', array_keys(drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.profile$/', 'profiles'))); - $this->folders += $this->getComponentNames('module', array_keys(drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0))); - $this->folders += $this->getComponentNames('theme', array_keys(drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info.yml$/', 'themes'))); + $folders = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.profile$/', 'profiles'); + $folders += drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules'); + $folders += drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info\.yml$/', 'themes'); + $this->folders = array_map(function ($file) { + return dirname($file->uri); + }, $folders); } return $this->folders; } diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/TestSchemaStorage.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/TestSchemaStorage.php index 8d240b0..234268e 100644 --- a/core/modules/config/tests/config_test/lib/Drupal/config_test/TestSchemaStorage.php +++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/TestSchemaStorage.php @@ -19,20 +19,17 @@ class TestSchemaStorage extends SchemaStorage { /** - * Overrides Drupal\Core\Config\ExtensionInstallStorage::__construct(). - */ - public function __construct() { - } - - /** * {@inheritdoc} */ protected function getAllFolders() { if (!isset($this->folders)) { - $this->folders = $this->getBaseDataTypeSchema(); - $this->folders += $this->getComponentNames('profile', array_keys(drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.profile$/', 'profiles'))); - $this->folders += $this->getComponentNames('module', array_keys(drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0))); - $this->folders += $this->getComponentNames('theme', array_keys(drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info.yml$/', 'themes'))); + $this->folders['core'] = 'core'; + $folders = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.profile$/', 'profiles'); + $folders += drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules'); + $folders += drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info\.yml$/', 'themes'); + $this->folders += array_map(function ($file) { + return dirname($file->uri); + }, $folders); } return $this->folders; } 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 ed4b1e2..c46e678 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 @@ -11,6 +11,7 @@ use Drupal\Component\Utility\Unicode; use Drupal\Core\Config\FileStorage; use Drupal\Core\Language\Language; +use Drupal\Core\Serialization\Yaml; use Drupal\simpletest\WebTestBase; /** @@ -177,7 +178,7 @@ public function testSourceValueDuplicateSave() { $this->drupalPostForm("$translation_base_url/fr/add", $edit, t('Save translation')); // Read overridden file from active config. - $file_storage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]); + $file_storage = new FileStorage('', new Yaml(), $this->configDirectories[CONFIG_ACTIVE_DIRECTORY]); $language_config_name = \Drupal::configFactory()->getLanguageConfigName('fr', 'system.site'); $config_parsed = $file_storage->read($language_config_name); @@ -240,7 +241,7 @@ public function testSourceValueDuplicateSave() { public function testContactConfigEntityTranslation() { $this->drupalLogin($this->admin_user); - $file_storage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]); + $file_storage = new FileStorage('', new Yaml(), $this->configDirectories[CONFIG_ACTIVE_DIRECTORY]); $this->drupalGet('admin/structure/contact'); @@ -374,7 +375,7 @@ public function testContactConfigEntityTranslation() { */ public function testDateFormatTranslation() { $this->drupalLogin($this->admin_user); - $file_storage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]); + $file_storage = new FileStorage('', new Yaml(), $this->configDirectories[CONFIG_ACTIVE_DIRECTORY]); $this->drupalGet('admin/config/regional/date-time'); diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigManagerTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigManagerTest.php index 16fc30b..014c9e7 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigManagerTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigManagerTest.php @@ -22,7 +22,7 @@ class LocaleConfigManagerTest extends DrupalUnitTestBase { * * @var array */ - public static $modules = array('locale', 'locale_test'); + public static $modules = array('language', 'locale', 'locale_test'); /** * {@inheritdoc} @@ -39,27 +39,12 @@ public static function getInfo() { * Tests hasTranslation(). */ public function testHasTranslation() { - $locale_config_manager = new LocaleConfigManager( - // In contrast to the actual configuration we use the installer storage - // as the config storage. That way, we do not actually have to install - // the module and can extend DrupalUnitTestBase. - $this->container->get('config.storage.installer'), - $this->container->get('config.storage.schema'), - $this->container->get('config.storage.installer'), - $this->container->get('locale.storage'), - $this->container->get('cache.config'), - $this->container->get('config.factory') - ); + $this->installConfig(array('locale_test')); + $locale_config_manager = $this->container->get('locale.config.typed'); $language = new Language(array('id' => 'de')); - // The installer storage throws an expcetion when requesting a non-existing - // file. - try { - $locale_config_manager->hasTranslation('locale_test.no_translation', $language); - } - catch (StorageException $exception) { - $result = FALSE; - } + + $result = $locale_config_manager->hasTranslation('locale_test.no_translation', $language); $this->assertIdentical(FALSE, $result); $result = $locale_config_manager->hasTranslation('locale_test.translation', $language); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php index cab4b9e..bb5ec0b 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php @@ -226,6 +226,8 @@ public function containerBuild(ContainerBuilder $container) { $container ->register('config.storage', 'Drupal\Core\Config\FileStorage') + ->addArgument('') + ->addArgument(new Reference('serialization.yaml')) ->addArgument($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]); $this->settingsSet('keyvalue_default', 'keyvalue.memory'); diff --git a/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/DatabaseStorageTest.php b/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/DatabaseStorageTest.php index ad286d6..eddf7e7 100644 --- a/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/DatabaseStorageTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/DatabaseStorageTest.php @@ -34,7 +34,10 @@ protected function setUp() { ->addArgument('default'); $this->container ->register('keyvalue.database', 'Drupal\Core\KeyValueStore\KeyValueDatabaseFactory') - ->addArgument(new Reference('database')); + ->addArgument(new Reference('database')) + ->addArgument(new Reference('serialization.phpserialize')); + $this->container + ->register('serialization.phpserialize', 'Drupal\Core\Serialization\PhpSerialize'); $this->settingsSet('keyvalue_default', 'keyvalue.database'); } diff --git a/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/FileStorageTest.php b/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/FileStorageTest.php new file mode 100644 index 0000000..aaacef9 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/KeyValueStore/FileStorageTest.php @@ -0,0 +1,42 @@ + 'File storage', + 'description' => 'Tests the key-value file storage.', + 'group' => 'Key-value store', + ); + } + + protected function setUp() { + parent::setUp(); + + foreach ($this->collections as $collection) { + $path = $this->public_files_directory . '/' . $collection; + file_prepare_directory($path, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); + } + + $this->container + ->register('keyvalue.file', 'Drupal\Core\KeyValueStore\KeyValueFileFactory') + ->addArgument($this->public_files_directory) + ->addArgument(new Reference('serialization.phpserialize')); + $this->container + ->register('serialization.phpserialize', 'Drupal\Core\Serialization\PhpSerialize'); + $this->settingsSet('keyvalue_default', 'keyvalue.file'); + } + +} diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewTestData.php b/core/modules/views/lib/Drupal/views/Tests/ViewTestData.php index 1117bd8..5f2f23a 100644 --- a/core/modules/views/lib/Drupal/views/Tests/ViewTestData.php +++ b/core/modules/views/lib/Drupal/views/Tests/ViewTestData.php @@ -8,6 +8,7 @@ namespace Drupal\views\Tests; use Drupal\Core\Config\FileStorage; +use Drupal\Core\Serialization\Yaml; /** * Provides tests view data and the base test schema with sample data records. @@ -44,7 +45,7 @@ public static function createTestViews($class, array $modules) { continue; } - $file_storage = new FileStorage($config_dir); + $file_storage = new FileStorage('', new Yaml(), $config_dir); foreach ($file_storage->listAll('views.view.') as $config_name) { $id = str_replace('views.view.', '', $config_name); if (in_array($id, $views)) {