[TASK] Update php-cs-fixer to 2.5.0
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Cache / Backend / WincacheBackend.php
1 <?php
2 namespace TYPO3\CMS\Core\Cache\Backend;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 /**
18 * A caching backend which stores cache entries by using wincache.
19 *
20 * This backend uses the following types of keys:
21 * - tag_xxx
22 * xxx is tag name, value is array of associated identifiers identifier. This
23 * is "forward" tag index. It is mainly used for obtaining content by tag
24 * (get identifier by tag -> get content by identifier)
25 * - ident_xxx
26 * xxx is identifier, value is array of associated tags. This is "reverse" tag
27 * index. It provides quick access for all tags associated with this identifier
28 * and used when removing the identifier
29 *
30 * Each key is prepended with a prefix. By default prefix consists from two parts
31 * separated by underscore character and ends in yet another underscore character:
32 * - "TYPO3"
33 * - MD5 of script path and filename and SAPI name
34 * This prefix makes sure that keys from the different installations do not
35 * conflict.
36 */
37 class WincacheBackend extends \TYPO3\CMS\Core\Cache\Backend\AbstractBackend implements \TYPO3\CMS\Core\Cache\Backend\TaggableBackendInterface
38 {
39 /**
40 * A prefix to separate stored data from other data possible stored in the wincache
41 *
42 * @var string
43 */
44 protected $identifierPrefix;
45
46 /**
47 * Constructs this backend
48 *
49 * @param string $context FLOW3's application context
50 * @param array $options Configuration options
51 * @throws \TYPO3\CMS\Core\Cache\Exception If wincache PHP extension is not loaded
52 */
53 public function __construct($context, array $options = [])
54 {
55 if (!extension_loaded('wincache')) {
56 throw new \TYPO3\CMS\Core\Cache\Exception('The PHP extension "wincache" must be installed and loaded in order to use the wincache backend.', 1343331520);
57 }
58 parent::__construct($context, $options);
59 }
60
61 /**
62 * Saves data in the cache
63 *
64 * @param string $entryIdentifier An identifier for this specific cache entry
65 * @param string $data The data to be stored
66 * @param array $tags Tags to associate with this cache entry
67 * @param int $lifetime Lifetime of this cache entry in seconds. If NULL is specified, the default lifetime is used. "0" means unlimited lifetime.
68 * @throws \TYPO3\CMS\Core\Cache\Exception if no cache frontend has been set
69 * @throws \InvalidArgumentException if the identifier is not valid
70 * @throws \TYPO3\CMS\Core\Cache\Exception\InvalidDataException if $data is not a string
71 */
72 public function set($entryIdentifier, $data, array $tags = [], $lifetime = null)
73 {
74 if (!$this->cache instanceof \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface) {
75 throw new \TYPO3\CMS\Core\Cache\Exception('No cache frontend has been set yet via setCache().', 1343331521);
76 }
77 if (!is_string($data)) {
78 throw new \TYPO3\CMS\Core\Cache\Exception\InvalidDataException('The specified data is of type "' . gettype($data) . '" but a string is expected.', 1343331522);
79 }
80 $tags[] = '%WCBE%' . $this->cache->getIdentifier();
81 $expiration = $lifetime !== null ? $lifetime : $this->defaultLifetime;
82 $success = wincache_ucache_set($this->identifierPrefix . $entryIdentifier, $data, $expiration);
83 if ($success === true) {
84 $this->removeIdentifierFromAllTags($entryIdentifier);
85 $this->addIdentifierToTags($entryIdentifier, $tags);
86 } else {
87 throw new \TYPO3\CMS\Core\Cache\Exception('Could not set value.', 1343331523);
88 }
89 }
90
91 /**
92 * Loads data from the cache
93 *
94 * @param string $entryIdentifier An identifier which describes the cache entry to load
95 * @return mixed The cache entry's content as a string or FALSE if the cache entry could not be loaded
96 */
97 public function get($entryIdentifier)
98 {
99 $success = false;
100 $value = wincache_ucache_get($this->identifierPrefix . $entryIdentifier, $success);
101 return $success ? $value : $success;
102 }
103
104 /**
105 * Checks if a cache entry with the specified identifier exists
106 *
107 * @param string $entryIdentifier An identifier specifying the cache entry
108 * @return bool TRUE if such an entry exists, FALSE if not
109 */
110 public function has($entryIdentifier)
111 {
112 return wincache_ucache_exists($this->identifierPrefix . $entryIdentifier);
113 }
114
115 /**
116 * Removes all cache entries matching the specified identifier.
117 * Usually this only affects one entry but if - for what reason ever -
118 * old entries for the identifier still exist, they are removed as well.
119 *
120 * @param string $entryIdentifier Specifies the cache entry to remove
121 * @return bool TRUE if (at least) an entry could be removed or FALSE if no entry was found
122 */
123 public function remove($entryIdentifier)
124 {
125 $this->removeIdentifierFromAllTags($entryIdentifier);
126 return wincache_ucache_delete($this->identifierPrefix . $entryIdentifier);
127 }
128
129 /**
130 * Finds and returns all cache entry identifiers which are tagged by the
131 * specified tag.
132 *
133 * @param string $tag The tag to search for
134 * @return array An array with identifiers of all matching entries. An empty array if no entries matched
135 */
136 public function findIdentifiersByTag($tag)
137 {
138 $success = false;
139 $identifiers = wincache_ucache_get($this->identifierPrefix . 'tag_' . $tag, $success);
140 if ($success === false) {
141 return [];
142 }
143 return (array)$identifiers;
144 }
145
146 /**
147 * Finds all tags for the given identifier. This function uses reverse tag
148 * index to search for tags.
149 *
150 * @param string $identifier Identifier to find tags by
151 * @return array Array with tags
152 */
153 protected function findTagsByIdentifier($identifier)
154 {
155 $success = false;
156 $tags = wincache_ucache_get($this->identifierPrefix . 'ident_' . $identifier, $success);
157 return $success ? (array)$tags : [];
158 }
159
160 /**
161 * Removes all cache entries of this cache
162 *
163 * @throws \TYPO3\CMS\Core\Cache\Exception
164 */
165 public function flush()
166 {
167 if (!$this->cache instanceof \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface) {
168 throw new \TYPO3\CMS\Core\Cache\Exception('Yet no cache frontend has been set via setCache().', 1343331524);
169 }
170 $this->flushByTag('%WCBE%' . $this->cache->getIdentifier());
171 }
172
173 /**
174 * Removes all cache entries of this cache which are tagged by the specified
175 * tag.
176 *
177 * @param string $tag The tag the entries must have
178 */
179 public function flushByTag($tag)
180 {
181 $identifiers = $this->findIdentifiersByTag($tag);
182 foreach ($identifiers as $identifier) {
183 $this->remove($identifier);
184 }
185 }
186
187 /**
188 * Associates the identifier with the given tags
189 *
190 * @param string $entryIdentifier
191 * @param array $tags
192 */
193 protected function addIdentifierToTags($entryIdentifier, array $tags)
194 {
195 // Get identifier-to-tag index to look for updates
196 $existingTags = $this->findTagsByIdentifier($entryIdentifier);
197 $existingTagsUpdated = false;
198
199 foreach ($tags as $tag) {
200 // Update tag-to-identifier index
201 $identifiers = $this->findIdentifiersByTag($tag);
202 if (!in_array($entryIdentifier, $identifiers, true)) {
203 $identifiers[] = $entryIdentifier;
204 wincache_ucache_set($this->identifierPrefix . 'tag_' . $tag, $identifiers);
205 }
206 // Test if identifier-to-tag index needs update
207 if (!in_array($tag, $existingTags, true)) {
208 $existingTags[] = $tag;
209 $existingTagsUpdated = true;
210 }
211 }
212
213 // Update identifier-to-tag index if needed
214 if ($existingTagsUpdated) {
215 wincache_ucache_set($this->identifierPrefix . 'ident_' . $entryIdentifier, $existingTags);
216 }
217 }
218
219 /**
220 * Removes association of the identifier with the given tags
221 *
222 * @param string $entryIdentifier
223 */
224 protected function removeIdentifierFromAllTags($entryIdentifier)
225 {
226 // Get tags for this identifier
227 $tags = $this->findTagsByIdentifier($entryIdentifier);
228 // Deassociate tags with this identifier
229 foreach ($tags as $tag) {
230 $identifiers = $this->findIdentifiersByTag($tag);
231 // Formally array_search() below should never return false due to
232 // the behavior of findTagsByIdentifier(). But if reverse index is
233 // corrupted, we still can get 'false' from array_search(). This is
234 // not a problem because we are removing this identifier from
235 // anywhere.
236 if (($key = array_search($entryIdentifier, $identifiers)) !== false) {
237 unset($identifiers[$key]);
238 if (!empty($identifiers)) {
239 wincache_ucache_set($this->identifierPrefix . 'tag_' . $tag, $identifiers);
240 } else {
241 wincache_ucache_delete($this->identifierPrefix . 'tag_' . $tag);
242 }
243 }
244 }
245 // Clear reverse tag index for this identifier
246 wincache_ucache_delete($this->identifierPrefix . 'ident_' . $entryIdentifier);
247 }
248
249 /**
250 * Does nothing, as wincache does GC itself
251 */
252 public function collectGarbage()
253 {
254 }
255 }