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