[TASK] Re-work/simplify copyright header in PHP files - Part 2
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Cache / Backend / ApcBackend.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 * A caching backend which stores cache entries by using APC.
18 *
19 * This backend uses the following types of keys:
20 * - tag_xxx
21 * xxx is tag name, value is array of associated identifiers identifier. This
22 * is "forward" tag index. It is mainly used for obtaining content by tag
23 * (get identifier by tag -> get content by identifier)
24 * - ident_xxx
25 * xxx is identifier, value is array of associated tags. This is "reverse" tag
26 * index. It provides quick access for all tags associated with this identifier
27 * and used when removing the identifier
28 *
29 * Each key is prepended with a prefix. By default prefix consists from two parts
30 * separated by underscore character and ends in yet another underscore character:
31 * - "TYPO3"
32 * - MD5 of path to TYPO3 and user running TYPO3
33 * This prefix makes sure that keys from the different installations do not
34 * conflict.
35 *
36 * This file is a backport from FLOW3
37 *
38 * @author Robert Lemke <robert@typo3.org>
39 * @author Karsten Dambekalns <karsten@typo3.org>
40 * @author Christian Jul Jensen <julle@typo3.org>
41 * @author Dmitry Dulepov <dmitry@typo3.org>
42 * @api
43 */
44 class ApcBackend extends \TYPO3\CMS\Core\Cache\Backend\AbstractBackend implements \TYPO3\CMS\Core\Cache\Backend\TaggableBackendInterface {
45
46 /**
47 * A prefix to seperate stored data from other data possible stored in the APC
48 *
49 * @var string
50 */
51 protected $identifierPrefix;
52
53 /**
54 * Set the cache identifier prefix.
55 *
56 * @param string $identifierPrefix
57 */
58 protected function setIdentifierPrefix($identifierPrefix) {
59 $this->identifierPrefix = $identifierPrefix;
60 }
61
62 /**
63 * Retrieves the cache identifier prefix.
64 *
65 * @return string
66 */
67 protected function getIdentifierPrefix() {
68 return $this->identifierPrefix;
69 }
70
71 /**
72 * Constructs this backend
73 *
74 * @param string $context FLOW3's application context
75 * @param array $options Configuration options - unused here
76 * @throws \TYPO3\CMS\Core\Cache\Exception
77 */
78 public function __construct($context, array $options = array()) {
79 if (!extension_loaded('apc')) {
80 throw new \TYPO3\CMS\Core\Cache\Exception('The PHP extension "apc" or "apcu" must be installed and loaded in order to use the APC backend.', 1232985414);
81 }
82 if (PHP_SAPI === 'cli' && ini_get('apc.enable_cli') == 0) {
83 throw new \TYPO3\CMS\Core\Cache\Exception('The APC backend cannot be used because apc is disabled on CLI.', 1232985415);
84 }
85 parent::__construct($context, $options);
86 }
87
88 /**
89 * Initializes the identifier prefix when setting the cache.
90 *
91 * @param \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cache
92 * @return void
93 */
94 public function setCache(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cache) {
95 parent::setCache($cache);
96 $processUser = $this->getCurrentUserData();
97 $pathHash = \TYPO3\CMS\Core\Utility\GeneralUtility::shortMD5($this->getPathSite() . $processUser['name'] . $this->context . $cache->getIdentifier(), 12);
98 $this->setIdentifierPrefix('TYPO3_' . $pathHash);
99 }
100
101 /**
102 * Returns the current user data with posix_getpwuid or a default structure when
103 * posix_getpwuid is not available.
104 *
105 * @return array
106 */
107 protected function getCurrentUserData() {
108 return extension_loaded('posix') ? posix_getpwuid(posix_geteuid()) : array('name' => 'default');
109 }
110
111 /**
112 * Returns the PATH_site constant.
113 *
114 * @return string
115 */
116 protected function getPathSite() {
117 return PATH_site;
118 }
119
120 /**
121 * Saves data in the cache.
122 *
123 * @param string $entryIdentifier An identifier for this specific cache entry
124 * @param string $data The data to be stored
125 * @param array $tags Tags to associate with this cache entry
126 * @param integer $lifetime Lifetime of this cache entry in seconds. If NULL is specified, the default lifetime is used. "0" means unlimited liftime.
127 * @return void
128 * @throws \TYPO3\CMS\Core\Cache\Exception if no cache frontend has been set.
129 * @throws \TYPO3\CMS\Core\Cache\Exception\InvalidDataException if $data is not a string
130 * @api
131 */
132 public function set($entryIdentifier, $data, array $tags = array(), $lifetime = NULL) {
133 if (!$this->cache instanceof \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface) {
134 throw new \TYPO3\CMS\Core\Cache\Exception('No cache frontend has been set yet via setCache().', 1232986818);
135 }
136 if (!is_string($data)) {
137 throw new \TYPO3\CMS\Core\Cache\Exception\InvalidDataException('The specified data is of type "' . gettype($data) . '" but a string is expected.', 1232986825);
138 }
139 $tags[] = '%APCBE%' . $this->cacheIdentifier;
140 $expiration = $lifetime !== NULL ? $lifetime : $this->defaultLifetime;
141 $success = apc_store($this->getIdentifierPrefix() . $entryIdentifier, $data, $expiration);
142 if ($success === TRUE) {
143 $this->removeIdentifierFromAllTags($entryIdentifier);
144 $this->addIdentifierToTags($entryIdentifier, $tags);
145 } else {
146 throw new \TYPO3\CMS\Core\Cache\Exception('Could not set value.', 1232986877);
147 }
148 }
149
150 /**
151 * Loads data from the cache.
152 *
153 * @param string $entryIdentifier An identifier which describes the cache entry to load
154 * @return mixed The cache entry's content as a string or FALSE if the cache entry could not be loaded
155 * @api
156 */
157 public function get($entryIdentifier) {
158 $success = FALSE;
159 $value = apc_fetch($this->getIdentifierPrefix() . $entryIdentifier, $success);
160 return $success ? $value : $success;
161 }
162
163 /**
164 * Checks if a cache entry with the specified identifier exists.
165 *
166 * @param string $entryIdentifier An identifier specifying the cache entry
167 * @return boolean TRUE if such an entry exists, FALSE if not
168 * @api
169 */
170 public function has($entryIdentifier) {
171 $success = FALSE;
172 apc_fetch($this->getIdentifierPrefix() . $entryIdentifier, $success);
173 return $success;
174 }
175
176 /**
177 * Removes all cache entries matching the specified identifier.
178 * Usually this only affects one entry but if - for what reason ever -
179 * old entries for the identifier still exist, they are removed as well.
180 *
181 * @param string $entryIdentifier Specifies the cache entry to remove
182 * @return boolean TRUE if (at least) an entry could be removed or FALSE if no entry was found
183 * @api
184 */
185 public function remove($entryIdentifier) {
186 $this->removeIdentifierFromAllTags($entryIdentifier);
187 return apc_delete($this->getIdentifierPrefix() . $entryIdentifier);
188 }
189
190 /**
191 * Finds and returns all cache entry identifiers which are tagged by the
192 * specified tag.
193 *
194 * @param string $tag The tag to search for
195 * @return array An array with identifiers of all matching entries. An empty array if no entries matched
196 * @api
197 */
198 public function findIdentifiersByTag($tag) {
199 $success = FALSE;
200 $identifiers = apc_fetch($this->getIdentifierPrefix() . 'tag_' . $tag, $success);
201 if ($success === FALSE) {
202 return array();
203 } else {
204 return (array) $identifiers;
205 }
206 }
207
208 /**
209 * Finds all tags for the given identifier. This function uses reverse tag
210 * index to search for tags.
211 *
212 * @param string $identifier Identifier to find tags by
213 * @return array Array with tags
214 */
215 protected function findTagsByIdentifier($identifier) {
216 $success = FALSE;
217 $tags = apc_fetch($this->getIdentifierPrefix() . 'ident_' . $identifier, $success);
218 return $success ? (array) $tags : array();
219 }
220
221 /**
222 * Removes all cache entries of this cache.
223 *
224 * @return void
225 * @throws \TYPO3\CMS\Core\Cache\Exception
226 * @api
227 */
228 public function flush() {
229 if (!$this->cache instanceof \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface) {
230 throw new \TYPO3\CMS\Core\Cache\Exception('Yet no cache frontend has been set via setCache().', 1232986971);
231 }
232 $this->flushByTag('%APCBE%' . $this->cacheIdentifier);
233 }
234
235 /**
236 * Removes all cache entries of this cache which are tagged by the specified tag.
237 *
238 * @param string $tag The tag the entries must have
239 * @return void
240 * @api
241 */
242 public function flushByTag($tag) {
243 $identifiers = $this->findIdentifiersByTag($tag);
244 foreach ($identifiers as $identifier) {
245 $this->remove($identifier);
246 }
247 }
248
249 /**
250 * Associates the identifier with the given tags
251 *
252 * @param string $entryIdentifier
253 * @param array $tags
254 * @return void
255 */
256 protected function addIdentifierToTags($entryIdentifier, array $tags) {
257 foreach ($tags as $tag) {
258 // Update tag-to-identifier index
259 $identifiers = $this->findIdentifiersByTag($tag);
260 if (array_search($entryIdentifier, $identifiers) === FALSE) {
261 $identifiers[] = $entryIdentifier;
262 apc_store($this->getIdentifierPrefix() . 'tag_' . $tag, $identifiers);
263 }
264 // Update identifier-to-tag index
265 $existingTags = $this->findTagsByIdentifier($entryIdentifier);
266 if (array_search($entryIdentifier, $existingTags) === FALSE) {
267 apc_store($this->getIdentifierPrefix() . 'ident_' . $entryIdentifier, array_merge($existingTags, $tags));
268 }
269 }
270 }
271
272 /**
273 * Removes association of the identifier with the given tags
274 *
275 * @param string $entryIdentifier
276 * @return void
277 */
278 protected function removeIdentifierFromAllTags($entryIdentifier) {
279 // Get tags for this identifier
280 $tags = $this->findTagsByIdentifier($entryIdentifier);
281 // Deassociate tags with this identifier
282 foreach ($tags as $tag) {
283 $identifiers = $this->findIdentifiersByTag($tag);
284 // Formally array_search() below should never return FALSE due to
285 // the behavior of findTagsByIdentifier(). But if reverse index is
286 // corrupted, we still can get 'FALSE' from array_search(). This is
287 // not a problem because we are removing this identifier from
288 // anywhere.
289 if (($key = array_search($entryIdentifier, $identifiers)) !== FALSE) {
290 unset($identifiers[$key]);
291 if (count($identifiers)) {
292 apc_store($this->getIdentifierPrefix() . 'tag_' . $tag, $identifiers);
293 } else {
294 apc_delete($this->getIdentifierPrefix() . 'tag_' . $tag);
295 }
296 }
297 }
298 // Clear reverse tag index for this identifier
299 apc_delete($this->getIdentifierPrefix() . 'ident_' . $entryIdentifier);
300 }
301
302 /**
303 * Does nothing, as APC does GC itself
304 *
305 * @return void
306 */
307 public function collectGarbage() {
308
309 }
310
311 }