‪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  $expiration = $lifetime ?? ‪$this->defaultLifetime;
93  $success = apcu_store($this->identifierPrefix . $entryIdentifier, $data, $expiration);
94  if ($success === true) {
95  $this->‪removeIdentifierFromAllTags($entryIdentifier);
96  $this->‪addIdentifierToTags($entryIdentifier, $tags);
97  } else {
98  $this->logger->alert('Error using APCu: Could not save data in the cache.');
99  }
100  }
101 
108  public function get($entryIdentifier): mixed
109  {
110  $success = false;
111  $value = apcu_fetch($this->identifierPrefix . $entryIdentifier, $success);
112  return $success ? $value : $success;
113  }
114 
121  public function ‪has($entryIdentifier): bool
122  {
123  $success = false;
124  apcu_fetch($this->identifierPrefix . $entryIdentifier, $success);
125  return $success;
126  }
127 
136  public function remove($entryIdentifier): bool
137  {
138  $this->‪removeIdentifierFromAllTags($entryIdentifier);
139  return apcu_delete($this->identifierPrefix . $entryIdentifier);
140  }
141 
149  public function ‪findIdentifiersByTag($tag): array
150  {
151  $success = false;
152  $identifiers = apcu_fetch($this->identifierPrefix . 'tag_' . $tag, $success);
153  if ($success === false) {
154  return [];
155  }
156  return (array)$identifiers;
157  }
158 
164  public function ‪flush(): void
165  {
166  if (!$this->cache instanceof ‪FrontendInterface) {
167  throw new ‪Exception('Yet no cache frontend has been set via setCache().', 1232986571);
168  }
169  apcu_delete(new \APCUIterator('/^' . preg_quote($this->identifierPrefix, '/') . '/'));
170  }
171 
177  public function ‪flushByTag($tag): void
178  {
179  $identifiers = $this->‪findIdentifiersByTag($tag);
180  foreach ($identifiers as ‪$identifier) {
181  $this->remove(‪$identifier);
182  }
183  }
184 
188  private function ‪addIdentifierToTags(string $entryIdentifier, array $tags): void
189  {
190  // Get identifier-to-tag index to look for updates
191  $existingTags = $this->‪findTagsByIdentifier($entryIdentifier);
192  $existingTagsUpdated = false;
193 
194  foreach ($tags as $tag) {
195  // Update tag-to-identifier index
196  $identifiers = $this->‪findIdentifiersByTag($tag);
197  if (!in_array($entryIdentifier, $identifiers, true)) {
198  $identifiers[] = $entryIdentifier;
199  apcu_store($this->identifierPrefix . 'tag_' . $tag, $identifiers);
200  }
201  // Test if identifier-to-tag index needs update
202  if (!in_array($tag, $existingTags, true)) {
203  $existingTags[] = $tag;
204  $existingTagsUpdated = true;
205  }
206  }
207 
208  // Update identifier-to-tag index if needed
209  if ($existingTagsUpdated) {
210  apcu_store($this->identifierPrefix . 'ident_' . $entryIdentifier, $existingTags);
211  }
212  }
213 
217  private function ‪removeIdentifierFromAllTags(string $entryIdentifier): void
218  {
219  // Get tags for this identifier
220  $tags = $this->‪findTagsByIdentifier($entryIdentifier);
221  // De-associate tags with this identifier
222  foreach ($tags as $tag) {
223  $identifiers = $this->‪findIdentifiersByTag($tag);
224  // Formally array_search() below should never return FALSE due to
225  // the behavior of findTagsByIdentifier(). But if reverse index is
226  // corrupted, we still can get 'FALSE' from array_search(). This is
227  // not a problem because we are removing this identifier from
228  // anywhere.
229  if (($key = array_search($entryIdentifier, $identifiers)) !== false) {
230  unset($identifiers[$key]);
231  if (!empty($identifiers)) {
232  apcu_store($this->identifierPrefix . 'tag_' . $tag, $identifiers);
233  } else {
234  apcu_delete($this->identifierPrefix . 'tag_' . $tag);
235  }
236  }
237  }
238  // Clear reverse tag index for this identifier
239  apcu_delete($this->identifierPrefix . 'ident_' . $entryIdentifier);
240  }
241 
246  private function ‪findTagsByIdentifier(string ‪$identifier): array
247  {
248  $success = false;
249  $tags = apcu_fetch($this->identifierPrefix . 'ident_' . ‪$identifier, $success);
250  return $success ? (array)$tags : [];
251  }
252 
253  public function ‪collectGarbage(): void
254  {
255  // Noop, APCu has internal GC
256  }
257 }
‪TYPO3\CMS\Core\Cache\Backend\ApcuBackend\removeIdentifierFromAllTags
‪removeIdentifierFromAllTags(string $entryIdentifier)
Definition: ApcuBackend.php:217
‪TYPO3\CMS\Core\Cache\Backend\TransientBackendInterface
Definition: TransientBackendInterface.php:32
‪TYPO3\CMS\Core\Cache\Frontend\FrontendInterface\getIdentifier
‪string getIdentifier()
‪TYPO3\CMS\Core\Cache\Backend\ApcuBackend\findTagsByIdentifier
‪findTagsByIdentifier(string $identifier)
Definition: ApcuBackend.php:246
‪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:121
‪TYPO3\CMS\Core\Cache\Backend\TaggableBackendInterface
Definition: TaggableBackendInterface.php:22
‪TYPO3\CMS\Core\Cache\Backend\ApcuBackend\collectGarbage
‪collectGarbage()
Definition: ApcuBackend.php:253
‪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:177
‪TYPO3\CMS\Core\Cache\Backend\ApcuBackend\findIdentifiersByTag
‪array findIdentifiersByTag($tag)
Definition: ApcuBackend.php:149
‪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:188
‪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
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:164
‪TYPO3\CMS\Webhooks\Message\$identifier
‪identifier readonly string $identifier
Definition: FileAddedMessage.php:37
‪TYPO3\CMS\Core\Cache\Exception
Definition: Exception.php:21