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