|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2009-2011 Ingo Renner <ingo@typo3.org> 00006 * All rights reserved 00007 * 00008 * This script is part of the TYPO3 project. The TYPO3 project is 00009 * free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * The GNU General Public License can be found at 00015 * http://www.gnu.org/copyleft/gpl.html. 00016 * 00017 * This script is distributed in the hope that it will be useful, 00018 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 * GNU General Public License for more details. 00021 * 00022 * This copyright notice MUST APPEAR in all copies of the script! 00023 ***************************************************************/ 00024 00025 00026 /** 00027 * A caching backend which stores cache entries in files 00028 * 00029 * This file is a backport from FLOW3 00030 * 00031 * @package TYPO3 00032 * @subpackage t3lib_cache 00033 * @api 00034 * @version $Id: class.t3lib_cache_backend_filebackend.php 10121 2011-01-18 20:15:30Z ohader $ 00035 */ 00036 class t3lib_cache_backend_FileBackend extends t3lib_cache_backend_AbstractBackend implements t3lib_cache_backend_PhpCapableBackend { 00037 00038 const SEPARATOR = '^'; 00039 00040 const EXPIRYTIME_FORMAT = 'YmdHis'; 00041 const EXPIRYTIME_LENGTH = 14; 00042 00043 const DATASIZE_DIGITS = 10; 00044 00045 /** 00046 * @var string Directory where the files are stored 00047 */ 00048 protected $cacheDirectory = ''; 00049 00050 /** 00051 * @var string Absolute path to root, usually document root of website 00052 */ 00053 protected $root = '/'; 00054 00055 /** 00056 * Maximum allowed file path length in the current environment. 00057 * 00058 * @var integer 00059 */ 00060 protected $maximumPathLength = null; 00061 00062 /** 00063 * Constructs this backend 00064 * 00065 * @param array $options Configuration options - depends on the actual backend 00066 */ 00067 public function __construct(array $options = array()) { 00068 parent::__construct($options); 00069 00070 if (is_null($this->maximumPathLength)) { 00071 $this->maximumPathLength = t3lib_div::getMaximumPathLength(); 00072 } 00073 } 00074 00075 /** 00076 * Sets a reference to the cache frontend which uses this backend and 00077 * initializes the default cache directory 00078 * 00079 * @void 00080 * @author Robert Lemke <robert@typo3.org> 00081 */ 00082 public function setCache(t3lib_cache_frontend_Frontend $cache) { 00083 parent::setCache($cache); 00084 00085 if (empty($this->cacheDirectory)) { 00086 $cacheDirectory = 'typo3temp/cache/'; 00087 try { 00088 $this->setCacheDirectory($cacheDirectory); 00089 } catch (t3lib_cache_Exception $exception) { 00090 } 00091 } 00092 } 00093 00094 /** 00095 * Sets the directory where the cache files are stored. By default it is 00096 * assumed that the directory is below the TYPO3_DOCUMENT_ROOT. However, an 00097 * absolute path can be selected, too. 00098 * 00099 * @param string The directory. If a relative path is given, it's assumed it's in TYPO3_DOCUMENT_ROOT. If an absolute path is given it is taken as is. 00100 * @return void 00101 * @throws t3lib_cache_Exception if the directory does not exist, is not writable or could not be created. 00102 * @author Robert Lemke <robert@typo3.org> 00103 * @author Ingo Renner <ingo@typo3.org> 00104 */ 00105 public function setCacheDirectory($cacheDirectory) { 00106 $documentRoot = PATH_site; 00107 00108 if (($open_basedir = ini_get('open_basedir'))) { 00109 if (TYPO3_OS === 'WIN') { 00110 $delimiter = ';'; 00111 $cacheDirectory = str_replace('\\', '/', $cacheDirectory); 00112 if (!(preg_match('/[A-Z]:/', substr($cacheDirectory, 0, 2)))) { 00113 $cacheDirectory = PATH_site . $cacheDirectory; 00114 } 00115 } else { 00116 $delimiter = ':'; 00117 if ($cacheDirectory[0] != '/') { 00118 // relative path to cache directory. 00119 $cacheDirectory = PATH_site . $cacheDirectory; 00120 } 00121 } 00122 00123 $basedirs = explode($delimiter, $open_basedir); 00124 $cacheDirectoryInBaseDir = FALSE; 00125 foreach ($basedirs as $basedir) { 00126 if (TYPO3_OS === 'WIN') { 00127 $basedir = str_replace('\\', '/', $basedir); 00128 } 00129 if ($basedir[strlen($basedir) - 1] !== '/') { 00130 $basedir .= '/'; 00131 } 00132 if (t3lib_div::isFirstPartOfStr($cacheDirectory, $basedir)) { 00133 $documentRoot = $basedir; 00134 $cacheDirectory = str_replace($basedir, '', $cacheDirectory); 00135 $cacheDirectoryInBaseDir = TRUE; 00136 break; 00137 } 00138 } 00139 if (!$cacheDirectoryInBaseDir) { 00140 throw new t3lib_cache_Exception( 00141 'Open_basedir restriction in effect. The directory "' . $cacheDirectory . '" is not in an allowed path.' 00142 ); 00143 } 00144 } else { 00145 if ($cacheDirectory[0] == '/') { 00146 // Absolute path to cache directory. 00147 $documentRoot = '/'; 00148 } 00149 if (TYPO3_OS === 'WIN') { 00150 if (substr($cacheDirectory, 0, strlen($documentRoot)) === $documentRoot) { 00151 $documentRoot = ''; 00152 } 00153 } 00154 } 00155 00156 // After this point all paths have '/' as directory seperator 00157 if ($cacheDirectory[strlen($cacheDirectory) - 1] !== '/') { 00158 $cacheDirectory .= '/'; 00159 } 00160 $cacheDirectory .= $this->cacheIdentifier; 00161 if ($cacheDirectory[strlen($cacheDirectory) - 1] !== '/') { 00162 $cacheDirectory .= '/'; 00163 } 00164 00165 if (!is_writable($documentRoot . $cacheDirectory)) { 00166 t3lib_div::mkdir_deep( 00167 $documentRoot, 00168 $cacheDirectory 00169 ); 00170 } 00171 if (!is_dir($documentRoot . $cacheDirectory)) { 00172 throw new t3lib_cache_Exception( 00173 'The directory "' . $documentRoot . $cacheDirectory . '" does not exist.', 00174 1203965199 00175 ); 00176 } 00177 if (!is_writable($documentRoot . $cacheDirectory)) { 00178 throw new t3lib_cache_Exception( 00179 'The directory "' . $documentRoot . $cacheDirectory . '" is not writable.', 00180 1203965200 00181 ); 00182 } 00183 00184 $this->root = $documentRoot; 00185 $this->cacheDirectory = $cacheDirectory; 00186 } 00187 00188 /** 00189 * Returns the directory where the cache files are stored 00190 * 00191 * @return string Full path of the cache directory 00192 * @author Robert Lemke <robert@typo3.org> 00193 * @api 00194 */ 00195 public function getCacheDirectory() { 00196 return $this->root . $this->cacheDirectory; 00197 } 00198 00199 /** 00200 * Saves data in a cache file. 00201 * 00202 * @param string $entryIdentifier An identifier for this specific cache entry 00203 * @param string $data The data to be stored 00204 * @param array $tags Tags to associate with this cache entry 00205 * @param integer $lifetime Lifetime of this cache entry in seconds. If NULL is specified, the default lifetime is used. "0" means unlimited lifetime. 00206 * @return void 00207 * @throws t3lib_cache_Exception if the directory does not exist or is not writable or exceeds the maximum allowed path length, or if no cache frontend has been set. 00208 * @throws t3lib_cache_exception_InvalidData if the data to bes stored is not a string. 00209 * @author Robert Lemke <robert@typo3.org> 00210 * @api 00211 */ 00212 public function set($entryIdentifier, $data, array $tags = array(), $lifetime = NULL) { 00213 if (!$this->cache instanceof t3lib_cache_frontend_Frontend) { 00214 throw new t3lib_cache_Exception( 00215 'No cache frontend has been set yet via setCache().', 00216 1204111375 00217 ); 00218 } 00219 00220 if (!is_string($data)) { 00221 throw new t3lib_cache_Exception_InvalidData( 00222 'The specified data is of type "' . gettype($data) . '" but a string is expected.', 00223 1204481674 00224 ); 00225 } 00226 00227 if ($entryIdentifier !== basename($entryIdentifier)) { 00228 throw new InvalidArgumentException( 00229 'The specified entry identifier must not contain a path segment.', 00230 1282073032 00231 ); 00232 } 00233 00234 $this->remove($entryIdentifier); 00235 00236 $temporaryCacheEntryPathAndFilename = $this->root . $this->cacheDirectory . uniqid() . '.temp'; 00237 if (strlen($temporaryCacheEntryPathAndFilename) > $this->maximumPathLength) { 00238 throw new t3lib_cache_Exception( 00239 'The length of the temporary cache file path "' . $temporaryCacheEntryPathAndFilename . 00240 '" is ' . strlen($temporaryCacheEntryPathAndFilename) . ' characters long and exceeds the maximum path length of ' . 00241 $this->maximumPathLength . '. Please consider setting the temporaryDirectoryBase option to a shorter path. ', 00242 1248710426 00243 ); 00244 } 00245 00246 $expiryTime = ($lifetime === NULL) ? 0 : ($GLOBALS['EXEC_TIME'] + $lifetime); 00247 $metaData = str_pad($expiryTime, self::EXPIRYTIME_LENGTH) . implode(' ', $tags) . str_pad(strlen($data), self::DATASIZE_DIGITS); 00248 $result = file_put_contents($temporaryCacheEntryPathAndFilename, $data . $metaData); 00249 00250 if ($result === FALSE) { 00251 throw new t3lib_cache_exception( 00252 'The temporary cache file "' . $temporaryCacheEntryPathAndFilename . '" could not be written.', 00253 1204026251 00254 ); 00255 } 00256 00257 $i = 0; 00258 $cacheEntryPathAndFilename = $this->root . $this->cacheDirectory . $entryIdentifier; 00259 // @TODO: Figure out why the heck this is done and maybe find a smarter solution, report to FLOW3 00260 while (!rename($temporaryCacheEntryPathAndFilename, $cacheEntryPathAndFilename) && $i < 5) { 00261 $i++; 00262 } 00263 00264 // @FIXME: At least the result of rename() should be handled here, report to FLOW3 00265 if ($result === FALSE) { 00266 throw new t3lib_cache_exception( 00267 'The cache file "' . $cacheEntryPathAndFilename . '" could not be written.', 00268 1222361632 00269 ); 00270 } 00271 } 00272 00273 /** 00274 * Loads data from a cache file. 00275 * 00276 * @param string $entryIdentifier An identifier which describes the cache entry to load 00277 * @return mixed The cache entry's content as a string or FALSE if the cache entry could not be loaded 00278 * @author Robert Lemke <robert@typo3.org> 00279 * @author Karsten Dambekalns <karsten@typo3.org> 00280 * @api 00281 */ 00282 public function get($entryIdentifier) { 00283 if ($entryIdentifier !== basename($entryIdentifier)) { 00284 throw new InvalidArgumentException( 00285 'The specified entry identifier must not contain a path segment.', 00286 1282073033 00287 ); 00288 } 00289 00290 $pathAndFilename = $this->root . $this->cacheDirectory . $entryIdentifier; 00291 if ($this->isCacheFileExpired($pathAndFilename)) { 00292 return FALSE; 00293 } 00294 $dataSize = (integer) file_get_contents($pathAndFilename, NULL, NULL, filesize($pathAndFilename) - self::DATASIZE_DIGITS, self::DATASIZE_DIGITS); 00295 return file_get_contents($pathAndFilename, NULL, NULL, 0, $dataSize); 00296 } 00297 00298 /** 00299 * Checks if a cache entry with the specified identifier exists. 00300 * 00301 * @param string $entryIdentifier Specifies the cache entry to remove 00302 * @return boolean TRUE if (at least) an entry could be removed or FALSE if no entry was found 00303 * @author Robert Lemke <robert@typo3.org> 00304 * @api 00305 */ 00306 public function has($entryIdentifier) { 00307 if ($entryIdentifier !== basename($entryIdentifier)) { 00308 throw new InvalidArgumentException( 00309 'The specified entry identifier must not contain a path segment.', 00310 1282073034 00311 ); 00312 } 00313 00314 return !$this->isCacheFileExpired($this->root . $this->cacheDirectory . $entryIdentifier); 00315 } 00316 00317 /** 00318 * Removes all cache entries matching the specified identifier. 00319 * Usually this only affects one entry. 00320 * 00321 * @param string $entryIdentifier Specifies the cache entry to remove 00322 * @return boolean TRUE if (at least) an entry could be removed or FALSE if no entry was found 00323 * @author Robert Lemke <robert@typo3.org> 00324 * @api 00325 */ 00326 public function remove($entryIdentifier) { 00327 if ($entryIdentifier !== basename($entryIdentifier)) { 00328 throw new InvalidArgumentException( 00329 'The specified entry identifier must not contain a path segment.', 00330 1282073035 00331 ); 00332 } 00333 00334 $pathAndFilename = $this->root . $this->cacheDirectory . $entryIdentifier; 00335 if (!file_exists($pathAndFilename)) { 00336 return FALSE; 00337 } 00338 if (unlink($pathAndFilename) === FALSE) { 00339 return FALSE; 00340 } 00341 return TRUE; 00342 } 00343 00344 /** 00345 * Finds and returns all cache entry identifiers which are tagged by the 00346 * specified tag. 00347 * 00348 * @param string $searchedTag The tag to search for 00349 * @return array An array with identifiers of all matching entries. An empty array if no entries matched 00350 * @author Robert Lemke <robert@typo3.org> 00351 * @author Karsten Dambekalns <karsten@typo3.org> 00352 * @api 00353 */ 00354 public function findIdentifiersByTag($searchedTag) { 00355 $entryIdentifiers = array(); 00356 $now = $GLOBALS['EXEC_TIME']; 00357 for ($directoryIterator = t3lib_div::makeInstance('DirectoryIterator', $this->root . $this->cacheDirectory); $directoryIterator->valid(); $directoryIterator->next()) { 00358 if ($directoryIterator->isDot()) { 00359 continue; 00360 } 00361 $cacheEntryPathAndFilename = $directoryIterator->getPathname(); 00362 $index = (integer) file_get_contents($cacheEntryPathAndFilename, NULL, NULL, filesize($cacheEntryPathAndFilename) - self::DATASIZE_DIGITS, self::DATASIZE_DIGITS); 00363 $metaData = file_get_contents($cacheEntryPathAndFilename, NULL, NULL, $index); 00364 00365 $expiryTime = (integer) substr($metaData, 0, self::EXPIRYTIME_LENGTH); 00366 if ($expiryTime !== 0 && $expiryTime < $now) { 00367 continue; 00368 } 00369 if (in_array($searchedTag, explode(' ', substr($metaData, self::EXPIRYTIME_LENGTH, -self::DATASIZE_DIGITS)))) { 00370 $entryIdentifiers[] = $directoryIterator->getFilename(); 00371 } 00372 } 00373 return $entryIdentifiers; 00374 } 00375 00376 /** 00377 * Finds and returns all cache entry identifiers which are tagged by the 00378 * specified tags. 00379 * 00380 * @param array $searchedTags Array of tags to search for 00381 * @return array An array with identifiers of all matching entries. An empty array if no entries matched 00382 * @author Ingo Renner <ingo@typo3.org> 00383 * @author Christian Kuhn <lolli@schwarzbu.ch> 00384 * @api 00385 */ 00386 public function findIdentifiersByTags(array $searchedTags) { 00387 $entryIdentifiers = array(); 00388 for ($directoryIterator = t3lib_div::makeInstance('DirectoryIterator', $this->root . $this->cacheDirectory); $directoryIterator->valid(); $directoryIterator->next()) { 00389 if ($directoryIterator->isDot()) { 00390 continue; 00391 } 00392 $cacheEntryPathAndFilename = $directoryIterator->getPathname(); 00393 $index = (integer) file_get_contents($cacheEntryPathAndFilename, NULL, NULL, filesize($cacheEntryPathAndFilename) - self::DATASIZE_DIGITS, self::DATASIZE_DIGITS); 00394 $metaData = file_get_contents($cacheEntryPathAndFilename, NULL, NULL, $index); 00395 00396 $expiryTime = (integer) substr($metaData, 0, self::EXPIRYTIME_LENGTH); 00397 if ($expiryTime !== 0 && $expiryTime < $GLOBALS['EXEC_TIME']) { 00398 continue; 00399 } 00400 if (in_array($searchedTags, explode(' ', substr($metaData, self::EXPIRYTIME_LENGTH, -self::DATASIZE_DIGITS)))) { 00401 $entryIdentifiers[] = $directoryIterator->getFilename(); 00402 } 00403 } 00404 return $entryIdentifiers; 00405 } 00406 00407 /** 00408 * Removes all cache entries of this cache. 00409 * 00410 * @return void 00411 * @author Robert Lemke <robert@typo3.org> 00412 * @author Christian Kuhn <lolli@schwarzbu.ch> 00413 * @api 00414 */ 00415 public function flush() { 00416 t3lib_div::rmdir($this->root . $this->cacheDirectory, TRUE); 00417 } 00418 00419 /** 00420 * Removes all cache entries of this cache which are tagged by the specified tag. 00421 * 00422 * @param string $tag The tag the entries must have 00423 * @return void 00424 * @author Ingo Renner <ingo@typo3.org> 00425 * @api 00426 */ 00427 public function flushByTag($tag) { 00428 $identifiers = $this->findIdentifiersByTag($tag); 00429 if (count($identifiers) === 0) { 00430 return; 00431 } 00432 00433 foreach ($identifiers as $entryIdentifier) { 00434 $this->remove($entryIdentifier); 00435 } 00436 } 00437 00438 /** 00439 * Removes all cache entries of this cache which are tagged by the specified tag. 00440 * 00441 * @param array $tags The tags the entries must have 00442 * @return void 00443 * @author Ingo Renner <ingo@typo3.org> 00444 * @api 00445 */ 00446 public function flushByTags(array $tags) { 00447 foreach ($tags as $tag) { 00448 $this->flushByTag($tag); 00449 } 00450 } 00451 00452 /** 00453 * Checks if the given cache entry files are still valid or if their 00454 * lifetime has exceeded. 00455 * 00456 * @param string $cacheEntryPathAndFilename 00457 * @return boolean 00458 * @author Robert Lemke <robert@typo3.org> 00459 */ 00460 protected function isCacheFileExpired($cacheEntryPathAndFilename) { 00461 if (!file_exists($cacheEntryPathAndFilename)) { 00462 return TRUE; 00463 } 00464 $index = (integer) file_get_contents($cacheEntryPathAndFilename, NULL, NULL, filesize($cacheEntryPathAndFilename) - self::DATASIZE_DIGITS, self::DATASIZE_DIGITS); 00465 $expiryTime = file_get_contents($cacheEntryPathAndFilename, NULL, NULL, $index, self::EXPIRYTIME_LENGTH); 00466 return ($expiryTime != 0 && $expiryTime < $GLOBALS['EXEC_TIME']); 00467 } 00468 00469 /** 00470 * Does garbage collection 00471 * 00472 * @return void 00473 * @author Karsten Dambekalns <karsten@typo3.org> 00474 * @api 00475 */ 00476 public function collectGarbage() { 00477 if (!$this->cache instanceof t3lib_cache_frontend_Frontend) { 00478 throw new t3lib_cache_Exception( 00479 'Yet no cache frontend has been set via setCache().', 00480 1222686150 00481 ); 00482 } 00483 00484 $pattern = $this->root . $this->cacheDirectory . '*'; 00485 $filesFound = glob($pattern); 00486 00487 if (is_array($filesFound)) { 00488 foreach ($filesFound as $cacheFilename) { 00489 if ($this->isCacheFileExpired($cacheFilename)) { 00490 $this->remove(basename($cacheFilename)); 00491 } 00492 } 00493 } 00494 } 00495 00496 /** 00497 * Tries to find the cache entry for the specified identifier. 00498 * Usually only one cache entry should be found - if more than one exist, this 00499 * is due to some error or crash. 00500 * 00501 * @param string $entryIdentifier The cache entry identifier 00502 * @return mixed The file names (including path) as an array if one or more entries could be found, otherwise FALSE 00503 * @author Robert Lemke <robert@typo3.org> 00504 * @throws t3lib_cache_Exception if no frontend has been set 00505 * @internal 00506 */ 00507 protected function findCacheFilesByIdentifier($entryIdentifier) { 00508 if (!$this->cache instanceof t3lib_cache_frontend_Frontend) { 00509 throw new t3lib_cache_Exception( 00510 'Yet no cache frontend has been set via setCache().', 00511 1204111376 00512 ); 00513 } 00514 00515 $pattern = $this->root . $this->cacheDirectory . $entryIdentifier; 00516 $filesFound = glob($pattern); 00517 if ($filesFound === FALSE || count($filesFound) === 0) { 00518 return FALSE; 00519 } 00520 00521 return $filesFound; 00522 } 00523 00524 /** 00525 * Loads PHP code from the cache and require_onces it right away. 00526 * 00527 * @param string $entryIdentifier An identifier which describes the cache entry to load 00528 * @return mixed Potential return value from the include operation 00529 * @api 00530 */ 00531 public function requireOnce($entryIdentifier) { 00532 if ($entryIdentifier !== basename($entryIdentifier)) { 00533 throw new InvalidArgumentException( 00534 'The specified entry identifier must not contain a path segment.', 00535 1282073036 00536 ); 00537 } 00538 00539 $pathAndFilename = $this->root . $this->cacheDirectory . $entryIdentifier; 00540 return ($this->isCacheFileExpired($pathAndFilename)) ? FALSE : require_once($pathAndFilename); 00541 } 00542 } 00543 00544 00545 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/cache/backend/class.t3lib_cache_backend_filebackend.php'])) { 00546 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/cache/backend/class.t3lib_cache_backend_filebackend.php']); 00547 } 00548 00549 ?>
1.8.0