Pull merge.
[yaffs-website] / web / core / lib / Drupal / Core / Config / Config.php
1 <?php
2
3 namespace Drupal\Core\Config;
4
5 use Drupal\Component\Utility\NestedArray;
6 use Drupal\Core\Cache\Cache;
7 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
8
9 /**
10  * Defines the default configuration object.
11  *
12  * Encapsulates all capabilities needed for configuration handling for a
13  * specific configuration object, including support for runtime overrides. The
14  * overrides are handled on top of the stored configuration so they are not
15  * saved back to storage.
16  *
17  * @ingroup config_api
18  */
19 class Config extends StorableConfigBase {
20
21   /**
22    * An event dispatcher instance to use for configuration events.
23    *
24    * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
25    */
26   protected $eventDispatcher;
27
28   /**
29    * The current runtime data.
30    *
31    * The configuration data from storage merged with module and settings
32    * overrides.
33    *
34    * @var array
35    */
36   protected $overriddenData;
37
38   /**
39    * The current module overrides.
40    *
41    * @var array
42    */
43   protected $moduleOverrides;
44
45   /**
46    * The current settings overrides.
47    *
48    * @var array
49    */
50   protected $settingsOverrides;
51
52   /**
53    * Constructs a configuration object.
54    *
55    * @param string $name
56    *   The name of the configuration object being constructed.
57    * @param \Drupal\Core\Config\StorageInterface $storage
58    *   A storage object to use for reading and writing the
59    *   configuration data.
60    * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
61    *   An event dispatcher instance to use for configuration events.
62    * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
63    *   The typed configuration manager service.
64    */
65   public function __construct($name, StorageInterface $storage, EventDispatcherInterface $event_dispatcher, TypedConfigManagerInterface $typed_config) {
66     $this->name = $name;
67     $this->storage = $storage;
68     $this->eventDispatcher = $event_dispatcher;
69     $this->typedConfigManager = $typed_config;
70   }
71
72   /**
73    * {@inheritdoc}
74    */
75   public function initWithData(array $data) {
76     parent::initWithData($data);
77     $this->resetOverriddenData();
78     return $this;
79   }
80
81   /**
82    * {@inheritdoc}
83    */
84   public function get($key = '') {
85     if (!isset($this->overriddenData)) {
86       $this->setOverriddenData();
87     }
88     if (empty($key)) {
89       return $this->overriddenData;
90     }
91     else {
92       $parts = explode('.', $key);
93       if (count($parts) == 1) {
94         return isset($this->overriddenData[$key]) ? $this->overriddenData[$key] : NULL;
95       }
96       else {
97         $value = NestedArray::getValue($this->overriddenData, $parts, $key_exists);
98         return $key_exists ? $value : NULL;
99       }
100     }
101   }
102
103   /**
104    * {@inheritdoc}
105    */
106   public function setData(array $data) {
107     parent::setData($data);
108     $this->resetOverriddenData();
109     return $this;
110   }
111
112   /**
113    * Sets settings.php overrides for this configuration object.
114    *
115    * The overridden data only applies to this configuration object.
116    *
117    * @param array $data
118    *   The overridden values of the configuration data.
119    *
120    * @return \Drupal\Core\Config\Config
121    *   The configuration object.
122    */
123   public function setSettingsOverride(array $data) {
124     $this->settingsOverrides = $data;
125     $this->resetOverriddenData();
126     return $this;
127   }
128
129   /**
130    * Sets module overrides for this configuration object.
131    *
132    * @param array $data
133    *   The overridden values of the configuration data.
134    *
135    * @return \Drupal\Core\Config\Config
136    *   The configuration object.
137    */
138   public function setModuleOverride(array $data) {
139     $this->moduleOverrides = $data;
140     $this->resetOverriddenData();
141     return $this;
142   }
143
144   /**
145    * Sets the current data for this configuration object.
146    *
147    * Configuration overrides operate at two distinct layers: modules and
148    * settings.php. Overrides in settings.php take precedence over values
149    * provided by modules. Precedence or different module overrides is
150    * determined by the priority of the config.factory.override tagged services.
151    *
152    * @return \Drupal\Core\Config\Config
153    *   The configuration object.
154    */
155   protected function setOverriddenData() {
156     $this->overriddenData = $this->data;
157     if (isset($this->moduleOverrides) && is_array($this->moduleOverrides)) {
158       $this->overriddenData = NestedArray::mergeDeepArray([$this->overriddenData, $this->moduleOverrides], TRUE);
159     }
160     if (isset($this->settingsOverrides) && is_array($this->settingsOverrides)) {
161       $this->overriddenData = NestedArray::mergeDeepArray([$this->overriddenData, $this->settingsOverrides], TRUE);
162     }
163     return $this;
164   }
165
166   /**
167    * Resets the current data, so overrides are re-applied.
168    *
169    * This method should be called after the original data or the overridden data
170    * has been changed.
171    *
172    * @return \Drupal\Core\Config\Config
173    *   The configuration object.
174    */
175   protected function resetOverriddenData() {
176     unset($this->overriddenData);
177     return $this;
178   }
179
180   /**
181    * {@inheritdoc}
182    */
183   public function set($key, $value) {
184     parent::set($key, $value);
185     $this->resetOverriddenData();
186     return $this;
187   }
188
189   /**
190    * {@inheritdoc}
191    */
192   public function clear($key) {
193     parent::clear($key);
194     $this->resetOverriddenData();
195     return $this;
196   }
197
198   /**
199    * {@inheritdoc}
200    */
201   public function save($has_trusted_data = FALSE) {
202     // Validate the configuration object name before saving.
203     static::validateName($this->name);
204
205     // If there is a schema for this configuration object, cast all values to
206     // conform to the schema.
207     if (!$has_trusted_data) {
208       if ($this->typedConfigManager->hasConfigSchema($this->name)) {
209         // Ensure that the schema wrapper has the latest data.
210         $this->schemaWrapper = NULL;
211         foreach ($this->data as $key => $value) {
212           $this->data[$key] = $this->castValue($key, $value);
213         }
214       }
215       else {
216         foreach ($this->data as $key => $value) {
217           $this->validateValue($key, $value);
218         }
219       }
220     }
221
222     // Potentially configuration schema could have changed the underlying data's
223     // types.
224     $this->resetOverriddenData();
225
226     $this->storage->write($this->name, $this->data);
227     if (!$this->isNew) {
228       Cache::invalidateTags($this->getCacheTags());
229     }
230     $this->isNew = FALSE;
231     $this->eventDispatcher->dispatch(ConfigEvents::SAVE, new ConfigCrudEvent($this));
232     $this->originalData = $this->data;
233     return $this;
234   }
235
236   /**
237    * Deletes the configuration object.
238    *
239    * @return \Drupal\Core\Config\Config
240    *   The configuration object.
241    */
242   public function delete() {
243     $this->data = [];
244     $this->storage->delete($this->name);
245     Cache::invalidateTags($this->getCacheTags());
246     $this->isNew = TRUE;
247     $this->resetOverriddenData();
248     $this->eventDispatcher->dispatch(ConfigEvents::DELETE, new ConfigCrudEvent($this));
249     $this->originalData = $this->data;
250     return $this;
251   }
252
253   /**
254    * Gets the raw data without overrides.
255    *
256    * @return array
257    *   The raw data.
258    */
259   public function getRawData() {
260     return $this->data;
261   }
262
263   /**
264    * Gets original data from this configuration object.
265    *
266    * Original data is the data as it is immediately after loading from
267    * configuration storage before any changes. If this is a new configuration
268    * object it will be an empty array.
269    *
270    * @see \Drupal\Core\Config\Config::get()
271    *
272    * @param string $key
273    *   A string that maps to a key within the configuration data.
274    * @param bool $apply_overrides
275    *   Apply any overrides to the original data. Defaults to TRUE.
276    *
277    * @return mixed
278    *   The data that was requested.
279    */
280   public function getOriginal($key = '', $apply_overrides = TRUE) {
281     $original_data = $this->originalData;
282     if ($apply_overrides) {
283       // Apply overrides.
284       if (isset($this->moduleOverrides) && is_array($this->moduleOverrides)) {
285         $original_data = NestedArray::mergeDeepArray([$original_data, $this->moduleOverrides], TRUE);
286       }
287       if (isset($this->settingsOverrides) && is_array($this->settingsOverrides)) {
288         $original_data = NestedArray::mergeDeepArray([$original_data, $this->settingsOverrides], TRUE);
289       }
290     }
291
292     if (empty($key)) {
293       return $original_data;
294     }
295     else {
296       $parts = explode('.', $key);
297       if (count($parts) == 1) {
298         return isset($original_data[$key]) ? $original_data[$key] : NULL;
299       }
300       else {
301         $value = NestedArray::getValue($original_data, $parts, $key_exists);
302         return $key_exists ? $value : NULL;
303       }
304     }
305   }
306
307   /**
308    * Determines if overrides are applied to a key for this configuration object.
309    *
310    * @param string $key
311    *   (optional) A string that maps to a key within the configuration data.
312    *   For instance in the following configuration array:
313    *   @code
314    *   array(
315    *     'foo' => array(
316    *       'bar' => 'baz',
317    *     ),
318    *   );
319    *   @endcode
320    *   A key of 'foo.bar' would map to the string 'baz'. However, a key of 'foo'
321    *   would map to the array('bar' => 'baz').
322    *   If not supplied TRUE will be returned if there are any overrides at all
323    *   for this configuration object.
324    *
325    * @return bool
326    *   TRUE if there are any overrides for the key, otherwise FALSE.
327    */
328   public function hasOverrides($key = '') {
329     if (empty($key)) {
330       return !(empty($this->moduleOverrides) && empty($this->settingsOverrides));
331     }
332     else {
333       $parts = explode('.', $key);
334       $override_exists = FALSE;
335       if (isset($this->moduleOverrides) && is_array($this->moduleOverrides)) {
336         $override_exists = NestedArray::keyExists($this->moduleOverrides, $parts);
337       }
338       if (!$override_exists && isset($this->settingsOverrides) && is_array($this->settingsOverrides)) {
339         $override_exists = NestedArray::keyExists($this->settingsOverrides, $parts);
340       }
341       return $override_exists;
342     }
343   }
344
345 }