diff --git a/core/includes/config.inc b/core/includes/config.inc index 8d0eea1..59b4364 100644 --- a/core/includes/config.inc +++ b/core/includes/config.inc @@ -38,8 +38,7 @@ function config_get_config_directory() { */ function config_install_default_config($module) { $module_config_dir = drupal_get_path('module', $module) . '/config'; - $drupal_config_dir = config_get_config_directory(); - if (is_dir(drupal_get_path('module', $module) . '/config')) { + if (is_dir($module_config_dir)) { $files = glob($module_config_dir . '/*.' . FileStorage::getFileExtension()); foreach ($files as $key => $file) { // Load config data into the active store and write it out to the @@ -47,10 +46,9 @@ function config_install_default_config($module) { // needs to be the same as the file name WITHOUT the extension. $config_name = basename($file, '.' . FileStorage::getFileExtension()); - $database_storage = new DatabaseStorage($config_name); - $file_storage = new FileStorage($config_name); - $file_storage->setPath($module_config_dir); - $database_storage->write($file_storage->read()); + $database_storage = new DatabaseStorage(); + $file_storage = new FileStorage(array('directory' => $module_config_dir)); + $database_storage->write($config_name, $file_storage->read($config_name)); } } } @@ -80,9 +78,30 @@ function config_get_storage_names_with_prefix($prefix = '') { * @return * An instance of the class specified in the $class parameter. * - * @todo Replace this with an appropriate factory / ability to inject in - * alternate storage engines.. + * @todo Replace with DI container. */ function config($name, $class = 'Drupal\Core\Config\DrupalConfig') { - return new $class(new DatabaseStorage($name)); + global $config_info; + static $instances = array(); + + if (!isset($config_info)) { + $config_info = array( + 'Drupal\Core\Config\DatabaseStorage' => array( + 'target' => 'default', + 'read' => TRUE, + 'write' => TRUE, + ), + 'Drupal\Core\Config\FileStorage' => array( + 'directory' => config_get_config_directory(), + 'read' => TRUE, + 'write' => TRUE, + ), + ); + } + + if (!isset($instances[$class])) { + $instances[$class] = new $class($config_info); + } + + return $instances[$class]->load($name); } diff --git a/core/includes/update.inc b/core/includes/update.inc index f711507..a46f125 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -902,9 +902,8 @@ function update_variables_to_config($config_name, array $variable_map) { // Load and set default configuration values. // Throws a FileStorageReadException if there is no default configuration // file, which is required to exist. - $file = new FileStorage($config_name); - $file->setPath(drupal_get_path('module', $module) . '/config'); - $default_data = $file->read(); + $file = new FileStorage(array('directory' => drupal_get_path('module', $module) . '/config')); + $default_data = $file->read($config_name); // Merge any possibly existing original data into default values. // Only relevant when being called repetitively on the same config object. diff --git a/core/lib/Drupal/Core/Config/DrupalConfig.php b/core/lib/Drupal/Core/Config/ConfigObject.php similarity index 75% copy from core/lib/Drupal/Core/Config/DrupalConfig.php copy to core/lib/Drupal/Core/Config/ConfigObject.php index f5a9220..cb5aee5 100644 --- a/core/lib/Drupal/Core/Config/DrupalConfig.php +++ b/core/lib/Drupal/Core/Config/ConfigObject.php @@ -2,20 +2,19 @@ namespace Drupal\Core\Config; -use Drupal\Core\Config\StorageInterface; -use Drupal\Core\Config\ConfigException; +use Drupal\Core\Config\DrupalConfig; /** - * Represents the default configuration storage object. + * Defines the default configuration object. */ -class DrupalConfig { +class ConfigObject { /** - * The storage engine to save this config object to. + * The name of the configuration object. * - * @var StorageInterface + * @var string */ - protected $storage; + protected $name; /** * The data of the configuration object. @@ -25,38 +24,38 @@ class DrupalConfig { protected $data = array(); /** - * Constructs a DrupalConfig object. + * The wrapping configuration manager object. * - * @param StorageInterface $storage - * The storage engine where this config object should be saved. + * @var Drupal\Core\Config\DrupalConfig + */ + protected $drupalConfig; + + /** + * Constructs a configuration object. * - * @todo $this should really know about $name and make it publicly accessible. + * @param string $name + * The name of the configuration object. + * @param Drupal\Core\Config\DrupalConfig $drupalConfig + * The wrapping configuration manager object. */ - public function __construct(StorageInterface $storage) { - $this->storage = $storage; - $this->read(); + public function __construct($name, DrupalConfig $drupalConfig) { + $this->setName($name); + $this->drupalConfig = $drupalConfig; } /** - * Reads config data from the active store into our object. + * Returns the name of this configuration object. */ - public function read() { - $data = $this->storage->read(); - $this->setData($data !== FALSE ? $data : array()); - return $this; + public function getName() { + return $this->name; } /** - * Checks whether a particular value is overridden. - * - * @param $key - * @todo - * - * @return - * @todo + * Sets the name of this configuration object. */ - public function isOverridden($key) { - return isset($this->_overrides[$key]); + public function setName($name) { + $this->name = $name; + return $this; } /** @@ -89,7 +88,7 @@ class DrupalConfig { public function get($key = '') { global $conf; - $name = $this->storage->getName(); + $name = $this->getName(); if (isset($conf[$name])) { $merged_data = drupal_array_merge_deep($this->data, $conf[$name]); } @@ -201,13 +200,26 @@ class DrupalConfig { else { drupal_array_unset_nested_value($this->data, $parts); } + return $this; + } + + /** + * Loads configuration data into this object. + */ + public function load() { + $storage = $this->drupalConfig->selectStorage('read', $this->name); + $data = $storage->read($this->name); + $data = ($data !== FALSE ? $data : array()); + $this->setData($data); + return $this; } /** * Saves the configuration object. */ public function save() { - $this->storage->write($this->data); + $this->drupalConfig->save($this->name, $this->data); + return $this; } /** @@ -215,6 +227,7 @@ class DrupalConfig { */ public function delete() { $this->data = array(); - $this->storage->delete(); + $this->drupalConfig->delete($this->name); + return $this; } } diff --git a/core/lib/Drupal/Core/Config/DatabaseStorage.php b/core/lib/Drupal/Core/Config/DatabaseStorage.php index c736245..f2e5aca 100644 --- a/core/lib/Drupal/Core/Config/DatabaseStorage.php +++ b/core/lib/Drupal/Core/Config/DatabaseStorage.php @@ -2,25 +2,45 @@ namespace Drupal\Core\Config; -use Drupal\Core\Config\StorageBase; +use Drupal\Core\Config\StorageInterface; +use Drupal\Core\Database\Database; use Exception; /** - * Represents an SQL-based configuration storage object. + * Defines the Database storage controller. */ -class DatabaseStorage extends StorageBase { +class DatabaseStorage implements StorageInterface { + + protected $options; + + /** + * Implements StorageInterface::__construct(). + */ + public function __construct(array $info = array()) { + $info += array( + 'target' => 'default', + ); + $this->options = $info; + } + + /** + * Returns the database connection to use. + */ + protected function getConnection() { + return Database::getConnection($this->options['target']); + } /** * Implements StorageInterface::read(). */ - public function read() { + public function read($name) { // 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. $data = array(); try { - $raw = db_query('SELECT data FROM {config} WHERE name = :name', array(':name' => $this->name))->fetchField(); + $raw = $this->getConnection()->query('SELECT data FROM {config} WHERE name = :name', array(':name' => $name), $this->options)->fetchField(); if ($raw !== FALSE) { $data = $this->decode($raw); } @@ -31,22 +51,22 @@ class DatabaseStorage extends StorageBase { } /** - * Implements StorageInterface::writeToActive(). + * Implements StorageInterface::write(). */ - public function writeToActive($data) { + public function write($name, array $data) { $data = $this->encode($data); - return db_merge('config') - ->key(array('name' => $this->name)) + return $this->getConnection()->merge('config', $this->options) + ->key(array('name' => $name)) ->fields(array('data' => $data)) ->execute(); } /** - * @todo + * Implements StorageInterface::delete(). */ - public function deleteFromActive() { - db_delete('config') - ->condition('name', $this->name) + public function delete($name) { + $this->getConnection()->delete('config', $this->options) + ->condition('name', $name) ->execute(); } @@ -68,6 +88,9 @@ class DatabaseStorage extends StorageBase { * Implements StorageInterface::getNamesWithPrefix(). */ static public function getNamesWithPrefix($prefix = '') { - return db_query('SELECT name FROM {config} WHERE name LIKE :name', array(':name' => db_like($prefix) . '%'))->fetchCol(); + $prefix = $this->getConnection()->escapeLike($prefix) . '%'; + return $this->getConnection() + ->query('SELECT name FROM {config} WHERE name LIKE :name', array(':name' => $prefix), $this->options) + ->fetchCol(); } } diff --git a/core/lib/Drupal/Core/Config/DrupalConfig.php b/core/lib/Drupal/Core/Config/DrupalConfig.php index f5a9220..d10031d 100644 --- a/core/lib/Drupal/Core/Config/DrupalConfig.php +++ b/core/lib/Drupal/Core/Config/DrupalConfig.php @@ -2,219 +2,108 @@ namespace Drupal\Core\Config; -use Drupal\Core\Config\StorageInterface; -use Drupal\Core\Config\ConfigException; - /** - * Represents the default configuration storage object. + * Defines the default configuration manager. */ class DrupalConfig { /** - * The storage engine to save this config object to. - * - * @var StorageInterface - */ - protected $storage; - - /** - * The data of the configuration object. + * Information about available storage controllers. * * @var array */ - protected $data = array(); + protected $storageInfo; /** - * Constructs a DrupalConfig object. + * Instantiated storage controller objects. * - * @param StorageInterface $storage - * The storage engine where this config object should be saved. + * @see Drupal\Core\Config\StorageInterface * - * @todo $this should really know about $name and make it publicly accessible. - */ - public function __construct(StorageInterface $storage) { - $this->storage = $storage; - $this->read(); - } - - /** - * Reads config data from the active store into our object. + * @var array */ - public function read() { - $data = $this->storage->read(); - $this->setData($data !== FALSE ? $data : array()); - return $this; - } + protected $storageInstances; /** - * Checks whether a particular value is overridden. - * - * @param $key - * @todo + * Config Objects. * - * @return - * @todo + * @var array */ - public function isOverridden($key) { - return isset($this->_overrides[$key]); - } + protected $configObjects = array(); /** - * Gets data from this config object. + * Constructs a DrupalConfig object. * - * @param $key - * A string that maps to a key within the configuration data. - * For instance in the following configuation array: + * @param array $storage_info + * An associative array defining the storage controllers to use and any + * required configuration for them; e.g.: * @code * array( - * 'foo' => array( - * 'bar' => 'baz', + * 'Drupal\Core\Config\DatabaseStorage' => array( + * 'target' => 'default', + * 'read' => TRUE, + * 'write' => TRUE, * ), - * ); + * 'Drupal\Core\Config\FileStorage' => array( + * 'directory' => 'sites/default/files/config', + * 'read' => TRUE, + * 'write' => FALSE, + * ), + * ) * @endcode - * A key of 'foo.bar' would return the string 'baz'. However, a key of 'foo' - * would return array('bar' => 'baz'). - * If no key is specified, then the entire data array is returned. - * - * The configuration system does not retain data types. Every saved value is - * casted to a string. In most cases this is not an issue; however, it can - * cause issues with Booleans, which are casted to "1" (TRUE) or "0" (FALSE). - * In particular, code relying on === or !== will no longer function properly. - * - * @see http://php.net/manual/language.operators.comparison.php. - * - * @return - * The data that was requested. */ - public function get($key = '') { - global $conf; - - $name = $this->storage->getName(); - if (isset($conf[$name])) { - $merged_data = drupal_array_merge_deep($this->data, $conf[$name]); - } - else { - $merged_data = $this->data; - } - - if (empty($key)) { - return $merged_data; - } - else { - $parts = explode('.', $key); - if (count($parts) == 1) { - return isset($merged_data[$key]) ? $merged_data[$key] : NULL; - } - else { - $key_exists = NULL; - $value = drupal_array_get_nested_value($merged_data, $parts, $key_exists); - return $key_exists ? $value : NULL; - } - } + public function __construct($storage_info) { + $this->storageInfo = $storage_info; } /** - * Replaces the data of this configuration object. - * - * @param array $data - * The new configuration data. + * Loads a configuration object. */ - public function setData(array $data) { - $this->data = $data; - return $this; + public function load($name) { + $storage = $this->selectStorage('read', $name); + $data = $storage->read($name); + $data = ($data !== FALSE ? $data : array()); + if (!isset($this->configObjects[$name])) { + $this->configObjects[$name] = new ConfigObject($name, $this); + } + $this->configObjects[$name]->setData($data); + return $this->configObjects[$name]; } /** - * Sets value in this config object. - * - * @param $key - * @todo - * @param $value - * @todo + * Saves a configuration object. */ - public function set($key, $value) { - // Type-cast value into a string. - $value = $this->castValue($value); - - // The dot/period is a reserved character; it may appear between keys, but - // not within keys. - $parts = explode('.', $key); - if (count($parts) == 1) { - $this->data[$key] = $value; - } - else { - drupal_array_set_nested_value($this->data, $parts, $value); - } - return $this; + public function save($name, $data) { + $storage = $this->selectStorage('write', $name); + $storage->write($name, $data); } /** - * Casts a saved value to a string. - * - * The configuration system only saves strings or arrays. Any scalar - * non-string value is cast to a string. The one exception is boolean FALSE - * which would normally become '' when cast to a string, but is manually - * cast to '0' here for convenience and consistency. - * - * Any non-scalar value that is not an array (aka objects) gets cast - * to an array. - * - * @param $value - * A value being saved into the configuration system. - * @param $value - * The value cast to a string or array. + * Deletes a configuration object. */ - public function castValue($value) { - if (is_scalar($value)) { - // Handle special case of FALSE, which should be '0' instead of ''. - if ($value === FALSE) { - $value = '0'; - } - else { - $value = (string) $value; - } - } - else { - // Any non-scalar value must be an array. - if (!is_array($value)) { - $value = (array) $value; - } - // Recurse into any nested keys. - foreach ($value as $key => $nested_value) { - $value[$key] = $this->castValue($nested_value); - } - } - return $value; + public function delete($name) { + $storage = $this->selectStorage('write', $name); + $storage->delete($name); } /** - * Unsets value in this config object. - * - * @param $key - * Name of the key whose value should be unset. + * Selects a storage controller to use for a given operation. */ - public function clear($key) { - $parts = explode('.', $key); - if (count($parts) == 1) { - unset($this->data[$key]); - } - else { - drupal_array_unset_nested_value($this->data, $parts); + public function selectStorage($op, $name) { + foreach ($this->storageInfo as $class => $storage_config) { + // Take the first storage that allows $op. + if (!empty($storage_config[$op])) { + break; + } } - } + // @todo Bail out if no storage is available. - /** - * Saves the configuration object. - */ - public function save() { - $this->storage->write($this->data); + if (!isset($this->storageInstances[$class])) { + $this->storageInstances[$class] = new $class($storage_config); + } + return $this->storageInstances[$class]; } /** - * Deletes the configuration object. + * More site-level stuff here, like config sync... */ - public function delete() { - $this->data = array(); - $this->storage->delete(); - } } diff --git a/core/lib/Drupal/Core/Config/FileStorage.php b/core/lib/Drupal/Core/Config/FileStorage.php index 2a6d448..9c1fc07 100644 --- a/core/lib/Drupal/Core/Config/FileStorage.php +++ b/core/lib/Drupal/Core/Config/FileStorage.php @@ -2,58 +2,31 @@ namespace Drupal\Core\Config; +use Drupal\Core\Config\StorageInterface; use Symfony\Component\Yaml\Yaml; /** - * Represents the file storage controller. - * - * @todo Implement StorageInterface after removing DrupalConfig methods. - * @todo Consider to extend StorageBase. + * Defines the file storage controller. */ -class FileStorage { +class FileStorage implements StorageInterface { /** - * The name of the configuration object. + * Configuration options for this storage controller. * - * @var string - */ - protected $name; - - /** - * The filesystem path containing the configuration object. + * - directory: The filesystem path for configuration objects. * - * @var string + * @var array */ - protected $path; + protected $info; /** * Implements StorageInterface::__construct(). */ - public function __construct($name = NULL) { - $this->name = $name; - } - - /** - * Returns the path containing the configuration file. - * - * @return string - * The relative path to the configuration object. - */ - public function getPath() { - // If the path has not been set yet, retrieve and assign the default path - // for configuration files. - if (!isset($this->path)) { - $this->setPath(config_get_config_directory()); + public function __construct(array $info = array()) { + if (!isset($info['directory'])) { + $info['directory'] = config_get_config_directory(); } - return $this->path; - } - - /** - * Sets the path containing the configuration file. - */ - public function setPath($directory) { - $this->path = $directory; - return $this; + $this->info = $info; } /** @@ -62,8 +35,8 @@ class FileStorage { * @return string * The path to the configuration file. */ - public function getFilePath() { - return $this->getPath() . '/' . $this->getName() . '.' . self::getFileExtension(); + public function getFilePath($name) { + return $this->info['directory'] . '/' . $name . '.' . self::getFileExtension(); } /** @@ -82,8 +55,8 @@ class FileStorage { * @return bool * TRUE if the configuration file exists, FALSE otherwise. */ - protected function exists() { - return file_exists($this->getFilePath()); + public function exists($name) { + return file_exists($this->getFilePath($name)); } /** @@ -91,10 +64,10 @@ class FileStorage { * * @throws FileStorageException */ - public function write($data) { + public function write($name, array $data) { $data = $this->encode($data); - if (!file_put_contents($this->getFilePath(), $data)) { - throw new FileStorageException('Failed to write configuration file: ' . $this->getFilePath()); + if (!file_put_contents($this->getFilePath($name), $data)) { + throw new FileStorageException('Failed to write configuration file: ' . $this->getFilePath($name)); } } @@ -103,15 +76,15 @@ class FileStorage { * * @throws FileStorageReadException */ - public function read() { - if (!$this->exists()) { - throw new FileStorageReadException("Configuration file '$this->name' does not exist."); + public function read($name) { + if (!$this->exists($name)) { + throw new FileStorageReadException("Configuration file '$name' does not exist."); } - $data = file_get_contents($this->getFilePath()); + $data = file_get_contents($this->getFilePath($name)); $data = $this->decode($data); if ($data === FALSE) { - throw new FileStorageReadException("Failed to decode configuration file '$this->name'."); + throw new FileStorageReadException("Failed to decode configuration file '$name'."); } return $data; } @@ -119,9 +92,9 @@ class FileStorage { /** * Deletes a configuration file. */ - public function delete() { + public function delete($name) { // Needs error handling and etc. - @drupal_unlink($this->getFilePath()); + @drupal_unlink($this->getFilePath($name)); } /** @@ -144,25 +117,11 @@ class FileStorage { } /** - * Implements StorageInterface::getName(). - */ - public function getName() { - return $this->name; - } - - /** - * Implements StorageInterface::setName(). - */ - public function setName($name) { - $this->name = $name; - } - - /** * Implements StorageInterface::getNamesWithPrefix(). + * + * @todo Allow to search for files in custom paths. */ public static function getNamesWithPrefix($prefix = '') { - // @todo Use $this->getPath() to allow for contextual search of files in - // custom paths. $files = glob(config_get_config_directory() . '/' . $prefix . '*.' . FileStorage::getFileExtension()); $clean_name = function ($value) { return basename($value, '.' . FileStorage::getFileExtension()); diff --git a/core/lib/Drupal/Core/Config/StorageBase.php b/core/lib/Drupal/Core/Config/StorageBase.php deleted file mode 100644 index b03ff27..0000000 --- a/core/lib/Drupal/Core/Config/StorageBase.php +++ /dev/null @@ -1,121 +0,0 @@ -name = $name; - } - - /** - * Instantiates a new file storage object or returns the existing one. - * - * @return Drupal\Core\Config\FileStorage - * The file object for this configuration object. - */ - protected function fileStorage() { - if (!isset($this->fileStorage)) { - $this->fileStorage = new FileStorage($this->name); - } - return $this->fileStorage; - } - - /** - * Implements StorageInterface::copyToFile(). - */ - public function copyToFile() { - return $this->writeToFile($this->read()); - } - - /** - * Implements StorageInterface::deleteFile(). - */ - public function deleteFile() { - return $this->fileStorage()->delete(); - } - - /** - * Implements StorageInterface::copyFromFile(). - */ - public function copyFromFile() { - return $this->writeToActive($this->readFromFile()); - } - - /** - * @todo - * - * @return - * @todo - */ - public function readFromFile() { - return $this->fileStorage()->read($this->name); - } - - /** - * Implements StorageInterface::isOutOfSync(). - */ - public function isOutOfSync() { - return $this->read() !== $this->readFromFile(); - } - - /** - * Implements StorageInterface::write(). - */ - public function write($data) { - $this->writeToActive($data); - $this->writeToFile($data); - } - - /** - * Implements StorageInterface::writeToFile(). - */ - public function writeToFile($data) { - return $this->fileStorage()->write($data); - } - - /** - * Implements StorageInterface::delete(). - */ - public function delete() { - $this->deleteFromActive(); - $this->deleteFile(); - } - - /** - * Implements StorageInterface::getName(). - */ - public function getName() { - return $this->name; - } - - /** - * Implements StorageInterface::setName(). - */ - public function setName($name) { - $this->name = $name; - } -} diff --git a/core/lib/Drupal/Core/Config/StorageInterface.php b/core/lib/Drupal/Core/Config/StorageInterface.php index 43141a5..e3ed848 100644 --- a/core/lib/Drupal/Core/Config/StorageInterface.php +++ b/core/lib/Drupal/Core/Config/StorageInterface.php @@ -7,74 +7,43 @@ namespace Drupal\Core\Config; * * Classes implementing this interface allow reading and writing configuration * data from and to the storage. - * - * @todo Remove all active/file methods. They belong onto DrupalConfig only. */ interface StorageInterface { /** - * Constructs a storage manipulation class. + * Constructs the storage controller. * - * @param string $name - * (optional) The name of a configuration object to load. - */ - function __construct($name = NULL); - - /** - * Reads the configuration data from the storage. - */ - function read(); - - /** - * Copies the configuration data from the storage into a file. + * @param array $info + * An associative array containing configuration options specific to the + * storage controller. */ - function copyToFile(); + public function __construct(array $info = array()); /** - * Copies the configuration data from the file into the storage. - */ - function copyFromFile(); - - /** - * Deletes the configuration data file. - */ - function deleteFile(); - - /** - * Checks whether the file and the storage is in sync. + * Reads configuration data from the storage. * - * @return - * TRUE if the file and the storage contains the same data, FALSE - * if not. + * @param string $name + * The name of a configuration object to load. */ - function isOutOfSync(); + public function read($name); /** - * Writes the configuration data into the active storage and the file. + * Writes configuration data to the storage. * - * @param $data + * @param string $name + * The name of a configuration object to save. + * @param array $data * The configuration data to write. */ - function write($data); - - /** - * Writes the configuration data into the active storage but not the file. - * - * Use this function if you need to make temporary changes to your - * configuration. - * - * @param $data - * The configuration data to write into active storage. - */ - function writeToActive($data); + public function write($name, array $data); /** - * Writes the configuration data into the file. + * Deletes a configuration object from the storage. * - * @param $data - * The configuration data to write into the file. + * @param string $name + * The name of a configuration object to delete. */ - function writeToFile($data); + public function delete($name); /** * Encodes configuration data into the storage-specific format. @@ -105,16 +74,6 @@ interface StorageInterface { public static function decode($raw); /** - * Gets the name of this object. - */ - public function getName(); - - /** - * Sets the name of this object. - */ - public function setName($name); - - /** * Gets configuration object names starting with a given prefix. * * Given the following configuration objects: