Unverified Commit daf53354 authored by eileen's avatar eileen 🎱 Committed by GitHub

Merge pull request #12342 from totten/master-psr16-min

Caching - Comply with PSR-16 interfaces
parents 067ec553 ebf0eb53
......@@ -31,6 +31,10 @@
* @copyright CiviCRM LLC (c) 2004-2018
*/
class CRM_Utils_Cache_APCcache implements CRM_Utils_Cache_Interface {
use CRM_Utils_Cache_NaiveMultipleTrait; // TODO Consider native implementation.
use CRM_Utils_Cache_NaiveHasTrait; // TODO Native implementation
const DEFAULT_TIMEOUT = 3600;
const DEFAULT_PREFIX = '';
......@@ -72,10 +76,14 @@ class CRM_Utils_Cache_APCcache implements CRM_Utils_Cache_Interface {
/**
* @param $key
* @param $value
* @param null|int|\DateInterval $ttl
*
* @return bool
*/
public function set($key, &$value) {
public function set($key, $value, $ttl = NULL) {
if ($ttl !== NULL) {
throw new \RuntimeException("FIXME: " . __CLASS__ . "::set() should support non-NULL TTL");
}
if (!apc_store($this->_prefix . $key, $value, $this->_timeout)) {
return FALSE;
}
......@@ -84,10 +92,14 @@ class CRM_Utils_Cache_APCcache implements CRM_Utils_Cache_Interface {
/**
* @param $key
* @param mixed $default
*
* @return mixed
*/
public function get($key) {
public function get($key, $default = NULL) {
if ($default !== NULL) {
throw new \RuntimeException("FIXME: " . __CLASS__ . "::get() only supports NULL default");
}
return apc_fetch($this->_prefix . $key);
}
......@@ -113,6 +125,11 @@ class CRM_Utils_Cache_APCcache implements CRM_Utils_Cache_Interface {
apc_delete($this->_prefix . $name);
}
}
return TRUE;
}
public function clear() {
return $this->flush();
}
}
......@@ -36,6 +36,9 @@
*/
class CRM_Utils_Cache_Arraycache implements CRM_Utils_Cache_Interface {
use CRM_Utils_Cache_NaiveMultipleTrait;
use CRM_Utils_Cache_NaiveHasTrait; // TODO Native implementation
/**
* The cache storage container, an in memory array by default
*/
......@@ -56,30 +59,44 @@ class CRM_Utils_Cache_Arraycache implements CRM_Utils_Cache_Interface {
/**
* @param string $key
* @param mixed $value
* @param null|int|\DateInterval $ttl
* @return bool
*/
public function set($key, &$value) {
public function set($key, $value, $ttl = NULL) {
if ($ttl !== NULL) {
throw new \RuntimeException("FIXME: " . __CLASS__ . "::set() should support non-NULL TTL");
}
$this->_cache[$key] = $value;
return TRUE;
}
/**
* @param string $key
* @param mixed $default
*
* @return mixed
*/
public function get($key) {
return CRM_Utils_Array::value($key, $this->_cache);
public function get($key, $default = NULL) {
return CRM_Utils_Array::value($key, $this->_cache, $default);
}
/**
* @param string $key
* @return bool
*/
public function delete($key) {
unset($this->_cache[$key]);
return TRUE;
}
public function flush() {
unset($this->_cache);
$this->_cache = array();
return TRUE;
}
public function clear() {
return $this->flush();
}
}
......@@ -30,60 +30,78 @@
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2018
*
* CRM_Utils_Cache_Interface
* CRM_Utils_Cache_Interface is a long-standing interface used within CiviCRM
* for interacting with a cache service. In style and substance, it is extremely
* similar to PHP-FIG's SimpleCache interface (PSR-16). Consequently, beginning
* with CiviCRM v5.4, this extends \Psr\SimpleCache\CacheInterface.
*
* PHP-FIG has been developing a draft standard for caching,
* PSR-6. The standard has not been ratified yet. When
* making changes to this interface, please take care to
* avoid *conflicst* with PSR-6's CacheItemPoolInterface. At
* time of writing, they do not conflict. Avoiding conflicts
* will enable more transition paths where Civi
* simultaneously supports both interfaces in the same
* implementation.
*
* For example, the current interface defines:
*
* function get($key) => mixed $value
*
* and PSR-6 defines:
*
* function getItem($key) => ItemInterface $item
*
* These are different styles (e.g. "weak item" vs "strong item"),
* but the two methods do not *conflict*. They can coexist,
* and you can trivially write adapters between the two.
*
* @see https://github.com/php-fig/fig-standards/blob/master/proposed/cache.md
* @see https://www.php-fig.org/psr/psr-16/
*/
interface CRM_Utils_Cache_Interface {
interface CRM_Utils_Cache_Interface extends \Psr\SimpleCache\CacheInterface {
/**
* Set the value in the cache.
*
* @param string $key
* @param mixed $value
* @param null|int|\DateInterval $ttl
* @return bool
*/
public function set($key, &$value);
public function set($key, $value, $ttl = NULL);
/**
* Get a value from the cache.
*
* @param string $key
* @param mixed $default
* @return mixed
* NULL if $key has not been previously set
* The previously set value value, or $default (NULL).
*/
public function get($key);
public function get($key, $default = NULL);
/**
* Delete a value from the cache.
*
* @param string $key
* @return bool
*/
public function delete($key);
/**
* Delete all values from the cache.
*
* NOTE: flush() and clear() should be aliases. flush() is specified by
* Civi's traditional interface, and clear() is specified by PSR-16.
*
* @return bool
* @see clear
* @deprecated
*/
public function flush();
/**
* Delete all values from the cache.
*
* NOTE: flush() and clear() should be aliases. flush() is specified by
* Civi's traditional interface, and clear() is specified by PSR-16.
*
* @return bool
* @see flush
*/
public function clear();
/**
* Determines whether an item is present in the cache.
*
* NOTE: It is recommended that has() is only to be used for cache warming type purposes
* and not to be used within your live applications operations for get/set, as this method
* is subject to a race condition where your has() will return true and immediately after,
* another script can remove it making the state of your app out of date.
*
* @param string $key The cache item key.
*
* @return bool
*/
public function has($key);
}
......@@ -31,6 +31,10 @@
* @copyright CiviCRM LLC (c) 2004-2018
*/
class CRM_Utils_Cache_Memcache implements CRM_Utils_Cache_Interface {
use CRM_Utils_Cache_NaiveMultipleTrait; // TODO Consider native implementation.
use CRM_Utils_Cache_NaiveHasTrait; // TODO Native implementation
const DEFAULT_HOST = 'localhost';
const DEFAULT_PORT = 11211;
const DEFAULT_TIMEOUT = 3600;
......@@ -109,10 +113,14 @@ class CRM_Utils_Cache_Memcache implements CRM_Utils_Cache_Interface {
/**
* @param $key
* @param $value
* @param null|int|\DateInterval $ttl
*
* @return bool
*/
public function set($key, &$value) {
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;
}
......@@ -121,10 +129,14 @@ class CRM_Utils_Cache_Memcache implements CRM_Utils_Cache_Interface {
/**
* @param $key
* @param mixed $default
*
* @return mixed
*/
public function &get($key) {
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;
}
......@@ -132,18 +144,22 @@ class CRM_Utils_Cache_Memcache implements CRM_Utils_Cache_Interface {
/**
* @param $key
*
* @return mixed
* @return bool
*/
public function delete($key) {
return $this->_cache->delete($this->_prefix . $key);
}
/**
* @return mixed
* @return bool
*/
public function flush() {
// FIXME: Only delete items matching `$this->_prefix`.
return $this->_cache->flush();
}
public function clear() {
return $this->flush();
}
}
......@@ -31,6 +31,10 @@
* @copyright CiviCRM LLC (c) 2004-2018
*/
class CRM_Utils_Cache_Memcached implements CRM_Utils_Cache_Interface {
use CRM_Utils_Cache_NaiveMultipleTrait; // TODO Consider native implementation.
use CRM_Utils_Cache_NaiveHasTrait; // TODO Native implementation
const DEFAULT_HOST = 'localhost';
const DEFAULT_PORT = 11211;
const DEFAULT_TIMEOUT = 3600;
......@@ -110,11 +114,15 @@ class CRM_Utils_Cache_Memcached implements CRM_Utils_Cache_Interface {
/**
* @param $key
* @param $value
* @param null|int|\DateInterval $ttl
*
* @return bool
* @throws Exception
*/
public function set($key, &$value) {
public function set($key, $value, $ttl = NULL) {
if ($ttl !== NULL) {
throw new \RuntimeException("FIXME: " . __CLASS__ . "::set() should support non-NULL TTL");
}
$key = $this->cleanKey($key);
if (!$this->_cache->set($key, $value, $this->_timeout)) {
CRM_Core_Error::debug('Result Code: ', $this->_cache->getResultMessage());
......@@ -126,10 +134,14 @@ class CRM_Utils_Cache_Memcached implements CRM_Utils_Cache_Interface {
/**
* @param $key
* @param mixed $default
*
* @return mixed
*/
public function &get($key) {
public function get($key, $default = NULL) {
if ($default !== NULL) {
throw new \RuntimeException("FIXME: " . __CLASS__ . "::get() only supports NULL default");
}
$key = $this->cleanKey($key);
$result = $this->_cache->get($key);
return $result;
......@@ -161,11 +173,15 @@ class CRM_Utils_Cache_Memcached implements CRM_Utils_Cache_Interface {
}
/**
* @return mixed
* @return bool
*/
public function flush() {
// FIXME: Only delete items matching `$this->_prefix`.
return $this->_cache->flush();
}
public function clear() {
return $this->flush();
}
}
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 5 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2018 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM 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 Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2018
*
* The traditional CRM_Utils_Cache_Interface did not support has().
* To get drop-in compliance with PSR-16, we use a naive adapter.
*
* Ideally, these should be replaced with more performant/native versions.
*/
trait CRM_Utils_Cache_NaiveHasTrait {
public function has($key) {
// This is crazy-talk. If you've got an environment setup where you might
// be investigating this, fix your preferred cache driver by
// replacing `NaiveHasTrait` with a decent function.
$hasDefaultA = ($this->get($key, NULL) === NULL);
$hasDefaultB = ($this->get($key, 123) === 123);
return !($hasDefaultA && $hasDefaultB);
}
}
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 5 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2018 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM 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 Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2018
*
* The traditional CRM_Utils_Cache_Interface did not support multiple-key
* operations. To get drop-in compliance with PSR-16, we use a naive adapter.
* An operation like `getMultiple()` just calls `get()` multiple times.
*
* Ideally, these should be replaced with more performant/native versions.
*/
trait CRM_Utils_Cache_NaiveMultipleTrait {
/**
* Obtains multiple cache items by their unique keys.
*
* @param iterable $keys A list of keys that can obtained in a single operation.
* @param mixed $default Default value to return for keys that do not exist.
*
* @return iterable A list of key => value pairs. Cache keys that do not exist or are stale will have $default as value.
*
* @throws \Psr\SimpleCache\InvalidArgumentException
* MUST be thrown if $keys is neither an array nor a Traversable,
* or if any of the $keys are not a legal value.
*/
public function getMultiple($keys, $default = NULL) {
$result = [];
foreach ($keys as $key) {
$result[$key] = $this->get($key, $default);
}
return $result;
}
/**
* Persists a set of key => value pairs in the cache, with an optional TTL.
*
* @param iterable $values A list of key => value pairs for a multiple-set operation.
* @param null|int|\DateInterval $ttl Optional. The TTL value of this item. If no value is sent and
* the driver supports TTL then the library may set a default value
* for it or let the driver take care of that.
*
* @return bool True on success and false on failure.
*
* @throws \Psr\SimpleCache\InvalidArgumentException
* MUST be thrown if $values is neither an array nor a Traversable,
* or if any of the $values are not a legal value.
*/
public function setMultiple($values, $ttl = NULL) {
$result = TRUE;
foreach ($values as $key => $value) {
$result = $this->set($key, $value, $ttl) || $result;
}
return $result;
}
/**
* Deletes multiple cache items in a single operation.
*
* @param iterable $keys A list of string-based keys to be deleted.
*
* @return bool True if the items were successfully removed. False if there was an error.
*
* @throws \Psr\SimpleCache\InvalidArgumentException
* MUST be thrown if $keys is neither an array nor a Traversable,
* or if any of the $keys are not a legal value.
*/
public function deleteMultiple($keys) {
$result = TRUE;
foreach ($keys as $key) {
$result = $this->delete($key) || $result;
}
return $result;
}
}
......@@ -32,6 +32,9 @@
*/
class CRM_Utils_Cache_NoCache implements CRM_Utils_Cache_Interface {
use CRM_Utils_Cache_NaiveMultipleTrait; // TODO Consider native implementation.
use CRM_Utils_Cache_NaiveHasTrait; // TODO Native implementation
/**
* We only need one instance of this object. So we use the singleton
* pattern and cache the instance in this variable
......@@ -54,20 +57,22 @@ class CRM_Utils_Cache_NoCache implements CRM_Utils_Cache_Interface {
/**
* @param string $key
* @param mixed $value
* @param null|int|\DateInterval $ttl
*
* @return bool
*/
public function set($key, &$value) {
public function set($key, $value, $ttl = NULL) {
return FALSE;
}
/**
* @param string $key
* @param mixed $default
*
* @return null
*/
public function get($key) {
return NULL;
public function get($key, $default = NULL) {
return $default;
}
/**
......@@ -86,4 +91,8 @@ class CRM_Utils_Cache_NoCache implements CRM_Utils_Cache_Interface {
return FALSE;
}
public function clear() {
return $this->flush();
}
}
......@@ -33,6 +33,10 @@
*
*/
class CRM_Utils_Cache_Redis implements CRM_Utils_Cache_Interface {
use CRM_Utils_Cache_NaiveMultipleTrait; // TODO Consider native implementation.
use CRM_Utils_Cache_NaiveHasTrait; // TODO Native implementation
const DEFAULT_HOST = 'localhost';
const DEFAULT_PORT = 6379;
const DEFAULT_TIMEOUT = 3600;
......@@ -113,11 +117,15 @@ class CRM_Utils_Cache_Redis implements CRM_Utils_Cache_Interface {
/**
* @param $key
* @param $value
* @param null|int|\DateInterval $ttl
*
* @return bool
* @throws Exception
*/
public function set($key, &$value) {
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, serialize($value), $this->_timeout)) {
if (PHP_SAPI === 'cli' || (Civi\Core\Container::isContainerBooted() && CRM_Core_Permission::check('view debug output'))) {
CRM_Core_Error::fatal("Redis set ($key) failed: " . $this->_cache->getLastError());
......@@ -133,25 +141,27 @@ class CRM_Utils_Cache_Redis implements CRM_Utils_Cache_Interface {
/**
* @param $key
* @param mixed $default
*
* @return mixed
*/
public function get($key) {
public function get($key, $default = NULL) {
$result = $this->_cache->get($this->_prefix . $key);
return ($result === FALSE) ? NULL : unserialize($result);
return ($result === FALSE) ? $default : unserialize($result);
}
/**
* @param $key
*
* @return mixed
* @return bool
*/
public function delete($key) {
return $this->_cache->delete($this->_prefix . $key);
$this->_cache->delete($this->_prefix . $key);
return TRUE;
}
/**
* @return mixed
* @return bool
*/
public function flush() {
// FIXME: Ideally, we'd map each prefix to a different 'hash' object in Redis,
......@@ -159,7 +169,12 @@ class CRM_Utils_Cache_Redis implements CRM_Utils_Cache_Interface {
// more general rethink of cache expiration/TTL.
$keys = $this->_cache->keys($this->_prefix . '*');
return $this->_cache->del($keys);
$this->_cache->del($keys);
return TRUE;
}
public function clear() {
return $this->flush();
}
}
......@@ -36,6 +36,9 @@
*/
class CRM_Utils_Cache_SerializeCache implements CRM_Utils_Cache_Interface {
use CRM_Utils_Cache_NaiveMultipleTrait;
use CRM_Utils_Cache_NaiveHasTrait; // TODO Native implementation
/**
* The cache storage container, an array by default, stored in a file under templates
*/
......@@ -67,10 +70,15 @@ class CRM_Utils_Cache_SerializeCache implements CRM_Utils_Cache_Interface {
/**
* @param string $key
* @param mixed $default
*
* @return mixed
*/
public function get($key) {
public function get($key, $default = NULL) {
if ($default !== NULL) {
throw new \RuntimeException("FIXME: " . __CLASS__ . "::get() only supports NULL default");
}
if (array_key_exists($key, $this->_cache)) {
return $this->_cache[$key];
}
......@@ -85,32 +93,41 @@ class CRM_Utils_Cache_SerializeCache implements CRM_Utils_Cache_Interface {
/**
* @param string $key
* @param mixed $value
* @param null|int|\DateInterval $ttl
* @return bool
*/
public function set($key, &$value) {
public function set($key, $value, $ttl = NULL) {
if ($ttl !== NULL) {
throw new \RuntimeException("FIXME: " . __CLASS__ . "::set() should support non-NULL TTL");
}
if (file_exists($this->fileName($key))) {
return;
return FALSE; // WTF, write-once cache?!
}
$this->_cache[$key] = $value;
<