diff --git a/core/lib/Drupal/Core/Config/ConfigFactory.php b/core/lib/Drupal/Core/Config/ConfigFactory.php index e29c3710cc2d7a92d211f3f2fb27d3b339f5634d..a10bfeccf635582f64528d4026c368555955d3bd 100644 --- a/core/lib/Drupal/Core/Config/ConfigFactory.php +++ b/core/lib/Drupal/Core/Config/ConfigFactory.php @@ -39,11 +39,11 @@ class ConfigFactory implements ConfigFactoryInterface, EventSubscriberInterface protected $eventDispatcher; /** - * Cached configuration objects. + * The cache backend for configuration objects. * - * @var \Drupal\Core\Config\Config[] + * @var \Drupal\Core\Config\SearchableMemoryCache */ - protected $cache = []; + protected $cache; /** * The typed config manager. @@ -68,11 +68,19 @@ class ConfigFactory implements ConfigFactoryInterface, EventSubscriberInterface * An event dispatcher instance to use for configuration events. * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config * The typed configuration manager. + * @param \Drupal\Core\Config\SearchableMemoryCache $cache + * The cache backend for configuration objects. */ - public function __construct(StorageInterface $storage, EventDispatcherInterface $event_dispatcher, TypedConfigManagerInterface $typed_config) { + public function __construct(StorageInterface $storage, EventDispatcherInterface $event_dispatcher, TypedConfigManagerInterface $typed_config, ?SearchableMemoryCache $cache = NULL) { $this->storage = $storage; $this->eventDispatcher = $event_dispatcher; $this->typedConfigManager = $typed_config; + // To obtain relevant cache keys, we need to search our cache's keys. + $this->cache = $cache; + if ($this->cache === NULL) { + @trigger_error('Calling ' . __METHOD__ . ' without the $cache argument is deprecated in drupal:11.2.0 and it will be required in drupal:12.0.0. See https://www.drupal.org/project/drupal/issues/3063687', E_USER_DEPRECATED); + $this->cache = \Drupal::service('cache.backend.memory'); + } } /** @@ -152,8 +160,8 @@ protected function doLoadMultiple(array $names, $immutable = TRUE) { foreach ($names as $key => $name) { $cache_key = $this->getConfigCacheKey($name, $immutable); - if (isset($this->cache[$cache_key])) { - $list[$name] = $this->cache[$cache_key]; + if (!is_null($this->cacheGet($cache_key))) { + $list[$name] = $this->cacheGet($cache_key); unset($names[$key]); } } @@ -172,26 +180,53 @@ protected function doLoadMultiple(array $names, $immutable = TRUE) { foreach ($storage_data as $name => $data) { $cache_key = $this->getConfigCacheKey($name, $immutable); - $this->cache[$cache_key] = $this->createConfigObject($name, $immutable); - $this->cache[$cache_key]->initWithData($data); + $config_object = $this->createConfigObject($name, $immutable); + $this->cacheSet($cache_key, $config_object); + $config_object->initWithData($data); if ($immutable) { if (isset($module_overrides[$name])) { - $this->cache[$cache_key]->setModuleOverride($module_overrides[$name]); + $config_object->setModuleOverride($module_overrides[$name]); } if (isset($GLOBALS['config'][$name])) { - $this->cache[$cache_key]->setSettingsOverride($GLOBALS['config'][$name]); + $config_object->setSettingsOverride($GLOBALS['config'][$name]); } } $this->propagateConfigOverrideCacheability($cache_key, $name); - $list[$name] = $this->cache[$cache_key]; + $list[$name] = $config_object; } } return $list; } + /** + * Retrieves a cached configuration value. + * + * @param string $cache_key + * The cache key to retrieve the configuration value. + * + * @return mixed + * The cached data if available, or NULL otherwise. + */ + protected function cacheGet($cache_key) { + $static_value = $this->cache->get($cache_key) ? $this->cache->get($cache_key)->data : NULL; + return $static_value; + } + + /** + * Stores a configuration value in the cache. + * + * @param string $cache_key + * The cache key under which the data should be stored. + * @param \Drupal\Core\Cache\CacheableDependencyInterface $data + * The data to be cached (must be cacheable). + */ + protected function cacheSet($cache_key, $data) { + $this->cache->set($cache_key, $data, Cache::PERMANENT, $data->getCacheTags()); + } + /** * Get arbitrary overrides for the named configuration objects from modules. * @@ -221,7 +256,7 @@ protected function loadOverrides(array $names) { */ protected function propagateConfigOverrideCacheability($cache_key, $name) { foreach ($this->configFactoryOverrides as $override) { - $this->cache[$cache_key]->addCacheableDependency($override->getCacheableMetadata($name)); + $this->cacheGet($cache_key)->addCacheableDependency($override->getCacheableMetadata($name)); } } @@ -231,12 +266,12 @@ protected function propagateConfigOverrideCacheability($cache_key, $name) { public function reset($name = NULL) { if ($name) { // Clear all cached configuration for this name. - foreach ($this->getConfigCacheKeys($name) as $cache_key) { - unset($this->cache[$cache_key]); + foreach ($this->cache->getConfigCacheKeys($name) as $cache_key) { + $this->cache->delete($cache_key); } } else { - $this->cache = []; + $this->cache->deleteAll(); } // Clear the static list cache if supported by the storage. @@ -254,8 +289,8 @@ public function rename($old_name, $new_name) { $this->storage->rename($old_name, $new_name); // Clear out the static cache of any references to the old name. - foreach ($this->getConfigCacheKeys($old_name) as $old_cache_key) { - unset($this->cache[$old_cache_key]); + foreach ($this->cache->getConfigCacheKeys($old_name) as $old_cache_key) { + $this->cache->delete($old_cache_key); } // Prime the cache and load the configuration with the correct overrides. @@ -318,7 +353,7 @@ protected function getConfigCacheKeys($name) { * {@inheritdoc} */ public function clearStaticCache() { - $this->cache = []; + $this->cache->deleteAll(); return $this; } @@ -348,12 +383,12 @@ public function onConfigSave(ConfigCrudEvent $event) { // Ensure that the static cache contains up to date configuration objects by // replacing the data on any entries for the configuration object apart // from the one that references the actual config object being saved. - foreach ($this->getConfigCacheKeys($saved_config->getName()) as $cache_key) { - $cached_config = $this->cache[$cache_key]; + foreach ($this->cache->getConfigCacheKeys($saved_config->getName()) as $cache_key) { + $cached_config = $this->cacheGet($cache_key); if ($cached_config !== $saved_config) { // We can not just update the data since other things about the object // might have changed. For example, whether or not it is new. - $this->cache[$cache_key]->initWithData($saved_config->getRawData()); + $cached_config->initWithData($saved_config->getRawData()); } } } @@ -375,8 +410,8 @@ public function onConfigDelete(ConfigCrudEvent $event) { } // Ensure that the static cache does not contain deleted configuration. - foreach ($this->getConfigCacheKeys($deleted_config->getName()) as $cache_key) { - unset($this->cache[$cache_key]); + foreach ($this->cache->getConfigCacheKeys($deleted_config->getName()) as $cache_key) { + $this->cache->delete($cache_key); } } diff --git a/core/lib/Drupal/Core/Config/SearchableMemoryCache.php b/core/lib/Drupal/Core/Config/SearchableMemoryCache.php new file mode 100644 index 0000000000000000000000000000000000000000..f7c4465544413985ec82f7d58b08c44851d8cb98 --- /dev/null +++ b/core/lib/Drupal/Core/Config/SearchableMemoryCache.php @@ -0,0 +1,35 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Config; + +use Drupal\Core\Cache\MemoryCache\MemoryCache; + +/** + * Defines a memory cache implementation that can search for keys. + * + * Stores cache items in memory using a PHP array. + * + * @ingroup cache + */ +class SearchableMemoryCache extends MemoryCache { + + /** + * Gets all the cache keys that match the provided config name. + * + * @param string $name + * The name of the configuration object. + * + * @return array + * An array of cache keys that match the provided config name. + */ + public function getConfigCacheKeys($name): array { + return array_filter(array_keys($this->cache), function ($key) use ($name) { + // Return TRUE if the key is the name or starts with the configuration + // name plus the delimiter. + return $key === $name || strpos($key, $name . ':') === 0; + }); + } + +}