4f445ea21c7755bf9a00827f28e10031afe42320
[yaffs-website] / web / core / lib / Drupal / Core / Config / ConfigBase.php
1 <?php
2
3 namespace Drupal\Core\Config;
4
5 use Drupal\Component\Utility\NestedArray;
6 use Drupal\Component\Render\MarkupInterface;
7 use Drupal\Core\Cache\Cache;
8 use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
9 use Drupal\Core\Cache\RefinableCacheableDependencyTrait;
10 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
11
12 /**
13  * Provides a base class for configuration objects with get/set support.
14  *
15  * Encapsulates all capabilities needed for runtime configuration handling for
16  * a specific configuration object.
17  *
18  * Extend directly from this class for non-storable configuration where the
19  * configuration API is desired but storage is not possible; for example, if
20  * the data is derived at runtime. For storable configuration, extend
21  * \Drupal\Core\Config\StorableConfigBase.
22  *
23  * @see \Drupal\Core\Config\StorableConfigBase
24  * @see \Drupal\Core\Config\Config
25  * @see \Drupal\Core\Theme\ThemeSettings
26  */
27 abstract class ConfigBase implements RefinableCacheableDependencyInterface {
28   use DependencySerializationTrait;
29   use RefinableCacheableDependencyTrait;
30
31   /**
32    * The name of the configuration object.
33    *
34    * @var string
35    */
36   protected $name;
37
38   /**
39    * The data of the configuration object.
40    *
41    * @var array
42    */
43   protected $data = [];
44
45   /**
46    * The maximum length of a configuration object name.
47    *
48    * Many filesystems (including HFS, NTFS, and ext4) have a maximum file name
49    * length of 255 characters. To ensure that no configuration objects
50    * incompatible with this limitation are created, we enforce a maximum name
51    * length of 250 characters (leaving 5 characters for the file extension).
52    *
53    * @see http://wikipedia.org/wiki/Comparison_of_file_systems
54    *
55    * Configuration objects not stored on the filesystem should still be
56    * restricted in name length so name can be used as a cache key.
57    */
58   const MAX_NAME_LENGTH = 250;
59
60   /**
61    * Returns the name of this configuration object.
62    *
63    * @return string
64    *   The name of the configuration object.
65    */
66   public function getName() {
67     return $this->name;
68   }
69
70   /**
71    * Sets the name of this configuration object.
72    *
73    * @param string $name
74    *   The name of the configuration object.
75    *
76    * @return $this
77    *   The configuration object.
78    */
79   public function setName($name) {
80     $this->name = $name;
81     return $this;
82   }
83
84   /**
85    * Validates the configuration object name.
86    *
87    * @param string $name
88    *   The name of the configuration object.
89    *
90    * @throws \Drupal\Core\Config\ConfigNameException
91    *
92    * @see Config::MAX_NAME_LENGTH
93    */
94   public static function validateName($name) {
95     // The name must be namespaced by owner.
96     if (strpos($name, '.') === FALSE) {
97       throw new ConfigNameException("Missing namespace in Config object name $name.");
98     }
99     // The name must be shorter than Config::MAX_NAME_LENGTH characters.
100     if (strlen($name) > self::MAX_NAME_LENGTH) {
101       throw new ConfigNameException("Config object name $name exceeds maximum allowed length of " . static::MAX_NAME_LENGTH . " characters.");
102     }
103
104     // The name must not contain any of the following characters:
105     // : ? * < > " ' / \
106     if (preg_match('/[:?*<>"\'\/\\\\]/', $name)) {
107       throw new ConfigNameException("Invalid character in Config object name $name.");
108     }
109   }
110
111   /**
112    * Gets data from this configuration object.
113    *
114    * @param string $key
115    *   A string that maps to a key within the configuration data.
116    *   For instance in the following configuration array:
117    *   @code
118    *   array(
119    *     'foo' => array(
120    *       'bar' => 'baz',
121    *     ),
122    *   );
123    *   @endcode
124    *   A key of 'foo.bar' would return the string 'baz'. However, a key of 'foo'
125    *   would return array('bar' => 'baz').
126    *   If no key is specified, then the entire data array is returned.
127    *
128    * @return mixed
129    *   The data that was requested.
130    */
131   public function get($key = '') {
132     if (empty($key)) {
133       return $this->data;
134     }
135     else {
136       $parts = explode('.', $key);
137       if (count($parts) == 1) {
138         return isset($this->data[$key]) ? $this->data[$key] : NULL;
139       }
140       else {
141         $value = NestedArray::getValue($this->data, $parts, $key_exists);
142         return $key_exists ? $value : NULL;
143       }
144     }
145   }
146
147   /**
148    * Replaces the data of this configuration object.
149    *
150    * @param array $data
151    *   The new configuration data.
152    *
153    * @return $this
154    *   The configuration object.
155    *
156    * @throws \Drupal\Core\Config\ConfigValueException
157    *   If any key in $data in any depth contains a dot.
158    */
159   public function setData(array $data) {
160     $data = $this->castSafeStrings($data);
161     $this->validateKeys($data);
162     $this->data = $data;
163     return $this;
164   }
165
166   /**
167    * Sets a value in this configuration object.
168    *
169    * @param string $key
170    *   Identifier to store value in configuration.
171    * @param mixed $value
172    *   Value to associate with identifier.
173    *
174    * @return $this
175    *   The configuration object.
176    *
177    * @throws \Drupal\Core\Config\ConfigValueException
178    *   If $value is an array and any of its keys in any depth contains a dot.
179    */
180   public function set($key, $value) {
181     $value = $this->castSafeStrings($value);
182     // The dot/period is a reserved character; it may appear between keys, but
183     // not within keys.
184     if (is_array($value)) {
185       $this->validateKeys($value);
186     }
187     $parts = explode('.', $key);
188     if (count($parts) == 1) {
189       $this->data[$key] = $value;
190     }
191     else {
192       NestedArray::setValue($this->data, $parts, $value);
193     }
194     return $this;
195   }
196
197   /**
198    * Validates all keys in a passed in config array structure.
199    *
200    * @param array $data
201    *   Configuration array structure.
202    *
203    * @return null
204    *
205    * @throws \Drupal\Core\Config\ConfigValueException
206    *   If any key in $data in any depth contains a dot.
207    */
208   protected function validateKeys(array $data) {
209     foreach ($data as $key => $value) {
210       if (strpos($key, '.') !== FALSE) {
211         throw new ConfigValueException("$key key contains a dot which is not supported.");
212       }
213       if (is_array($value)) {
214         $this->validateKeys($value);
215       }
216     }
217   }
218
219   /**
220    * Unsets a value in this configuration object.
221    *
222    * @param string $key
223    *   Name of the key whose value should be unset.
224    *
225    * @return $this
226    *   The configuration object.
227    */
228   public function clear($key) {
229     $parts = explode('.', $key);
230     if (count($parts) == 1) {
231       unset($this->data[$key]);
232     }
233     else {
234       NestedArray::unsetValue($this->data, $parts);
235     }
236     return $this;
237   }
238
239   /**
240    * Merges data into a configuration object.
241    *
242    * @param array $data_to_merge
243    *   An array containing data to merge.
244    *
245    * @return $this
246    *   The configuration object.
247    */
248   public function merge(array $data_to_merge) {
249     // Preserve integer keys so that configuration keys are not changed.
250     $this->setData(NestedArray::mergeDeepArray([$this->data, $data_to_merge], TRUE));
251     return $this;
252   }
253
254   /**
255    * {@inheritdoc}
256    */
257   public function getCacheContexts() {
258     return $this->cacheContexts;
259   }
260
261   /**
262    * {@inheritdoc}
263    */
264   public function getCacheTags() {
265     return Cache::mergeTags(['config:' . $this->name], $this->cacheTags);
266   }
267
268   /**
269    * {@inheritdoc}
270    */
271   public function getCacheMaxAge() {
272     return $this->cacheMaxAge;
273   }
274
275   /**
276    * Casts any objects that implement MarkupInterface to string.
277    *
278    * @param mixed $data
279    *   The configuration data.
280    *
281    * @return mixed
282    *   The data with any safe strings cast to string.
283    */
284   protected function castSafeStrings($data) {
285     if ($data instanceof MarkupInterface) {
286       $data = (string) $data;
287     }
288     elseif (is_array($data)) {
289       array_walk_recursive($data, function (&$value) {
290         if ($value instanceof MarkupInterface) {
291           $value = (string) $value;
292         }
293       });
294     }
295     return $data;
296   }
297
298 }