Revert changes in CRM/Core/BAO{Cache|ConfigSetting}.php CRM/Extension/ClassLoader...
[civicrm-core.git] / CRM / Utils / Cache / Memcache.php
index c85b85559b28bfd703951b5c6ac742d47222e832..511663792e575030897694f495f184a2f505ce73 100644 (file)
@@ -3,7 +3,7 @@
  +--------------------------------------------------------------------+
  | CiviCRM version 5                                                  |
  +--------------------------------------------------------------------+
- | Copyright CiviCRM LLC (c) 2004-2018                                |
+ | Copyright CiviCRM LLC (c) 2004-2019                                |
  +--------------------------------------------------------------------+
  | This file is a part of CiviCRM.                                    |
  |                                                                    |
 /**
  *
  * @package CRM
- * @copyright CiviCRM LLC (c) 2004-2018
+ * @copyright CiviCRM LLC (c) 2004-2019
  */
 class CRM_Utils_Cache_Memcache implements CRM_Utils_Cache_Interface {
 
-  use CRM_Utils_Cache_NaiveMultipleTrait; // TODO Consider native implementation.
+  // TODO Consider native implementation.
+  use CRM_Utils_Cache_NaiveMultipleTrait;
 
   const DEFAULT_HOST = 'localhost';
   const DEFAULT_PORT = 11211;
   const DEFAULT_TIMEOUT = 3600;
   const DEFAULT_PREFIX = '';
 
+  /**
+   * If another process clears namespace, we'll find out in ~5 sec.
+   */
+  const NS_LOCAL_TTL = 5;
+
   /**
    * The host name of the memcached server.
    *
@@ -78,6 +84,15 @@ class CRM_Utils_Cache_Memcache implements CRM_Utils_Cache_Interface {
    */
   protected $_cache;
 
+  /**
+   * @var NULL|array
+   *
+   * This is the effective prefix. It may be bumped up whenever the dataset is flushed.
+   *
+   * @see https://github.com/memcached/memcached/wiki/ProgrammingTricks#deleting-by-namespace
+   */
+  protected $_truePrefix = NULL;
+
   /**
    * Constructor.
    *
@@ -117,13 +132,12 @@ class CRM_Utils_Cache_Memcache implements CRM_Utils_Cache_Interface {
    * @return bool
    */
   public function set($key, $value, $ttl = NULL) {
-    if ($ttl !== NULL) {
-      throw new \RuntimeException("FIXME: " . __CLASS__ . "::set() should support non-NULL TTL");
-    }
-    if (!$this->_cache->set($this->_prefix . $key, $value, FALSE, $this->_timeout)) {
-      return FALSE;
+    CRM_Utils_Cache::assertValidKey($key);
+    if (is_int($ttl) && $ttl <= 0) {
+      return $this->delete($key);
     }
-    return TRUE;
+    $expires = CRM_Utils_Date::convertCacheTtlToExpires($ttl, $this->_timeout);
+    return $this->_cache->set($this->getTruePrefix() . $key, serialize($value), FALSE, $expires);
   }
 
   /**
@@ -133,28 +147,62 @@ class CRM_Utils_Cache_Memcache implements CRM_Utils_Cache_Interface {
    * @return mixed
    */
   public function get($key, $default = NULL) {
-    if ($default !== NULL) {
-      throw new \RuntimeException("FIXME: " . __CLASS__ . "::get() only supports NULL default");
-    }
-    $result = $this->_cache->get($this->_prefix . $key);
-    return $result;
+    CRM_Utils_Cache::assertValidKey($key);
+    $result = $this->_cache->get($this->getTruePrefix() . $key);
+    return ($result === FALSE) ? $default : unserialize($result);
+  }
+
+  /**
+   * @param string $key
+   *
+   * @return bool
+   * @throws \Psr\SimpleCache\CacheException
+   */
+  public function has($key) {
+    CRM_Utils_Cache::assertValidKey($key);
+    $result = $this->_cache->get($this->getTruePrefix() . $key);
+    return ($result !== FALSE);
   }
 
   /**
    * @param $key
    *
-   * @return mixed
+   * @return bool
    */
   public function delete($key) {
-    return $this->_cache->delete($this->_prefix . $key);
+    CRM_Utils_Cache::assertValidKey($key);
+    $this->_cache->delete($this->getTruePrefix() . $key);
+    return TRUE;
   }
 
   /**
-   * @return mixed
+   * @return bool
    */
   public function flush() {
-    // FIXME: Only delete items matching `$this->_prefix`.
-    return $this->_cache->flush();
+    $this->_truePrefix = NULL;
+    $this->_cache->delete($this->_prefix);
+    return TRUE;
+  }
+
+  public function clear() {
+    return $this->flush();
+  }
+
+  protected function getTruePrefix() {
+    if ($this->_truePrefix === NULL || $this->_truePrefix['expires'] < time()) {
+      $key = $this->_prefix;
+      $value = $this->_cache->get($key);
+      if ($value === FALSE) {
+        $value = uniqid();
+        // Indefinite.
+        $this->_cache->set($key, $value, FALSE, 0);
+      }
+      $this->_truePrefix = [
+        'value' => $value,
+        'expires' => time() + self::NS_LOCAL_TTL,
+      ];
+    }
+    return $this->_prefix . $this->_truePrefix['value'] . '/';
   }
 
 }