Bug #19506 » 9645.diff
t3lib/cache/backend/class.t3lib_cache_backend_memcached.php (working copy) | ||
---|---|---|
/**
|
||
* A caching backend which stores cache entries by using Memcached
|
||
* A caching backend which stores cache entries by using Memcached.
|
||
*
|
||
* This file is a backport from FLOW3
|
||
* This backend uses the following types of Memcache keys:
|
||
* - tag_xxx
|
||
* xxx is tag name, value is array of associated identifiers identifier. This
|
||
* is "forward" tag index. It is mainly used for obtaining content by tag
|
||
* (get identifier by tag -> get content by identifier)
|
||
* - ident_xxx
|
||
* xxx is identifier, value is array of associated tags. This is "reverse" tag
|
||
* index. It provides quick access for all tags associated with this identifier
|
||
* and used when removing the identifier
|
||
* - tagIndex
|
||
* Value is a List of all tags (array)
|
||
* Each key is prepended with a prefix. By default prefix consists from two parts
|
||
* separated by underscore character and ends in yet another underscore character:
|
||
* - "TYPO3"
|
||
* - Current host name obtained from the HTTP_HOST variable
|
||
* This prefix makes sure that keys from the different installations do not
|
||
* conflict.
|
||
*
|
||
* This file is a backport from FLOW3 by Ingo Renner.
|
||
*
|
||
* @package TYPO3
|
||
* @subpackage t3lib_cache
|
||
* @version $Id$
|
||
... | ... | |
class t3lib_cache_backend_Memcached extends t3lib_cache_AbstractBackend {
|
||
/**
|
||
* Instance of the PHP Memcache class
|
||
*
|
||
* @var Memcache
|
||
*/
|
||
protected $memcache;
|
||
/**
|
||
* Array of Memcache server configurations
|
||
*
|
||
* @var array
|
||
*/
|
||
protected $servers = array();
|
||
/**
|
||
* @var boolean whether the memcache uses compression or not (requires zlib)
|
||
* Indicates whether the memcache uses compression or not (requires zlib)
|
||
*
|
||
* @var boolean
|
||
*/
|
||
protected $useCompressed;
|
||
/**
|
||
* @var string A prefix to seperate stored data from other data possible stored in the memcache
|
||
* A prefix to seperate stored data from other data possibly stored in the
|
||
* memcache. This prefix must be unique for each site in the tree. Default
|
||
* implementation uses MD5 of the current host name to make identifier prefix
|
||
* unique.
|
||
*
|
||
* @var string
|
||
*/
|
||
protected $identifierPrefix;
|
||
/**
|
||
* @var string The ID of this TYPO3 server. If many sites are using the same memcached, it prevents conflicts
|
||
*/
|
||
protected $serverId;
|
||
/**
|
||
* Constructs this backend
|
||
*
|
||
* @param string $context FLOW3's application context
|
||
* @param mixed $options Configuration options - depends on the actual backend
|
||
* @author Robert Lemke <robert@typo3.org>
|
||
*/
|
||
public function __construct($options = array()) {
|
||
if (!extension_loaded('memcache')) {
|
||
throw new t3lib_cache_Exception(
|
||
'The PHP extension "memcached" must be installed and loaded in order to use the Memcached backend.',
|
||
'The PHP extension "memcached" must be installed and loaded in ' .
|
||
'order to use the Memcached backend.',
|
||
1213987706
|
||
);
|
||
}
|
||
// Set default value for the server ID
|
||
$this->serverId = t3lib_div::getIndpEnv('HTTP_HOST');
|
||
parent::__construct($options);
|
||
$this->memcache = new Memcache();
|
||
$this->identifierPrefix = 'TYPO3_' . md5(
|
||
t3lib_div::getIndpEnv('SCRIPT_FILENAME')
|
||
. php_sapi_name()
|
||
. $this->serverId
|
||
) . '_';
|
||
$this->identifierPrefix = $this->getIdentifierPrefix();
|
||
if (!count($this->servers)) {
|
||
throw new t3lib_cache_Exception(
|
||
... | ... | |
}
|
||
/**
|
||
* Setter for serverId property.
|
||
* Sets servers property.
|
||
*
|
||
* @param int $serverId The value of the property
|
||
* @param array An array of servers to add (format: "host:port")
|
||
* @return void
|
||
* @author Christian Jul Jensen <julle@typo3.org>
|
||
*/
|
||
protected function setServerId($serverId) {
|
||
$this->serverId = $serverId;
|
||
}
|
||
/**
|
||
* setter for servers property
|
||
* should be an array of entries like host:port
|
||
*
|
||
* @param array An array of servers to add
|
||
* @return void
|
||
* @author Christian Jul Jensen <julle@typo3.org>
|
||
*/
|
||
protected function setServers(array $servers) {
|
||
$this->servers = $servers;
|
||
}
|
||
/**
|
||
* Setter for useCompressed
|
||
* Sets for compression flag
|
||
*
|
||
* @param boolean $enableCompression
|
||
* @return void
|
||
* @author Christian Jul Jensen <julle@typo3.org>
|
||
* @param boolean $enableCompression New value of compression flag
|
||
* @return void
|
||
* @author Christian Jul Jensen <julle@typo3.org>
|
||
*/
|
||
protected function setCompression($enableCompression) {
|
||
$this->useCompressed = $enableCompression;
|
||
... | ... | |
if (!is_string($data)) {
|
||
throw new t3lib_cache_Exception_InvalidData(
|
||
'The specified data is of type "' . gettype($data) . '" but a string is expected.',
|
||
'The specified data is of type "' . gettype($data) .
|
||
'" but a string is expected.',
|
||
1207149231
|
||
);
|
||
}
|
||
... | ... | |
$this->addIdentifierToTags($entryIdentifier, $tags);
|
||
} catch(Exception $exception) {
|
||
throw new t3lib_cache_Exception(
|
||
'Memcache was unable to connect to any server. ' . $exception->getMessage(),
|
||
'Memcache was unable to connect to any server. ' .
|
||
$exception->getMessage(),
|
||
1207208100
|
||
);
|
||
}
|
||
... | ... | |
* @author Karsten Dambekalns <karsten@typo3.org>
|
||
*/
|
||
public function has($entryIdentifier) {
|
||
return (boolean) $this->memcache->get($this->identifierPrefix . $entryIdentifier);
|
||
return $this->memcache->get($this->identifierPrefix . $entryIdentifier) !== false;
|
||
}
|
||
/**
|
||
... | ... | |
*/
|
||
public function remove($entryIdentifier) {
|
||
$this->removeIdentifierFromAllTags($entryIdentifier);
|
||
$this->memcache->delete($this->identifierPrefix . 'ident_' . $entryIdentifier);
|
||
return $this->memcache->delete($this->identifierPrefix . $entryIdentifier);
|
||
}
|
||
... | ... | |
);
|
||
}
|
||
$entries = array();
|
||
$entries = array();
|
||
$identifiers = $this->findIdentifiersTaggedWith($tag);
|
||
foreach($identifiers as $identifier) {
|
||
$entries[] = $this->get($identifier);
|
||
... | ... | |
* @author Karsten Dambekalns <karsten@typo3.org>
|
||
*/
|
||
protected function getTagIndex() {
|
||
return (array) $this->memcache->get($this->identifierPrefix . '_tagIndex');
|
||
$tagIndex = $this->memcache->get($this->identifierPrefix . 'tagIndex');
|
||
return ($tagIndex == false ? array() : (array)$tagIndex);
|
||
}
|
||
/**
|
||
... | ... | |
* @author Karsten Dambekalns <karsten@typo3.org>
|
||
*/
|
||
protected function setTagIndex(array $tags) {
|
||
$this->memcache->set(
|
||
$this->identifierPrefix . '_tagIndex',
|
||
array_unique($tags),
|
||
0,
|
||
0
|
||
);
|
||
$this->memcache->set($this->identifierPrefix . 'tagIndex', array_unique($tags), 0, 0);
|
||
}
|
||
/**
|
||
... | ... | |
* @param string $entryIdentifier
|
||
* @param array Array of tags
|
||
* @author Karsten Dambekalns <karsten@typo3.org>
|
||
* @author Dmitry Dulepov
|
||
*/
|
||
protected function addIdentifierToTags($entryIdentifier, array $tags) {
|
||
foreach($tags as $tag) {
|
||
$identifiers = $this->findIdentifiersTaggedWith($tag);
|
||
$identifiers[] = $entryIdentifier;
|
||
$this->memcache->set(
|
||
$this->identifierPrefix . '_tag_' . $tag,
|
||
array_unique($identifiers)
|
||
);
|
||
// Update tag-to-identifier index
|
||
$identifiers = $this->findIdentifiersTaggedWith($tag);
|
||
if (array_search($entryIdentifier, $identifiers) === false) {
|
||
$identifiers[] = $entryIdentifier;
|
||
$this->memcache->set($this->identifierPrefix . 'tag_' . $tag,
|
||
$identifiers);
|
||
}
|
||
// Update identifier-to-tag index
|
||
$existingTags = $this->findTagsForIdentifier($entryIdentifier);
|
||
if (array_search($entryIdentifier, $existingTags) === false) {
|
||
$this->memcache->set($this->identifierPrefix . 'ident_' . $entryIdentifier,
|
||
array_merge($existingTags, $tags));
|
||
}
|
||
}
|
||
}
|
||
... | ... | |
* @param string $entryIdentifier
|
||
* @param array Array of tags
|
||
* @author Karsten Dambekalns <karsten@typo3.org>
|
||
* @author Dmitry Dulepov
|
||
*/
|
||
protected function removeIdentifierFromAllTags($entryIdentifier) {
|
||
$tags = $this->getTagIndex();
|
||
foreach($tags as $tag) {
|
||
// Get tags for this identifier
|
||
$tags = $this->findTagsForIdentifier($entryIdentifier);
|
||
// Deassociate tags with this identifier
|
||
foreach ($tags as $tag) {
|
||
$identifiers = $this->findIdentifiersTaggedWith($tag);
|
||
// Formally array_search() below should never return false due to
|
||
// the behavior of findTagsForIdentifier(). But if reverse index is
|
||
// corrupted, we still can get 'false' from array_search(). This is
|
||
// not a problem because we are removing this identifier from
|
||
// anywhere.
|
||
if (($key = array_search($entryIdentifier, $identifiers)) !== false) {
|
||
unset($identifiers[$key]);
|
||
if(array_search($entryIdentifier, $identifiers) !== FALSE) {
|
||
unset($identifiers[array_search($entryIdentifier, $identifiers)]);
|
||
if(count($identifiers)) {
|
||
$this->memcache->set(
|
||
$this->identifierPrefix . 'tag_' . $tag,
|
||
$identifiers
|
||
);
|
||
} else {
|
||
$this->removeTagsFromTagIndex(array($tag));
|
||
$this->memcache->delete($this->identifierPrefix . 'tag_' . $tag);
|
||
}
|
||
}
|
||
if(count($identifiers)) {
|
||
$this->memcache->set(
|
||
$this->identifierPrefix . '_tag_' . $tag,
|
||
array_unique($identifiers)
|
||
);
|
||
} else {
|
||
$this->removeTagsFromTagIndex(array($tag));
|
||
$this->memcache->delete($this->identifierPrefix . '_tag_' . $tag);
|
||
}
|
||
}
|
||
// Clear reverse tag index for this identifier
|
||
$this->memcache->delete($this->identifierPrefix . 'ident_' . $entryIdentifier);
|
||
}
|
||
/**
|
||
... | ... | |
* @author Karsten Dambekalns <karsten@typo3.org>
|
||
*/
|
||
public function findIdentifiersTaggedWith($tag) {
|
||
$identifiers = $this->memcache->get($this->identifierPrefix . '_tag_' . $tag);
|
||
$identifiers = $this->memcache->get($this->identifierPrefix . 'tag_' . $tag);
|
||
return ($identifiers === false ? array() : $identifiers);
|
||
}
|
||
if($identifiers !== FALSE) {
|
||
return (array) $identifiers;
|
||
} else {
|
||
return array();
|
||
}
|
||
/**
|
||
* Finds all tags for the given identifier. This function uses reverse tag
|
||
* index to search for tags.
|
||
*
|
||
* @param string $identifier Identifier to search tags for
|
||
* @return array Array with tags
|
||
*/
|
||
protected function findTagsForIdentifier($identifier) {
|
||
$tags = $this->memcache->get($this->identifierPrefix . 'ident_' . $identifier);
|
||
return ($tags == false ? array() : (array)$tags);
|
||
}
|
||
/**
|
||
* Returns idenfier prefix. Extensions can override this function to provide
|
||
* another identifier prefix if it is necessary for special purposes.
|
||
* Default identifier prefix is based on HTTP_HOST only. In most cases
|
||
* it is enough because there should be one host, which serves requests
|
||
* and other hosts that redirect to the serving host.
|
||
*
|
||
* @return string Identifier prefix, ending with underscore
|
||
* @author Dmitry Dulepov
|
||
*/
|
||
protected function getIdentifierPrefix() {
|
||
return 'TYPO3_' . md5(t3lib_div::getIndpEnv('HTTP_HOST')) . '_';
|
||
}
|
||
}
|
||