TYPO3 CMS  TYPO3_6-2
MemcachedBackend.php
Go to the documentation of this file.
1 <?php
3 
48 
54  const MAX_BUCKET_SIZE = 1048534;
60  protected $memcache;
61 
67  protected $servers = array();
68 
75  protected $flags;
76 
82  protected $identifierPrefix;
83 
91  public function __construct($context, array $options = array()) {
92  if (!extension_loaded('memcache')) {
93  throw new \TYPO3\CMS\Core\Cache\Exception('The PHP extension "memcache" must be installed and loaded in ' . 'order to use the Memcached backend.', 1213987706);
94  }
95  parent::__construct($context, $options);
96  }
97 
106  protected function setServers(array $servers) {
107  $this->servers = $servers;
108  }
109 
117  protected function setCompression($useCompression) {
118  if ($useCompression === TRUE) {
119  $this->flags ^= MEMCACHE_COMPRESSED;
120  } else {
121  $this->flags &= ~MEMCACHE_COMPRESSED;
122  }
123  }
124 
131  public function initializeObject() {
132  if (!count($this->servers)) {
133  throw new \TYPO3\CMS\Core\Cache\Exception('No servers were given to Memcache', 1213115903);
134  }
135  $this->memcache = new \Memcache();
136  $defaultPort = ini_get('memcache.default_port');
137  foreach ($this->servers as $server) {
138  if (substr($server, 0, 7) == 'unix://') {
139  $host = $server;
140  $port = 0;
141  } else {
142  if (substr($server, 0, 6) === 'tcp://') {
143  $server = substr($server, 6);
144  }
145  if (strpos($server, ':') !== FALSE) {
146  list($host, $port) = explode(':', $server, 2);
147  } else {
148  $host = $server;
149  $port = $defaultPort;
150  }
151  }
152  $this->memcache->addserver($host, $port);
153  }
154  }
155 
162  public function setCache(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cache) {
163  parent::setCache($cache);
164  $identifierHash = substr(md5(PATH_site . $this->context . $this->cacheIdentifier), 0, 12);
165  $this->identifierPrefix = 'TYPO3_' . $identifierHash . '_';
166  }
167 
181  public function set($entryIdentifier, $data, array $tags = array(), $lifetime = NULL) {
182  if (strlen($this->identifierPrefix . $entryIdentifier) > 250) {
183  throw new \InvalidArgumentException('Could not set value. Key more than 250 characters (' . $this->identifierPrefix . $entryIdentifier . ').', 1232969508);
184  }
185  if (!$this->cache instanceof \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface) {
186  throw new \TYPO3\CMS\Core\Cache\Exception('No cache frontend has been set yet via setCache().', 1207149215);
187  }
188  if (!is_string($data)) {
189  throw new \TYPO3\CMS\Core\Cache\Exception\InvalidDataException('The specified data is of type "' . gettype($data) . '" but a string is expected.', 1207149231);
190  }
191  $tags[] = '%MEMCACHEBE%' . $this->cacheIdentifier;
192  $expiration = $lifetime !== NULL ? $lifetime : $this->defaultLifetime;
193  // Memcached consideres values over 2592000 sec (30 days) as UNIX timestamp
194  // thus $expiration should be converted from lifetime to UNIX timestamp
195  if ($expiration > 2592000) {
196  $expiration += $GLOBALS['EXEC_TIME'];
197  }
198  try {
199  if (strlen($data) > self::MAX_BUCKET_SIZE) {
200  $data = str_split($data, 1024 * 1000);
201  $success = TRUE;
202  $chunkNumber = 1;
203  foreach ($data as $chunk) {
204  $success = $success && $this->memcache->set($this->identifierPrefix . $entryIdentifier . '_chunk_' . $chunkNumber, $chunk, $this->flags, $expiration);
205  $chunkNumber++;
206  }
207  $success = $success && $this->memcache->set($this->identifierPrefix . $entryIdentifier, 'TYPO3*chunked:' . $chunkNumber, $this->flags, $expiration);
208  } else {
209  $success = $this->memcache->set($this->identifierPrefix . $entryIdentifier, $data, $this->flags, $expiration);
210  }
211  if ($success === TRUE) {
212  $this->removeIdentifierFromAllTags($entryIdentifier);
213  $this->addIdentifierToTags($entryIdentifier, $tags);
214  } else {
215  throw new \TYPO3\CMS\Core\Cache\Exception('Could not set data to memcache server.', 1275830266);
216  }
217  } catch (\Exception $exception) {
218  \TYPO3\CMS\Core\Utility\GeneralUtility::sysLog('Memcache: could not set value. Reason: ' . $exception->getMessage(), 'Core', \TYPO3\CMS\Core\Utility\GeneralUtility::SYSLOG_SEVERITY_WARNING);
219  }
220  }
221 
229  public function get($entryIdentifier) {
230  $value = $this->memcache->get($this->identifierPrefix . $entryIdentifier);
231  if (substr($value, 0, 14) === 'TYPO3*chunked:') {
232  list(, $chunkCount) = explode(':', $value);
233  $value = '';
234  for ($chunkNumber = 1; $chunkNumber < $chunkCount; $chunkNumber++) {
235  $value .= $this->memcache->get($this->identifierPrefix . $entryIdentifier . '_chunk_' . $chunkNumber);
236  }
237  }
238  return $value;
239  }
240 
248  public function has($entryIdentifier) {
249  return $this->memcache->get($this->identifierPrefix . $entryIdentifier) !== FALSE;
250  }
251 
261  public function remove($entryIdentifier) {
262  $this->removeIdentifierFromAllTags($entryIdentifier);
263  return $this->memcache->delete($this->identifierPrefix . $entryIdentifier, 0);
264  }
265 
274  public function findIdentifiersByTag($tag) {
275  $identifiers = $this->memcache->get($this->identifierPrefix . 'tag_' . $tag);
276  if ($identifiers !== FALSE) {
277  return (array) $identifiers;
278  } else {
279  return array();
280  }
281  }
282 
290  public function flush() {
291  if (!$this->cache instanceof \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface) {
292  throw new \TYPO3\CMS\Core\Cache\Exception('No cache frontend has been set via setCache() yet.', 1204111376);
293  }
294  $this->flushByTag('%MEMCACHEBE%' . $this->cacheIdentifier);
295  }
296 
304  public function flushByTag($tag) {
305  $identifiers = $this->findIdentifiersByTag($tag);
306  foreach ($identifiers as $identifier) {
307  $this->remove($identifier);
308  }
309  }
310 
318  protected function addIdentifierToTags($entryIdentifier, array $tags) {
319  // Get identifier-to-tag index to look for updates
320  $existingTags = $this->findTagsByIdentifier($entryIdentifier);
321  $existingTagsUpdated = FALSE;
322 
323  foreach ($tags as $tag) {
324  // Update tag-to-identifier index
325  $identifiers = $this->findIdentifiersByTag($tag);
326  if (!in_array($entryIdentifier, $identifiers, TRUE)) {
327  $identifiers[] = $entryIdentifier;
328  $this->memcache->set($this->identifierPrefix . 'tag_' . $tag, $identifiers);
329  }
330  // Test if identifier-to-tag index needs update
331  if (!in_array($tag, $existingTags, TRUE)) {
332  $existingTags[] = $tag;
333  $existingTagsUpdated = TRUE;
334  }
335  }
336 
337  // Update identifier-to-tag index if needed
338  if ($existingTagsUpdated) {
339  $this->memcache->set($this->identifierPrefix . 'ident_' . $entryIdentifier, $existingTags);
340  }
341  }
342 
350  protected function removeIdentifierFromAllTags($entryIdentifier) {
351  // Get tags for this identifier
352  $tags = $this->findTagsByIdentifier($entryIdentifier);
353  // Deassociate tags with this identifier
354  foreach ($tags as $tag) {
355  $identifiers = $this->findIdentifiersByTag($tag);
356  // Formally array_search() below should never return FALSE due to
357  // the behavior of findTagsByIdentifier(). But if reverse index is
358  // corrupted, we still can get 'FALSE' from array_search(). This is
359  // not a problem because we are removing this identifier from
360  // anywhere.
361  if (($key = array_search($entryIdentifier, $identifiers)) !== FALSE) {
362  unset($identifiers[$key]);
363  if (count($identifiers)) {
364  $this->memcache->set($this->identifierPrefix . 'tag_' . $tag, $identifiers);
365  } else {
366  $this->memcache->delete($this->identifierPrefix . 'tag_' . $tag, 0);
367  }
368  }
369  }
370  // Clear reverse tag index for this identifier
371  $this->memcache->delete($this->identifierPrefix . 'ident_' . $entryIdentifier, 0);
372  }
373 
382  protected function findTagsByIdentifier($identifier) {
383  $tags = $this->memcache->get($this->identifierPrefix . 'ident_' . $identifier);
384  return $tags === FALSE ? array() : (array) $tags;
385  }
386 
393  public function collectGarbage() {
394 
395  }
396 
397 }
setCache(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cache)
__construct($context, array $options=array())
addIdentifierToTags($entryIdentifier, array $tags)
$host
Definition: server.php:35
if(!defined('TYPO3_MODE')) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'][]