Version 1
[yaffs-website] / web / modules / contrib / memcache / src / MemcacheBackend.php
diff --git a/web/modules/contrib/memcache/src/MemcacheBackend.php b/web/modules/contrib/memcache/src/MemcacheBackend.php
new file mode 100644 (file)
index 0000000..13b6da7
--- /dev/null
@@ -0,0 +1,322 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\memcache\MemcacheBackend.
+ */
+
+namespace Drupal\memcache;
+
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Cache\CacheTagsChecksumInterface;
+use Drupal\Core\Lock\LockBackendInterface;
+
+/**
+ * Defines a Memcache cache backend.
+ */
+class MemcacheBackend implements CacheBackendInterface {
+
+  /**
+   * The cache bin to use.
+   *
+   * @var string
+   */
+  protected $bin;
+
+  /**
+   * The lock count.
+   *
+   * @var int
+   */
+  protected $lockCount = 0;
+
+  /**
+   * The memcache wrapper object.
+   *
+   * @var \Drupal\memcache\DrupalMemcacheInterface
+   */
+  protected $memcache;
+
+  /**
+   * The lock backend that should be used.
+   *
+   * @var \Drupal\Core\Lock\LockBackendInterface
+   */
+  protected $lock;
+
+  /**
+   * The Settings instance.
+   *
+   * @var \Drupal\memcache\DrupalMemcacheConfig
+   */
+  protected $settings;
+
+  /**
+   * The cache tags checksum provider.
+   *
+   * @var \Drupal\Core\Cache\CacheTagsChecksumInterface
+   */
+  protected $checksumProvider;
+
+  /**
+   * Constructs a MemcacheBackend object.
+   *\Drupal\Core\Site\Settings
+   * @param string $bin
+   *   The bin name.
+   * @param \Drupal\memcache\DrupalMemcacheInterface $memcache
+   *   The memcache object.
+   * @param \Drupal\Core\Lock\LockBackendInterface $lock
+   *   The lock backend.
+   * @param \Drupal\memcache\DrupalMemcacheConfig $settings
+   *   The settings instance.
+   * @param \Drupal\Core\Cache\CacheTagsChecksumInterface $checksum_provider
+   *   The cache tags checksum service.
+   */
+  public function __construct($bin, DrupalMemcacheInterface $memcache, LockBackendInterface $lock, DrupalMemcacheConfig $settings, CacheTagsChecksumInterface $checksum_provider) {
+    $this->bin = $bin;
+    $this->memcache = $memcache;
+    $this->lock = $lock;
+    $this->settings = $settings;
+    $this->checksumProvider = $checksum_provider;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function get($cid, $allow_invalid = FALSE) {
+    $cids = array($cid);
+    $cache = $this->getMultiple($cids, $allow_invalid);
+    return reset($cache);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMultiple(&$cids, $allow_invalid = FALSE) {
+    $keys = array_map(function($cid) {
+      return $this->key($cid);
+    }, $cids);
+
+    $cache = $this->memcache->getMulti($keys);
+    $fetched = [];
+
+    foreach ($cache as $key => $result) {
+      if ($this->valid($result->cid, $result) || $allow_invalid) {
+        // Add it to the fetched items to diff later.
+        $fetched[$result->cid] = $result;
+      }
+    }
+
+    // Remove items from the referenced $cids array that we are returning,
+    // per comment in Drupal\Core\Cache\CacheBackendInterface::getMultiple().
+    $cids = array_diff($cids, array_keys($fetched));
+
+    return $fetched;
+  }
+
+  /**
+   * Determines if the cache item is valid.
+   *
+   * This also alters the valid property of the cache item itself.
+   *
+   * @param string $cid
+   *   The cache ID.
+   * @param \stdClass $cache
+   *   The cache item.
+   *
+   * @return bool
+   */
+  protected function valid($cid, \stdClass $cache) {
+    $lock_key = "memcache_$cid:$this->bin";
+    $cache->valid = FALSE;
+
+    if ($cache) {
+      // Items that have expired are invalid.
+      if (isset($cache->expire) && ($cache->expire != CacheBackendInterface::CACHE_PERMANENT) && ($cache->expire <= REQUEST_TIME)) {
+        // If the memcache_stampede_protection variable is set, allow one
+        // process to rebuild the cache entry while serving expired content to
+        // the rest.
+        if ($this->settings->get('stampede_protection', FALSE)) {
+          // The process that acquires the lock will get a cache miss, all
+          // others will get a cache hit.
+          if (!$this->lock->acquire($lock_key, $this->settings->get('stampede_semaphore', 15))) {
+            $cache->valid = TRUE;
+          }
+        }
+      }
+      else {
+        $cache->valid = TRUE;
+      }
+    }
+    // On cache misses, attempt to avoid stampedes when the
+    // memcache_stampede_protection variable is enabled.
+    else {
+      if ($this->settings->get('stampede_protection', FALSE) && !$this->lock->acquire($lock_key, $this->settings->get('stampede_semaphore', 15))) {
+        // Prevent any single request from waiting more than three times due to
+        // stampede protection. By default this is a maximum total wait of 15
+        // seconds. This accounts for two possibilities - a cache and lock miss
+        // more than once for the same item. Or a cache and lock miss for
+        // different items during the same request.
+        // @todo: it would be better to base this on time waited rather than
+        // number of waits, but the lock API does not currently provide this
+        // information. Currently the limit will kick in for three waits of 25ms
+        // or three waits of 5000ms.
+        $this->lockCount++;
+        if ($this->lockCount <= $this->settings->get('stampede_wait_limit', 3)) {
+          // The memcache_stampede_semaphore variable was used in previous
+          // releases of memcache, but the max_wait variable was not, so by
+          // default divide the semaphore value by 3 (5 seconds).
+         $this->lock->wait($lock_key, $this->settings->get('stampede_wait_time', 5));
+          $cache = $this->get($cid);
+        }
+      }
+    }
+
+    // Check if invalidateTags() has been called with any of the items's tags.
+    if (!$this->checksumProvider->isValid($cache->checksum, $cache->tags)) {
+      $cache->valid = FALSE;
+    }
+
+    return (bool) $cache->valid;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = array()) {
+    assert('\Drupal\Component\Assertion\Inspector::assertAllStrings($tags)');
+    $tags = array_unique($tags);
+    // Sort the cache tags so that they are stored consistently.
+    sort($tags);
+
+    // Create new cache object.
+    $cache = new \stdClass();
+    $cache->cid = $cid;
+    $cache->data = is_object($data) ? clone $data : $data;
+    $cache->created = round(microtime(TRUE), 3);
+    $cache->expire = $expire;
+    $cache->tags = $tags;
+    $cache->checksum = $this->checksumProvider->getCurrentChecksum($tags);
+
+    // Cache all items permanently. We handle expiration in our own logic.
+    return $this->memcache->set($this->key($cid), $cache);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setMultiple(array $items) {
+    foreach ($items as $cid => $item) {
+      $item += array(
+        'expire' => CacheBackendInterface::CACHE_PERMANENT,
+        'tags' => array(),
+      );
+
+      $this->set($cid, $item['data'], $item['expire'], $item['tags']);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete($cid) {
+    $this->memcache->delete($this->key($cid));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteMultiple(array $cids) {
+    foreach ($cids as $cid) {
+      $this->memcache->delete($this->key($cid));
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteAll() {
+    // Invalidate all keys, as we can't actually delete all?
+    $this->invalidateAll();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function invalidate($cid) {
+    $this->invalidateMultiple((array) $cid);
+  }
+
+  /**
+   * Marks cache items as invalid.
+   *
+   * Invalid items may be returned in later calls to get(), if the
+   * $allow_invalid argument is TRUE.
+   *
+   * @param array $cids
+   *   An array of cache IDs to invalidate.
+   *
+   * @see Drupal\Core\Cache\CacheBackendInterface::deleteMultiple()
+   * @see Drupal\Core\Cache\CacheBackendInterface::invalidate()
+   * @see Drupal\Core\Cache\CacheBackendInterface::invalidateTags()
+   * @see Drupal\Core\Cache\CacheBackendInterface::invalidateAll()
+   */
+  public function invalidateMultiple(array $cids) {
+    foreach ($cids as $cid) {
+      if ($item = $this->get($cid)) {
+        $item->expire = REQUEST_TIME - 1;
+        $this->memcache->set($this->key($cid), $item);
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function invalidateAll() {
+    $this->memcache->flush();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function invalidateTags(array $tags) {
+    $this->checksumProvider->invalidateTags($tags);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function removeBin() {
+    // Do nothing here too?
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function garbageCollection() {
+    // Memcache will invalidate items; That items memory allocation is then
+    // freed up and reused. So nothing needs to be deleted/cleaned up here.
+  }
+
+  /**
+   * (@inheritdoc)
+   */
+  public function isEmpty() {
+    // We do not know so err on the safe side? Not sure if we can know this?
+    return TRUE;
+  }
+
+  /**
+   * Returns a cache key prefixed with the current bin.
+   *
+   * @param string $cid
+   *
+   * @return string
+   */
+  protected function key($cid) {
+    return $this->bin . '-' . $cid;
+  }
+
+}