TYPO3 CMS  TYPO3_8-7
SimpleFileBackend.php
Go to the documentation of this file.
1 <?php
3 
4 /* *
5  * This script belongs to the FLOW3 framework. *
6  * *
7  * It is free software; you can redistribute it and/or modify it under *
8  * the terms of the GNU Lesser General Public License, either version 3 *
9  * of the License, or (at your option) any later version. *
10  * *
11  * The TYPO3 project - inspiring people to share! *
12  * */
13 
17 
25 {
26  const SEPARATOR = '^';
27  const EXPIRYTIME_FORMAT = 'YmdHis';
28  const EXPIRYTIME_LENGTH = 14;
29  const DATASIZE_DIGITS = 10;
35  protected $cacheDirectory = '';
36 
46  protected $temporaryCacheDirectory = '';
47 
53  protected $cacheEntryFileExtension = '';
54 
58  protected $cacheEntryIdentifiers = [];
59 
63  protected $frozen = false;
64 
76  public function setCache(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cache)
77  {
78  parent::setCache($cache);
79  if (empty($this->temporaryCacheDirectory)) {
80  // If no cache directory was given with cacheDirectory
81  // configuration option, set it to a path below typo3temp/var/
82  $temporaryCacheDirectory = PATH_site . 'typo3temp/var/';
83  } else {
85  }
86  $codeOrData = $cache instanceof \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend ? 'Code' : 'Data';
87  $finalCacheDirectory = $temporaryCacheDirectory . 'Cache/' . $codeOrData . '/' . $this->cacheIdentifier . '/';
88  if (!is_dir($finalCacheDirectory)) {
89  $this->createFinalCacheDirectory($finalCacheDirectory);
90  }
91  unset($this->temporaryCacheDirectory);
92  $this->cacheDirectory = $finalCacheDirectory;
93  $this->cacheEntryFileExtension = $cache instanceof \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend ? '.php' : '';
94  if (strlen($this->cacheDirectory) + 23 > PHP_MAXPATHLEN) {
95  throw new \TYPO3\CMS\Core\Cache\Exception('The length of the temporary cache file path "' . $this->cacheDirectory . '" exceeds the ' . 'maximum path length of ' . (PHP_MAXPATHLEN - 23) . '. Please consider ' . 'setting the temporaryDirectoryBase option to a shorter path.', 1248710426);
96  }
97  }
98 
114  {
115  // Skip handling if directory is a stream ressource
116  // This is used by unit tests with vfs:// directoryies
117  if (strpos($cacheDirectory, '://')) {
118  $this->temporaryCacheDirectory = $cacheDirectory;
119  return;
120  }
121  $documentRoot = PATH_site;
122  if ($open_basedir = ini_get('open_basedir')) {
123  if (TYPO3_OS === 'WIN') {
124  $delimiter = ';';
125  $cacheDirectory = str_replace('\\', '/', $cacheDirectory);
126  if (!preg_match('/[A-Z]:/', substr($cacheDirectory, 0, 2))) {
127  $cacheDirectory = PATH_site . $cacheDirectory;
128  }
129  } else {
130  $delimiter = ':';
131  if ($cacheDirectory[0] !== '/') {
132  // relative path to cache directory.
133  $cacheDirectory = PATH_site . $cacheDirectory;
134  }
135  }
136  $basedirs = explode($delimiter, $open_basedir);
137  $cacheDirectoryInBaseDir = false;
138  foreach ($basedirs as $basedir) {
139  if (TYPO3_OS === 'WIN') {
140  $basedir = str_replace('\\', '/', $basedir);
141  }
142  if ($basedir[strlen($basedir) - 1] !== '/') {
143  $basedir .= '/';
144  }
145  if (\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($cacheDirectory, $basedir)) {
146  $documentRoot = $basedir;
147  $cacheDirectory = str_replace($basedir, '', $cacheDirectory);
148  $cacheDirectoryInBaseDir = true;
149  break;
150  }
151  }
152  if (!$cacheDirectoryInBaseDir) {
153  throw new \TYPO3\CMS\Core\Cache\Exception(
154  'Open_basedir restriction in effect. The directory "' . $cacheDirectory . '" is not in an allowed path.',
155  1476045417
156  );
157  }
158  } else {
159  if ($cacheDirectory[0] === '/') {
160  // Absolute path to cache directory.
161  $documentRoot = '';
162  }
163  if (TYPO3_OS === 'WIN') {
164  if (substr($cacheDirectory, 0, strlen($documentRoot)) === $documentRoot) {
165  $documentRoot = '';
166  }
167  }
168  }
169  // After this point all paths have '/' as directory separator
170  if ($cacheDirectory[strlen($cacheDirectory) - 1] !== '/') {
171  $cacheDirectory .= '/';
172  }
173  $this->temporaryCacheDirectory = $documentRoot . $cacheDirectory;
174  }
175 
183  protected function createFinalCacheDirectory($finalCacheDirectory)
184  {
185  try {
187  } catch (\RuntimeException $e) {
188  throw new \TYPO3\CMS\Core\Cache\Exception('The directory "' . $finalCacheDirectory . '" can not be created.', 1303669848, $e);
189  }
190  if (!is_writable($finalCacheDirectory)) {
191  throw new \TYPO3\CMS\Core\Cache\Exception('The directory "' . $finalCacheDirectory . '" is not writable.', 1203965200);
192  }
193  }
194 
201  public function getCacheDirectory()
202  {
203  return $this->cacheDirectory;
204  }
205 
218  public function set($entryIdentifier, $data, array $tags = [], $lifetime = null)
219  {
220  if (!is_string($data)) {
221  throw new \TYPO3\CMS\Core\Cache\Exception\InvalidDataException('The specified data is of type "' . gettype($data) . '" but a string is expected.', 1334756734);
222  }
223  if ($entryIdentifier !== basename($entryIdentifier)) {
224  throw new \InvalidArgumentException('The specified entry identifier must not contain a path segment.', 1334756735);
225  }
226  if ($entryIdentifier === '') {
227  throw new \InvalidArgumentException('The specified entry identifier must not be empty.', 1334756736);
228  }
229  $temporaryCacheEntryPathAndFilename = $this->cacheDirectory . StringUtility::getUniqueId() . '.temp';
230  $result = file_put_contents($temporaryCacheEntryPathAndFilename, $data);
231  \TYPO3\CMS\Core\Utility\GeneralUtility::fixPermissions($temporaryCacheEntryPathAndFilename);
232  if ($result === false) {
233  throw new \TYPO3\CMS\Core\Cache\Exception('The temporary cache file "' . $temporaryCacheEntryPathAndFilename . '" could not be written.', 1334756737);
234  }
235  $cacheEntryPathAndFilename = $this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension;
236  rename($temporaryCacheEntryPathAndFilename, $cacheEntryPathAndFilename);
237  if ($this->cacheEntryFileExtension === '.php') {
238  GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive($cacheEntryPathAndFilename);
239  }
240  }
241 
250  public function get($entryIdentifier)
251  {
252  if ($entryIdentifier !== basename($entryIdentifier)) {
253  throw new \InvalidArgumentException('The specified entry identifier must not contain a path segment.', 1334756877);
254  }
255  $pathAndFilename = $this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension;
256  if (!file_exists($pathAndFilename)) {
257  return false;
258  }
259  return file_get_contents($pathAndFilename);
260  }
261 
270  public function has($entryIdentifier)
271  {
272  if ($entryIdentifier !== basename($entryIdentifier)) {
273  throw new \InvalidArgumentException('The specified entry identifier must not contain a path segment.', 1334756878);
274  }
275  return file_exists($this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension);
276  }
277 
287  public function remove($entryIdentifier)
288  {
289  if ($entryIdentifier !== basename($entryIdentifier)) {
290  throw new \InvalidArgumentException('The specified entry identifier must not contain a path segment.', 1334756960);
291  }
292  if ($entryIdentifier === '') {
293  throw new \InvalidArgumentException('The specified entry identifier must not be empty.', 1334756961);
294  }
295  try {
296  unlink($this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension);
297  } catch (\Exception $e) {
298  return false;
299  }
300  return true;
301  }
302 
308  public function flush()
309  {
310  \TYPO3\CMS\Core\Utility\GeneralUtility::flushDirectory($this->cacheDirectory, true);
311  }
312 
321  protected function isCacheFileExpired($cacheEntryPathAndFilename)
322  {
323  return file_exists($cacheEntryPathAndFilename) === false;
324  }
325 
331  public function collectGarbage()
332  {
333  }
334 
341  protected function findCacheFilesByIdentifier($entryIdentifier)
342  {
343  $pathAndFilename = $this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension;
344  return file_exists($pathAndFilename) ? [$pathAndFilename] : false;
345  }
346 
355  public function requireOnce($entryIdentifier)
356  {
357  $pathAndFilename = $this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension;
358  if ($entryIdentifier !== basename($entryIdentifier)) {
359  throw new \InvalidArgumentException('The specified entry identifier must not contain a path segment.', 1282073037);
360  }
361  return file_exists($pathAndFilename) ? require_once $pathAndFilename : false;
362  }
363 }
static mkdir_deep($directory, $deepDirectory='')
static flushDirectory($directory, $keepOriginalDirectory=false, $flushOpcodeCache=false)
static isFirstPartOfStr($str, $partStr)
static makeInstance($className,... $constructorArguments)
setCache(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cache)
static fixPermissions($path, $recursive=false)