TYPO3 CMS  TYPO3_7-6
ApcuBackend.php
Go to the documentation of this file.
1 <?php
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  */
18 
42 {
48  protected $identifierPrefix;
49 
56  {
57  $this->identifierPrefix = $identifierPrefix;
58  }
59 
65  protected function getIdentifierPrefix()
66  {
68  }
69 
77  public function __construct($context, array $options = [])
78  {
79  if (!extension_loaded('apcu')) {
80  throw new Cache\Exception('The PHP extension "apcu" must be installed and loaded in order to use the APCu backend.', 1232985914);
81  }
82  if (PHP_SAPI === 'cli' && ini_get('apc.enable_cli') == 0) {
83  throw new Cache\Exception('The APCu backend cannot be used because apcu is disabled on CLI.', 1232985915);
84  }
85  parent::__construct($context, $options);
86  }
87 
94  public function setCache(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cache)
95  {
96  parent::setCache($cache);
97  $processUser = $this->getCurrentUserData();
98  $pathHash = GeneralUtility::shortMD5($this->getPathSite() . $processUser['name'] . $this->context . $cache->getIdentifier(), 12);
99  $this->setIdentifierPrefix('TYPO3_' . $pathHash);
100  }
101 
108  protected function getCurrentUserData()
109  {
110  return extension_loaded('posix') ? posix_getpwuid(posix_geteuid()) : ['name' => 'default'];
111  }
112 
118  protected function getPathSite()
119  {
120  return PATH_site;
121  }
122 
135  public function set($entryIdentifier, $data, array $tags = [], $lifetime = null)
136  {
137  if (!$this->cache instanceof Cache\Frontend\FrontendInterface) {
138  throw new Cache\Exception('No cache frontend has been set yet via setCache().', 1232986118);
139  }
140  if (!is_string($data)) {
141  throw new Cache\Exception\InvalidDataException('The specified data is of type "' . gettype($data) . '" but a string is expected.', 1232986125);
142  }
143  $tags[] = '%APCBE%' . $this->cacheIdentifier;
144  $expiration = $lifetime !== null ? $lifetime : $this->defaultLifetime;
145  $success = apcu_store($this->getIdentifierPrefix() . $entryIdentifier, $data, $expiration);
146  if ($success === true) {
147  $this->removeIdentifierFromAllTags($entryIdentifier);
148  $this->addIdentifierToTags($entryIdentifier, $tags);
149  } else {
150  throw new Cache\Exception('Could not set value.', 1232986277);
151  }
152  }
153 
161  public function get($entryIdentifier)
162  {
163  $success = false;
164  $value = apcu_fetch($this->getIdentifierPrefix() . $entryIdentifier, $success);
165  return $success ? $value : $success;
166  }
167 
175  public function has($entryIdentifier)
176  {
177  $success = false;
178  apcu_fetch($this->getIdentifierPrefix() . $entryIdentifier, $success);
179  return $success;
180  }
181 
191  public function remove($entryIdentifier)
192  {
193  $this->removeIdentifierFromAllTags($entryIdentifier);
194  return apcu_delete($this->getIdentifierPrefix() . $entryIdentifier);
195  }
196 
205  public function findIdentifiersByTag($tag)
206  {
207  $success = false;
208  $identifiers = apcu_fetch($this->getIdentifierPrefix() . 'tag_' . $tag, $success);
209  if ($success === false) {
210  return [];
211  } else {
212  return (array)$identifiers;
213  }
214  }
215 
223  protected function findTagsByIdentifier($identifier)
224  {
225  $success = false;
226  $tags = apcu_fetch($this->getIdentifierPrefix() . 'ident_' . $identifier, $success);
227  return $success ? (array)$tags : [];
228  }
229 
237  public function flush()
238  {
239  if (!$this->cache instanceof Cache\Frontend\FrontendInterface) {
240  throw new Cache\Exception('Yet no cache frontend has been set via setCache().', 1232986571);
241  }
242  $this->flushByTag('%APCBE%' . $this->cacheIdentifier);
243  }
244 
252  public function flushByTag($tag)
253  {
254  $identifiers = $this->findIdentifiersByTag($tag);
255  foreach ($identifiers as $identifier) {
256  $this->remove($identifier);
257  }
258  }
259 
267  protected function addIdentifierToTags($entryIdentifier, array $tags)
268  {
269  // Get identifier-to-tag index to look for updates
270  $existingTags = $this->findTagsByIdentifier($entryIdentifier);
271  $existingTagsUpdated = false;
272 
273  foreach ($tags as $tag) {
274  // Update tag-to-identifier index
275  $identifiers = $this->findIdentifiersByTag($tag);
276  if (!in_array($entryIdentifier, $identifiers, true)) {
277  $identifiers[] = $entryIdentifier;
278  apcu_store($this->getIdentifierPrefix() . 'tag_' . $tag, $identifiers);
279  }
280  // Test if identifier-to-tag index needs update
281  if (!in_array($tag, $existingTags, true)) {
282  $existingTags[] = $tag;
283  $existingTagsUpdated = true;
284  }
285  }
286 
287  // Update identifier-to-tag index if needed
288  if ($existingTagsUpdated) {
289  apcu_store($this->getIdentifierPrefix() . 'ident_' . $entryIdentifier, $existingTags);
290  }
291  }
292 
299  protected function removeIdentifierFromAllTags($entryIdentifier)
300  {
301  // Get tags for this identifier
302  $tags = $this->findTagsByIdentifier($entryIdentifier);
303  // De-associate tags with this identifier
304  foreach ($tags as $tag) {
305  $identifiers = $this->findIdentifiersByTag($tag);
306  // Formally array_search() below should never return FALSE due to
307  // the behavior of findTagsByIdentifier(). But if reverse index is
308  // corrupted, we still can get 'FALSE' from array_search(). This is
309  // not a problem because we are removing this identifier from
310  // anywhere.
311  if (($key = array_search($entryIdentifier, $identifiers)) !== false) {
312  unset($identifiers[$key]);
313  if (!empty($identifiers)) {
314  apcu_store($this->getIdentifierPrefix() . 'tag_' . $tag, $identifiers);
315  } else {
316  apcu_delete($this->getIdentifierPrefix() . 'tag_' . $tag);
317  }
318  }
319  }
320  // Clear reverse tag index for this identifier
321  apcu_delete($this->getIdentifierPrefix() . 'ident_' . $entryIdentifier);
322  }
323 
329  public function collectGarbage()
330  {
331  }
332 }
setCache(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cache)
Definition: ApcuBackend.php:94
__construct($context, array $options=[])
Definition: ApcuBackend.php:77
addIdentifierToTags($entryIdentifier, array $tags)