‪TYPO3CMS  9.5
FileBackend.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 
24 
29 {
30  const ‪SEPARATOR = '^';
31  const ‪EXPIRYTIME_FORMAT = 'YmdHis';
33  const ‪DATASIZE_DIGITS = 10;
39  protected ‪$cacheEntryFileExtension = '';
40 
44  protected ‪$cacheEntryIdentifiers = [];
45 
49  protected ‪$frozen = false;
50 
63  public function ‪freeze()
64  {
65  if ($this->frozen === true) {
66  throw new \RuntimeException(sprintf('The cache "%s" is already frozen.', $this->cacheIdentifier), 1323353176);
67  }
68  $cacheEntryFileExtensionLength = strlen($this->cacheEntryFileExtension);
69  for ($directoryIterator = new \DirectoryIterator($this->cacheDirectory); $directoryIterator->valid(); $directoryIterator->next()) {
70  if ($directoryIterator->isDot()) {
71  continue;
72  }
73  if ($cacheEntryFileExtensionLength > 0) {
74  $entryIdentifier = substr($directoryIterator->getFilename(), 0, -$cacheEntryFileExtensionLength);
75  } else {
76  $entryIdentifier = $directoryIterator->getFilename();
77  }
78  $this->cacheEntryIdentifiers[$entryIdentifier] = true;
79  file_put_contents($this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension, $this->get($entryIdentifier));
80  }
81  file_put_contents($this->cacheDirectory . 'FrozenCache.data', serialize($this->cacheEntryIdentifiers));
82  $this->frozen = true;
83  }
84 
90  public function ‪isFrozen()
91  {
92  return ‪$this->frozen;
93  }
94 
105  {
106  parent::setCache(‪$cache);
107  if (file_exists($this->cacheDirectory . 'FrozenCache.data')) {
108  $this->frozen = true;
109  $this->cacheEntryIdentifiers = unserialize(file_get_contents($this->cacheDirectory . 'FrozenCache.data'));
110  }
111  }
112 
125  public function set($entryIdentifier, $data, array $tags = [], $lifetime = null)
126  {
127  if (!is_string($data)) {
128  throw new InvalidDataException('The specified data is of type "' . gettype($data) . '" but a string is expected.', 1204481674);
129  }
130  if ($entryIdentifier !== ‪PathUtility::basename($entryIdentifier)) {
131  throw new \InvalidArgumentException('The specified entry identifier must not contain a path segment.', 1282073032);
132  }
133  if ($entryIdentifier === '') {
134  throw new \InvalidArgumentException('The specified entry identifier must not be empty.', 1298114280);
135  }
136  if ($this->frozen === true) {
137  throw new \RuntimeException(sprintf('Cannot add or modify cache entry because the backend of cache "%s" is frozen.', $this->cacheIdentifier), 1323344192);
138  }
139  $this->remove($entryIdentifier);
140  $temporaryCacheEntryPathAndFilename = $this->cacheDirectory . ‪StringUtility::getUniqueId() . '.temp';
141  $lifetime = $lifetime ?? ‪$this->defaultLifetime;
142  $expiryTime = $lifetime === 0 ? 0 : ‪$GLOBALS['EXEC_TIME'] + $lifetime;
143  $metaData = str_pad($expiryTime, self::EXPIRYTIME_LENGTH) . implode(' ', $tags) . str_pad(strlen($data), self::DATASIZE_DIGITS);
144  $result = file_put_contents($temporaryCacheEntryPathAndFilename, $data . $metaData);
145  GeneralUtility::fixPermissions($temporaryCacheEntryPathAndFilename);
146  if ($result === false) {
147  throw new Exception('The temporary cache file "' . $temporaryCacheEntryPathAndFilename . '" could not be written.', 1204026251);
148  }
149  $i = 0;
150  $cacheEntryPathAndFilename = $this->cacheDirectory . $entryIdentifier . ‪$this->cacheEntryFileExtension;
151  while (($result = rename($temporaryCacheEntryPathAndFilename, $cacheEntryPathAndFilename)) === false && $i < 5) {
152  $i++;
153  }
154  if ($result === false) {
155  throw new Exception('The cache file "' . $cacheEntryPathAndFilename . '" could not be written.', 1222361632);
156  }
157  if ($this->cacheEntryFileExtension === '.php') {
158  GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive($cacheEntryPathAndFilename);
159  }
160  }
161 
169  public function get($entryIdentifier)
170  {
171  if ($this->frozen === true) {
172  return isset($this->cacheEntryIdentifiers[$entryIdentifier]) ? file_get_contents($this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension) : false;
173  }
174  if ($entryIdentifier !== ‪PathUtility::basename($entryIdentifier)) {
175  throw new \InvalidArgumentException('The specified entry identifier must not contain a path segment.', 1282073033);
176  }
177  $pathAndFilename = $this->cacheDirectory . $entryIdentifier . ‪$this->cacheEntryFileExtension;
178  if ($this->‪isCacheFileExpired($pathAndFilename)) {
179  return false;
180  }
181  $dataSize = (int)file_get_contents(
182  $pathAndFilename,
183  null,
184  null,
185  filesize($pathAndFilename) - ‪self::DATASIZE_DIGITS,
187  );
188  return file_get_contents($pathAndFilename, null, null, 0, $dataSize);
189  }
190 
198  public function ‪has($entryIdentifier)
199  {
200  if ($this->frozen === true) {
201  return isset($this->cacheEntryIdentifiers[$entryIdentifier]);
202  }
203  if ($entryIdentifier !== ‪PathUtility::basename($entryIdentifier)) {
204  throw new \InvalidArgumentException('The specified entry identifier must not contain a path segment.', 1282073034);
205  }
206  return !$this->‪isCacheFileExpired($this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension);
207  }
208 
218  public function remove($entryIdentifier)
219  {
220  if ($entryIdentifier !== ‪PathUtility::basename($entryIdentifier)) {
221  throw new \InvalidArgumentException('The specified entry identifier must not contain a path segment.', 1282073035);
222  }
223  if ($entryIdentifier === '') {
224  throw new \InvalidArgumentException('The specified entry identifier must not be empty.', 1298114279);
225  }
226  if ($this->frozen === true) {
227  throw new \RuntimeException(sprintf('Cannot remove cache entry because the backend of cache "%s" is frozen.', $this->cacheIdentifier), 1323344193);
228  }
229  $pathAndFilename = $this->cacheDirectory . $entryIdentifier . ‪$this->cacheEntryFileExtension;
230  if (file_exists($pathAndFilename) === false) {
231  return false;
232  }
233  if (@unlink($pathAndFilename) === false) {
234  return false;
235  }
236  return true;
237  }
238 
246  public function ‪findIdentifiersByTag($searchedTag)
247  {
248  $entryIdentifiers = [];
249  $now = ‪$GLOBALS['EXEC_TIME'];
250  $cacheEntryFileExtensionLength = strlen($this->cacheEntryFileExtension);
251  for ($directoryIterator = GeneralUtility::makeInstance(\DirectoryIterator::class, $this->cacheDirectory); $directoryIterator->valid(); $directoryIterator->next()) {
252  if ($directoryIterator->isDot()) {
253  continue;
254  }
255  $cacheEntryPathAndFilename = $directoryIterator->getPathname();
256  $index = (int)file_get_contents(
257  $cacheEntryPathAndFilename,
258  null,
259  null,
260  filesize($cacheEntryPathAndFilename) - ‪self::DATASIZE_DIGITS,
262  );
263  $metaData = file_get_contents($cacheEntryPathAndFilename, null, null, $index);
264  $expiryTime = (int)substr($metaData, 0, self::EXPIRYTIME_LENGTH);
265  if ($expiryTime !== 0 && $expiryTime < $now) {
266  continue;
267  }
268  if (in_array($searchedTag, explode(' ', substr($metaData, self::EXPIRYTIME_LENGTH, -self::DATASIZE_DIGITS)))) {
269  if ($cacheEntryFileExtensionLength > 0) {
270  $entryIdentifiers[] = substr($directoryIterator->getFilename(), 0, -$cacheEntryFileExtensionLength);
271  } else {
272  $entryIdentifiers[] = $directoryIterator->getFilename();
273  }
274  }
275  }
276  return $entryIdentifiers;
277  }
278 
282  public function ‪flush()
283  {
284  parent::flush();
285  if ($this->frozen === true) {
286  $this->frozen = false;
287  }
288  }
289 
295  public function ‪flushByTag($tag)
296  {
297  $identifiers = $this->‪findIdentifiersByTag($tag);
298  if (empty($identifiers)) {
299  return;
300  }
301  foreach ($identifiers as $entryIdentifier) {
302  $this->remove($entryIdentifier);
303  }
304  }
305 
313  protected function ‪isCacheFileExpired($cacheEntryPathAndFilename)
314  {
315  if (file_exists($cacheEntryPathAndFilename) === false) {
316  return true;
317  }
318  $index = (int)file_get_contents(
319  $cacheEntryPathAndFilename,
320  null,
321  null,
322  filesize($cacheEntryPathAndFilename) - ‪self::DATASIZE_DIGITS,
324  );
325  $expiryTime = (int)file_get_contents($cacheEntryPathAndFilename, null, null, $index, self::EXPIRYTIME_LENGTH);
326  return $expiryTime !== 0 && $expiryTime < ‪$GLOBALS['EXEC_TIME'];
327  }
328 
332  public function ‪collectGarbage()
333  {
334  if ($this->frozen === true) {
335  return;
336  }
337  for ($directoryIterator = new \DirectoryIterator($this->cacheDirectory); $directoryIterator->valid(); $directoryIterator->next()) {
338  if ($directoryIterator->isDot()) {
339  continue;
340  }
341  if ($this->‪isCacheFileExpired($directoryIterator->getPathname())) {
342  $cacheEntryFileExtensionLength = strlen($this->cacheEntryFileExtension);
343  if ($cacheEntryFileExtensionLength > 0) {
344  $this->remove(substr($directoryIterator->getFilename(), 0, -$cacheEntryFileExtensionLength));
345  } else {
346  $this->remove($directoryIterator->getFilename());
347  }
348  }
349  }
350  }
351 
360  protected function ‪findCacheFilesByIdentifier($entryIdentifier)
361  {
362  $pattern = $this->cacheDirectory . $entryIdentifier;
363  $filesFound = glob($pattern);
364  if ($filesFound === false || empty($filesFound)) {
365  return false;
366  }
367  return $filesFound;
368  }
369 
377  public function ‪requireOnce($entryIdentifier)
378  {
379  if ($this->frozen === true) {
380  if (isset($this->cacheEntryIdentifiers[$entryIdentifier])) {
381  return require_once $this->cacheDirectory . $entryIdentifier . ‪$this->cacheEntryFileExtension;
382  }
383  return false;
384  }
385  if ($entryIdentifier !== ‪PathUtility::basename($entryIdentifier)) {
386  throw new \InvalidArgumentException('The specified entry identifier must not contain a path segment.', 1282073036);
387  }
388  $pathAndFilename = $this->cacheDirectory . $entryIdentifier . ‪$this->cacheEntryFileExtension;
389  return $this->‪isCacheFileExpired($pathAndFilename) ? false : require_once $pathAndFilename;
390  }
391 
399  public function require(string $entryIdentifier)
400  {
401  if ($this->frozen) {
402  if (isset($this->cacheEntryIdentifiers[$entryIdentifier])) {
403  return require $this->cacheDirectory . $entryIdentifier . ‪$this->cacheEntryFileExtension;
404  }
405  return false;
406  }
407  if ($entryIdentifier !== ‪PathUtility::basename($entryIdentifier)) {
408  throw new \InvalidArgumentException('The specified entry identifier must not contain a path segment.', 1532528246);
409  }
410  $pathAndFilename = $this->cacheDirectory . $entryIdentifier . ‪$this->cacheEntryFileExtension;
411  return $this->‪isCacheFileExpired($pathAndFilename) ? false : require $pathAndFilename;
412  }
413 }
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\requireOnce
‪mixed requireOnce($entryIdentifier)
Definition: FileBackend.php:374
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:23
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\EXPIRYTIME_LENGTH
‪const EXPIRYTIME_LENGTH
Definition: FileBackend.php:32
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\setCache
‪setCache(FrontendInterface $cache)
Definition: FileBackend.php:101
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\EXPIRYTIME_FORMAT
‪const EXPIRYTIME_FORMAT
Definition: FileBackend.php:31
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\has
‪bool has($entryIdentifier)
Definition: FileBackend.php:195
‪TYPO3\CMS\Core\Cache\Backend\TaggableBackendInterface
Definition: TaggableBackendInterface.php:21
‪TYPO3\CMS\Core\Cache\Backend\FreezableBackendInterface
Definition: FreezableBackendInterface.php:21
‪TYPO3\CMS\Core\Cache\Backend\FileBackend
Definition: FileBackend.php:29
‪TYPO3\CMS\Core\Cache\Backend\AbstractBackend\$defaultLifetime
‪int $defaultLifetime
Definition: AbstractBackend.php:54
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\isCacheFileExpired
‪bool isCacheFileExpired($cacheEntryPathAndFilename)
Definition: FileBackend.php:310
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\isFrozen
‪bool isFrozen()
Definition: FileBackend.php:87
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\collectGarbage
‪collectGarbage()
Definition: FileBackend.php:329
‪TYPO3\CMS\Core\Utility\PathUtility\basename
‪static string basename($path)
Definition: PathUtility.php:164
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\DATASIZE_DIGITS
‪const DATASIZE_DIGITS
Definition: FileBackend.php:33
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\flushByTag
‪flushByTag($tag)
Definition: FileBackend.php:292
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\$cacheEntryIdentifiers
‪array $cacheEntryIdentifiers
Definition: FileBackend.php:42
‪TYPO3\CMS\Core\Cache\Exception
Definition: DuplicateIdentifierException.php:2
‪TYPO3\CMS\Core\Cache\Exception\InvalidDataException
Definition: InvalidDataException.php:21
‪TYPO3\CMS\Core\Cache\Backend\SimpleFileBackend
Definition: SimpleFileBackend.php:32
‪TYPO3\CMS\Core\Cache\Backend\AbstractBackend\$cache
‪TYPO3 CMS Core Cache Frontend FrontendInterface $cache
Definition: AbstractBackend.php:35
‪TYPO3\CMS\Core\Service\OpcodeCacheService
Definition: OpcodeCacheService.php:24
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\flush
‪flush()
Definition: FileBackend.php:279
‪TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
Definition: FrontendInterface.php:21
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static string getUniqueId($prefix='')
Definition: StringUtility.php:91
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\findIdentifiersByTag
‪array findIdentifiersByTag($searchedTag)
Definition: FileBackend.php:243
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\$cacheEntryFileExtension
‪string $cacheEntryFileExtension
Definition: FileBackend.php:38
‪TYPO3\CMS\Core\Cache\Backend
Definition: AbstractBackend.php:2
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\findCacheFilesByIdentifier
‪mixed findCacheFilesByIdentifier($entryIdentifier)
Definition: FileBackend.php:357
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:21
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\$frozen
‪bool $frozen
Definition: FileBackend.php:46
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\SEPARATOR
‪const SEPARATOR
Definition: FileBackend.php:30
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\freeze
‪freeze()
Definition: FileBackend.php:60
‪TYPO3\CMS\Core\Cache\Exception
Definition: Exception.php:21