TYPO3 CMS  TYPO3_8-7
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  */
16 
19 
43 {
49  protected $identifierPrefix;
50 
57  {
58  $this->identifierPrefix = $identifierPrefix;
59  }
60 
66  protected function getIdentifierPrefix()
67  {
69  }
70 
78  public function __construct($context, array $options = [])
79  {
80  if (!extension_loaded('apcu')) {
81  throw new Cache\Exception('The PHP extension "apcu" must be installed and loaded in order to use the APCu backend.', 1232985914);
82  }
83  if (PHP_SAPI === 'cli' && ini_get('apc.enable_cli') == 0) {
84  throw new Cache\Exception('The APCu backend cannot be used because apcu is disabled on CLI.', 1232985915);
85  }
86  parent::__construct($context, $options);
87  }
88 
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 
134  public function set($entryIdentifier, $data, array $tags = [], $lifetime = null)
135  {
136  if (!$this->cache instanceof Cache\Frontend\FrontendInterface) {
137  throw new Cache\Exception('No cache frontend has been set yet via setCache().', 1232986118);
138  }
139  if (!is_string($data)) {
140  throw new Cache\Exception\InvalidDataException('The specified data is of type "' . gettype($data) . '" but a string is expected.', 1232986125);
141  }
142  $tags[] = '%APCBE%' . $this->cacheIdentifier;
143  $expiration = $lifetime !== null ? $lifetime : $this->defaultLifetime;
144  $success = apcu_store($this->getIdentifierPrefix() . $entryIdentifier, $data, $expiration);
145  if ($success === true) {
146  $this->removeIdentifierFromAllTags($entryIdentifier);
147  $this->addIdentifierToTags($entryIdentifier, $tags);
148  } else {
149  $errorMessage = 'Error using APCu: Could not save data in the cache.';
150  GeneralUtility::sysLog($errorMessage, 'core', GeneralUtility::SYSLOG_SEVERITY_ERROR);
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  }
212  return (array)$identifiers;
213  }
214 
222  protected function findTagsByIdentifier($identifier)
223  {
224  $success = false;
225  $tags = apcu_fetch($this->getIdentifierPrefix() . 'ident_' . $identifier, $success);
226  return $success ? (array)$tags : [];
227  }
228 
235  public function flush()
236  {
237  if (!$this->cache instanceof Cache\Frontend\FrontendInterface) {
238  throw new Cache\Exception('Yet no cache frontend has been set via setCache().', 1232986571);
239  }
240  $this->flushByTag('%APCBE%' . $this->cacheIdentifier);
241  }
242 
249  public function flushByTag($tag)
250  {
251  $identifiers = $this->findIdentifiersByTag($tag);
252  foreach ($identifiers as $identifier) {
253  $this->remove($identifier);
254  }
255  }
256 
263  protected function addIdentifierToTags($entryIdentifier, array $tags)
264  {
265  // Get identifier-to-tag index to look for updates
266  $existingTags = $this->findTagsByIdentifier($entryIdentifier);
267  $existingTagsUpdated = false;
268 
269  foreach ($tags as $tag) {
270  // Update tag-to-identifier index
271  $identifiers = $this->findIdentifiersByTag($tag);
272  if (!in_array($entryIdentifier, $identifiers, true)) {
273  $identifiers[] = $entryIdentifier;
274  apcu_store($this->getIdentifierPrefix() . 'tag_' . $tag, $identifiers);
275  }
276  // Test if identifier-to-tag index needs update
277  if (!in_array($tag, $existingTags, true)) {
278  $existingTags[] = $tag;
279  $existingTagsUpdated = true;
280  }
281  }
282 
283  // Update identifier-to-tag index if needed
284  if ($existingTagsUpdated) {
285  apcu_store($this->getIdentifierPrefix() . 'ident_' . $entryIdentifier, $existingTags);
286  }
287  }
288 
294  protected function removeIdentifierFromAllTags($entryIdentifier)
295  {
296  // Get tags for this identifier
297  $tags = $this->findTagsByIdentifier($entryIdentifier);
298  // De-associate tags with this identifier
299  foreach ($tags as $tag) {
300  $identifiers = $this->findIdentifiersByTag($tag);
301  // Formally array_search() below should never return FALSE due to
302  // the behavior of findTagsByIdentifier(). But if reverse index is
303  // corrupted, we still can get 'FALSE' from array_search(). This is
304  // not a problem because we are removing this identifier from
305  // anywhere.
306  if (($key = array_search($entryIdentifier, $identifiers)) !== false) {
307  unset($identifiers[$key]);
308  if (!empty($identifiers)) {
309  apcu_store($this->getIdentifierPrefix() . 'tag_' . $tag, $identifiers);
310  } else {
311  apcu_delete($this->getIdentifierPrefix() . 'tag_' . $tag);
312  }
313  }
314  }
315  // Clear reverse tag index for this identifier
316  apcu_delete($this->getIdentifierPrefix() . 'ident_' . $entryIdentifier);
317  }
318 
322  public function collectGarbage()
323  {
324  }
325 }
setCache(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cache)
Definition: ApcuBackend.php:94
__construct($context, array $options=[])
Definition: ApcuBackend.php:78
addIdentifierToTags($entryIdentifier, array $tags)