CachedStorage.php 6.85 KB
Newer Older
1
2
3
4
5
6
7
8
9
<?php

/**
 * @file
 * Contains Drupal\Core\Config\CachedStorage.
 */

namespace Drupal\Core\Config;

10
use Drupal\Core\Cache\Cache;
11
12
13
14
15
16
17
18
19
use Drupal\Core\Cache\CacheBackendInterface;

/**
 * Defines the cached storage controller.
 *
 * The class gets another storage and a cache backend injected. It reads from
 * the cache and delegates the read to the storage on a cache miss. It also
 * handles cache invalidation.
 */
20
class CachedStorage implements StorageInterface, StorageCacheInterface {
21
22
23
24

  /**
   * The configuration storage to be cached.
   *
25
   * @var \Drupal\Core\Config\StorageInterface
26
27
28
29
30
31
   */
  protected $storage;

  /**
   * The instantiated Cache backend.
   *
32
   * @var \Drupal\Core\Cache\CacheBackendInterface
33
34
35
   */
  protected $cache;

36
37
38
39
40
41
42
  /**
   * List of listAll() prefixes with their results.
   *
   * @var array
   */
  protected $findByPrefixCache = array();

43
44
45
  /**
   * Constructs a new CachedStorage controller.
   *
46
   * @param \Drupal\Core\Config\StorageInterface $storage
47
   *   A configuration storage controller to be cached.
48
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
   *   A cache backend instance to use for caching.
   */
  public function __construct(StorageInterface $storage, CacheBackendInterface $cache) {
    $this->storage = $storage;
    $this->cache = $cache;
  }

  /**
   * Implements Drupal\Core\Config\StorageInterface::exists().
   */
  public function exists($name) {
    // The cache would read in the entire data (instead of only checking whether
    // any data exists), and on a potential cache miss, an additional storage
    // lookup would have to happen, so check the storage directly.
    return $this->storage->exists($name);
  }

  /**
   * Implements Drupal\Core\Config\StorageInterface::read().
   */
  public function read($name) {
    if ($cache = $this->cache->get($name)) {
71
72
73
      // The cache contains either the cached configuration data or FALSE
      // if the configuration file does not exist.
      return $cache->data;
74
    }
75
76
    // Read from the storage on a cache miss and cache the data. Also cache
    // information about missing configuration objects.
77
    $data = $this->storage->read($name);
78
    $this->cache->set($name, $data);
79
80
81
    return $data;
  }

82
83
84
85
86
87
88
  /**
   * {@inheritdoc}
   */
  public function readMultiple(array $names) {
    $list = array();
    // The names array is passed by reference and will only contain the names of
    // config object not found after the method call.
89
    // @see \Drupal\Core\Cache\CacheBackendInterface::getMultiple()
90
91
92
93
    $cached_list = $this->cache->getMultiple($names);

    if (!empty($names)) {
      $list = $this->storage->readMultiple($names);
94
95
96
97
      // Cache configuration objects that were loaded from the storage, cache
      // missing configuration objects as an explicit FALSE.
      foreach ($names as $name) {
        $this->cache->set($name, isset($list[$name]) ? $list[$name] : FALSE);
98
99
100
101
102
103
104
105
      }
    }

    // Add the configuration objects from the cache to the list.
    foreach ($cached_list as $name => $cache) {
      $list[$name] = $cache->data;
    }

106
107
108
    // Ensure that only existing configuration objects are returned, filter out
    // cached information about missing objects.
    return array_filter($list);
109
110
  }

111
112
113
114
115
116
117
  /**
   * Implements Drupal\Core\Config\StorageInterface::write().
   */
  public function write($name, array $data) {
    if ($this->storage->write($name, $data)) {
      // While not all written data is read back, setting the cache instead of
      // just deleting it avoids cache rebuild stampedes.
118
      $this->cache->set($name, $data);
119
      Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE));
120
      $this->findByPrefixCache = array();
121
122
123
124
125
126
127
128
129
130
131
132
133
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Implements Drupal\Core\Config\StorageInterface::delete().
   */
  public function delete($name) {
    // If the cache was the first to be deleted, another process might start
    // rebuilding the cache before the storage is gone.
    if ($this->storage->delete($name)) {
      $this->cache->delete($name);
134
      Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE));
135
      $this->findByPrefixCache = array();
136
137
138
139
140
141
142
143
144
145
146
147
148
149
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Implements Drupal\Core\Config\StorageInterface::rename().
   */
  public function rename($name, $new_name) {
    // If the cache was the first to be deleted, another process might start
    // rebuilding the cache before the storage is renamed.
    if ($this->storage->rename($name, $new_name)) {
      $this->cache->delete($name);
      $this->cache->delete($new_name);
150
      Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE));
151
      $this->findByPrefixCache = array();
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Implements Drupal\Core\Config\StorageInterface::encode().
   */
  public function encode($data) {
    return $this->storage->encode($data);
  }

  /**
   * Implements Drupal\Core\Config\StorageInterface::decode().
   */
  public function decode($raw) {
    return $this->storage->decode($raw);
  }

  /**
172
   * {@inheritdoc}
173
174
   */
  public function listAll($prefix = '') {
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
    // Do not cache when a prefix is not provided.
    if ($prefix) {
      return $this->findByPrefix($prefix);
    }
    return $this->storage->listAll();
  }

  /**
   * Finds configuration object names starting with a given prefix.
   *
   * Given the following configuration objects:
   * - node.type.article
   * - node.type.page
   *
   * Passing the prefix 'node.type.' will return an array containing the above
   * names.
   *
   * @param string $prefix
   *   The prefix to search for
   *
   * @return array
   *   An array containing matching configuration object names.
   */
  protected function findByPrefix($prefix) {
    if (!isset($this->findByPrefixCache[$prefix])) {
      // The : character is not allowed in config file names, so this can not
      // conflict.
      if ($cache = $this->cache->get('find:' . $prefix)) {
        $this->findByPrefixCache[$prefix] = $cache->data;
      }
      else {
        $this->findByPrefixCache[$prefix] = $this->storage->listAll($prefix);
        $this->cache->set(
          'find:' . $prefix,
          $this->findByPrefixCache[$prefix],
210
          Cache::PERMANENT,
211
212
213
214
215
          array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE)
        );
      }
    }
    return $this->findByPrefixCache[$prefix];
216
  }
217
218
219
220
221
222
223
224
225
226
227
228
229
230

  /**
   * Implements Drupal\Core\Config\StorageInterface::deleteAll().
   */
  public function deleteAll($prefix = '') {
    // If the cache was the first to be deleted, another process might start
    // rebuilding the cache before the storage is renamed.
    $cids = $this->storage->listAll($prefix);
    if ($this->storage->deleteAll($prefix)) {
      $this->cache->deleteMultiple($cids);
      return TRUE;
    }
    return FALSE;
  }
231
232
233
234
235
236
237

  /**
   * Clears the static list cache.
   */
  public function resetListCache() {
    $this->findByPrefixCache = array();
  }
238
}