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