[BUGFIX] Cache FileBackend does not respect fileCreateMask
[Packages/TYPO3.CMS.git] / t3lib / cache / backend / class.t3lib_cache_backend_filebackend.php
index 94f2e1d..578136e 100644 (file)
@@ -1,27 +1,26 @@
 <?php
 /***************************************************************
-*  Copyright notice
-*
-*  (c) 2009 Ingo Renner <ingo@typo3.org>
-*  All rights reserved
-*
-*  This script is part of the TYPO3 project. The TYPO3 project is
-*  free software; you can redistribute it and/or modify
-*  it under the terms of the GNU General Public License as published by
-*  the Free Software Foundation; either version 2 of the License, or
-*  (at your option) any later version.
-*
-*  The GNU General Public License can be found at
-*  http://www.gnu.org/copyleft/gpl.html.
-*
-*  This script is distributed in the hope that it will be useful,
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-*  GNU General Public License for more details.
-*
-*  This copyright notice MUST APPEAR in all copies of the script!
-***************************************************************/
-
+ *  Copyright notice
+ *
+ *  (c) 2009-2011 Ingo Renner <ingo@typo3.org>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
 
 /**
  * A caching backend which stores cache entries in files
  *
  * @package TYPO3
  * @subpackage t3lib_cache
- * @version $Id$
+ * @api
+ * @scope prototype
  */
-class t3lib_cache_backend_FileBackend extends t3lib_cache_backend_AbstractBackend {
+class t3lib_cache_backend_FileBackend extends t3lib_cache_backend_AbstractBackend implements t3lib_cache_backend_PhpCapableBackend {
 
        const SEPARATOR = '^';
 
        const EXPIRYTIME_FORMAT = 'YmdHis';
        const EXPIRYTIME_LENGTH = 14;
 
+       const DATASIZE_DIGITS = 10;
+
        /**
-        * @var string Directory where the files are stored
+        * Directory where the files are stored
+        *
+        * @var string
         */
        protected $cacheDirectory = '';
 
-       protected $root = '/';
+       /**
+        * TYPO3 v4 note: This variable is only available in v5
+        * Temporary path to cache directory before setCache() was called. It is
+        * set by setCacheDirectory() and used in setCache() method which calls
+        * the directory creation if needed. The variable is not used afterwards,
+        * the final cache directory path is stored in $this->cacheDirectory then.
+        *
+        * @var string Temporary path to cache directory
+        */
+       protected $temporaryCacheDirectory = '';
 
        /**
-        * Maximum allowed file path length in the current environment.
-        * Will be set in initializeObject()
+        * A file extension to use for each cache entry.
         *
-        * @var integer
+        * @var string
         */
-       protected $maximumPathLength = null;
+       protected $cacheEntryFileExtension = '';
+
 
        /**
-        * Constructs this backend
+        * Sets a reference to the cache frontend which uses this backend and
+        * initializes the default cache directory.
+        *
+        * TYPO3 v4 note: This method is different between TYPO3 v4 and FLOW3
+        * because the Environment class to get the path to a temporary directory
+        * does not exist in v4.
         *
-        * @param mixed Configuration options - depends on the actual backend
+        * @param t3lib_cache_frontend_Frontend $cache The cache frontend
+        * @return void
+        * @author Robert Lemke <robert@typo3.org>
         */
-       public function __construct(array $options = array()) {
-               parent::__construct($options);
-               if (TYPO3_OS === 'WIN') {
-                       $this->root = '';
+       public function setCache(t3lib_cache_frontend_Frontend $cache) {
+               parent::setCache($cache);
+
+               if (empty($this->temporaryCacheDirectory)) {
+                               // If no cache directory was given with cacheDirectory
+                               // configuration option, set it to a path below typo3temp/
+                       $temporaryCacheDirectory = PATH_site . 'typo3temp/';
+               } else {
+                       $temporaryCacheDirectory = $this->temporaryCacheDirectory;
                }
 
-               if (empty($this->cacheDirectory)) {
-                       $cacheDirectory = 'typo3temp/cache/';
-                       try {
-                               $this->setCacheDirectory($cacheDirectory);
-                       } catch(t3lib_cache_Exception $exception) {
+               $codeOrData = ($cache instanceof t3lib_cache_frontend_PhpFrontend) ? 'Code' : 'Data';
+               $finalCacheDirectory = $temporaryCacheDirectory . 'Cache/' . $codeOrData . '/' . $this->cacheIdentifier . '/';
 
-                       }
+               if (!is_dir($finalCacheDirectory)) {
+                       $this->createFinalCacheDirectory($finalCacheDirectory);
                }
+               unset($this->temporaryCacheDirectory);
+               $this->cacheDirectory = $finalCacheDirectory;
 
-               if (is_null($this->maximumPathLength)) {
-                       $this->maximumPathLength = t3lib_div::getMaximumPathLength();
-               }
+               $this->cacheEntryFileExtension = ($cache instanceof t3lib_cache_frontend_PhpFrontend) ? '.php' : '';
        }
 
        /**
@@ -84,55 +107,110 @@ class t3lib_cache_backend_FileBackend extends t3lib_cache_backend_AbstractBacken
         * assumed that the directory is below the TYPO3_DOCUMENT_ROOT. However, an
         * absolute path can be selected, too.
         *
-        * @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.
+        * This method does not exist in FLOW3 anymore, but it is needed in
+        * TYPO3 v4 to enable a cache path outside of document root. The final
+        * cache path is checked and created in createFinalCachDirectory(),
+        * called by setCache() method, which is done _after_ the cacheDirectory
+        * option was handled.
+        *
+        * @param string $cacheDirectory The cache base directory. If a relative path
+        *              is given, it is assumed it is in TYPO3_DOCUMENT_ROOT. If an absolute
+        *              path is given it is taken as is.
         * @return void
-        * @throws t3lib_cache_Exception if the directory does not exist, is not writable or could not be created.
-        * @author Robert Lemke <robert@typo3.org>
-        * @author Ingo Renner <ingo@typo3.org>
+        * @throws t3lib_cache_Exception if the directory is not within allowed
+        *              open_basedir path.
+        * @author Christian Kuhn <lolli@schwarzbu.ch>
         */
        public function setCacheDirectory($cacheDirectory) {
+                       // Skip handling if directory is a stream ressource
+                       // This is used by unit tests with vfs:// directoryies
+               if (strpos($cacheDirectory, '://')) {
+                       $this->temporaryCacheDirectory = $cacheDirectory;
+                       return;
+               }
+
                $documentRoot = PATH_site;
 
-                       // resetting if an absolute path is given
-               if ($cacheDirectory{0} == '/') {
-                       $documentRoot = '/';
+               if (($open_basedir = ini_get('open_basedir'))) {
+                       if (TYPO3_OS === 'WIN') {
+                               $delimiter = ';';
+                               $cacheDirectory = str_replace('\\', '/', $cacheDirectory);
+                               if (!(preg_match('/[A-Z]:/', substr($cacheDirectory, 0, 2)))) {
+                                       $cacheDirectory = PATH_site . $cacheDirectory;
+                               }
+                       } else {
+                               $delimiter = ':';
+                               if ($cacheDirectory[0] != '/') {
+                                               // relative path to cache directory.
+                                       $cacheDirectory = PATH_site . $cacheDirectory;
+                               }
+                       }
+
+                       $basedirs = explode($delimiter, $open_basedir);
+                       $cacheDirectoryInBaseDir = FALSE;
+                       foreach ($basedirs as $basedir) {
+                               if (TYPO3_OS === 'WIN') {
+                                       $basedir = str_replace('\\', '/', $basedir);
+                               }
+                               if ($basedir[strlen($basedir) - 1] !== '/') {
+                                       $basedir .= '/';
+                               }
+                               if (t3lib_div::isFirstPartOfStr($cacheDirectory, $basedir)) {
+                                       $documentRoot = $basedir;
+                                       $cacheDirectory = str_replace($basedir, '', $cacheDirectory);
+                                       $cacheDirectoryInBaseDir = TRUE;
+                                       break;
+                               }
+                       }
+                       if (!$cacheDirectoryInBaseDir) {
+                               throw new t3lib_cache_Exception(
+                                       'Open_basedir restriction in effect. The directory "' . $cacheDirectory . '" is not in an allowed path.'
+                               );
+                       }
+               } else {
+                       if ($cacheDirectory[0] == '/') {
+                                       // Absolute path to cache directory.
+                               $documentRoot = '/';
+                       }
+                       if (TYPO3_OS === 'WIN') {
+                               if (substr($cacheDirectory, 0,  strlen($documentRoot)) === $documentRoot) {
+                                       $documentRoot = '';
+                               }
+                       }
                }
 
-               if ($cacheDirectory{strlen($cacheDirectory) - 1} !== '/') {
+                       // After this point all paths have '/' as directory seperator
+               if ($cacheDirectory[strlen($cacheDirectory) - 1] !== '/') {
                        $cacheDirectory .= '/';
                }
 
-               if (!is_writable($documentRoot . $cacheDirectory)) {
-                       t3lib_div::mkdir_deep(
-                               $documentRoot,
-                               $cacheDirectory
-                       );
-               }
+               $this->temporaryCacheDirectory = $documentRoot . $cacheDirectory . $this->cacheIdentifier . '/';
+       }
 
-               if (!is_dir($documentRoot . $cacheDirectory)) {
-                       throw new t3lib_cache_Exception(
-                               'The directory "' . $cacheDirectory . '" does not exist.',
-                               1203965199
+       /**
+        * Create the final cache directory if it does not exist. This method
+        * exists in TYPO3 v4 only.
+        *
+        * @param string $finalCacheDirectory Absolute path to final cache directory
+        * @return void
+        * @throws \t3lib_cache_Exception If directory is not writable after creation
+        */
+       protected function createFinalCacheDirectory($finalCacheDirectory) {
+               try {
+                       t3lib_div::mkdir_deep($finalCacheDirectory);
+               } catch (\RuntimeException $e) {
+                       throw new \t3lib_cache_Exception(
+                               'The directory "' . $finalCacheDirectory . '" can not be created.',
+                               1303669848,
+                               $e
                        );
                }
-
-               if (!is_writable($documentRoot . $cacheDirectory)) {
-                       throw new t3lib_cache_Exception(
-                               'The directory "' . $cacheDirectory . '" is not writable.',
+               if (!is_writable($finalCacheDirectory)) {
+                       throw new \t3lib_cache_Exception(
+                               'The directory "' . $finalCacheDirectory . '" is not writable.',
                                1203965200
                        );
                }
-
-               $tagsDirectory = $cacheDirectory . 'tags/';
-
-               if (!is_writable($tagsDirectory)) {
-                       t3lib_div::mkdir_deep(
-                               $documentRoot,
-                               $tagsDirectory
-                       );
-               }
-
-               $this->cacheDirectory = $documentRoot . $cacheDirectory;
        }
 
        /**
@@ -140,6 +218,7 @@ class t3lib_cache_backend_FileBackend extends t3lib_cache_backend_AbstractBacken
         *
         * @return string Full path of the cache directory
         * @author Robert Lemke <robert@typo3.org>
+        * @api
         */
        public function getCacheDirectory() {
                return $this->cacheDirectory;
@@ -148,23 +227,17 @@ class t3lib_cache_backend_FileBackend extends t3lib_cache_backend_AbstractBacken
        /**
         * Saves data in a cache file.
         *
-        * @param string An identifier for this specific cache entry
-        * @param string The data to be stored
-        * @param array Tags to associate with this cache entry
-        * @param integer Lifetime of this cache entry in seconds. If NULL is specified, the default lifetime is used. "0" means unlimited lifetime.
+        * @param string $entryIdentifier An identifier for this specific cache entry
+        * @param string $data The data to be stored
+        * @param array $tags Tags to associate with this cache entry
+        * @param integer $lifetime Lifetime of this cache entry in seconds. If NULL is specified, the default lifetime is used. "0" means unlimited lifetime.
         * @return void
         * @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.
         * @throws t3lib_cache_exception_InvalidData if the data to bes stored is not a string.
         * @author Robert Lemke <robert@typo3.org>
+        * @api
         */
        public function set($entryIdentifier, $data, array $tags = array(), $lifetime = NULL) {
-               if (!$this->cache instanceof t3lib_cache_frontend_Frontend) {
-                       throw new t3lib_cache_Exception(
-                               'No cache frontend has been set yet via setCache().',
-                               1204111375
-                       );
-               }
-
                if (!is_string($data)) {
                        throw new t3lib_cache_Exception_InvalidData(
                                'The specified data is of type "' . gettype($data) . '" but a string is expected.',
@@ -172,151 +245,135 @@ class t3lib_cache_backend_FileBackend extends t3lib_cache_backend_AbstractBacken
                        );
                }
 
-               $expirytime     = $this->calculateExpiryTime($lifetime);
-               $cacheEntryPath = $this->renderCacheEntryPath($entryIdentifier);
-
-               if (!is_writable($cacheEntryPath)) {
-                       try {
-                               t3lib_div::mkdir_deep(
-                                       $this->root,
-                                       $cacheEntryPath
-                               );
-                       } catch(Exception $exception) {
+               if ($entryIdentifier !== basename($entryIdentifier)) {
+                       throw new \InvalidArgumentException(
+                               'The specified entry identifier must not contain a path segment.',
+                               1282073032
+                       );
+               }
 
-                       }
-                       if (!is_writable($cacheEntryPath)) {
-                               throw new t3lib_cache_Exception(
-                                       'The cache directory "' . $cacheEntryPath . '" could not be created.',
-                                       1204026250
-                               );
-                       }
+               if ($entryIdentifier === '') {
+                       throw new \InvalidArgumentException(
+                               'The specified entry identifier must not be empty.',
+                               1298114280
+                       );
                }
 
                $this->remove($entryIdentifier);
 
-               $data = $expirytime->format(self::EXPIRYTIME_FORMAT) . $data;
-               $cacheEntryPathAndFilename = $cacheEntryPath . uniqid() . '.temp';
-               if (strlen($cacheEntryPathAndFilename) > $this->maximumPathLength) {
+               $temporaryCacheEntryPathAndFilename = $this->cacheDirectory . uniqid() . '.temp';
+               if (strlen($temporaryCacheEntryPathAndFilename) > t3lib_div::getMaximumPathLength()) {
                        throw new t3lib_cache_Exception(
-                               'The length of the temporary cache file path "' . $cacheEntryPathAndFilename . '" is ' . strlen($cacheEntryPathAndFilename) . ' characters long and exceeds the maximum path length of ' . $this->maximumPathLength . '. Please consider setting the temporaryDirectoryBase option to a shorter path. ',
+                               'The length of the temporary cache file path "' . $temporaryCacheEntryPathAndFilename .
+                               '" is ' . strlen($temporaryCacheEntryPathAndFilename) . ' characters long and exceeds the maximum path length of ' .
+                               t3lib_div::getMaximumPathLength() . '. Please consider setting the temporaryDirectoryBase option to a shorter path. ',
                                1248710426
                        );
                }
 
-               $result = file_put_contents($cacheEntryPathAndFilename, $data);
+               $expiryTime = ($lifetime === NULL) ? 0 : ($GLOBALS['EXEC_TIME'] + $lifetime);
+               $metaData = str_pad($expiryTime, self::EXPIRYTIME_LENGTH) . implode(' ', $tags) . str_pad(strlen($data), self::DATASIZE_DIGITS);
+               $result = file_put_contents($temporaryCacheEntryPathAndFilename, $data . $metaData);
+               t3lib_div::fixPermissions($temporaryCacheEntryPathAndFilename);
+
                if ($result === FALSE) {
-                       throw new t3lib_cache_Exception(
-                               'The temporary cache file "' . $cacheEntryPathAndFilename . '" could not be written.',
+                       throw new t3lib_cache_exception(
+                               'The temporary cache file "' . $temporaryCacheEntryPathAndFilename . '" could not be written.',
                                1204026251
                        );
                }
 
-               for ($i = 0; $i < 5; $i++) {
-                       $result = rename($cacheEntryPathAndFilename, $cacheEntryPath . $entryIdentifier);
-                       if ($result === TRUE) {
-                               break;
-                       }
+               $i = 0;
+               $cacheEntryPathAndFilename = $this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension;
+                       // @TODO: Figure out why the heck this is done and maybe find a smarter solution, report to FLOW3
+               while (!rename($temporaryCacheEntryPathAndFilename, $cacheEntryPathAndFilename) && $i < 5) {
+                       $i++;
                }
 
+                       // @FIXME: At least the result of rename() should be handled here, report to FLOW3
                if ($result === FALSE) {
-                       throw new t3lib_cache_Exception(
-                               'The cache file "' . $entryIdentifier . '" could not be written.',
+                       throw new t3lib_cache_exception(
+                               'The cache file "' . $cacheEntryPathAndFilename . '" could not be written.',
                                1222361632
                        );
                }
-
-               foreach ($tags as $tag) {
-                       $this->setTag($entryIdentifier, $tag);
-               }
-       }
-
-       /**
-        * Creates a tag that is associated with the given cache identifier
-        *
-        * @param string $entryIdentifier An identifier for this specific cache entry
-        * @param string Tag to associate with this cache entry
-        * @return void
-        * @throws t3lib_cache_Exception if the tag path is not writable or exceeds the maximum allowed path length
-        * @author Bastian Waidelich <bastian@typo3.org>
-        * @author Ingo Renner <ingo@typo3.org>
-        */
-       protected function setTag($entryIdentifier, $tag) {
-               $tagPath = $this->cacheDirectory . 'tags/' . $tag . '/';
-
-               if (!is_writable($tagPath)) {
-                       t3lib_div::mkdir_deep($this->root, $tagPath);
-                       if (!is_writable($tagPath)) {
-                               throw new t3lib_cache_Exception(
-                                       'The tag directory "' . $tagPath . '" could not be created.',
-                                       1238242144
-                               );
-                       }
-               }
-
-               $tagPathAndFilename = $tagPath . $this->cache->getIdentifier()
-                       . self::SEPARATOR . $entryIdentifier;
-               if (strlen($tagPathAndFilename) > $this->maximumPathLength) {
-                       throw new t3lib_cache_Exception(
-                               'The length of the tag path "' . $tagPathAndFilename . '" is ' . strlen($tagPathAndFilename) . ' characters long and exceeds the maximum path length of ' . $maximumPathLength . '. Please consider setting the temporaryDirectoryBase option to a shorter path. ',
-                               1248710426
-                       );
-               }
-               touch($tagPathAndFilename);
        }
 
        /**
         * Loads data from a cache file.
         *
-        * @param string An identifier which describes the cache entry to load
+        * @param string $entryIdentifier An identifier which describes the cache entry to load
         * @return mixed The cache entry's content as a string or FALSE if the cache entry could not be loaded
+        * @throws \InvalidArgumentException If identifier is invalid
         * @author Robert Lemke <robert@typo3.org>
         * @author Karsten Dambekalns <karsten@typo3.org>
+        * @api
         */
        public function get($entryIdentifier) {
-               $pathAndFilename = $this->renderCacheEntryPath($entryIdentifier) . $entryIdentifier;
-               return ($this->isCacheFileExpired($pathAndFilename)) ? FALSE : file_get_contents($pathAndFilename, NULL, NULL, self::EXPIRYTIME_LENGTH);
+               if ($entryIdentifier !== basename($entryIdentifier)) {
+                       throw new \InvalidArgumentException(
+                               'The specified entry identifier must not contain a path segment.',
+                               1282073033
+                       );
+               }
+
+               $pathAndFilename = $this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension;
+               if ($this->isCacheFileExpired($pathAndFilename)) {
+                       return FALSE;
+               }
+               $dataSize = (integer) file_get_contents($pathAndFilename, NULL, NULL, filesize($pathAndFilename) - self::DATASIZE_DIGITS, self::DATASIZE_DIGITS);
+               return file_get_contents($pathAndFilename, NULL, NULL, 0, $dataSize);
        }
 
        /**
         * Checks if a cache entry with the specified identifier exists.
         *
-        * @param       string $entryIdentifier
-        * @return      boolean TRUE if such an entry exists, FALSE if not
+        * @param string $entryIdentifier
+        * @return boolean TRUE if such an entry exists, FALSE if not
         * @author Robert Lemke <robert@typo3.org>
+        * @api
         */
        public function has($entryIdentifier) {
-               return !$this->isCacheFileExpired($this->renderCacheEntryPath($entryIdentifier) . $entryIdentifier);
+               if ($entryIdentifier !== basename($entryIdentifier)) {
+                       throw new \InvalidArgumentException(
+                               'The specified entry identifier must not contain a path segment.',
+                               1282073034
+                       );
+               }
+
+               return !$this->isCacheFileExpired($this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension);
        }
 
        /**
         * Removes all cache entries matching the specified identifier.
         * Usually this only affects one entry.
         *
-        * @param string Specifies the cache entry to remove
+        * @param string $entryIdentifier Specifies the cache entry to remove
         * @return boolean TRUE if (at least) an entry could be removed or FALSE if no entry was found
         * @author Robert Lemke <robert@typo3.org>
+        * @api
         */
        public function remove($entryIdentifier) {
-               $pathAndFilename = $this->renderCacheEntryPath($entryIdentifier) . $entryIdentifier;
-
-               if (!file_exists($pathAndFilename)) {
-                       return FALSE;
+               if ($entryIdentifier !== basename($entryIdentifier)) {
+                       throw new \InvalidArgumentException(
+                               'The specified entry identifier must not contain a path segment.',
+                               1282073035
+                       );
+               }
+               if ($entryIdentifier === '') {
+                       throw new \InvalidArgumentException(
+                               'The specified entry identifier must not be empty.',
+                               1298114279
+                       );
                }
 
-               if (unlink ($pathAndFilename) === FALSE) {
+               $pathAndFilename = $this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension;
+               if (file_exists($pathAndFilename) === FALSE) {
                        return FALSE;
                }
-
-               foreach($this->findTagFilesByEntry($entryIdentifier) as $pathAndFilename) {
-                       if (!file_exists($pathAndFilename)) {
-                               return FALSE;
-                       }
-
-                       if (unlink ($pathAndFilename) === FALSE) {
-                               return FALSE;
-                       }
+               if (unlink($pathAndFilename) === FALSE) {
+                       return FALSE;
                }
-
                return TRUE;
        }
 
@@ -324,64 +381,36 @@ class t3lib_cache_backend_FileBackend extends t3lib_cache_backend_AbstractBacken
         * Finds and returns all cache entry identifiers which are tagged by the
         * specified tag.
         *
-        * @param string The tag to search for
+        * @param string $searchedTag The tag to search for
         * @return array An array with identifiers of all matching entries. An empty array if no entries matched
         * @author Robert Lemke <robert@typo3.org>
-        * @author Karsten Dambekalns <karsten@typo3.org>
+        * @api
         */
-       public function findIdentifiersByTag($tag) {
-               if (!$this->cache instanceof t3lib_cache_frontend_Frontend) {
-                       throw new t3lib_cache_Exception(
-                               'Yet no cache frontend has been set via setCache().',
-                               1204111376
-                       );
-               }
-
-               $path    = $this->cacheDirectory . 'tags/';
-               $pattern = $path . $tag . '/'
-                       . $this->cache->getIdentifier() . self::SEPARATOR . '*';
-               $filesFound = glob($pattern);
-
-               if ($filesFound === FALSE || count($filesFound) === 0) {
-                       return array();
-               }
-
-               $cacheEntries = array();
-               foreach ($filesFound as $filename) {
-                       list(,$entryIdentifier) = explode(self::SEPARATOR, basename($filename));
-                       if ($this->has($entryIdentifier)) {
-                               $cacheEntries[$entryIdentifier] = $entryIdentifier;
+       public function findIdentifiersByTag($searchedTag) {
+               $entryIdentifiers = array();
+               $now = $GLOBALS['EXEC_TIME'];
+               $cacheEntryFileExtensionLength = strlen($this->cacheEntryFileExtension);
+               for ($directoryIterator = t3lib_div::makeInstance('DirectoryIterator', $this->cacheDirectory); $directoryIterator->valid(); $directoryIterator->next()) {
+                       if ($directoryIterator->isDot()) {
+                               continue;
                        }
-               }
-
-               return array_values($cacheEntries);
-       }
+                       $cacheEntryPathAndFilename = $directoryIterator->getPathname();
+                       $index = (integer) file_get_contents($cacheEntryPathAndFilename, NULL, NULL, filesize($cacheEntryPathAndFilename) - self::DATASIZE_DIGITS, self::DATASIZE_DIGITS);
+                       $metaData = file_get_contents($cacheEntryPathAndFilename, NULL, NULL, $index);
 
-       /**
-        * Finds and returns all cache entry identifiers which are tagged by the
-        * specified tags.
-        *
-        * @param array Array of tags to search for
-        * @return array An array with identifiers of all matching entries. An empty array if no entries matched
-        * @author Ingo Renner <ingo@typo3.org>
-        */
-       public function findIdentifiersByTags(array $tags) {
-               $taggedEntries = array();
-               $foundEntries  = array();
-
-               foreach ($tags as $tag) {
-                       $taggedEntries[$tag] = $this->findIdentifiersByTag($tag);
-               }
-
-               $intersectedTaggedEntries = call_user_func_array('array_intersect', $taggedEntries);
-
-               foreach ($intersectedTaggedEntries as $entryIdentifier) {
-                       if ($this->has($entryIdentifier)) {
-                               $foundEntries[$entryIdentifier] = $entryIdentifier;
+                       $expiryTime = (integer) substr($metaData, 0, self::EXPIRYTIME_LENGTH);
+                       if ($expiryTime !== 0 && $expiryTime < $now) {
+                               continue;
+                       }
+                       if (in_array($searchedTag, explode(' ', substr($metaData, self::EXPIRYTIME_LENGTH, -self::DATASIZE_DIGITS)))) {
+                               if ($cacheEntryFileExtensionLength > 0) {
+                                       $entryIdentifiers[] = substr($directoryIterator->getFilename(), 0, - $cacheEntryFileExtensionLength);
+                               } else {
+                                       $entryIdentifiers[] = $directoryIterator->getFilename();
+                               }
                        }
                }
-
-               return $foundEntries;
+               return $entryIdentifiers;
        }
 
        /**
@@ -389,28 +418,22 @@ class t3lib_cache_backend_FileBackend extends t3lib_cache_backend_AbstractBacken
         *
         * @return void
         * @author Robert Lemke <robert@typo3.org>
+        * @author Christian Kuhn <lolli@schwarzbu.ch>
+        * @api
         */
        public function flush() {
-               if (!$this->cache instanceof t3lib_cache_frontend_Frontend) {
-                       throw new t3lib_cache_Exception(
-                               'Yet no cache frontend has been set via setCache().',
-                               1204111376
-                       );
-               }
-
-               $dataPath = $this->cacheDirectory . 'data/' . $this->cache->getIdentifier() . '/';
-               $tagsPath = $this->cacheDirectory . 'tags/';
-
-               t3lib_div::rmdir($dataPath, true);
-               t3lib_div::rmdir($tagsPath, true);
+               t3lib_div::rmdir($this->cacheDirectory, TRUE);
+               $this->createFinalCacheDirectory($this->cacheDirectory);
        }
 
        /**
         * Removes all cache entries of this cache which are tagged by the specified tag.
         *
-        * @param string The tag the entries must have
+        * @param string $tag The tag the entries must have
         * @return void
+        * @author Robert Lemke <robert@typo3.org>
         * @author Ingo Renner <ingo@typo3.org>
+        * @api
         */
        public function flushByTag($tag) {
                $identifiers = $this->findIdentifiersByTag($tag);
@@ -423,30 +446,22 @@ class t3lib_cache_backend_FileBackend extends t3lib_cache_backend_AbstractBacken
                }
        }
 
-       /**
-        * Removes all cache entries of this cache which are tagged by the specified tag.
-        *
-        * @param array The tags the entries must have
-        * @return void
-        * @author Ingo Renner <ingo@typo3.org>
-        */
-       public function flushByTags(array $tags) {
-               foreach ($tags as $tag) {
-                       $this->flushByTag($tag);
-               }
-       }
-
        /**
         * Checks if the given cache entry files are still valid or if their
         * lifetime has exceeded.
         *
-        * @param       string  $cacheFilename
-        * @return      boolean
+        * @param string $cacheEntryPathAndFilename
+        * @return boolean
         * @author Robert Lemke <robert@typo3.org>
+        * @api
         */
-       protected function isCacheFileExpired($cacheFilename) {
-               $timestamp = (file_exists($cacheFilename)) ? file_get_contents($cacheFilename, NULL, NULL, 0, self::EXPIRYTIME_LENGTH) : 1;
-               return $timestamp < gmdate(self::EXPIRYTIME_FORMAT);
+       protected function isCacheFileExpired($cacheEntryPathAndFilename) {
+               if (file_exists($cacheEntryPathAndFilename) === FALSE) {
+                       return TRUE;
+               }
+               $index = (integer) file_get_contents($cacheEntryPathAndFilename, NULL, NULL, filesize($cacheEntryPathAndFilename) - self::DATASIZE_DIGITS, self::DATASIZE_DIGITS);
+               $expiryTime = file_get_contents($cacheEntryPathAndFilename, NULL, NULL, $index, self::EXPIRYTIME_LENGTH);
+               return ($expiryTime != 0 && $expiryTime < $GLOBALS['EXEC_TIME']);
        }
 
        /**
@@ -454,37 +469,23 @@ class t3lib_cache_backend_FileBackend extends t3lib_cache_backend_AbstractBacken
         *
         * @return void
         * @author Karsten Dambekalns <karsten@typo3.org>
+        * @api
         */
        public function collectGarbage() {
-               if (!$this->cache instanceof t3lib_cache_frontend_Frontend) {
-                       throw new t3lib_cache_Exception(
-                               'Yet no cache frontend has been set via setCache().',
-                               1222686150
-                       );
-               }
-
-               $pattern = $this->cacheDirectory . 'data/' . $this->cache->getIdentifier() . '/*/*/*';
-               $filesFound = glob($pattern);
-
-               foreach ($filesFound as $cacheFilename) {
-                       if ($this->isCacheFileExpired($cacheFilename)) {
-                               $this->remove(basename($cacheFilename));
-                       }
-               }
-       }
+               for ($directoryIterator = new \DirectoryIterator($this->cacheDirectory); $directoryIterator->valid(); $directoryIterator->next()) {
+                       if ($directoryIterator->isDot()) {
+                               continue;
+                       }
 
-       /**
-        * Renders the full path (excluding file name) leading to the given cache entry.
-        * Doesn't check if such a cache entry really exists.
-        *
-        * @param string $identifier Identifier for the cache entry
-        * @return string Absolute path leading to the directory containing the cache entry
-        * @author Robert Lemke <robert@typo3.org>
-        * @internal
-        */
-       protected function renderCacheEntryPath($identifier) {
-               $identifierHash = sha1($identifier);
-               return $this->cacheDirectory . 'data/' . $this->cache->getIdentifier() . '/' . $identifierHash[0] . '/' . $identifierHash[1] . '/';
+                       if ($this->isCacheFileExpired($directoryIterator->getPathname())) {
+                               $cacheEntryFileExtensionLength = strlen($this->cacheEntryFileExtension);
+                               if ($cacheEntryFileExtensionLength > 0) {
+                                       $this->remove(substr($directoryIterator->getFilename(), 0, - $cacheEntryFileExtensionLength));
+                               } else {
+                                       $this->remove($directoryIterator->getFilename());
+                               }
+                       }
+               }
        }
 
        /**
@@ -492,21 +493,13 @@ class t3lib_cache_backend_FileBackend extends t3lib_cache_backend_AbstractBacken
         * Usually only one cache entry should be found - if more than one exist, this
         * is due to some error or crash.
         *
-        * @param string The cache entry identifier
+        * @param string $entryIdentifier The cache entry identifier
         * @return mixed The file names (including path) as an array if one or more entries could be found, otherwise FALSE
         * @author Robert Lemke <robert@typo3.org>
-        * @throws t3lib_cache_Exception if no frontend has been set
         * @internal
         */
        protected function findCacheFilesByIdentifier($entryIdentifier) {
-               if (!$this->cache instanceof t3lib_cache_frontend_Frontend) {
-                       throw new t3lib_cache_Exception(
-                               'Yet no cache frontend has been set via setCache().',
-                               1204111376
-                       );
-               }
-
-               $pattern    = $this->renderCacheEntryPath($entryIdentifier) . $entryIdentifier;
+               $pattern = $this->cacheDirectory . $entryIdentifier;
                $filesFound = glob($pattern);
                if ($filesFound === FALSE || count($filesFound) === 0) {
                        return FALSE;
@@ -515,33 +508,23 @@ class t3lib_cache_backend_FileBackend extends t3lib_cache_backend_AbstractBacken
                return $filesFound;
        }
 
-
        /**
-        * Tries to find the tag entries for the specified cache entry.
+        * Loads PHP code from the cache and require_onces it right away.
         *
-        * @param string The cache entry identifier to find tag files for
-        * @return array The file names (including path)
-        * @author Robert Lemke <robert@typo3.org>
-        * @throws t3lib_cache_Exception if no frontend has been set
-        * @internal
+        * @param string $entryIdentifier An identifier which describes the cache entry to load
+        * @return mixed Potential return value from the include operation
+        * @api
         */
-       protected function findTagFilesByEntry($entryIdentifier) {
-               if (!$this->cache instanceof t3lib_cache_frontend_Frontend) {
-                       throw new t3lib_cache_Exception(
-                               'Yet no cache frontend has been set via setCache().',
-                               1204111376
+       public function requireOnce($entryIdentifier) {
+               if ($entryIdentifier !== basename($entryIdentifier)) {
+                       throw new \InvalidArgumentException(
+                               'The specified entry identifier must not contain a path segment.',
+                               1282073036
                        );
                }
 
-               $path    = $this->cacheDirectory . 'tags/';
-               $pattern = $path . '*/' . $this->cache->getIdentifier() . self::SEPARATOR . $entryIdentifier;
-               return glob($pattern);
+               $pathAndFilename = $this->cacheDirectory . $entryIdentifier . $this->cacheEntryFileExtension;
+               return ($this->isCacheFileExpired($pathAndFilename)) ? FALSE : require_once($pathAndFilename);
        }
 }
-
-
-if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/cache/backend/class.t3lib_cache_backend_filebackend.php'])  {
-       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/cache/backend/class.t3lib_cache_backend_filebackend.php']);
-}
-
 ?>
\ No newline at end of file