2 /***************************************************************
5 * (c) 2008-2011 Ingo Renner <ingo@typo3.org>
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
17 * This script is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * This copyright notice MUST APPEAR in all copies of the script!
23 ***************************************************************/
26 * A caching backend which stores cache entries in database tables
29 * @subpackage t3lib_cache
33 class t3lib_cache_backend_DbBackend
extends t3lib_cache_backend_AbstractBackend
{
36 * @var string Name of the cache data table
38 protected $cacheTable;
41 * @var string Name of the cache tags table
46 * @var boolean Indicates wether data is compressed or not (requires php zlib)
48 protected $compression = FALSE;
51 * @var integer -1 to 9, indicates zlib compression level: -1 = default level 6, 0 = no compression, 9 maximum compression
53 protected $compressionLevel = -1;
55 protected $identifierField;
56 protected $creationField;
57 protected $lifetimeField;
58 protected $notExpiredStatement;
63 * Set cache frontend instance and calculate data and tags table name
65 * @param t3lib_cache_frontend_Frontend $cache The frontend for this backend
67 * @author Christian Kuhn <lolli@schwarzbu.ch>
70 public function setCache(t3lib_cache_frontend_Frontend
$cache) {
71 parent
::setCache($cache);
73 $this->cacheTable
= 'cachingframework_' . $this->cacheIdentifier
;
74 $this->tagsTable
= 'cachingframework_' . $this->cacheIdentifier
. '_tags';
75 $this->initializeCommonReferences();
79 * Initializes common references used in this backend.
83 protected function initializeCommonReferences() {
84 $this->identifierField
= $this->cacheTable
. '.identifier';
85 $this->creationField
= $this->cacheTable
. '.crdate';
86 $this->lifetimeField
= $this->cacheTable
. '.lifetime';
87 $this->tableList
= $this->cacheTable
. ', ' . $this->tagsTable
;
88 $this->tableJoin
= $this->identifierField
. ' = ' . $this->tagsTable
. '.identifier';
89 $this->notExpiredStatement
= '(' . $this->creationField
. ' + ' . $this->lifetimeField
.
90 ' >= ' . $GLOBALS['EXEC_TIME'] . ' OR ' . $this->lifetimeField
. ' = 0)';
94 * Saves data in a cache file.
96 * @param string An identifier for this specific cache entry
97 * @param string The data to be stored
98 * @param array Tags to associate with this cache entry
99 * @param integer Lifetime of this cache entry in seconds. If NULL is specified, the default lifetime is used. "0" means unlimited liftime.
101 * @throws t3lib_cache_Exception if no cache frontend has been set.
102 * @throws t3lib_cache_exception_InvalidData if the data to be stored is not a string.
103 * @author Ingo Renner <ingo@typo3.org>
105 public function set($entryIdentifier, $data, array $tags = array(), $lifetime = NULL) {
106 if (!$this->cache
instanceof t3lib_cache_frontend_Frontend
) {
107 throw new t3lib_cache_Exception(
108 'No cache frontend has been set via setCache() yet.',
113 if (!is_string($data)) {
114 throw new t3lib_cache_exception_InvalidData(
115 'The specified data is of type "' . gettype($data) . '" but a string is expected.',
120 if (is_null($lifetime)) {
121 $lifetime = $this->defaultLifetime
;
124 $this->remove($entryIdentifier);
126 if ($this->compression
) {
127 $data = gzcompress($data, $this->compressionLevel
);
130 $GLOBALS['TYPO3_DB']->exec_INSERTquery(
133 'identifier' => $entryIdentifier,
134 'crdate' => $GLOBALS['EXEC_TIME'],
136 'lifetime' => $lifetime
142 $fields[] = 'identifier';
146 foreach ($tags as $tag) {
148 $tagRow[] = $entryIdentifier;
150 $tagRows[] = $tagRow;
153 $GLOBALS['TYPO3_DB']->exec_INSERTmultipleRows(
162 * Loads data from a cache file.
164 * @param string An identifier which describes the cache entry to load
165 * @return mixed The cache entry's data as a string or FALSE if the cache entry could not be loaded
166 * @author Ingo Renner <ingo@typo3.org>
168 public function get($entryIdentifier) {
169 if (!$this->cache
instanceof t3lib_cache_frontend_Frontend
) {
170 throw new t3lib_cache_Exception(
171 'No cache frontend has been set via setCache() yet.',
178 $cacheEntry = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
181 'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->cacheTable
) . ' '
182 . 'AND (crdate + lifetime >= ' . $GLOBALS['EXEC_TIME'] . ' OR lifetime = 0)'
185 if (is_array($cacheEntry)) {
186 $cacheEntry = $cacheEntry['content'];
189 if ($this->compression
&& strlen($cacheEntry)) {
190 $cacheEntry = gzuncompress($cacheEntry);
197 * Checks if a cache entry with the specified identifier exists.
199 * @param string Specifies the identifier to check for existence
200 * @return boolean TRUE if such an entry exists, FALSE if not
201 * @author Ingo Renner <ingo@typo3.org>
203 public function has($entryIdentifier) {
204 if (!$this->cache
instanceof t3lib_cache_frontend_Frontend
) {
205 throw new t3lib_cache_Exception(
206 'No cache frontend has been set via setCache() yet.',
213 $cacheEntries = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
216 'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->cacheTable
) .
217 ' AND (crdate + lifetime >= ' . $GLOBALS['EXEC_TIME'] . ' OR lifetime = 0)'
219 if ($cacheEntries >= 1) {
227 * Removes all cache entries matching the specified identifier.
228 * Usually this only affects one entry.
230 * @param string Specifies the cache entry to remove
231 * @return boolean TRUE if (at least) an entry could be removed or FALSE if no entry was found
232 * @author Ingo Renner <ingo@typo3.org>
234 public function remove($entryIdentifier) {
235 if (!$this->cache
instanceof t3lib_cache_frontend_Frontend
) {
236 throw new t3lib_cache_Exception(
237 'No cache frontend has been set via setCache() yet.',
242 $entryRemoved = FALSE;
244 $res = $GLOBALS['TYPO3_DB']->exec_DELETEquery(
246 'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->cacheTable
)
249 $GLOBALS['TYPO3_DB']->exec_DELETEquery(
251 'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->tagsTable
)
254 if ($GLOBALS['TYPO3_DB']->sql_affected_rows($res) == 1) {
255 $entryRemoved = TRUE;
258 return $entryRemoved;
262 * Finds and returns all cache entries which are tagged by the specified tag.
264 * @param string The tag to search for
265 * @return array An array with identifiers of all matching entries. An empty array if no entries matched
266 * @author Ingo Renner <ingo@typo3.org>
268 public function findIdentifiersByTag($tag) {
269 if (!$this->cache
instanceof t3lib_cache_frontend_Frontend
) {
270 throw new t3lib_cache_Exception(
271 'No cache frontend has been set via setCache() yet.',
276 $cacheEntryIdentifiers = array();
278 $cacheEntryIdentifierRows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
279 $this->identifierField
,
281 $this->getQueryForTag($tag) .
282 ' AND ' . $this->tableJoin
.
283 ' AND ' . $this->notExpiredStatement
,
284 $this->identifierField
287 foreach ($cacheEntryIdentifierRows as $cacheEntryIdentifierRow) {
288 $cacheEntryIdentifiers[$cacheEntryIdentifierRow['identifier']] = $cacheEntryIdentifierRow['identifier'];
291 return $cacheEntryIdentifiers;
295 * Removes all cache entries of this cache.
298 * @author Ingo Renner <ingo@typo3.org>
300 public function flush() {
301 if (!$this->cache
instanceof t3lib_cache_frontend_Frontend
) {
302 throw new t3lib_cache_Exception(
303 'No cache frontend has been set via setCache() yet.',
308 $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery($this->cacheTable
);
309 $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery($this->tagsTable
);
310 $GLOBALS['TYPO3_DB']->admin_query('DROP TABLE IF EXISTS ' . $this->cacheTable
);
311 $GLOBALS['TYPO3_DB']->admin_query('DROP TABLE IF EXISTS ' . $this->tagsTable
);
312 $this->createCacheTable();
313 $this->createTagsTable();
317 * Removes all cache entries of this cache which are tagged by the specified tag.
319 * @param string The tag the entries must have
322 public function flushByTag($tag) {
323 if (!$this->cache
instanceof t3lib_cache_frontend_Frontend
) {
324 throw new t3lib_cache_Exception(
325 'No cache frontend has been set via setCache() yet.',
330 $tagsTableWhereClause = $this->getQueryForTag($tag);
332 $this->deleteCacheTableRowsByTagsTableWhereClause($tagsTableWhereClause);
334 $GLOBALS['TYPO3_DB']->exec_DELETEquery(
336 $tagsTableWhereClause
341 * Does garbage collection
344 * @author Ingo Renner <ingo@typo3.org>
346 public function collectGarbage() {
347 if (!$this->cache
instanceof t3lib_cache_frontend_Frontend
) {
348 throw new t3lib_cache_Exception(
349 'No cache frontend has been set via setCache() yet.',
354 // Get identifiers of expired cache entries
355 $tagsEntryIdentifierRowsResource = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
358 'crdate + lifetime < ' . $GLOBALS['EXEC_TIME'] . ' AND lifetime > 0'
361 $tagsEntryIdentifiers = array();
362 while ($tagsEntryIdentifierRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($tagsEntryIdentifierRowsResource)) {
363 $tagsEntryIdentifiers[] = $GLOBALS['TYPO3_DB']->fullQuoteStr(
364 $tagsEntryIdentifierRow['identifier'],
368 $GLOBALS['TYPO3_DB']->sql_free_result($tagsEntryIdentifierRowsResource);
370 // Delete tag rows connected to expired cache entries
371 if (count($tagsEntryIdentifiers)) {
372 $GLOBALS['TYPO3_DB']->exec_DELETEquery(
374 'identifier IN (' . implode(', ', $tagsEntryIdentifiers) . ')'
378 // Delete expired cache rows
379 $GLOBALS['TYPO3_DB']->exec_DELETEquery(
381 'crdate + lifetime < ' . $GLOBALS['EXEC_TIME'] . ' AND lifetime > 0'
386 * Sets the table where the cache entries are stored.
388 * @deprecated since TYPO3 4.6: The backend calculates the
389 * table name internally, this method does nothing anymore
390 * @param string $cacheTable Table name
392 * @author Ingo Renner <ingo@typo3.org>
394 public function setCacheTable($cacheTable) {
395 t3lib_div
::logDeprecatedFunction();
399 * Returns the table where the cache entries are stored.
401 * @return string The cache table.
402 * @author Ingo Renner <ingo@typo3.org>
404 public function getCacheTable() {
405 if (!$this->cache
instanceof t3lib_cache_frontend_Frontend
) {
406 throw new t3lib_cache_Exception(
407 'No cache frontend has been set via setCache() yet.',
412 return $this->cacheTable
;
416 * Sets the table where cache tags are stored.
418 * @deprecated since TYPO3 4.6: The backend calculates the
419 * table name internally, this method does nothing anymore
420 * @param string $tagsTable: Tags table name
423 public function setTagsTable($tagsTable) {
424 t3lib_div
::logDeprecatedFunction();
428 * Gets the table where cache tags are stored.
430 * @return string Name of the table storing tags
432 public function getTagsTable() {
433 if (!$this->cache
instanceof t3lib_cache_frontend_Frontend
) {
434 throw new t3lib_cache_Exception(
435 'No cache frontend has been set via setCache() yet.',
440 return $this->tagsTable
;
444 * Enable data compression
446 * @param boolean TRUE to enable compression
448 public function setCompression($compression) {
449 $this->compression
= $compression;
453 * Set data compression level.
454 * If compression is enabled and this is not set,
455 * gzcompress default level will be used
457 * @param integer -1 to 9: Compression level
459 public function setCompressionLevel($compressionLevel) {
460 if ($compressionLevel >= -1 && $compressionLevel <= 9) {
461 $this->compressionLevel
= $compressionLevel;
466 * Create data table of cache
470 protected function createCacheTable() {
471 $sql = file_get_contents(PATH_t3lib
. 'cache/backend/resources/dbbackend-layout-cache.sql');
472 $sql = str_replace('###CACHE_TABLE###', $this->cacheTable
, $sql);
473 $GLOBALS['TYPO3_DB']->admin_query($sql);
477 * Create tags table of cache
481 protected function createTagsTable() {
482 $sql = file_get_contents(PATH_t3lib
. 'cache/backend/resources/dbbackend-layout-tags.sql');
483 $sql = str_replace('###TAGS_TABLE###', $this->tagsTable
, $sql);
484 $GLOBALS['TYPO3_DB']->admin_query($sql);
488 * Gets the query to be used for selecting entries by a tag. The asterisk ("*")
489 * is allowed as a wildcard at the beginning and the end of a tag.
491 * @param string The tag to search for, the "*" wildcard is supported
492 * @return string the query to be used for selecting entries
493 * @author Oliver Hader <oliver@typo3.org>
495 protected function getQueryForTag($tag) {
496 if (strpos($tag, '*') === FALSE) {
497 $query = $this->tagsTable
. '.tag = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($tag, $this->tagsTable
);
499 $patternForLike = $GLOBALS['TYPO3_DB']->escapeStrForLike(
500 $GLOBALS['TYPO3_DB']->quoteStr($tag, $this->tagsTable
),
503 $query = $this->tagsTable
. '.tag LIKE \'' . $patternForLike . '\'';
510 * Deletes rows in cache table found by where clause on tags table
512 * @param string The where clause for the tags table
515 protected function deleteCacheTableRowsByTagsTableWhereClause($tagsTableWhereClause) {
516 $cacheEntryIdentifierRowsRessource = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
517 'DISTINCT identifier',
519 $tagsTableWhereClause
522 $cacheEntryIdentifiers = array();
523 while ($cacheEntryIdentifierRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($cacheEntryIdentifierRowsRessource)) {
524 $cacheEntryIdentifiers[] = $GLOBALS['TYPO3_DB']->fullQuoteStr(
525 $cacheEntryIdentifierRow['identifier'],
529 $GLOBALS['TYPO3_DB']->sql_free_result($cacheEntryIdentifierRowsRessource);
531 if (count($cacheEntryIdentifiers)) {
532 $GLOBALS['TYPO3_DB']->exec_DELETEquery(
534 'identifier IN (' . implode(', ', $cacheEntryIdentifiers) . ')'
541 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE
]['XCLASS']['t3lib/cache/backend/class.t3lib_cache_backend_dbbackend.php'])) {
542 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE
]['XCLASS']['t3lib/cache/backend/class.t3lib_cache_backend_dbbackend.php']);