Bug #25119 ยป cache_be_db.patch
t3lib/config_default.php (working copy) | ||
---|---|---|
),
|
||
'cacheBackends' => array(
|
||
't3lib_cache_backend_DbBackend' => 't3lib_cache_backend_DbBackend',
|
||
't3lib_cache_backend_DbBackend2' => 't3lib_cache_backend_DbBackend2',
|
||
't3lib_cache_backend_FileBackend' => 't3lib_cache_backend_FileBackend',
|
||
't3lib_cache_backend_MemcachedBackend' => 't3lib_cache_backend_MemcachedBackend',
|
||
't3lib_cache_backend_PdoBackend' => 't3lib_cache_backend_PdoBackend',
|
t3lib/cache/backend/class.t3lib_cache_backend_dbbackend2.php (revision 0) | ||
---|---|---|
<?php
|
||
/***************************************************************
|
||
* Copyright notice
|
||
*
|
||
* (c) 2008-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 database tables
|
||
*
|
||
* @package TYPO3
|
||
* @subpackage t3lib_cache
|
||
* @api
|
||
* @version $Id: class.t3lib_cache_backend_dbbackend.php 10128 2011-01-18 21:56:30Z lolli $
|
||
*/
|
||
define (LIFETIME_MAX,2147483647); // see e.g. http://bugs.mysql.com/bug.php?id=9191 for source of limit
|
||
class t3lib_cache_backend_DbBackend2 extends t3lib_cache_backend_AbstractBackend {
|
||
protected $cacheTable;
|
||
protected $tagsTable;
|
||
/**
|
||
* @var boolean Indicates wether data is compressed or not (requires php zlib)
|
||
*/
|
||
protected $compression = FALSE;
|
||
/**
|
||
* @var integer -1 to 9, indicates zlib compression level: -1 = default level 6, 0 = no compression, 9 maximum compression
|
||
*/
|
||
protected $compressionLevel = -1;
|
||
protected $identifierField;
|
||
protected $creationField;
|
||
protected $lifetimeField;
|
||
protected $notExpiredStatement;
|
||
protected $tableList;
|
||
protected $tableJoin;
|
||
/**
|
||
* Constructs this backend
|
||
*
|
||
* @param array $options Configuration options - depends on the actual backend
|
||
*/
|
||
public function __construct(array $options = array()) {
|
||
parent::__construct($options);
|
||
if (!$this->cacheTable) {
|
||
throw new t3lib_cache_Exception(
|
||
'No table to write data to has been set using the setting "cacheTable".',
|
||
1253534136
|
||
);
|
||
}
|
||
if (!$this->tagsTable) {
|
||
throw new t3lib_cache_Exception(
|
||
'No table to write tags to has been set using the setting "tagsTable".',
|
||
1253534137
|
||
);
|
||
}
|
||
$this->initializeCommonReferences();
|
||
}
|
||
/**
|
||
* Initializes common references used in this backend.
|
||
*
|
||
* @return void
|
||
*/
|
||
protected function initializeCommonReferences() {
|
||
$this->identifierField = $this->cacheTable . '.identifier';
|
||
$this->creationField = $this->cacheTable . '.crdate';
|
||
$this->expiredField = $this->cacheTable . '.expire';
|
||
$this->tableList = $this->cacheTable . ', ' . $this->tagsTable;
|
||
$this->tableJoin = $this->identifierField . ' = ' . $this->tagsTable . '.identifier';
|
||
$this->notExpiredStatement = '(' . $this->expiredField . ' >= ' . $GLOBALS['EXEC_TIME'] . ')';
|
||
$this->expiredStatement = '(' . $this->expiredField . ' < ' . $GLOBALS['EXEC_TIME'] . ')';
|
||
}
|
||
/**
|
||
* 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 liftime.
|
||
* @return void
|
||
* @throws t3lib_cache_Exception if no cache frontend has been set.
|
||
* @throws t3lib_cache_exception_InvalidData if the data to be stored is not a string.
|
||
* @author Ingo Renner <ingo@typo3.org>
|
||
*/
|
||
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 via setCache() yet.',
|
||
1236518288
|
||
);
|
||
}
|
||
if (!is_string($data)) {
|
||
throw new t3lib_cache_exception_InvalidData(
|
||
'The specified data is of type "' . gettype($data) . '" but a string is expected.',
|
||
1236518298
|
||
);
|
||
}
|
||
if (is_null($lifetime)) {
|
||
$expire = LIFETIME_MAX;
|
||
} else {
|
||
$expire=$GLOBALS['EXEC_TIME']+$lifetime;
|
||
}
|
||
$this->remove($entryIdentifier);
|
||
if ($this->compression) {
|
||
$data = gzcompress($data, $this->compressionLevel);
|
||
}
|
||
$GLOBALS['TYPO3_DB']->exec_INSERTquery(
|
||
$this->cacheTable,
|
||
array(
|
||
'identifier' => $entryIdentifier,
|
||
'crdate' => $GLOBALS['EXEC_TIME'],
|
||
'content' => $data,
|
||
'expire' => $expire
|
||
)
|
||
);
|
||
if (count($tags)) {
|
||
$fields = array();
|
||
$fields[] = 'identifier';
|
||
$fields[] = 'tag';
|
||
$tagRows = array();
|
||
foreach ($tags as $tag) {
|
||
$tagRow = array();
|
||
$tagRow[] = $entryIdentifier;
|
||
$tagRow[] = $tag;
|
||
$tagRows[] = $tagRow;
|
||
}
|
||
$GLOBALS['TYPO3_DB']->exec_INSERTmultipleRows(
|
||
$this->tagsTable,
|
||
$fields,
|
||
$tagRows
|
||
);
|
||
}
|
||
}
|
||
/**
|
||
* Loads data from a cache file.
|
||
*
|
||
* @param string An identifier which describes the cache entry to load
|
||
* @return mixed The cache entry's data as a string or FALSE if the cache entry could not be loaded
|
||
* @author Ingo Renner <ingo@typo3.org>
|
||
*/
|
||
public function get($entryIdentifier) {
|
||
$cacheEntry = false;
|
||
$cacheEntry = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
|
||
'content',
|
||
$this->cacheTable,
|
||
'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->cacheTable) . ' '
|
||
. 'AND ' . $this->notExpiredStatement
|
||
);
|
||
if (is_array($cacheEntry)) {
|
||
$cacheEntry = $cacheEntry['content'];
|
||
}
|
||
if ($this->compression && strlen($cacheEntry)) {
|
||
$cacheEntry = gzuncompress($cacheEntry);
|
||
}
|
||
return $cacheEntry;
|
||
}
|
||
/**
|
||
* Checks if a cache entry with the specified identifier exists.
|
||
*
|
||
* @param string Specifies the identifier to check for existence
|
||
* @return boolean TRUE if such an entry exists, FALSE if not
|
||
* @author Ingo Renner <ingo@typo3.org>
|
||
*/
|
||
public function has($entryIdentifier) {
|
||
$hasEntry = FALSE;
|
||
$cacheEntries = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
|
||
'*',
|
||
$this->cacheTable,
|
||
'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->cacheTable) .
|
||
' AND ' . $this->notExpiredStatement
|
||
);
|
||
if ($cacheEntries >= 1) {
|
||
$hasEntry = TRUE;
|
||
}
|
||
return $hasEntry;
|
||
}
|
||
/**
|
||
* Removes all cache entries matching the specified identifier.
|
||
* Usually this only affects one entry.
|
||
*
|
||
* @param string 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 Ingo Renner <ingo@typo3.org>
|
||
*/
|
||
public function remove($entryIdentifier) {
|
||
$entryRemoved = false;
|
||
$res = $GLOBALS['TYPO3_DB']->exec_DELETEquery(
|
||
$this->cacheTable,
|
||
'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->cacheTable)
|
||
);
|
||
$GLOBALS['TYPO3_DB']->exec_DELETEquery(
|
||
$this->tagsTable,
|
||
'identifier = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($entryIdentifier, $this->tagsTable)
|
||
);
|
||
if ($GLOBALS['TYPO3_DB']->sql_affected_rows($res) == 1) {
|
||
$entryRemoved = true;
|
||
}
|
||
return $entryRemoved;
|
||
}
|
||
/**
|
||
* Finds and returns all cache entries which are tagged by the specified tag.
|
||
*
|
||
* @param string The tag 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 findIdentifiersByTag($tag) {
|
||
$cacheEntryIdentifiers = array();
|
||
$cacheEntryIdentifierRows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
|
||
$this->identifierField,
|
||
$this->tableList,
|
||
$this->getQueryForTag($tag) .
|
||
' AND ' . $this->tableJoin .
|
||
' AND ' . $this->notExpiredStatement,
|
||
$this->identifierField
|
||
);
|
||
foreach ($cacheEntryIdentifierRows as $cacheEntryIdentifierRow) {
|
||
$cacheEntryIdentifiers[$cacheEntryIdentifierRow['identifier']] = $cacheEntryIdentifierRow['identifier'];
|
||
}
|
||
return $cacheEntryIdentifiers;
|
||
}
|
||
/**
|
||
* 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) {
|
||
$cacheEntryIdentifiers = array();
|
||
$whereClause = array();
|
||
foreach ($tags as $tag) {
|
||
$whereClause[] = $this->getQueryForTag($tag);
|
||
}
|
||
$whereClause[] = $this->tableJoin;
|
||
$whereClause[] = $this->notExpiredStatement;
|
||
$cacheEntryIdentifierRows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
|
||
$this->identifierField,
|
||
$this->tableList,
|
||
implode(' AND ', $whereClause),
|
||
$this->identifierField
|
||
);
|
||
foreach ($cacheEntryIdentifierRows as $cacheEntryIdentifierRow) {
|
||
$cacheEntryIdentifiers[$cacheEntryIdentifierRow['identifier']] = $cacheEntryIdentifierRow['identifier'];
|
||
}
|
||
return $cacheEntryIdentifiers;
|
||
}
|
||
/**
|
||
* Removes all cache entries of this cache.
|
||
*
|
||
* @return void
|
||
* @author Ingo Renner <ingo@typo3.org>
|
||
*/
|
||
public function flush() {
|
||
$GLOBALS['TYPO3_DB']->exec_TRUNCATEquery($this->cacheTable);
|
||
$GLOBALS['TYPO3_DB']->exec_TRUNCATEquery($this->tagsTable);
|
||
}
|
||
/**
|
||
* Removes all cache entries of this cache which are tagged by the specified tag.
|
||
*
|
||
* @param string The tag the entries must have
|
||
* @return void
|
||
*/
|
||
public function flushByTag($tag) {
|
||
$tagsTableWhereClause = $this->getQueryForTag($tag);
|
||
$this->deleteCacheTableRowsByTagsTableWhereClause($tagsTableWhereClause);
|
||
$GLOBALS['TYPO3_DB']->exec_DELETEquery(
|
||
$this->tagsTable,
|
||
$tagsTableWhereClause
|
||
);
|
||
}
|
||
/**
|
||
* Removes all cache entries of this cache which are tagged by the specified tags.
|
||
*
|
||
* @param array The tags the entries must have
|
||
* @return void
|
||
*/
|
||
public function flushByTags(array $tags) {
|
||
if (count($tags)) {
|
||
$listQueryConditions = array();
|
||
foreach ($tags as $tag) {
|
||
$listQueryConditions[$tag] = $this->getQueryForTag($tag);
|
||
}
|
||
$tagsTableWhereClause = implode(' OR ', $listQueryConditions);
|
||
$this->deleteCacheTableRowsByTagsTableWhereClause($tagsTableWhereClause);
|
||
$GLOBALS['TYPO3_DB']->exec_DELETEquery(
|
||
$this->tagsTable,
|
||
$tagsTableWhereClause
|
||
);
|
||
}
|
||
}
|
||
/**
|
||
* Does garbage collection
|
||
*
|
||
* @return void
|
||
* @author Ingo Renner <ingo@typo3.org>
|
||
*/
|
||
public function collectGarbage() {
|
||
// Get identifiers of expired cache entries
|
||
$tagsEntryIdentifierRowsResource = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
|
||
'identifier',
|
||
$this->cacheTable,
|
||
$this->expiredStatement
|
||
);
|
||
$tagsEntryIdentifiers = array();
|
||
while ($tagsEntryIdentifierRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($tagsEntryIdentifierRowsResource)) {
|
||
$tagsEntryIdentifiers[] = $GLOBALS['TYPO3_DB']->fullQuoteStr(
|
||
$tagsEntryIdentifierRow['identifier'],
|
||
$this->tagsTable
|
||
);
|
||
}
|
||
$GLOBALS['TYPO3_DB']->sql_free_result($tagsEntryIdentifierRowsResource);
|
||
// Delete tag rows connected to expired cache entries
|
||
if (count($tagsEntryIdentifiers)) {
|
||
$GLOBALS['TYPO3_DB']->exec_DELETEquery(
|
||
$this->tagsTable,
|
||
'identifier IN (' . implode(', ', $tagsEntryIdentifiers) . ')'
|
||
);
|
||
}
|
||
// Delete expired cache rows
|
||
$GLOBALS['TYPO3_DB']->exec_DELETEquery(
|
||
$this->cacheTable,
|
||
$this->expiredStatement
|
||
);
|
||
}
|
||
/**
|
||
* Sets the table where the cache entries are stored. The specified table
|
||
* must exist already.
|
||
*
|
||
* @param string The table.
|
||
* @return void
|
||
* @throws t3lib_cache_Exception if the table does not exist.
|
||
* @author Ingo Renner <ingo@typo3.org>
|
||
*/
|
||
public function setCacheTable($cacheTable) {
|
||
$this->cacheTable = $cacheTable;
|
||
$this->initializeCommonReferences();
|
||
}
|
||
/**
|
||
* Returns the table where the cache entries are stored.
|
||
*
|
||
* @return string The cache table.
|
||
* @author Ingo Renner <ingo@typo3.org>
|
||
*/
|
||
public function getCacheTable() {
|
||
return $this->cacheTable;
|
||
}
|
||
/**
|
||
* Sets the table where cache tags are stored.
|
||
*
|
||
* @param string $tagsTabls: Name of the table
|
||
* @return void
|
||
*/
|
||
public function setTagsTable($tagsTable) {
|
||
$this->tagsTable = $tagsTable;
|
||
$this->initializeCommonReferences();
|
||
}
|
||
/**
|
||
* Gets the table where cache tags are stored.
|
||
*
|
||
* @return string Name of the table storing tags
|
||
*/
|
||
public function getTagsTable() {
|
||
return $this->tagsTable;
|
||
}
|
||
/**
|
||
* Enable data compression
|
||
*
|
||
* @param boolean TRUE to enable compression
|
||
*/
|
||
public function setCompression($compression) {
|
||
$this->compression = $compression;
|
||
}
|
||
/**
|
||
* Set data compression level.
|
||
* If compression is enabled and this is not set,
|
||
* gzcompress default level will be used
|
||
*
|
||
* @param integer -1 to 9: Compression level
|
||
*/
|
||
public function setCompressionLevel($compressionLevel) {
|
||
if ($compressionLevel >= -1 && $compressionLevel <= 9) {
|
||
$this->compressionLevel = $compressionLevel;
|
||
}
|
||
}
|
||
/**
|
||
* Gets the query to be used for selecting entries by a tag. The asterisk ("*")
|
||
* is allowed as a wildcard at the beginning and the end of a tag.
|
||
*
|
||
* @param string The tag to search for, the "*" wildcard is supported
|
||
* @return string the query to be used for selecting entries
|
||
* @author Oliver Hader <oliver@typo3.org>
|
||
*/
|
||
protected function getQueryForTag($tag) {
|
||
if (strpos($tag, '*') === false) {
|
||
$query = $this->tagsTable . '.tag = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr($tag, $this->tagsTable);
|
||
} else {
|
||
$patternForLike = $GLOBALS['TYPO3_DB']->escapeStrForLike(
|
||
$GLOBALS['TYPO3_DB']->quoteStr($tag, $this->tagsTable),
|
||
$this->tagsTable
|
||
);
|
||
$query = $this->tagsTable . '.tag LIKE \'' . $patternForLike . '\'';
|
||
}
|
||
return $query;
|
||
}
|
||
/**
|
||
* Deletes rows in cache table found by where clause on tags table
|
||
*
|
||
* @param string The where clause for the tags table
|
||
* @return void
|
||
*/
|
||
protected function deleteCacheTableRowsByTagsTableWhereClause($tagsTableWhereClause) {
|
||
$cacheEntryIdentifierRowsRessource = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
|
||
'DISTINCT identifier',
|
||
$this->tagsTable,
|
||
$tagsTableWhereClause
|
||
);
|
||
$cacheEntryIdentifiers = array();
|
||
while ($cacheEntryIdentifierRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($cacheEntryIdentifierRowsRessource)) {
|
||
$cacheEntryIdentifiers[] = $GLOBALS['TYPO3_DB']->fullQuoteStr(
|
||
$cacheEntryIdentifierRow['identifier'],
|
||
$this->cacheTable
|
||
);
|
||
}
|
||
$GLOBALS['TYPO3_DB']->sql_free_result($cacheEntryIdentifierRowsRessource);
|
||
if (count($cacheEntryIdentifiers)) {
|
||
$GLOBALS['TYPO3_DB']->exec_DELETEquery(
|
||
$this->cacheTable,
|
||
'identifier IN (' . implode(', ', $cacheEntryIdentifiers) . ')'
|
||
);
|
||
}
|
||
}
|
||
}
|
||
if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/cache/backend/class.t3lib_cache_backend_dbbackend.php'])) {
|
||
include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/cache/backend/class.t3lib_cache_backend_dbbackend.php']);
|
||
}
|
||
?>
|
t3lib/core_autoload.php (working copy) | ||
---|---|---|
't3lib_cache_backend_abstractbackend' => PATH_t3lib . 'cache/backend/class.t3lib_cache_backend_abstractbackend.php',
|
||
't3lib_cache_backend_apcbackend' => PATH_t3lib . 'cache/backend/class.t3lib_cache_backend_apcbackend.php',
|
||
't3lib_cache_backend_dbbackend' => PATH_t3lib . 'cache/backend/class.t3lib_cache_backend_dbbackend.php',
|
||
't3lib_cache_backend_dbbackend2' => PATH_t3lib . 'cache/backend/class.t3lib_cache_backend_dbbackend2.php',
|
||
't3lib_cache_backend_filebackend' => PATH_t3lib . 'cache/backend/class.t3lib_cache_backend_filebackend.php',
|
||
't3lib_cache_backend_memcachedbackend' => PATH_t3lib . 'cache/backend/class.t3lib_cache_backend_memcachedbackend.php',
|
||
't3lib_cache_backend_nullbackend' => PATH_t3lib . 'cache/backend/class.t3lib_cache_backend_nullbackend.php',
|
typo3/sysext/cms/ext_tables.sql (working copy) | ||
---|---|---|
identifier varchar(128) DEFAULT '' NOT NULL,
|
||
crdate int(11) unsigned DEFAULT '0' NOT NULL,
|
||
content mediumblob,
|
||
lifetime int(11) unsigned DEFAULT '0' NOT NULL,
|
||
expires int(11) unsigned DEFAULT '0' NOT NULL,
|
||
PRIMARY KEY (id),
|
||
KEY cache_id (identifier)
|
||
KEY expires (identifier,expires)
|
||
) ENGINE=InnoDB;
|
||
... | ... | |
identifier varchar(128) DEFAULT '' NOT NULL,
|
||
crdate int(11) unsigned DEFAULT '0' NOT NULL,
|
||
content mediumblob,
|
||
lifetime int(11) unsigned DEFAULT '0' NOT NULL,
|
||
expires int(11) unsigned DEFAULT '0' NOT NULL,
|
||
PRIMARY KEY (id),
|
||
KEY cache_id (identifier)
|
||
KEY expires (identifier,expires)
|
||
) ENGINE=InnoDB;
|
||