TYPO3 API  SVNRelease
class.t3lib_cache_backend_apcbackend.php
Go to the documentation of this file.
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 by using APC.
00028  *
00029  * This backend uses the following types of keys:
00030  * - tag_xxx
00031  *   xxx is tag name, value is array of associated identifiers identifier. This
00032  *   is "forward" tag index. It is mainly used for obtaining content by tag
00033  *   (get identifier by tag -> get content by identifier)
00034  * - ident_xxx
00035  *   xxx is identifier, value is array of associated tags. This is "reverse" tag
00036  *   index. It provides quick access for all tags associated with this identifier
00037  *   and used when removing the identifier
00038  *
00039  * Each key is prepended with a prefix. By default prefix consists from two parts
00040  * separated by underscore character and ends in yet another underscore character:
00041  * - "TYPO3"
00042  * - MD5 of path to TYPO3 and user running TYPO3
00043  * This prefix makes sure that keys from the different installations do not
00044  * conflict.
00045  *
00046  * This file is a backport from FLOW3
00047  *
00048  * @package TYPO3
00049  * @subpackage t3lib_cache
00050  * @api
00051  * @version $Id: class.t3lib_cache_backend_apcbackend.php 10121 2011-01-18 20:15:30Z ohader $
00052  */
00053 class t3lib_cache_backend_ApcBackend extends t3lib_cache_backend_AbstractBackend {
00054 
00055     /**
00056      * A prefix to seperate stored data from other data possible stored in the APC
00057      *
00058      * @var string
00059      */
00060     protected $identifierPrefix;
00061 
00062     /**
00063      * Constructs this backend
00064      *
00065      * @param array $options Configuration options - unused here
00066      * @author Robert Lemke <robert@typo3.org>
00067      * @author Karsten Dambekalns <karsten@typo3.org>
00068      */
00069     public function __construct(array $options = array()) {
00070         if (!extension_loaded('apc')) {
00071             throw new t3lib_cache_Exception(
00072                 'The PHP extension "apc" must be installed and loaded in order to use the APC backend.',
00073                 1232985414
00074             );
00075         }
00076 
00077         parent::__construct($options);
00078     }
00079 
00080     /**
00081      * Initializes the identifier prefix when setting the cache.
00082      *
00083      * @param t3lib_cache_frontend_Frontend $cache The frontend for this backend
00084      * @return void
00085      * @author Robert Lemke <robert@typo3.org>
00086      */
00087     public function setCache(t3lib_cache_frontend_Frontend $cache) {
00088         parent::setCache($cache);
00089         $processUser = extension_loaded('posix') ? posix_getpwuid(posix_geteuid()) : array('name' => 'default');
00090         $pathHash = t3lib_div::shortMD5(PATH_site . $processUser['name'], 12);
00091         $this->identifierPrefix = 'TYPO3_' . $pathHash;
00092     }
00093 
00094     /**
00095      * Saves data in the cache.
00096      *
00097      * @param string $entryIdentifier An identifier for this specific cache entry
00098      * @param string $data The data to be stored
00099      * @param array $tags Tags to associate with this cache entry
00100      * @param integer $lifetime Lifetime of this cache entry in seconds. If NULL is specified, the default lifetime is used. "0" means unlimited liftime.
00101      * @return void
00102      * @throws t3lib_cache_Exception if no cache frontend has been set.
00103      * @throws InvalidArgumentException if the identifier is not valid
00104      * @throws t3lib_cache_exception_InvalidData if $data is not a string
00105      * @author Christian Jul Jensen <julle@typo3.org>
00106      * @author Karsten Dambekalns <karsten@typo3.org>
00107      */
00108     public function set($entryIdentifier, $data, array $tags = array(), $lifetime = NULL) {
00109         if (!$this->cache instanceof t3lib_cache_frontend_Frontend) {
00110             throw new t3lib_cache_Exception(
00111                 'No cache frontend has been set yet via setCache().',
00112                 1232986818
00113             );
00114         }
00115 
00116         if (!is_string($data)) {
00117             throw new t3lib_cache_exception_InvalidData(
00118                 'The specified data is of type "' . gettype($data) . '" but a string is expected.',
00119                 1232986825
00120             );
00121         }
00122 
00123         $tags[] = '%APCBE%' . $this->cacheIdentifier;
00124         $expiration = $lifetime !== NULL ? $lifetime : $this->defaultLifetime;
00125 
00126         $success = apc_store($this->identifierPrefix . $entryIdentifier, $data, $expiration);
00127         if ($success === TRUE) {
00128             $this->removeIdentifierFromAllTags($entryIdentifier);
00129             $this->addIdentifierToTags($entryIdentifier, $tags);
00130         } else {
00131             throw new t3lib_cache_Exception(
00132                 'Could not set value.',
00133                 1232986877
00134             );
00135         }
00136     }
00137 
00138     /**
00139      * Loads data from the cache.
00140      *
00141      * @param string $entryIdentifier An identifier which describes the cache entry to load
00142      * @return mixed The cache entry's content as a string or FALSE if the cache entry could not be loaded
00143      * @author Karsten Dambekalns <karsten@typo3.org>
00144      */
00145     public function get($entryIdentifier) {
00146         $success = FALSE;
00147         $value = apc_fetch($this->identifierPrefix . $entryIdentifier, $success);
00148 
00149         return ($success ? $value : $success);
00150     }
00151 
00152     /**
00153      * Checks if a cache entry with the specified identifier exists.
00154      *
00155      * @param string $entryIdentifier An identifier specifying the cache entry
00156      * @return boolean TRUE if such an entry exists, FALSE if not
00157      * @author Karsten Dambekalns <karsten@typo3.org>
00158      */
00159     public function has($entryIdentifier) {
00160         $success = FALSE;
00161         apc_fetch($this->identifierPrefix . $entryIdentifier, $success);
00162         return $success;
00163     }
00164 
00165     /**
00166      * Removes all cache entries matching the specified identifier.
00167      * Usually this only affects one entry but if - for what reason ever -
00168      * old entries for the identifier still exist, they are removed as well.
00169      *
00170      * @param string $entryIdentifier Specifies the cache entry to remove
00171      * @return boolean TRUE if (at least) an entry could be removed or FALSE if no entry was found
00172      * @author Christian Jul Jensen <julle@typo3.org>
00173      * @author Karsten Dambekalns <karsten@typo3.org>
00174      */
00175     public function remove($entryIdentifier) {
00176         $this->removeIdentifierFromAllTags($entryIdentifier);
00177 
00178         return apc_delete($this->identifierPrefix . $entryIdentifier);
00179     }
00180 
00181     /**
00182      * Finds and returns all cache entry identifiers which are tagged by the
00183      * specified tag.
00184      *
00185      * @param string $tag The tag to search for
00186      * @return array An array with identifiers of all matching entries. An empty array if no entries matched
00187      * @author Karsten Dambekalns <karsten@typo3.org>
00188      */
00189     public function findIdentifiersByTag($tag) {
00190         $success = FALSE;
00191         $identifiers = apc_fetch($this->identifierPrefix . 'tag_' . $tag, $success);
00192 
00193         if ($success === FALSE) {
00194             return array();
00195         } else {
00196             return (array) $identifiers;
00197         }
00198     }
00199 
00200     /**
00201      * Finds and returns all cache entry identifiers which are tagged by the
00202      * specified tags.
00203      *
00204      * @param array Array of tags to search for
00205      * @return array An array with identifiers of all matching entries. An empty array if no entries matched
00206      * @author Ingo Renner <ingo@typo3.org>
00207      */
00208     public function findIdentifiersByTags(array $tags) {
00209         $taggedEntries = array();
00210         $foundEntries = array();
00211 
00212         foreach ($tags as $tag) {
00213             $taggedEntries[$tag] = $this->findIdentifiersByTag($tag);
00214         }
00215 
00216         $intersectedTaggedEntries = call_user_func_array('array_intersect', $taggedEntries);
00217 
00218         foreach ($intersectedTaggedEntries as $entryIdentifier) {
00219             if ($this->has($entryIdentifier)) {
00220                 $foundEntries[$entryIdentifier] = $entryIdentifier;
00221             }
00222         }
00223 
00224         return $foundEntries;
00225     }
00226 
00227     /**
00228      * Finds all tags for the given identifier. This function uses reverse tag
00229      * index to search for tags.
00230      *
00231      * @param string $identifier Identifier to find tags by
00232      * @return array Array with tags
00233      * @author Karsten Dambekalns <karsten@typo3.org>
00234      */
00235     protected function findTagsByIdentifier($identifier) {
00236         $success = FALSE;
00237         $tags = apc_fetch($this->identifierPrefix . 'ident_' . $identifier, $success);
00238 
00239         return ($success ? (array) $tags : array());
00240     }
00241 
00242     /**
00243      * Removes all cache entries of this cache.
00244      *
00245      * @return void
00246      * @author Karsten Dambekalns <karsten@typo3.org>
00247      */
00248     public function flush() {
00249         if (!$this->cache instanceof t3lib_cache_frontend_Frontend) {
00250             throw new t3lib_cache_Exception(
00251                 'Yet no cache frontend has been set via setCache().',
00252                 1232986971
00253             );
00254         }
00255 
00256         $this->flushByTag('%APCBE%' . $this->cacheIdentifier);
00257     }
00258 
00259     /**
00260      * Removes all cache entries of this cache which are tagged by the specified
00261      * tag.
00262      *
00263      * @param string $tag The tag the entries must have
00264      * @return void
00265      * @author Karsten Dambekalns <karsten@typo3.org>
00266      */
00267     public function flushByTag($tag) {
00268         $identifiers = $this->findIdentifiersByTag($tag);
00269 
00270         foreach ($identifiers as $identifier) {
00271             $this->remove($identifier);
00272         }
00273     }
00274 
00275     /**
00276      * Removes all cache entries of this cache which are tagged by the specified tag.
00277      *
00278      * @param array The tags the entries must have
00279      * @return void
00280      * @author Ingo Renner <ingo@typo3.org>
00281      */
00282     public function flushByTags(array $tags) {
00283         foreach ($tags as $tag) {
00284             $this->flushByTag($tag);
00285         }
00286     }
00287 
00288     /**
00289      * Associates the identifier with the given tags
00290      *
00291      * @param string $entryIdentifier
00292      * @param array $tags
00293      * @author Karsten Dambekalns <karsten@typo3.org>
00294      * @author Dmitry Dulepov <dmitry.@typo3.org>
00295      */
00296     protected function addIdentifierToTags($entryIdentifier, array $tags) {
00297         foreach ($tags as $tag) {
00298                 // Update tag-to-identifier index
00299             $identifiers = $this->findIdentifiersByTag($tag);
00300             if (array_search($entryIdentifier, $identifiers) === FALSE) {
00301                 $identifiers[] = $entryIdentifier;
00302                 apc_store($this->identifierPrefix . 'tag_' . $tag, $identifiers);
00303             }
00304 
00305                 // Update identifier-to-tag index
00306             $existingTags = $this->findTagsByIdentifier($entryIdentifier);
00307             if (array_search($entryIdentifier, $existingTags) === false) {
00308                 apc_store($this->identifierPrefix . 'ident_' . $entryIdentifier, array_merge($existingTags, $tags));
00309             }
00310 
00311         }
00312     }
00313 
00314     /**
00315      * Removes association of the identifier with the given tags
00316      *
00317      * @param string $entryIdentifier
00318      * @param array $tags
00319      * @author Karsten Dambekalns <karsten@typo3.org>
00320      * @author Dmitry Dulepov <dmitry.@typo3.org>
00321      */
00322     protected function removeIdentifierFromAllTags($entryIdentifier) {
00323             // Get tags for this identifier
00324         $tags = $this->findTagsByIdentifier($entryIdentifier);
00325             // Deassociate tags with this identifier
00326         foreach ($tags as $tag) {
00327             $identifiers = $this->findIdentifiersByTag($tag);
00328                 // Formally array_search() below should never return false due to
00329                 // the behavior of findTagsByIdentifier(). But if reverse index is
00330                 // corrupted, we still can get 'false' from array_search(). This is
00331                 // not a problem because we are removing this identifier from
00332                 // anywhere.
00333             if (($key = array_search($entryIdentifier, $identifiers)) !== FALSE) {
00334                 unset($identifiers[$key]);
00335                 if (count($identifiers)) {
00336                     apc_store($this->identifierPrefix . 'tag_' . $tag, $identifiers);
00337                 } else {
00338                     apc_delete($this->identifierPrefix . 'tag_' . $tag);
00339                 }
00340             }
00341         }
00342             // Clear reverse tag index for this identifier
00343         apc_delete($this->identifierPrefix . 'ident_' . $entryIdentifier);
00344     }
00345 
00346     /**
00347      * Does nothing, as APC does GC itself
00348      *
00349      * @return void
00350      */
00351     public function collectGarbage() {
00352 
00353     }
00354 }
00355 
00356 
00357 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/cache/backend/class.t3lib_cache_backend_apcbackend.php'])) {
00358     include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/cache/backend/class.t3lib_cache_backend_apcbackend.php']);
00359 }
00360 
00361 ?>