3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
19 class CRM_Utils_Cache_Redis
implements CRM_Utils_Cache_Interface
{
21 // TODO Consider native implementation.
22 use CRM_Utils_Cache_NaiveMultipleTrait
;
23 // TODO Native implementation
24 use CRM_Utils_Cache_NaiveHasTrait
;
26 const DEFAULT_HOST
= 'localhost';
27 const DEFAULT_PORT
= 6379;
28 const DEFAULT_TIMEOUT
= 3600;
29 const DEFAULT_PREFIX
= '';
32 * The default timeout to use
36 protected $_timeout = self
::DEFAULT_TIMEOUT
;
39 * The prefix prepended to cache keys.
41 * If we are using the same redis instance for multiple CiviCRM
42 * installs, we must have a unique prefix for each install to prevent
43 * the keys from clobbering each other.
47 protected $_prefix = self
::DEFAULT_PREFIX
;
50 * The actual redis object
57 * Create a connection. If a connection already exists, re-use it.
59 * @param array $config
62 public static function connect($config) {
63 $host = isset($config['host']) ?
$config['host'] : self
::DEFAULT_HOST
;
64 $port = isset($config['port']) ?
$config['port'] : self
::DEFAULT_PORT
;
66 $pass = CRM_Utils_Constant
::value('CIVICRM_DB_CACHE_PASSWORD');
67 $id = implode(':', ['connect', $host, $port /* $pass is constant */]);
68 if (!isset(Civi
::$statics[__CLASS__
][$id])) {
69 // Ideally, we'd track the connection in the service-container, but the
70 // cache connection is boot-critical.
72 if (!$redis->connect($host, $port)) {
73 // dont use fatal here since we can go in an infinite loop
74 echo 'Could not connect to redisd server';
75 CRM_Utils_System
::civiExit();
80 Civi
::$statics[__CLASS__
][$id] = $redis;
82 return Civi
::$statics[__CLASS__
][$id];
88 * @param array $config
89 * An array of configuration params.
91 * @return \CRM_Utils_Cache_Redis
93 public function __construct($config) {
94 if (isset($config['timeout'])) {
95 $this->_timeout
= $config['timeout'];
97 if (isset($config['prefix'])) {
98 $this->_prefix
= $config['prefix'];
101 $this->_cache
= self
::connect($config);
107 * @param null|int|\DateInterval $ttl
112 public function set($key, $value, $ttl = NULL) {
113 CRM_Utils_Cache
::assertValidKey($key);
114 if (is_int($ttl) && $ttl <= 0) {
115 return $this->delete($key);
117 $ttl = CRM_Utils_Date
::convertCacheTtl($ttl, self
::DEFAULT_TIMEOUT
);
118 if (!$this->_cache
->setex($this->_prefix
. $key, $ttl, serialize($value))) {
119 if (PHP_SAPI
=== 'cli' ||
(Civi\Core\Container
::isContainerBooted() && CRM_Core_Permission
::check('view debug output'))) {
120 throw new CRM_Utils_Cache_CacheException("Redis set ($key) failed: " . $this->_cache
->getLastError());
123 Civi
::log()->error("Redis set ($key) failed: " . $this->_cache
->getLastError());
124 throw new CRM_Utils_Cache_CacheException("Redis set ($key) failed");
133 * @param mixed $default
137 public function get($key, $default = NULL) {
138 CRM_Utils_Cache
::assertValidKey($key);
139 $result = $this->_cache
->get($this->_prefix
. $key);
140 return ($result === FALSE) ?
$default : unserialize($result);
148 public function delete($key) {
149 CRM_Utils_Cache
::assertValidKey($key);
150 $this->_cache
->delete($this->_prefix
. $key);
157 public function flush() {
158 // FIXME: Ideally, we'd map each prefix to a different 'hash' object in Redis,
159 // and this would be simpler. However, that needs to go in tandem with a
160 // more general rethink of cache expiration/TTL.
162 $keys = $this->_cache
->keys($this->_prefix
. '*');
163 $this->_cache
->del($keys);
167 public function clear() {
168 return $this->flush();