3 namespace Drupal\Core\Config;
5 use Drupal\Core\Config\Schema\Ignore;
6 use Drupal\Core\Config\Schema\Sequence;
7 use Drupal\Core\Config\Schema\SequenceDataDefinition;
8 use Drupal\Core\TypedData\PrimitiveInterface;
9 use Drupal\Core\TypedData\Type\FloatInterface;
10 use Drupal\Core\TypedData\Type\IntegerInterface;
11 use Drupal\Core\Config\Schema\Undefined;
14 * Provides a base class for configuration objects with storage support.
16 * Encapsulates all capabilities needed for configuration handling for a
17 * specific configuration object, including storage and data type casting.
19 * The default implementation in \Drupal\Core\Config\Config adds support for
20 * runtime overrides. Extend from StorableConfigBase directly to manage
21 * configuration with a storage backend that does not support overrides.
23 * @see \Drupal\Core\Config\Config
25 abstract class StorableConfigBase extends ConfigBase {
28 * The storage used to load and save this configuration object.
30 * @var \Drupal\Core\Config\StorageInterface
35 * The config schema wrapper object for this configuration object.
37 * @var \Drupal\Core\Config\Schema\Element
39 protected $schemaWrapper;
42 * The typed config manager.
44 * @var \Drupal\Core\Config\TypedConfigManagerInterface
46 protected $typedConfigManager;
49 * Whether the configuration object is new or has been saved to the storage.
53 protected $isNew = TRUE;
56 * The data of the configuration object.
60 protected $originalData = [];
63 * Saves the configuration object.
65 * Must invalidate the cache tags associated with the configuration object.
67 * @param bool $has_trusted_data
68 * Set to TRUE if the configuration data has already been checked to ensure
69 * it conforms to schema. Generally this is only used during module and
74 * @see \Drupal\Core\Config\ConfigInstaller::createConfiguration()
76 abstract public function save($has_trusted_data = FALSE);
79 * Deletes the configuration object.
81 * Must invalidate the cache tags associated with the configuration object.
85 abstract public function delete();
88 * Initializes a configuration object with pre-loaded data.
91 * Array of loaded data for this configuration object.
94 * The configuration object.
96 public function initWithData(array $data) {
99 $this->originalData = $this->data;
104 * Returns whether this configuration object is new.
107 * TRUE if this configuration object does not exist in storage.
109 public function isNew() {
114 * Retrieves the storage used to load and save this configuration object.
116 * @return \Drupal\Core\Config\StorageInterface
117 * The configuration storage object.
119 public function getStorage() {
120 return $this->storage;
124 * Gets the schema wrapper for the whole configuration object.
126 * The schema wrapper is dependent on the configuration name and the whole
127 * data structure, so if the name or the data changes in any way, the wrapper
130 * @return \Drupal\Core\Config\Schema\Element
132 protected function getSchemaWrapper() {
133 if (!isset($this->schemaWrapper)) {
134 $this->schemaWrapper = $this->typedConfigManager->createFromNameAndData($this->name, $this->data);
136 return $this->schemaWrapper;
140 * Validate the values are allowed data types.
143 * A string that maps to a key within the configuration data.
144 * @param string $value
145 * Value to associate with the key.
149 * @throws \Drupal\Core\Config\UnsupportedDataTypeConfigException
150 * If the value is unsupported in configuration.
152 protected function validateValue($key, $value) {
153 // Minimal validation. Should not try to serialize resources or non-arrays.
154 if (is_array($value)) {
155 foreach ($value as $nested_value_key => $nested_value) {
156 $this->validateValue($key . '.' . $nested_value_key, $nested_value);
159 elseif ($value !== NULL && !is_scalar($value)) {
160 throw new UnsupportedDataTypeConfigException("Invalid data type for config element {$this->getName()}:$key");
165 * Casts the value to correct data type using the configuration schema.
168 * A string that maps to a key within the configuration data.
169 * @param string $value
170 * Value to associate with the key.
173 * The value cast to the type indicated in the schema.
175 * @throws \Drupal\Core\Config\UnsupportedDataTypeConfigException
176 * If the value is unsupported in configuration.
178 protected function castValue($key, $value) {
179 $element = $this->getSchemaWrapper()->get($key);
180 // Do not cast value if it is unknown or defined to be ignored.
181 if ($element && ($element instanceof Undefined || $element instanceof Ignore)) {
182 // Do validate the value (may throw UnsupportedDataTypeConfigException)
183 // to ensure unsupported types are not supported in this case either.
184 $this->validateValue($key, $value);
187 if (is_scalar($value) || $value === NULL) {
188 if ($element && $element instanceof PrimitiveInterface) {
189 // Special handling for integers and floats since the configuration
190 // system is primarily concerned with saving values from the Form API
191 // we have to special case the meaning of an empty string for numeric
192 // types. In PHP this would be casted to a 0 but for the purposes of
193 // configuration we need to treat this as a NULL.
194 $empty_value = $value === '' && ($element instanceof IntegerInterface || $element instanceof FloatInterface);
196 if ($value === NULL || $empty_value) {
200 $value = $element->getCastedValue();
205 // Throw exception on any non-scalar or non-array value.
206 if (!is_array($value)) {
207 throw new UnsupportedDataTypeConfigException("Invalid data type for config element {$this->getName()}:$key");
209 // Recurse into any nested keys.
210 foreach ($value as $nested_value_key => $nested_value) {
211 $value[$nested_value_key] = $this->castValue($key . '.' . $nested_value_key, $nested_value);
214 if ($element instanceof Sequence) {
215 $data_definition = $element->getDataDefinition();
216 if ($data_definition instanceof SequenceDataDefinition) {
217 // Apply any sorting defined on the schema.
218 switch ($data_definition->getOrderBy()) {
224 // The PHP documentation notes that "Be careful when sorting
225 // arrays with mixed types values because sort() can produce
226 // unpredictable results". There is no risk here because
227 // \Drupal\Core\Config\StorableConfigBase::castValue() has
228 // already cast all values to the same type using the
229 // configuration schema.