diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 81ecd11..2caf789 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -928,17 +928,17 @@ function theme_get_setting($setting_name, $theme = NULL) { } foreach ($theme_keys as $theme_key) { if (!empty($themes[$theme_key]->info['settings'])) { - $cache[$theme]->mergeData($themes[$theme_key]->info['settings']); + $cache[$theme]->merge($themes[$theme_key]->info['settings']); } } } // Get the global settings from configuration. - $cache[$theme]->mergeData(\Drupal::config('system.theme.global')->get()); + $cache[$theme]->merge(\Drupal::config('system.theme.global')->get()); if ($theme) { // Get the saved theme-specific settings from the configuration system. - $cache[$theme]->mergeData(\Drupal::config($theme . '.settings')->get()); + $cache[$theme]->merge(\Drupal::config($theme . '.settings')->get()); // If the theme does not support a particular feature, override the global // setting and set the value to NULL. diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php index e000200..8f3cf84 100644 --- a/core/lib/Drupal/Core/Config/Config.php +++ b/core/lib/Drupal/Core/Config/Config.php @@ -9,9 +9,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\String; -use Drupal\Core\Config\ConfigNameException; use Drupal\Core\Config\Schema\SchemaIncompleteException; -use Drupal\Core\DependencyInjection\DependencySerialization; use Drupal\Core\TypedData\PrimitiveInterface; use Drupal\Core\TypedData\Type\FloatInterface; use Drupal\Core\TypedData\Type\IntegerInterface; @@ -21,20 +19,7 @@ /** * Defines the default configuration object. */ -class Config extends DependencySerialization { - - /** - * The maximum length of a configuration object name. - * - * Many filesystems (including HFS, NTFS, and ext4) have a maximum file name - * length of 255 characters. To ensure that no configuration objects - * incompatible with this limitation are created, we enforce a maximum name - * length of 250 characters (leaving 5 characters for the file extension). - * - * @see http://en.wikipedia.org/wiki/Comparison_of_file_systems - */ - const MAX_NAME_LENGTH = 250; - +class Config extends StorableConfigBase { /** * An event dispatcher instance to use for configuration events. * @@ -45,39 +30,11 @@ class Config extends DependencySerialization { /** * The language object used to override configuration data. * - * @var Drupal\Core\Language\Language + * @var \Drupal\Core\Language\Language */ protected $language; /** - * The name of the configuration object. - * - * @var string - */ - protected $name; - - /** - * Whether the configuration object is new or has been saved to the storage. - * - * @var bool - */ - protected $isNew = TRUE; - - /** - * The data of the configuration object. - * - * @var array - */ - protected $data = array(); - - /** - * The original data of the configuration object. - * - * @var array - */ - protected $originalData = array(); - - /** * The current runtime data. * * The configuration data from storage merged with language, module and @@ -102,25 +59,11 @@ class Config extends DependencySerialization { protected $moduleOverrides; /** - * The storage used to load and save this configuration object. - * - * @var \Drupal\Core\Config\StorageInterface - */ - protected $storage; - - /** - * The config schema wrapper object for this configuration object. - * - * @var \Drupal\Core\Config\Schema\Element - */ - protected $schemaWrapper; - - /** - * The typed config manager. + * The current settings overrides. * - * @var \Drupal\Core\Config\TypedConfigManager + * @var array */ - protected $typedConfigManager; + protected $settingsOverrides; /** * Constructs a configuration object. @@ -146,111 +89,19 @@ public function __construct($name, StorageInterface $storage, EventDispatcherInt } /** - * Initializes a configuration object with pre-loaded data. - * - * @param array $data - * Array of loaded data for this configuration object. - * - * @return \Drupal\Core\Config\Config - * The configuration object. + * {@inheritdoc} */ public function initWithData(array $data) { + parent::initWithData($data); $this->settingsOverrides = array(); $this->languageOverrides = array(); $this->moduleOverrides = array(); - $this->isNew = FALSE; - $this->replaceData($data); - $this->originalData = $this->data; - return $this; - } - - /** - * Returns the name of this configuration object. - * - * @return string - * The name of the configuration object. - */ - public function getName() { - return $this->name; - } - - /** - * Sets the name of this configuration object. - * - * @param string $name - * The name of the configuration object. - * - * @return \Drupal\Core\Config\Config - * The configuration object. - */ - public function setName($name) { - $this->name = $name; + $this->setData($data); return $this; } /** - * Validates the configuration object name. - * - * @param string $name - * The name of the configuration object. - * - * @throws \Drupal\Core\Config\ConfigNameException - * - * @see Config::MAX_NAME_LENGTH - */ - public static function validateName($name) { - // The name must be namespaced by owner. - if (strpos($name, '.') === FALSE) { - throw new ConfigNameException(format_string('Missing namespace in Config object name @name.', array( - '@name' => $name, - ))); - } - // The name must be shorter than Config::MAX_NAME_LENGTH characters. - if (strlen($name) > self::MAX_NAME_LENGTH) { - throw new ConfigNameException(format_string('Config object name @name exceeds maximum allowed length of @length characters.', array( - '@name' => $name, - '@length' => self::MAX_NAME_LENGTH, - ))); - } - - // The name must not contain any of the following characters: - // : ? * < > " ' / \ - if (preg_match('/[:?*<>"\'\/\\\\]/', $name)) { - throw new ConfigNameException(format_string('Invalid character in Config object name @name.', array( - '@name' => $name, - ))); - } - } - - /** - * Returns whether this configuration object is new. - * - * @return bool - * TRUE if this configuration object does not exist in storage. - */ - public function isNew() { - return $this->isNew; - } - - /** - * Gets data from this configuration object. - * - * @param string $key - * A string that maps to a key within the configuration data. - * For instance in the following configuration array: - * @code - * array( - * 'foo' => array( - * 'bar' => 'baz', - * ), - * ); - * @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. - * - * @return mixed - * The data that was requested. + * {@inheritdoc} */ public function get($key = '') { if (!isset($this->overriddenData)) { @@ -272,33 +123,9 @@ public function get($key = '') { } /** - * Replaces the data of this configuration object. - * - * @param array $data - * The new configuration data. - * - * @return \Drupal\Core\Config\Config - * The configuration object. + * {@inheritdoc} */ public function setData(array $data) { - $this->replaceData($data); - return $this; - } - - /** - * Replaces the data of this configuration object. - * - * This function is separate from setData() to avoid load() state tracking. - * A load() would destroy the replaced data (for example on import). Do not - * call set() when inside load(). - * - * @param array $data - * The new configuration data. - * - * @return \Drupal\Core\Config\Config - * The configuration object. - */ - protected function replaceData(array $data) { $this->data = $data; $this->resetOverriddenData(); return $this; @@ -391,56 +218,23 @@ protected function resetOverriddenData() { } /** - * Sets a value in this configuration object. - * - * @param string $key - * Identifier to store value in configuration. - * @param mixed $value - * Value to associate with identifier. - * - * @return \Drupal\Core\Config\Config - * The configuration object. + * {@inheritdoc} */ public function set($key, $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 { - NestedArray::setValue($this->data, $parts, $value); - } - $this->resetOverriddenData(); - return $this; + parent::set($key, $value); + return $this->resetOverriddenData(); } /** - * Unsets a value in this configuration object. - * - * @param string $key - * Name of the key whose value should be unset. - * - * @return \Drupal\Core\Config\Config - * The configuration object. + * {@inheritdoc} */ public function clear($key) { - $parts = explode('.', $key); - if (count($parts) == 1) { - unset($this->data[$key]); - } - else { - NestedArray::unsetValue($this->data, $parts); - } - $this->resetOverriddenData(); - return $this; + parent::clear($key); + return $this->resetOverriddenData(); } /** - * Saves the configuration object. - * - * @return \Drupal\Core\Config\Config - * The configuration object. + * {@inheritdoc} */ public function save() { // Validate the configuration object name before saving. @@ -501,7 +295,7 @@ public function getStorage() { */ public function merge(array $data_to_merge) { // Preserve integer keys so that configuration keys are not changed. - $this->replaceData(NestedArray::mergeDeepArray(array($this->data, $data_to_merge), TRUE)); + $this->setData(NestedArray::mergeDeepArray(array($this->data, $data_to_merge), TRUE)); return $this; } @@ -651,4 +445,3 @@ public function getOriginal($key = '', $apply_overrides = TRUE) { } } } - diff --git a/core/lib/Drupal/Core/Config/ConfigBase.php b/core/lib/Drupal/Core/Config/ConfigBase.php new file mode 100644 index 0000000..7d12d51 --- /dev/null +++ b/core/lib/Drupal/Core/Config/ConfigBase.php @@ -0,0 +1,209 @@ +name; + } + + /** + * Sets the name of this configuration object. + * + * @param string $name + * The name of the configuration object. + * + * @return $this + * The configuration object. + */ + public function setName($name) { + $this->name = $name; + return $this; + } + + /** + * Validates the configuration object name. + * + * @param string $name + * The name of the configuration object. + * + * @throws \Drupal\Core\Config\ConfigNameException + * + * @see Config::MAX_NAME_LENGTH + */ + public static function validateName($name) { + // The name must be namespaced by owner. + if (strpos($name, '.') === FALSE) { + throw new ConfigNameException(String::format('Missing namespace in Config object name @name.', array( + '@name' => $name, + ))); + } + // The name must be shorter than Config::MAX_NAME_LENGTH characters. + if (strlen($name) > self::MAX_NAME_LENGTH) { + throw new ConfigNameException(String::format('Config object name @name exceeds maximum allowed length of @length characters.', array( + '@name' => $name, + '@length' => self::MAX_NAME_LENGTH, + ))); + } + + // The name must not contain any of the following characters: + // : ? * < > " ' / \ + if (preg_match('/[:?*<>"\'\/\\\\]/', $name)) { + throw new ConfigNameException(String::format('Invalid character in Config object name @name.', array( + '@name' => $name, + ))); + } + } + + /** + * Gets data from this configuration object. + * + * @param string $key + * A string that maps to a key within the configuration data. + * For instance in the following configuration array: + * @code + * array( + * 'foo' => array( + * 'bar' => 'baz', + * ), + * ); + * @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. + * + * @return mixed + * The data that was requested. + */ + public function get($key = '') { + if (empty($key)) { + return $this->data; + } + else { + $parts = explode('.', $key); + if (count($parts) == 1) { + return isset($this->data[$key]) ? $this->data[$key] : NULL; + } + else { + $value = NestedArray::getValue($this->data, $parts, $key_exists); + return $key_exists ? $value : NULL; + } + } + } + + /** + * Replaces the data of this configuration object. + * + * @param array $data + * The new configuration data. + * + * @return $this + * The configuration object. + */ + public function setData(array $data) { + $this->data = $data; + return $this; + } + + /** + * Sets a value in this configuration object. + * + * @param string $key + * Identifier to store value in configuration. + * @param mixed $value + * Value to associate with identifier. + * + * @return $this + * The configuration object. + */ + public function set($key, $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 { + NestedArray::setValue($this->data, $parts, $value); + } + return $this; + } + + /** + * Unsets a value in this configuration object. + * + * @param string $key + * Name of the key whose value should be unset. + * + * @return $this + * The configuration object. + */ + public function clear($key) { + $parts = explode('.', $key); + if (count($parts) == 1) { + unset($this->data[$key]); + } + else { + NestedArray::unsetValue($this->data, $parts); + } + return $this; + } + + /** + * Merges data into a configuration object. + * + * @param array $data_to_merge + * An array containing data to merge. + * + * @return $this + * The configuration object. + */ + public function merge(array $data_to_merge) { + return $this->setData(NestedArray::mergeDeepArray(array($this->data, $data_to_merge), TRUE)); + } +} diff --git a/core/lib/Drupal/Core/Config/StorableConfigBase.php b/core/lib/Drupal/Core/Config/StorableConfigBase.php new file mode 100644 index 0000000..8b05f95 --- /dev/null +++ b/core/lib/Drupal/Core/Config/StorableConfigBase.php @@ -0,0 +1,185 @@ +isNew = FALSE; + $this->setData($data); + $this->originalData = $this->data; + return $this; + } + + /** + * Returns whether this configuration object is new. + * + * @return bool + * TRUE if this configuration object does not exist in storage. + */ + public function isNew() { + return $this->isNew; + } + + /** + * Retrieves the storage used to load and save this configuration object. + * + * @return \Drupal\Core\Config\StorageInterface + * The configuration storage object. + */ + public function getStorage() { + return $this->storage; + } + + /** + * Gets the schema wrapper for the whole configuration object. + * + * The schema wrapper is dependent on the configuration name and the whole + * data structure, so if the name or the data changes in any way, the wrapper + * should be reset. + * + * @return \Drupal\Core\Config\Schema\Element + */ + protected function getSchemaWrapper() { + if (!isset($this->schemaWrapper)) { + $definition = $this->typedConfigManager->getDefinition($this->name); + $this->schemaWrapper = $this->typedConfigManager->create($definition, $this->data); + } + return $this->schemaWrapper; + } + + /** + * Casts the value to correct data type using the configuration schema. + * + * @param string $key + * A string that maps to a key within the configuration data. + * @param string $value + * Value to associate with the key. + * + * @return mixed + * The value cast to the type indicated in the schema. + * + * @throws \Drupal\Core\Config\UnsupportedDataTypeConfigException + * Exception on unsupported/undefined data type deducted. + */ + protected function castValue($key, $value) { + if ($value === NULL) { + $value = NULL; + } + elseif (is_scalar($value)) { + try { + $element = $this->getSchemaWrapper()->get($key); + if ($element instanceof PrimitiveInterface) { + // Special handling for integers and floats since the configuration + // system is primarily concerned with saving values from the Form API + // we have to special case the meaning of an empty string for numeric + // types. In PHP this would be casted to a 0 but for the purposes of + // configuration we need to treat this as a NULL. + if ($value === '' && ($element instanceof IntegerInterface || $element instanceof FloatInterface)) { + $value = NULL; + } + else { + $value = $element->getCastedValue(); + } + } + else { + // Config only supports primitive data types. If the config schema + // does define a type $element will be an instance of + // \Drupal\Core\Config\Schema\Property. Convert it to string since it + // is the safest possible type. + $value = $element->getString(); + } + } + catch (SchemaIncompleteException $e) { + // @todo throw an exception due to an incomplete schema. + // Fix as part of https://drupal.org/node/2183983. + } + } + 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( + '@name' => $this->getName(), + '@key' => $key, + ))); + } + // Recurse into any nested keys. + foreach ($value as $nested_value_key => $nested_value) { + $value[$nested_value_key] = $this->castValue($key . '.' . $nested_value_key, $nested_value); + } + } + return $value; + } + +} diff --git a/core/lib/Drupal/Core/Theme/ThemeSettings.php b/core/lib/Drupal/Core/Theme/ThemeSettings.php index d20b843..effcc4c 100644 --- a/core/lib/Drupal/Core/Theme/ThemeSettings.php +++ b/core/lib/Drupal/Core/Theme/ThemeSettings.php @@ -7,154 +7,37 @@ namespace Drupal\Core\Theme; -use Drupal\Component\Utility\NestedArray; +use Drupal\Core\Config\ConfigBase; /** * Defines the default theme settings object. */ -class ThemeSettings { +class ThemeSettings extends ConfigBase { /** - * The theme of the theme settings object. + * The name of the theme settings object. * * @var string */ protected $theme; /** - * The data of the theme settings object. - * - * @var array - */ - protected $data; - - /** * Constructs a theme settings object. * - * @param string $name + * @param string $theme * The name of the theme settings object being constructed. */ public function __construct($theme) { $this->theme = $theme; - $this->data = array(); } /** - * Returns the theme of this theme settings object. + * Returns the name of this theme settings object. * * @return string - * The theme of this theme settings object. + * The name of this theme settings object. */ public function getTheme() { return $this->theme; } - - /** - * Gets data from this theme settings object. - * - * @param string $key - * A string that maps to a key within the theme settings data. - * For instance in the following theme settings array: - * @code - * array( - * 'foo' => array( - * 'bar' => 'baz', - * ), - * ); - * @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. - * - * - * @return mixed - * The data that was requested. - */ - public function get($key = '') { - if (empty($key)) { - return $this->data; - } - else { - $parts = explode('.', $key); - if (count($parts) == 1) { - return isset($this->data[$key]) ? $this->data[$key] : NULL; - } - else { - $value = NestedArray::getValue($this->data, $parts, $key_exists); - return $key_exists ? $value : NULL; - } - } - } - - /** - * Replaces the data of this theme settings object. - * - * @param array $data - * The new theme settings data. - * - * @return \Drupal\Core\Theme\ThemeSettings - * The theme settings object. - */ - public function setData(array $data) { - $this->data = $data; - return $this; - } - - /** - * Sets value in this theme settings object. - * - * @param string $key - * Identifier to store value in theme settings. - * @param string $value - * Value to associate with identifier. - * - * @return \Drupal\Core\Theme\ThemeSettings - * The theme settings object. - */ - public function set($key, $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 { - NestedArray::setValue($this->data, $parts, $value); - } - return $this; - } - - /** - * Unsets value in this theme settings object. - * - * @param string $key - * Name of the key whose value should be unset. - * - * @return \Drupal\Core\Theme\ThemeSettings - * The theme settings object. - */ - public function clear($key) { - $parts = explode('.', $key); - if (count($parts) == 1) { - unset($this->data[$key]); - } - else { - NestedArray::unsetValue($this->data, $parts); - } - return $this; - } - - /** - * Merges the data into this theme settings object. - * - * @param array $data - * Theme settings data to merge. - * - * @return \Drupal\Core\Theme\ThemeSettings - * The theme settings object. - */ - public function mergeData ($data) { - $this->data = NestedArray::mergeDeep($this->data, $data); - return $this; - } }