Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(dev/core#174) Full PSR-16 compliance for ArrayCache, SqlGroup, Redis, Memcache, APC #12360

Closed
wants to merge 7 commits into from
14 changes: 11 additions & 3 deletions CRM/Core/BAO/Cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ public static function restoreSessionFromCache($names) {
* @param bool $table
* @param bool $prevNext
*/
public static function cleanup($session = FALSE, $table = FALSE, $prevNext = FALSE) {
public static function cleanup($session = FALSE, $table = FALSE, $prevNext = FALSE, $expired = FALSE) {
// first delete all sessions more than 20 minutes old which are related to any potential transaction
$timeIntervalMins = (int) Civi::settings()->get('secure_cache_timeout_minutes');
if ($timeIntervalMins && $session) {
Expand Down Expand Up @@ -338,10 +338,10 @@ public static function cleanup($session = FALSE, $table = FALSE, $prevNext = FAL
$timeIntervalDays = 2;

if (mt_rand(1, 100000) % $cleanUpNumber == 0) {
$session = $table = $prevNext = TRUE;
$expired = $session = $table = $prevNext = TRUE;
}

if (!$session && !$table && !$prevNext) {
if (!$session && !$table && !$prevNext && !$expired) {
return;
}

Expand All @@ -363,6 +363,14 @@ public static function cleanup($session = FALSE, $table = FALSE, $prevNext = FAL
";
CRM_Core_DAO::executeQuery($sql);
}

if ($expired) {
$sql = "DELETE FROM civicrm_cache WHERE expired_date < %1";
$params = [
1 => [date(CRM_Utils_Cache_SqlGroup::TS_FMT, CRM_Utils_Time::getTimeRaw()), 'String'],
];
CRM_Core_DAO::executeQuery($sql, $params);
}
}

/**
Expand Down
30 changes: 21 additions & 9 deletions CRM/Utils/Cache/APCcache.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,14 @@ public function __construct(&$config) {
* @return bool
*/
public function set($key, $value, $ttl = NULL) {
if ($ttl !== NULL) {
throw new \RuntimeException("FIXME: " . __CLASS__ . "::set() should support non-NULL TTL");
CRM_Utils_Cache::assertValidKey($key);
if (is_int($ttl) && $ttl <= 0) {
return $this->delete($key);
}
if (!apc_store($this->_prefix . $key, $value, $this->_timeout)) {

$ttl = CRM_Utils_Date::convertCacheTtl($ttl, $this->_timeout);
$expires = time() + $ttl;
if (!apc_store($this->_prefix . $key, ['e' => $expires, 'v' => $value], $ttl)) {
return FALSE;
}
return TRUE;
Expand All @@ -97,10 +101,12 @@ public function set($key, $value, $ttl = NULL) {
* @return mixed
*/
public function get($key, $default = NULL) {
if ($default !== NULL) {
throw new \RuntimeException("FIXME: " . __CLASS__ . "::get() only supports NULL default");
CRM_Utils_Cache::assertValidKey($key);
$result = apc_fetch($this->_prefix . $key, $success);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$success does not seem to be defined

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds like some metaphysical management widsom. ;)

More seriously, $success is actually an output of this line. apc_fetch($key, &$success) sets the value of $success to indicate whether it worked.

if ($success && isset($result['e']) && $result['e'] > time()) {
return $this->reobjectify($result['v']);
}
return apc_fetch($this->_prefix . $key);
return $default;
}

/**
Expand All @@ -109,20 +115,22 @@ public function get($key, $default = NULL) {
* @return bool|string[]
*/
public function delete($key) {
return apc_delete($this->_prefix . $key);
CRM_Utils_Cache::assertValidKey($key);
apc_delete($this->_prefix . $key);
return TRUE;
}

public function flush() {
$allinfo = apc_cache_info('user');
$keys = $allinfo['cache_list'];
$prefix = $this->_prefix . "CRM_"; // Our keys follows this pattern: ([A-Za-z0-9_]+)?CRM_[A-Za-z0-9_]+
$prefix = $this->_prefix; // Our keys follows this pattern: ([A-Za-z0-9_]+)?CRM_[A-Za-z0-9_]+
$lp = strlen($prefix); // Get prefix length

foreach ($keys as $key) {
$name = $key['info'];
if ($prefix == substr($name, 0, $lp)) {
// Ours?
apc_delete($this->_prefix . $name);
apc_delete($name);
}
}
return TRUE;
Expand All @@ -132,4 +140,8 @@ public function clear() {
return $this->flush();
}

private function reobjectify($value) {
return is_object($value) ? unserialize(serialize($value)) : $value;
}

}
32 changes: 27 additions & 5 deletions CRM/Utils/Cache/ArrayCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,15 @@ class CRM_Utils_Cache_Arraycache implements CRM_Utils_Cache_Interface {
use CRM_Utils_Cache_NaiveMultipleTrait;
use CRM_Utils_Cache_NaiveHasTrait; // TODO Native implementation

const DEFAULT_TIMEOUT = 3600;

/**
* The cache storage container, an in memory array by default
*/
protected $_cache;

protected $_expires;

/**
* Constructor.
*
Expand All @@ -54,19 +58,20 @@ class CRM_Utils_Cache_Arraycache implements CRM_Utils_Cache_Interface {
*/
public function __construct($config) {
$this->_cache = array();
$this->_expires = array();
}

/**
* @param string $key
* @param mixed $value
* @param null|int|\DateInterval $ttl
* @return bool
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
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;
CRM_Utils_Cache::assertValidKey($key);
$this->_cache[$key] = $this->reobjectify($value);
$this->_expires[$key] = CRM_Utils_Date::convertCacheTtlToExpires($ttl, self::DEFAULT_TIMEOUT);
return TRUE;
}

Expand All @@ -75,22 +80,35 @@ public function set($key, $value, $ttl = NULL) {
* @param mixed $default
*
* @return mixed
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function get($key, $default = NULL) {
return CRM_Utils_Array::value($key, $this->_cache, $default);
CRM_Utils_Cache::assertValidKey($key);
if (isset($this->_expires[$key]) && is_numeric($this->_expires[$key]) && $this->_expires[$key] <= time()) {
return $default;
}
if (array_key_exists($key, $this->_cache)) {
return $this->reobjectify($this->_cache[$key]);
}
return $default;
}

/**
* @param string $key
* @return bool
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function delete($key) {
CRM_Utils_Cache::assertValidKey($key);

unset($this->_cache[$key]);
unset($this->_expires[$key]);
return TRUE;
}

public function flush() {
unset($this->_cache);
unset($this->_expires);
$this->_cache = array();
return TRUE;
}
Expand All @@ -99,4 +117,8 @@ public function clear() {
return $this->flush();
}

private function reobjectify($value) {
return is_object($value) ? unserialize(serialize($value)) : $value;
}

}
72 changes: 57 additions & 15 deletions CRM/Utils/Cache/Memcache.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,17 @@
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;
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.
*
Expand Down Expand Up @@ -79,6 +83,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.
*
Expand Down Expand Up @@ -118,13 +131,12 @@ public function __construct($config) {
* @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);
}

/**
Expand All @@ -134,32 +146,62 @@ public function set($key, $value, $ttl = NULL) {
* @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 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 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();
$this->_cache->set($key, $value, FALSE, 0); // Indefinite.
}
$this->_truePrefix = [
'value' => $value,
'expires' => time() + self::NS_LOCAL_TTL,
];
}
return $this->_prefix . $this->_truePrefix['value'] . '/';
}

}
Loading