3 namespace Drupal\Core\Config;
5 use Drupal\Core\Cache\CacheBackendInterface;
6 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
9 * Defines the cached storage.
11 * The class gets another storage and a cache backend injected. It reads from
12 * the cache and delegates the read to the storage on a cache miss. It also
13 * handles cache invalidation.
15 class CachedStorage implements StorageInterface, StorageCacheInterface {
16 use DependencySerializationTrait;
19 * The configuration storage to be cached.
21 * @var \Drupal\Core\Config\StorageInterface
26 * The instantiated Cache backend.
28 * @var \Drupal\Core\Cache\CacheBackendInterface
33 * List of listAll() prefixes with their results.
37 protected $findByPrefixCache = [];
40 * Constructs a new CachedStorage.
42 * @param \Drupal\Core\Config\StorageInterface $storage
43 * A configuration storage to be cached.
44 * @param \Drupal\Core\Cache\CacheBackendInterface $cache
45 * A cache backend used to store configuration.
47 public function __construct(StorageInterface $storage, CacheBackendInterface $cache) {
48 $this->storage = $storage;
49 $this->cache = $cache;
55 public function exists($name) {
56 // The cache would read in the entire data (instead of only checking whether
57 // any data exists), and on a potential cache miss, an additional storage
58 // lookup would have to happen, so check the storage directly.
59 return $this->storage->exists($name);
65 public function read($name) {
66 $cache_key = $this->getCacheKey($name);
67 if ($cache = $this->cache->get($cache_key)) {
68 // The cache contains either the cached configuration data or FALSE
69 // if the configuration file does not exist.
72 // Read from the storage on a cache miss and cache the data. Also cache
73 // information about missing configuration objects.
74 $data = $this->storage->read($name);
75 $this->cache->set($cache_key, $data);
82 public function readMultiple(array $names) {
85 $cache_keys_map = $this->getCacheKeys($names);
86 $cache_keys = array_values($cache_keys_map);
87 $cached_list = $this->cache->getMultiple($cache_keys);
89 if (!empty($cache_keys)) {
90 // $cache_keys_map contains the full $name => $cache_key map, while
91 // $cache_keys contains just the $cache_key values that weren't found in
93 // @see \Drupal\Core\Cache\CacheBackendInterface::getMultiple()
94 $names_to_get = array_keys(array_intersect($cache_keys_map, $cache_keys));
95 $list = $this->storage->readMultiple($names_to_get);
96 // Cache configuration objects that were loaded from the storage, cache
97 // missing configuration objects as an explicit FALSE.
99 foreach ($names_to_get as $name) {
100 $data = isset($list[$name]) ? $list[$name] : FALSE;
101 $data_to_return[$name] = $data;
102 $items[$cache_keys_map[$name]] = ['data' => $data];
105 $this->cache->setMultiple($items);
108 // Add the configuration objects from the cache to the list.
109 $cache_keys_inverse_map = array_flip($cache_keys_map);
110 foreach ($cached_list as $cache_key => $cache) {
111 $name = $cache_keys_inverse_map[$cache_key];
112 $data_to_return[$name] = $cache->data;
115 // Ensure that only existing configuration objects are returned, filter out
116 // cached information about missing objects.
117 return array_filter($data_to_return);
123 public function write($name, array $data) {
124 if ($this->storage->write($name, $data)) {
125 // While not all written data is read back, setting the cache instead of
126 // just deleting it avoids cache rebuild stampedes.
127 $this->cache->set($this->getCacheKey($name), $data);
128 $this->findByPrefixCache = [];
137 public function delete($name) {
138 // If the cache was the first to be deleted, another process might start
139 // rebuilding the cache before the storage is gone.
140 if ($this->storage->delete($name)) {
141 $this->cache->delete($this->getCacheKey($name));
142 $this->findByPrefixCache = [];
151 public function rename($name, $new_name) {
152 // If the cache was the first to be deleted, another process might start
153 // rebuilding the cache before the storage is renamed.
154 if ($this->storage->rename($name, $new_name)) {
155 $this->cache->delete($this->getCacheKey($name));
156 $this->cache->delete($this->getCacheKey($new_name));
157 $this->findByPrefixCache = [];
166 public function encode($data) {
167 return $this->storage->encode($data);
173 public function decode($raw) {
174 return $this->storage->decode($raw);
180 public function listAll($prefix = '') {
181 // Do not cache when a prefix is not provided.
183 return $this->findByPrefix($prefix);
185 return $this->storage->listAll();
189 * Finds configuration object names starting with a given prefix.
191 * Given the following configuration objects:
192 * - node.type.article
195 * Passing the prefix 'node.type.' will return an array containing the above
198 * @param string $prefix
199 * The prefix to search for
202 * An array containing matching configuration object names.
204 protected function findByPrefix($prefix) {
205 $cache_key = $this->getCacheKey($prefix);
206 if (!isset($this->findByPrefixCache[$cache_key])) {
207 $this->findByPrefixCache[$cache_key] = $this->storage->listAll($prefix);
209 return $this->findByPrefixCache[$cache_key];
215 public function deleteAll($prefix = '') {
216 // If the cache was the first to be deleted, another process might start
217 // rebuilding the cache before the storage is renamed.
218 $names = $this->storage->listAll($prefix);
219 if ($this->storage->deleteAll($prefix)) {
220 $this->cache->deleteMultiple($this->getCacheKeys($names));
227 * Clears the static list cache.
229 public function resetListCache() {
230 $this->findByPrefixCache = [];
236 public function createCollection($collection) {
238 $this->storage->createCollection($collection),
246 public function getAllCollectionNames() {
247 return $this->storage->getAllCollectionNames();
253 public function getCollectionName() {
254 return $this->storage->getCollectionName();
258 * Returns a cache key for a configuration name using the collection.
260 * @param string $name
261 * The configuration name.
264 * The cache key for the configuration name.
266 protected function getCacheKey($name) {
267 return $this->getCollectionPrefix() . $name;
271 * Returns a cache key map for an array of configuration names.
273 * @param array $names
274 * The configuration names.
277 * An array of cache keys keyed by configuration names.
279 protected function getCacheKeys(array $names) {
280 $prefix = $this->getCollectionPrefix();
281 $cache_keys = array_map(function ($name) use ($prefix) {
282 return $prefix . $name;
285 return array_combine($names, $cache_keys);
289 * Returns a cache ID prefix to use for the collection.
292 * The cache ID prefix.
294 protected function getCollectionPrefix() {
295 $collection = $this->storage->getCollectionName();
296 if ($collection == StorageInterface::DEFAULT_COLLECTION) {
299 return $collection . ':';