TYPO3 API  SVNRelease
class.t3lib_cache_backend_dbbackend.php
Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003  *  Copyright notice
00004  *
00005  *  (c) 2008-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 database tables
00028  *
00029  * @package TYPO3
00030  * @subpackage t3lib_cache
00031  * @api
00032  * @version $Id: class.t3lib_cache_backend_dbbackend.php 10128 2011-01-18 21:56:30Z lolli $
00033  */
00034 class t3lib_cache_backend_DbBackend extends t3lib_cache_backend_AbstractBackend {
00035 
00036     protected $cacheTable;
00037     protected $tagsTable;
00038 
00039     /**
00040      * @var boolean Indicates wether data is compressed or not (requires php zlib)
00041      */
00042     protected $compression = FALSE;
00043 
00044     /**
00045      * @var integer -1 to 9, indicates zlib compression level: -1 = default level 6, 0 = no compression, 9 maximum compression
00046      */
00047     protected $compressionLevel = -1;
00048 
00049     protected $identifierField;
00050     protected $creationField;
00051     protected $lifetimeField;
00052     protected $notExpiredStatement;
00053     protected $tableList;
00054     protected $tableJoin;
00055 
00056     /**
00057      * Constructs this backend
00058      *
00059      * @param array $options Configuration options - depends on the actual backend
00060      */
00061     public function __construct(array $options = array()) {
00062         parent::__construct($options);
00063 
00064         if (!$this->cacheTable) {
00065             throw new t3lib_cache_Exception(
00066                 'No table to write data to has been set using the setting "cacheTable".',
00067                 1253534136
00068             );
00069         }
00070 
00071         if (!$this->tagsTable) {
00072             throw new t3lib_cache_Exception(
00073                 'No table to write tags to has been set using the setting "tagsTable".',
00074                 1253534137
00075             );
00076         }
00077 
00078         $this->initializeCommonReferences();
00079     }
00080 
00081     /**
00082      * Initializes common references used in this backend.
00083      *
00084      * @return  void
00085      */
00086     protected function initializeCommonReferences() {
00087         $this->identifierField = $this->cacheTable . '.identifier';
00088         $this->creationField = $this->cacheTable . '.crdate';
00089         $this->lifetimeField = $this->cacheTable . '.lifetime';
00090         $this->tableList = $this->cacheTable . ', ' . $this->tagsTable;
00091         $this->tableJoin = $this->identifierField . ' = ' . $this->tagsTable . '.identifier';
00092         $this->notExpiredStatement = '(' . $this->creationField . ' + ' . $this->lifetimeField .
00093                                      ' >= ' . $GLOBALS['EXEC_TIME'] . ' OR ' . $this->lifetimeField . ' = 0)';
00094     }
00095 
00096     /**
00097      * Saves data in a cache file.
00098      *
00099      * @param string An identifier for this specific cache entry
00100      * @param string The data to be stored
00101      * @param array Tags to associate with this cache entry
00102      * @param integer Lifetime of this cache entry in seconds. If NULL is specified, the default lifetime is used. "0" means unlimited liftime.
00103      * @return void
00104      * @throws t3lib_cache_Exception if no cache frontend has been set.
00105      * @throws t3lib_cache_exception_InvalidData if the data to be stored is not a string.
00106      * @author Ingo Renner <ingo@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 via setCache() yet.',
00112                 1236518288
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                 1236518298
00120             );
00121         }
00122 
00123         if (is_null($lifetime)) {
00124             $lifetime = $this->defaultLifetime;
00125         }
00126 
00127         $this->remove($entryIdentifier);
00128 
00129         if ($this->compression) {
00130             $data = gzcompress($data, $this->compressionLevel);
00131         }
00132 
00133         $GLOBALS['TYPO3_DB']->exec_INSERTquery(
00134             $this->cacheTable,
00135             array(
00136                  'identifier' => $entryIdentifier,
00137                  'crdate' => $GLOBALS['EXEC_TIME'],
00138                  'content' => $data,
00139                  'lifetime' => $lifetime
00140             )
00141         );
00142 
00143         if (count($tags)) {
00144             $fields = array();
00145             $fields[] = 'identifier';
00146             $fields[] = 'tag';
00147 
00148             $tagRows = array();
00149             foreach ($tags as $tag) {
00150                 $tagRow = array();
00151                 $tagRow[] = $entryIdentifier;
00152                 $tagRow[] = $tag;
00153                 $tagRows[] = $tagRow;
00154             }
00155 
00156             $GLOBALS['TYPO3_DB']->exec_INSERTmultipleRows(
00157                 $this->tagsTable,
00158                 $fields,
00159                 $tagRows
00160             );
00161         }
00162     }
00163 
00164     /**
00165      * Loads data from a cache file.
00166      *
00167      * @param string An identifier which describes the cache entry to load
00168      * @return mixed The cache entry's data as a string or FALSE if the cache entry could not be loaded
00169      * @author Ingo Renner <ingo@typo3.org>
00170      */
00171     public function get($entryIdentifier) {
00172         $cacheEntry = false;
00173 
00174         $cacheEntry = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
00175             'content',
00176             $this->cacheTable,
00177             'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->cacheTable) . ' '
00178             . 'AND (crdate + lifetime >= ' . $GLOBALS['EXEC_TIME'] . ' OR lifetime = 0)'
00179         );
00180 
00181         if (is_array($cacheEntry)) {
00182             $cacheEntry = $cacheEntry['content'];
00183         }
00184 
00185         if ($this->compression && strlen($cacheEntry)) {
00186             $cacheEntry = gzuncompress($cacheEntry);
00187         }
00188 
00189         return $cacheEntry;
00190     }
00191 
00192     /**
00193      * Checks if a cache entry with the specified identifier exists.
00194      *
00195      * @param string Specifies the identifier to check for existence
00196      * @return boolean TRUE if such an entry exists, FALSE if not
00197      * @author Ingo Renner <ingo@typo3.org>
00198      */
00199     public function has($entryIdentifier) {
00200         $hasEntry = FALSE;
00201 
00202         $cacheEntries = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
00203             '*',
00204             $this->cacheTable,
00205             'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->cacheTable) .
00206             ' AND (crdate + lifetime >= ' . $GLOBALS['EXEC_TIME'] . ' OR lifetime = 0)'
00207         );
00208         if ($cacheEntries >= 1) {
00209             $hasEntry = TRUE;
00210         }
00211 
00212         return $hasEntry;
00213     }
00214 
00215     /**
00216      * Removes all cache entries matching the specified identifier.
00217      * Usually this only affects one entry.
00218      *
00219      * @param string Specifies the cache entry to remove
00220      * @return boolean TRUE if (at least) an entry could be removed or FALSE if no entry was found
00221      * @author Ingo Renner <ingo@typo3.org>
00222      */
00223     public function remove($entryIdentifier) {
00224         $entryRemoved = false;
00225 
00226         $res = $GLOBALS['TYPO3_DB']->exec_DELETEquery(
00227             $this->cacheTable,
00228             'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->cacheTable)
00229         );
00230 
00231         $GLOBALS['TYPO3_DB']->exec_DELETEquery(
00232             $this->tagsTable,
00233             'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->tagsTable)
00234         );
00235 
00236         if ($GLOBALS['TYPO3_DB']->sql_affected_rows($res) == 1) {
00237             $entryRemoved = true;
00238         }
00239 
00240         return $entryRemoved;
00241     }
00242 
00243     /**
00244      * Finds and returns all cache entries which are tagged by the specified tag.
00245      *
00246      * @param string The tag to search for
00247      * @return array An array with identifiers of all matching entries. An empty array if no entries matched
00248      * @author Ingo Renner <ingo@typo3.org>
00249      */
00250     public function findIdentifiersByTag($tag) {
00251         $cacheEntryIdentifiers = array();
00252 
00253         $cacheEntryIdentifierRows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
00254             $this->identifierField,
00255             $this->tableList,
00256             $this->getQueryForTag($tag) .
00257             ' AND ' . $this->tableJoin .
00258             ' AND ' . $this->notExpiredStatement,
00259             $this->identifierField
00260         );
00261 
00262         foreach ($cacheEntryIdentifierRows as $cacheEntryIdentifierRow) {
00263             $cacheEntryIdentifiers[$cacheEntryIdentifierRow['identifier']] = $cacheEntryIdentifierRow['identifier'];
00264         }
00265 
00266         return $cacheEntryIdentifiers;
00267     }
00268 
00269     /**
00270      * Finds and returns all cache entry identifiers which are tagged by the
00271      * specified tags.
00272      *
00273      * @param array Array of tags to search for
00274      * @return array An array with identifiers of all matching entries. An empty array if no entries matched
00275      * @author Ingo Renner <ingo@typo3.org>
00276      */
00277     public function findIdentifiersByTags(array $tags) {
00278         $cacheEntryIdentifiers = array();
00279         $whereClause = array();
00280 
00281         foreach ($tags as $tag) {
00282             $whereClause[] = $this->getQueryForTag($tag);
00283         }
00284 
00285         $whereClause[] = $this->tableJoin;
00286         $whereClause[] = $this->notExpiredStatement;
00287 
00288         $cacheEntryIdentifierRows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
00289             $this->identifierField,
00290             $this->tableList,
00291             implode(' AND ', $whereClause),
00292             $this->identifierField
00293         );
00294 
00295         foreach ($cacheEntryIdentifierRows as $cacheEntryIdentifierRow) {
00296             $cacheEntryIdentifiers[$cacheEntryIdentifierRow['identifier']] = $cacheEntryIdentifierRow['identifier'];
00297         }
00298 
00299         return $cacheEntryIdentifiers;
00300     }
00301 
00302     /**
00303      * Removes all cache entries of this cache.
00304      *
00305      * @return void
00306      * @author Ingo Renner <ingo@typo3.org>
00307      */
00308     public function flush() {
00309         $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery($this->cacheTable);
00310         $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery($this->tagsTable);
00311     }
00312 
00313     /**
00314      * Removes all cache entries of this cache which are tagged by the specified tag.
00315      *
00316      * @param string The tag the entries must have
00317      * @return void
00318      */
00319     public function flushByTag($tag) {
00320         $tagsTableWhereClause = $this->getQueryForTag($tag);
00321 
00322         $this->deleteCacheTableRowsByTagsTableWhereClause($tagsTableWhereClause);
00323 
00324         $GLOBALS['TYPO3_DB']->exec_DELETEquery(
00325             $this->tagsTable,
00326             $tagsTableWhereClause
00327         );
00328     }
00329 
00330     /**
00331      * Removes all cache entries of this cache which are tagged by the specified tags.
00332      *
00333      * @param array The tags the entries must have
00334      * @return void
00335      */
00336     public function flushByTags(array $tags) {
00337         if (count($tags)) {
00338             $listQueryConditions = array();
00339             foreach ($tags as $tag) {
00340                 $listQueryConditions[$tag] = $this->getQueryForTag($tag);
00341             }
00342 
00343             $tagsTableWhereClause = implode(' OR ', $listQueryConditions);
00344 
00345             $this->deleteCacheTableRowsByTagsTableWhereClause($tagsTableWhereClause);
00346 
00347             $GLOBALS['TYPO3_DB']->exec_DELETEquery(
00348                 $this->tagsTable,
00349                 $tagsTableWhereClause
00350             );
00351         }
00352     }
00353 
00354     /**
00355      * Does garbage collection
00356      *
00357      * @return void
00358      * @author Ingo Renner <ingo@typo3.org>
00359      */
00360     public function collectGarbage() {
00361             // Get identifiers of expired cache entries
00362         $tagsEntryIdentifierRowsResource = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
00363             'identifier',
00364             $this->cacheTable,
00365             'crdate + lifetime < ' . $GLOBALS['EXEC_TIME'] . ' AND lifetime > 0'
00366         );
00367 
00368         $tagsEntryIdentifiers = array();
00369         while ($tagsEntryIdentifierRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($tagsEntryIdentifierRowsResource)) {
00370             $tagsEntryIdentifiers[] = $GLOBALS['TYPO3_DB']->fullQuoteStr(
00371                 $tagsEntryIdentifierRow['identifier'],
00372                 $this->tagsTable
00373             );
00374         }
00375         $GLOBALS['TYPO3_DB']->sql_free_result($tagsEntryIdentifierRowsResource);
00376 
00377             // Delete tag rows connected to expired cache entries
00378         if (count($tagsEntryIdentifiers)) {
00379             $GLOBALS['TYPO3_DB']->exec_DELETEquery(
00380                 $this->tagsTable,
00381                 'identifier IN (' . implode(', ', $tagsEntryIdentifiers) . ')'
00382             );
00383         }
00384 
00385             // Delete expired cache rows
00386         $GLOBALS['TYPO3_DB']->exec_DELETEquery(
00387             $this->cacheTable,
00388             'crdate + lifetime < ' . $GLOBALS['EXEC_TIME'] . ' AND lifetime > 0'
00389         );
00390     }
00391 
00392     /**
00393      * Sets the table where the cache entries are stored. The specified table
00394      * must exist already.
00395      *
00396      * @param   string  The table.
00397      * @return  void
00398      * @throws t3lib_cache_Exception if the table does not exist.
00399      * @author Ingo Renner <ingo@typo3.org>
00400      */
00401     public function setCacheTable($cacheTable) {
00402         $this->cacheTable = $cacheTable;
00403         $this->initializeCommonReferences();
00404     }
00405 
00406     /**
00407      * Returns the table where the cache entries are stored.
00408      *
00409      * @return  string  The cache table.
00410      * @author Ingo Renner <ingo@typo3.org>
00411      */
00412     public function getCacheTable() {
00413         return $this->cacheTable;
00414     }
00415 
00416     /**
00417      * Sets the table where cache tags are stored.
00418      *
00419      * @param   string      $tagsTabls: Name of the table
00420      * @return  void
00421      */
00422     public function setTagsTable($tagsTable) {
00423         $this->tagsTable = $tagsTable;
00424         $this->initializeCommonReferences();
00425     }
00426 
00427     /**
00428      * Gets the table where cache tags are stored.
00429      *
00430      * @return  string      Name of the table storing tags
00431      */
00432     public function getTagsTable() {
00433         return $this->tagsTable;
00434     }
00435 
00436     /**
00437      * Enable data compression
00438      *
00439      * @param boolean TRUE to enable compression
00440      */
00441     public function setCompression($compression) {
00442         $this->compression = $compression;
00443     }
00444 
00445     /**
00446      * Set data compression level.
00447      * If compression is enabled and this is not set,
00448      * gzcompress default level will be used
00449      *
00450      * @param integer -1 to 9: Compression level
00451      */
00452     public function setCompressionLevel($compressionLevel) {
00453         if ($compressionLevel >= -1 && $compressionLevel <= 9) {
00454             $this->compressionLevel = $compressionLevel;
00455         }
00456     }
00457 
00458     /**
00459      * Gets the query to be used for selecting entries by a tag. The asterisk ("*")
00460      * is allowed as a wildcard at the beginning and the end of a tag.
00461      *
00462      * @param string The tag to search for, the "*" wildcard is supported
00463      * @return string the query to be used for selecting entries
00464      * @author Oliver Hader <oliver@typo3.org>
00465      */
00466     protected function getQueryForTag($tag) {
00467         if (strpos($tag, '*') === false) {
00468             $query = $this->tagsTable . '.tag = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($tag, $this->tagsTable);
00469         } else {
00470             $patternForLike = $GLOBALS['TYPO3_DB']->escapeStrForLike(
00471                 $GLOBALS['TYPO3_DB']->quoteStr($tag, $this->tagsTable),
00472                 $this->tagsTable
00473             );
00474             $query = $this->tagsTable . '.tag LIKE \'' . $patternForLike . '\'';
00475         }
00476 
00477         return $query;
00478     }
00479 
00480     /**
00481      * Deletes rows in cache table found by where clause on tags table
00482      *
00483      * @param string The where clause for the tags table
00484      * @return void
00485      */
00486     protected function deleteCacheTableRowsByTagsTableWhereClause($tagsTableWhereClause) {
00487         $cacheEntryIdentifierRowsRessource = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
00488             'DISTINCT identifier',
00489             $this->tagsTable,
00490             $tagsTableWhereClause
00491         );
00492 
00493         $cacheEntryIdentifiers = array();
00494         while ($cacheEntryIdentifierRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($cacheEntryIdentifierRowsRessource)) {
00495             $cacheEntryIdentifiers[] = $GLOBALS['TYPO3_DB']->fullQuoteStr(
00496                 $cacheEntryIdentifierRow['identifier'],
00497                 $this->cacheTable
00498             );
00499         }
00500         $GLOBALS['TYPO3_DB']->sql_free_result($cacheEntryIdentifierRowsRessource);
00501 
00502         if (count($cacheEntryIdentifiers)) {
00503             $GLOBALS['TYPO3_DB']->exec_DELETEquery(
00504                 $this->cacheTable,
00505                 'identifier IN (' . implode(', ', $cacheEntryIdentifiers) . ')'
00506             );
00507         }
00508     }
00509 }
00510 
00511 
00512 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/cache/backend/class.t3lib_cache_backend_dbbackend.php'])) {
00513     include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/cache/backend/class.t3lib_cache_backend_dbbackend.php']);
00514 }
00515 
00516 ?>