TYPO3  7.6
ApcBackend.php
Go to the documentation of this file.
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 
41 {
47  protected $identifierPrefix;
48 
55  {
56  $this->identifierPrefix = $identifierPrefix;
57  }
58 
64  protected function getIdentifierPrefix()
65  {
67  }
68 
76  public function __construct($context, array $options = array())
77  {
78  if (!extension_loaded('apc')) {
79  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);
80  }
81  if (PHP_SAPI === 'cli' && ini_get('apc.enable_cli') == 0) {
82  throw new \TYPO3\CMS\Core\Cache\Exception('The APC backend cannot be used because apc is disabled on CLI.', 1232985415);
83  }
84  parent::__construct($context, $options);
85  }
86 
93  public function setCache(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cache)
94  {
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 
107  protected function getCurrentUserData()
108  {
109  return extension_loaded('posix') ? posix_getpwuid(posix_geteuid()) : array('name' => 'default');
110  }
111 
117  protected function getPathSite()
118  {
119  return PATH_site;
120  }
121 
134  public function set($entryIdentifier, $data, array $tags = array(), $lifetime = null)
135  {
136  if (!$this->cache instanceof \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface) {
137  throw new \TYPO3\CMS\Core\Cache\Exception('No cache frontend has been set yet via setCache().', 1232986818);
138  }
139  if (!is_string($data)) {
140  throw new \TYPO3\CMS\Core\Cache\Exception\InvalidDataException('The specified data is of type "' . gettype($data) . '" but a string is expected.', 1232986825);
141  }
142  $tags[] = '%APCBE%' . $this->cacheIdentifier;
143  $expiration = $lifetime !== null ? $lifetime : $this->defaultLifetime;
144  $success = apc_store($this->getIdentifierPrefix() . $entryIdentifier, $data, $expiration);
145  if ($success === true) {
146  $this->removeIdentifierFromAllTags($entryIdentifier);
147  $this->addIdentifierToTags($entryIdentifier, $tags);
148  } else {
149  throw new \TYPO3\CMS\Core\Cache\Exception('Could not set value.', 1232986877);
150  }
151  }
152 
160  public function get($entryIdentifier)
161  {
162  $success = false;
163  $value = apc_fetch($this->getIdentifierPrefix() . $entryIdentifier, $success);
164  return $success ? $value : $success;
165  }
166 
174  public function has($entryIdentifier)
175  {
176  $success = false;
177  apc_fetch($this->getIdentifierPrefix() . $entryIdentifier, $success);
178  return $success;
179  }
180 
190  public function remove($entryIdentifier)
191  {
192  $this->removeIdentifierFromAllTags($entryIdentifier);
193  return apc_delete($this->getIdentifierPrefix() . $entryIdentifier);
194  }
195 
204  public function findIdentifiersByTag($tag)
205  {
206  $success = false;
207  $identifiers = apc_fetch($this->getIdentifierPrefix() . 'tag_' . $tag, $success);
208  if ($success === false) {
209  return array();
210  } else {
211  return (array)$identifiers;
212  }
213  }
214 
222  protected function findTagsByIdentifier($identifier)
223  {
224  $success = false;
225  $tags = apc_fetch($this->getIdentifierPrefix() . 'ident_' . $identifier, $success);
226  return $success ? (array)$tags : array();
227  }
228 
236  public function flush()
237  {
238  if (!$this->cache instanceof \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface) {
239  throw new \TYPO3\CMS\Core\Cache\Exception('Yet no cache frontend has been set via setCache().', 1232986971);
240  }
241  $this->flushByTag('%APCBE%' . $this->cacheIdentifier);
242  }
243 
251  public function flushByTag($tag)
252  {
253  $identifiers = $this->findIdentifiersByTag($tag);
254  foreach ($identifiers as $identifier) {
255  $this->remove($identifier);
256  }
257  }
258 
266  protected function addIdentifierToTags($entryIdentifier, array $tags)
267  {
268  // Get identifier-to-tag index to look for updates
269  $existingTags = $this->findTagsByIdentifier($entryIdentifier);
270  $existingTagsUpdated = false;
271 
272  foreach ($tags as $tag) {
273  // Update tag-to-identifier index
274  $identifiers = $this->findIdentifiersByTag($tag);
275  if (!in_array($entryIdentifier, $identifiers, true)) {
276  $identifiers[] = $entryIdentifier;
277  apc_store($this->getIdentifierPrefix() . 'tag_' . $tag, $identifiers);
278  }
279  // Test if identifier-to-tag index needs update
280  if (!in_array($tag, $existingTags, true)) {
281  $existingTags[] = $tag;
282  $existingTagsUpdated = true;
283  }
284  }
285 
286  // Update identifier-to-tag index if needed
287  if ($existingTagsUpdated) {
288  apc_store($this->getIdentifierPrefix() . 'ident_' . $entryIdentifier, $existingTags);
289  }
290  }
291 
298  protected function removeIdentifierFromAllTags($entryIdentifier)
299  {
300  // Get tags for this identifier
301  $tags = $this->findTagsByIdentifier($entryIdentifier);
302  // Deassociate tags with this identifier
303  foreach ($tags as $tag) {
304  $identifiers = $this->findIdentifiersByTag($tag);
305  // Formally array_search() below should never return FALSE due to
306  // the behavior of findTagsByIdentifier(). But if reverse index is
307  // corrupted, we still can get 'FALSE' from array_search(). This is
308  // not a problem because we are removing this identifier from
309  // anywhere.
310  if (($key = array_search($entryIdentifier, $identifiers)) !== false) {
311  unset($identifiers[$key]);
312  if (!empty($identifiers)) {
313  apc_store($this->getIdentifierPrefix() . 'tag_' . $tag, $identifiers);
314  } else {
315  apc_delete($this->getIdentifierPrefix() . 'tag_' . $tag);
316  }
317  }
318  }
319  // Clear reverse tag index for this identifier
320  apc_delete($this->getIdentifierPrefix() . 'ident_' . $entryIdentifier);
321  }
322 
328  public function collectGarbage()
329  {
330  }
331 }