‪TYPO3CMS  ‪main
ApcuBackend.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
21 
45 {
49  private string ‪$identifierPrefix = '';
50 
58  public function ‪__construct(‪$context, array $options = [])
59  {
60  if (!extension_loaded('apcu')) {
61  throw new ‪Exception('The PHP extension "apcu" must be installed and loaded in order to use the APCu backend.', 1232985914);
62  }
63  if (PHP_SAPI === 'cli' && ini_get('apc.enable_cli') == 0) {
64  throw new ‪Exception('The APCu backend cannot be used because apcu is disabled on CLI.', 1232985915);
65  }
66  parent::__construct(‪$context, $options);
67  }
68 
73  {
74  parent::setCache(‪$cache);
75  $this->identifierPrefix = 'TYPO3_' . hash('xxh3', ‪Environment::getProjectPath() . $this->context . ‪$cache->‪getIdentifier()) . '_';
76  }
77 
87  public function set($entryIdentifier, $data, array $tags = [], $lifetime = null): void
88  {
89  if (!$this->cache instanceof ‪FrontendInterface) {
90  throw new ‪Exception('No cache frontend has been set yet via setCache().', 1232986118);
91  }
92  $tags[] = '%APCBE%' . ‪$this->cacheIdentifier;
93  $expiration = $lifetime ?? ‪$this->defaultLifetime;
94  $success = apcu_store($this->identifierPrefix . $entryIdentifier, $data, $expiration);
95  if ($success === true) {
96  $this->‪removeIdentifierFromAllTags($entryIdentifier);
97  $this->‪addIdentifierToTags($entryIdentifier, $tags);
98  } else {
99  $this->logger->alert('Error using APCu: Could not save data in the cache.');
100  }
101  }
102 
109  public function get($entryIdentifier): mixed
110  {
111  $success = false;
112  $value = apcu_fetch($this->identifierPrefix . $entryIdentifier, $success);
113  return $success ? $value : $success;
114  }
115 
122  public function ‪has($entryIdentifier): bool
123  {
124  $success = false;
125  apcu_fetch($this->identifierPrefix . $entryIdentifier, $success);
126  return $success;
127  }
128 
137  public function remove($entryIdentifier): bool
138  {
139  $this->‪removeIdentifierFromAllTags($entryIdentifier);
140  return apcu_delete($this->identifierPrefix . $entryIdentifier);
141  }
142 
150  public function ‪findIdentifiersByTag($tag): array
151  {
152  $success = false;
153  $identifiers = apcu_fetch($this->identifierPrefix . 'tag_' . $tag, $success);
154  if ($success === false) {
155  return [];
156  }
157  return (array)$identifiers;
158  }
159 
165  public function ‪flush(): void
166  {
167  if (!$this->cache instanceof ‪FrontendInterface) {
168  throw new ‪Exception('Yet no cache frontend has been set via setCache().', 1232986571);
169  }
170  $this->‪flushByTag('%APCBE%' . $this->cacheIdentifier);
171  }
172 
178  public function ‪flushByTag($tag): void
179  {
180  $identifiers = $this->‪findIdentifiersByTag($tag);
181  foreach ($identifiers as ‪$identifier) {
182  $this->remove(‪$identifier);
183  }
184  }
185 
189  private function ‪addIdentifierToTags(string $entryIdentifier, array $tags): void
190  {
191  // Get identifier-to-tag index to look for updates
192  $existingTags = $this->‪findTagsByIdentifier($entryIdentifier);
193  $existingTagsUpdated = false;
194 
195  foreach ($tags as $tag) {
196  // Update tag-to-identifier index
197  $identifiers = $this->‪findIdentifiersByTag($tag);
198  if (!in_array($entryIdentifier, $identifiers, true)) {
199  $identifiers[] = $entryIdentifier;
200  apcu_store($this->identifierPrefix . 'tag_' . $tag, $identifiers);
201  }
202  // Test if identifier-to-tag index needs update
203  if (!in_array($tag, $existingTags, true)) {
204  $existingTags[] = $tag;
205  $existingTagsUpdated = true;
206  }
207  }
208 
209  // Update identifier-to-tag index if needed
210  if ($existingTagsUpdated) {
211  apcu_store($this->identifierPrefix . 'ident_' . $entryIdentifier, $existingTags);
212  }
213  }
214 
218  private function ‪removeIdentifierFromAllTags(string $entryIdentifier): void
219  {
220  // Get tags for this identifier
221  $tags = $this->‪findTagsByIdentifier($entryIdentifier);
222  // De-associate tags with this identifier
223  foreach ($tags as $tag) {
224  $identifiers = $this->‪findIdentifiersByTag($tag);
225  // Formally array_search() below should never return FALSE due to
226  // the behavior of findTagsByIdentifier(). But if reverse index is
227  // corrupted, we still can get 'FALSE' from array_search(). This is
228  // not a problem because we are removing this identifier from
229  // anywhere.
230  if (($key = array_search($entryIdentifier, $identifiers)) !== false) {
231  unset($identifiers[$key]);
232  if (!empty($identifiers)) {
233  apcu_store($this->identifierPrefix . 'tag_' . $tag, $identifiers);
234  } else {
235  apcu_delete($this->identifierPrefix . 'tag_' . $tag);
236  }
237  }
238  }
239  // Clear reverse tag index for this identifier
240  apcu_delete($this->identifierPrefix . 'ident_' . $entryIdentifier);
241  }
242 
247  private function ‪findTagsByIdentifier(string ‪$identifier): array
248  {
249  $success = false;
250  $tags = apcu_fetch($this->identifierPrefix . 'ident_' . ‪$identifier, $success);
251  return $success ? (array)$tags : [];
252  }
253 
254  public function ‪collectGarbage(): void
255  {
256  // Noop, APCu has internal GC
257  }
258 }
‪TYPO3\CMS\Core\Cache\Backend\ApcuBackend\removeIdentifierFromAllTags
‪removeIdentifierFromAllTags(string $entryIdentifier)
Definition: ApcuBackend.php:218
‪TYPO3\CMS\Core\Cache\Backend\TransientBackendInterface
Definition: TransientBackendInterface.php:33
‪TYPO3\CMS\Core\Cache\Frontend\FrontendInterface\getIdentifier
‪string getIdentifier()
‪TYPO3\CMS\Core\Cache\Backend\ApcuBackend\findTagsByIdentifier
‪findTagsByIdentifier(string $identifier)
Definition: ApcuBackend.php:247
‪TYPO3\CMS\Core\Cache\Backend\AbstractBackend\$cache
‪FrontendInterface $cache
Definition: AbstractBackend.php:38
‪TYPO3\CMS\Core\Cache\Backend\ApcuBackend\__construct
‪__construct($context, array $options=[])
Definition: ApcuBackend.php:58
‪TYPO3\CMS\Core\Cache\Backend\ApcuBackend\has
‪bool has($entryIdentifier)
Definition: ApcuBackend.php:122
‪TYPO3\CMS\Core\Cache\Backend\TaggableBackendInterface
Definition: TaggableBackendInterface.php:22
‪TYPO3\CMS\Core\Cache\Backend\ApcuBackend\collectGarbage
‪collectGarbage()
Definition: ApcuBackend.php:254
‪TYPO3\CMS\Core\Cache\Backend\AbstractBackend\$defaultLifetime
‪int $defaultLifetime
Definition: AbstractBackend.php:57
‪TYPO3\CMS\Core\Cache\Backend\ApcuBackend\flushByTag
‪flushByTag($tag)
Definition: ApcuBackend.php:178
‪TYPO3\CMS\Core\Cache\Backend\ApcuBackend\findIdentifiersByTag
‪array findIdentifiersByTag($tag)
Definition: ApcuBackend.php:150
‪TYPO3\CMS\Core\Cache\Backend\ApcuBackend
Definition: ApcuBackend.php:45
‪TYPO3\CMS\Core\Cache\Backend\ApcuBackend\$identifierPrefix
‪string $identifierPrefix
Definition: ApcuBackend.php:49
‪TYPO3\CMS\Core\Core\Environment\getProjectPath
‪static string getProjectPath()
Definition: Environment.php:160
‪TYPO3\CMS\Core\Cache\Backend\ApcuBackend\addIdentifierToTags
‪addIdentifierToTags(string $entryIdentifier, array $tags)
Definition: ApcuBackend.php:189
‪TYPO3\CMS\Core\Cache\Exception
Definition: DuplicateIdentifierException.php:16
‪TYPO3\CMS\Core\Cache\Backend\ApcuBackend\setCache
‪setCache(FrontendInterface $cache)
Definition: ApcuBackend.php:72
‪TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
Definition: FrontendInterface.php:22
‪TYPO3\CMS\Core\Cache\Backend\AbstractBackend\$cacheIdentifier
‪string $cacheIdentifier
Definition: AbstractBackend.php:42
‪TYPO3\CMS\Core\Cache\Backend\AbstractBackend
Definition: AbstractBackend.php:28
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Core\Cache\Backend
Definition: AbstractBackend.php:16
‪TYPO3\CMS\Core\Cache\Backend\AbstractBackend\$context
‪string $context
Definition: AbstractBackend.php:51
‪TYPO3\CMS\Core\Cache\Backend\ApcuBackend\flush
‪flush()
Definition: ApcuBackend.php:165
‪TYPO3\CMS\Webhooks\Message\$identifier
‪identifier readonly string $identifier
Definition: FileAddedMessage.php:37
‪TYPO3\CMS\Core\Cache\Exception
Definition: Exception.php:22