ConfigFactory.php 13 KB

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