Upgraded drupal core with security updates
[yaffs-website] / web / core / lib / Drupal / Core / Cache / ApcuBackend.php
1 <?php
2
3 namespace Drupal\Core\Cache;
4
5 /**
6  * Stores cache items in the Alternative PHP Cache User Cache (APCu).
7  */
8 class ApcuBackend implements CacheBackendInterface {
9
10   /**
11    * The name of the cache bin to use.
12    *
13    * @var string
14    */
15   protected $bin;
16
17   /**
18    * Prefix for all keys in the storage that belong to this site.
19    *
20    * @var string
21    */
22   protected $sitePrefix;
23
24   /**
25    * Prefix for all keys in this cache bin.
26    *
27    * Includes the site-specific prefix in $sitePrefix.
28    *
29    * @var string
30    */
31   protected $binPrefix;
32
33   /**
34    * The cache tags checksum provider.
35    *
36    * @var \Drupal\Core\Cache\CacheTagsChecksumInterface
37    */
38   protected $checksumProvider;
39
40   /**
41    * Constructs a new ApcuBackend instance.
42    *
43    * @param string $bin
44    *   The name of the cache bin.
45    * @param string $site_prefix
46    *   The prefix to use for all keys in the storage that belong to this site.
47    * @param \Drupal\Core\Cache\CacheTagsChecksumInterface $checksum_provider
48    *   The cache tags checksum provider.
49    */
50   public function __construct($bin, $site_prefix, CacheTagsChecksumInterface $checksum_provider) {
51     $this->bin = $bin;
52     $this->sitePrefix = $site_prefix;
53     $this->checksumProvider = $checksum_provider;
54     $this->binPrefix = $this->sitePrefix . '::' . $this->bin . '::';
55   }
56
57   /**
58    * Prepends the APCu user variable prefix for this bin to a cache item ID.
59    *
60    * @param string $cid
61    *   The cache item ID to prefix.
62    *
63    * @return string
64    *   The APCu key for the cache item ID.
65    */
66   public function getApcuKey($cid) {
67     return $this->binPrefix . $cid;
68   }
69
70   /**
71    * {@inheritdoc}
72    */
73   public function get($cid, $allow_invalid = FALSE) {
74     $cache = apcu_fetch($this->getApcuKey($cid));
75     return $this->prepareItem($cache, $allow_invalid);
76   }
77
78   /**
79    * {@inheritdoc}
80    */
81   public function getMultiple(&$cids, $allow_invalid = FALSE) {
82     // Translate the requested cache item IDs to APCu keys.
83     $map = [];
84     foreach ($cids as $cid) {
85       $map[$this->getApcuKey($cid)] = $cid;
86     }
87
88     $result = apcu_fetch(array_keys($map));
89     $cache = [];
90     if ($result) {
91       foreach ($result as $key => $item) {
92         $item = $this->prepareItem($item, $allow_invalid);
93         if ($item) {
94           $cache[$map[$key]] = $item;
95         }
96       }
97     }
98     unset($result);
99
100     $cids = array_diff($cids, array_keys($cache));
101     return $cache;
102   }
103
104   /**
105    * Returns all cached items, optionally limited by a cache ID prefix.
106    *
107    * APCu is a memory cache, shared across all server processes. To prevent
108    * cache item clashes with other applications/installations, every cache item
109    * is prefixed with a unique string for this site. Therefore, functions like
110    * apcu_clear_cache() cannot be used, and instead, a list of all cache items
111    * belonging to this application need to be retrieved through this method
112    * instead.
113    *
114    * @param string $prefix
115    *   (optional) A cache ID prefix to limit the result to.
116    *
117    * @return \APCUIterator
118    *   An APCUIterator containing matched items.
119    */
120   protected function getAll($prefix = '') {
121     return $this->getIterator('/^' . preg_quote($this->getApcuKey($prefix), '/') . '/');
122   }
123
124   /**
125    * Prepares a cached item.
126    *
127    * Checks that the item is either permanent or did not expire.
128    *
129    * @param \stdClass $cache
130    *   An item loaded from cache_get() or cache_get_multiple().
131    * @param bool $allow_invalid
132    *   If TRUE, a cache item may be returned even if it is expired or has been
133    *   invalidated. See ::get().
134    *
135    * @return mixed
136    *   The cache item or FALSE if the item expired.
137    */
138   protected function prepareItem($cache, $allow_invalid) {
139     if (!isset($cache->data)) {
140       return FALSE;
141     }
142
143     $cache->tags = $cache->tags ? explode(' ', $cache->tags) : [];
144
145     // Check expire time.
146     $cache->valid = $cache->expire == Cache::PERMANENT || $cache->expire >= REQUEST_TIME;
147
148     // Check if invalidateTags() has been called with any of the entry's tags.
149     if (!$this->checksumProvider->isValid($cache->checksum, $cache->tags)) {
150       $cache->valid = FALSE;
151     }
152
153     if (!$allow_invalid && !$cache->valid) {
154       return FALSE;
155     }
156
157     return $cache;
158   }
159
160   /**
161    * {@inheritdoc}
162    */
163   public function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = []) {
164     assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($tags)', 'Cache tags must be strings.');
165     $tags = array_unique($tags);
166     $cache = new \stdClass();
167     $cache->cid = $cid;
168     $cache->created = round(microtime(TRUE), 3);
169     $cache->expire = $expire;
170     $cache->tags = implode(' ', $tags);
171     $cache->checksum = $this->checksumProvider->getCurrentChecksum($tags);
172     // APCu serializes/unserializes any structure itself.
173     $cache->serialized = 0;
174     $cache->data = $data;
175
176     // Expiration is handled by our own prepareItem(), not APCu.
177     apcu_store($this->getApcuKey($cid), $cache);
178   }
179
180   /**
181    * {@inheritdoc}
182    */
183   public function setMultiple(array $items = []) {
184     foreach ($items as $cid => $item) {
185       $this->set($cid, $item['data'], isset($item['expire']) ? $item['expire'] : CacheBackendInterface::CACHE_PERMANENT, isset($item['tags']) ? $item['tags'] : []);
186     }
187   }
188
189   /**
190    * {@inheritdoc}
191    */
192   public function delete($cid) {
193     apcu_delete($this->getApcuKey($cid));
194   }
195
196   /**
197    * {@inheritdoc}
198    */
199   public function deleteMultiple(array $cids) {
200     apcu_delete(array_map([$this, 'getApcuKey'], $cids));
201   }
202
203   /**
204    * {@inheritdoc}
205    */
206   public function deleteAll() {
207     apcu_delete($this->getIterator('/^' . preg_quote($this->binPrefix, '/') . '/'));
208   }
209
210   /**
211    * {@inheritdoc}
212    */
213   public function garbageCollection() {
214     // APCu performs garbage collection automatically.
215   }
216
217   /**
218    * {@inheritdoc}
219    */
220   public function removeBin() {
221     apcu_delete($this->getIterator('/^' . preg_quote($this->binPrefix, '/') . '/'));
222   }
223
224   /**
225    * {@inheritdoc}
226    */
227   public function invalidate($cid) {
228     $this->invalidateMultiple([$cid]);
229   }
230
231   /**
232    * {@inheritdoc}
233    */
234   public function invalidateMultiple(array $cids) {
235     foreach ($this->getMultiple($cids) as $cache) {
236       $this->set($cache->cid, $cache, REQUEST_TIME - 1);
237     }
238   }
239
240   /**
241    * {@inheritdoc}
242    */
243   public function invalidateAll() {
244     foreach ($this->getAll() as $data) {
245       $cid = str_replace($this->binPrefix, '', $data['key']);
246       $this->set($cid, $data['value'], REQUEST_TIME - 1);
247     }
248   }
249
250   /**
251    * Instantiates and returns the APCUIterator class.
252    *
253    * @param mixed $search
254    *   A PCRE regular expression that matches against APC key names, either as a
255    *   string for a single regular expression, or as an array of regular
256    *   expressions. Or, optionally pass in NULL to skip the search.
257    * @param int $format
258    *   The desired format, as configured with one or more of the APC_ITER_*
259    *   constants.
260    * @param int $chunk_size
261    *   The chunk size. Must be a value greater than 0. The default value is 100.
262    * @param int $list
263    *   The type to list. Either pass in APC_LIST_ACTIVE or APC_LIST_DELETED.
264    *
265    * @return \APCUIterator
266    */
267   protected function getIterator($search = NULL, $format = APC_ITER_ALL, $chunk_size = 100, $list = APC_LIST_ACTIVE) {
268     return new \APCUIterator($search, $format, $chunk_size, $list);
269   }
270
271 }