Security update for Core, with self-updated composer
[yaffs-website] / web / core / lib / Drupal / Core / Config / ConfigFactory.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 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
9
10 /**
11  * Defines the configuration object factory.
12  *
13  * The configuration object factory instantiates a Config object for each
14  * configuration object name that is accessed and returns it to callers.
15  *
16  * @see \Drupal\Core\Config\Config
17  *
18  * Each configuration object gets a storage object injected, which
19  * is used for reading and writing the configuration data.
20  *
21  * @see \Drupal\Core\Config\StorageInterface
22  *
23  * @ingroup config_api
24  */
25 class ConfigFactory implements ConfigFactoryInterface, EventSubscriberInterface {
26
27   /**
28    * A storage instance for reading and writing configuration data.
29    *
30    * @var \Drupal\Core\Config\StorageInterface
31    */
32   protected $storage;
33
34   /**
35    * An event dispatcher instance to use for configuration events.
36    *
37    * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
38    */
39   protected $eventDispatcher;
40
41   /**
42    * Cached configuration objects.
43    *
44    * @var \Drupal\Core\Config\Config[]
45    */
46   protected $cache = [];
47
48   /**
49    * The typed config manager.
50    *
51    * @var \Drupal\Core\Config\TypedConfigManagerInterface
52    */
53   protected $typedConfigManager;
54
55   /**
56    * An array of config factory override objects ordered by priority.
57    *
58    * @var \Drupal\Core\Config\ConfigFactoryOverrideInterface[]
59    */
60   protected $configFactoryOverrides = [];
61
62   /**
63    * Constructs the Config factory.
64    *
65    * @param \Drupal\Core\Config\StorageInterface $storage
66    *   The configuration storage engine.
67    * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
68    *   An event dispatcher instance to use for configuration events.
69    * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
70    *   The typed configuration manager.
71    */
72   public function __construct(StorageInterface $storage, EventDispatcherInterface $event_dispatcher, TypedConfigManagerInterface $typed_config) {
73     $this->storage = $storage;
74     $this->eventDispatcher = $event_dispatcher;
75     $this->typedConfigManager = $typed_config;
76   }
77
78   /**
79    * {@inheritdoc}
80    */
81   public function getEditable($name) {
82     return $this->doGet($name, FALSE);
83   }
84
85   /**
86    * {@inheritdoc}
87    */
88   public function get($name) {
89     return $this->doGet($name);
90   }
91
92   /**
93    * Returns a configuration object for a given name.
94    *
95    * @param string $name
96    *   The name of the configuration object to construct.
97    * @param bool $immutable
98    *   (optional) Create an immutable configuration object. Defaults to TRUE.
99    *
100    * @return \Drupal\Core\Config\Config|\Drupal\Core\Config\ImmutableConfig
101    *   A configuration object.
102    */
103   protected function doGet($name, $immutable = TRUE) {
104     if ($config = $this->doLoadMultiple([$name], $immutable)) {
105       return $config[$name];
106     }
107     else {
108       // If the configuration object does not exist in the configuration
109       // storage, create a new object.
110       $config = $this->createConfigObject($name, $immutable);
111
112       if ($immutable) {
113         // Get and apply any overrides.
114         $overrides = $this->loadOverrides([$name]);
115         if (isset($overrides[$name])) {
116           $config->setModuleOverride($overrides[$name]);
117         }
118         // Apply any settings.php overrides.
119         if (isset($GLOBALS['config'][$name])) {
120           $config->setSettingsOverride($GLOBALS['config'][$name]);
121         }
122       }
123
124       foreach ($this->configFactoryOverrides as $override) {
125         $config->addCacheableDependency($override->getCacheableMetadata($name));
126       }
127
128       return $config;
129     }
130   }
131
132   /**
133    * {@inheritdoc}
134    */
135   public function loadMultiple(array $names) {
136     return $this->doLoadMultiple($names);
137   }
138
139   /**
140    * Returns a list of configuration objects for the given names.
141    *
142    * @param array $names
143    *   List of names of configuration objects.
144    * @param bool $immutable
145    *   (optional) Create an immutable configuration objects. Defaults to TRUE.
146    *
147    * @return \Drupal\Core\Config\Config[]|\Drupal\Core\Config\ImmutableConfig[]
148    *   List of successfully loaded configuration objects, keyed by name.
149    */
150   protected function doLoadMultiple(array $names, $immutable = TRUE) {
151     $list = [];
152
153     foreach ($names as $key => $name) {
154       $cache_key = $this->getConfigCacheKey($name, $immutable);
155       if (isset($this->cache[$cache_key])) {
156         $list[$name] = $this->cache[$cache_key];
157         unset($names[$key]);
158       }
159     }
160
161     // Pre-load remaining configuration files.
162     if (!empty($names)) {
163       // Initialise override information.
164       $module_overrides = [];
165       $storage_data = $this->storage->readMultiple($names);
166
167       if ($immutable && !empty($storage_data)) {
168         // Only get module overrides if we have configuration to override.
169         $module_overrides = $this->loadOverrides($names);
170       }
171
172       foreach ($storage_data as $name => $data) {
173         $cache_key = $this->getConfigCacheKey($name, $immutable);
174
175         $this->cache[$cache_key] = $this->createConfigObject($name, $immutable);
176         $this->cache[$cache_key]->initWithData($data);
177         if ($immutable) {
178           if (isset($module_overrides[$name])) {
179             $this->cache[$cache_key]->setModuleOverride($module_overrides[$name]);
180           }
181           if (isset($GLOBALS['config'][$name])) {
182             $this->cache[$cache_key]->setSettingsOverride($GLOBALS['config'][$name]);
183           }
184         }
185
186         $this->propagateConfigOverrideCacheability($cache_key, $name);
187
188         $list[$name] = $this->cache[$cache_key];
189       }
190     }
191
192     return $list;
193   }
194
195   /**
196    * Get arbitrary overrides for the named configuration objects from modules.
197    *
198    * @param array $names
199    *   The names of the configuration objects to get overrides for.
200    *
201    * @return array
202    *   An array of overrides keyed by the configuration object name.
203    */
204   protected function loadOverrides(array $names) {
205     $overrides = [];
206     foreach ($this->configFactoryOverrides as $override) {
207       // Existing overrides take precedence since these will have been added
208       // by events with a higher priority.
209       $overrides = NestedArray::mergeDeepArray([$override->loadOverrides($names), $overrides], TRUE);
210     }
211     return $overrides;
212   }
213
214   /**
215    * Propagates cacheability of config overrides to cached config objects.
216    *
217    * @param string $cache_key
218    *   The key of the cached config object to update.
219    * @param string $name
220    *   The name of the configuration object to construct.
221    */
222   protected function propagateConfigOverrideCacheability($cache_key, $name) {
223     foreach ($this->configFactoryOverrides as $override) {
224       $this->cache[$cache_key]->addCacheableDependency($override->getCacheableMetadata($name));
225     }
226   }
227
228   /**
229    * {@inheritdoc}
230    */
231   public function reset($name = NULL) {
232     if ($name) {
233       // Clear all cached configuration for this name.
234       foreach ($this->getConfigCacheKeys($name) as $cache_key) {
235         unset($this->cache[$cache_key]);
236       }
237     }
238     else {
239       $this->cache = [];
240     }
241
242     // Clear the static list cache if supported by the storage.
243     if ($this->storage instanceof StorageCacheInterface) {
244       $this->storage->resetListCache();
245     }
246     return $this;
247   }
248
249   /**
250    * {@inheritdoc}
251    */
252   public function rename($old_name, $new_name) {
253     Cache::invalidateTags($this->get($old_name)->getCacheTags());
254     $this->storage->rename($old_name, $new_name);
255
256     // Clear out the static cache of any references to the old name.
257     foreach ($this->getConfigCacheKeys($old_name) as $old_cache_key) {
258       unset($this->cache[$old_cache_key]);
259     }
260
261     // Prime the cache and load the configuration with the correct overrides.
262     $config = $this->get($new_name);
263     $this->eventDispatcher->dispatch(ConfigEvents::RENAME, new ConfigRenameEvent($config, $old_name));
264     return $this;
265   }
266
267   /**
268    * {@inheritdoc}
269    */
270   public function getCacheKeys() {
271     // Because get() adds overrides both from $GLOBALS and from
272     // $this->configFactoryOverrides, add cache keys for each.
273     $keys[] = 'global_overrides';
274     foreach ($this->configFactoryOverrides as $override) {
275       $keys[] = $override->getCacheSuffix();
276     }
277     return $keys;
278   }
279
280   /**
281    * Gets the static cache key for a given config name.
282    *
283    * @param string $name
284    *   The name of the configuration object.
285    * @param bool $immutable
286    *   Whether or not the object is mutable.
287    *
288    * @return string
289    *   The cache key.
290    */
291   protected function getConfigCacheKey($name, $immutable) {
292     $suffix = '';
293     if ($immutable) {
294       $suffix = ':' . implode(':', $this->getCacheKeys());
295     }
296     return $name . $suffix;
297   }
298
299   /**
300    * Gets all the cache keys that match the provided config name.
301    *
302    * @param string $name
303    *   The name of the configuration object.
304    *
305    * @return array
306    *   An array of cache keys that match the provided config name.
307    */
308   protected function getConfigCacheKeys($name) {
309     return array_filter(array_keys($this->cache), function ($key) use ($name) {
310       // Return TRUE if the key is the name or starts with the configuration
311       // name plus the delimiter.
312       return $key === $name || strpos($key, $name . ':') === 0;
313     });
314   }
315
316   /**
317    * {@inheritdoc}
318    */
319   public function clearStaticCache() {
320     $this->cache = [];
321     return $this;
322   }
323
324   /**
325    * {@inheritdoc}
326    */
327   public function listAll($prefix = '') {
328     return $this->storage->listAll($prefix);
329   }
330
331   /**
332    * Updates stale static cache entries when configuration is saved.
333    *
334    * @param ConfigCrudEvent $event
335    *   The configuration event.
336    */
337   public function onConfigSave(ConfigCrudEvent $event) {
338     // Ensure that the static cache contains up to date configuration objects by
339     // replacing the data on any entries for the configuration object apart
340     // from the one that references the actual config object being saved.
341     $saved_config = $event->getConfig();
342     foreach ($this->getConfigCacheKeys($saved_config->getName()) as $cache_key) {
343       $cached_config = $this->cache[$cache_key];
344       if ($cached_config !== $saved_config) {
345         // We can not just update the data since other things about the object
346         // might have changed. For example, whether or not it is new.
347         $this->cache[$cache_key]->initWithData($saved_config->getRawData());
348       }
349     }
350   }
351
352   /**
353    * Removes stale static cache entries when configuration is deleted.
354    *
355    * @param \Drupal\Core\Config\ConfigCrudEvent $event
356    *   The configuration event.
357    */
358   public function onConfigDelete(ConfigCrudEvent $event) {
359     // Ensure that the static cache does not contain deleted configuration.
360     foreach ($this->getConfigCacheKeys($event->getConfig()->getName()) as $cache_key) {
361       unset($this->cache[$cache_key]);
362     }
363   }
364
365   /**
366    * {@inheritdoc}
367    */
368   public static function getSubscribedEvents() {
369     $events[ConfigEvents::SAVE][] = ['onConfigSave', 255];
370     $events[ConfigEvents::DELETE][] = ['onConfigDelete', 255];
371     return $events;
372   }
373
374   /**
375    * {@inheritdoc}
376    */
377   public function addOverride(ConfigFactoryOverrideInterface $config_factory_override) {
378     $this->configFactoryOverrides[] = $config_factory_override;
379   }
380
381   /**
382    * Creates a configuration object.
383    *
384    * @param string $name
385    *   Configuration object name.
386    * @param bool $immutable
387    *   Determines whether a mutable or immutable config object is returned.
388    *
389    * @return \Drupal\Core\Config\Config|\Drupal\Core\Config\ImmutableConfig
390    *   The configuration object.
391    */
392   protected function createConfigObject($name, $immutable) {
393     if ($immutable) {
394       return new ImmutableConfig($name, $this->storage, $this->eventDispatcher, $this->typedConfigManager);
395     }
396     return new Config($name, $this->storage, $this->eventDispatcher, $this->typedConfigManager);
397   }
398
399 }