Merge pull request #23912 from mlutfy/reportVars
[civicrm-core.git] / CRM / Utils / Cache / Redis.php
CommitLineData
59e56021
H
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
59e56021 5 | |
bc77d7c0
TO
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 |
59e56021 9 +--------------------------------------------------------------------+
4ca97b71 10 */
59e56021
H
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
59e56021
H
16 */
17class CRM_Utils_Cache_Redis implements CRM_Utils_Cache_Interface {
0d64c8fa 18
6714d8d2
SL
19 // TODO Consider native implementation.
20 use CRM_Utils_Cache_NaiveMultipleTrait;
21 // TODO Native implementation
22 use CRM_Utils_Cache_NaiveHasTrait;
0d64c8fa 23
59e56021 24 const DEFAULT_HOST = 'localhost';
9d2f1acb 25 const DEFAULT_PORT = 6379;
59e56021
H
26 const DEFAULT_TIMEOUT = 3600;
27 const DEFAULT_PREFIX = '';
28
59e56021
H
29 /**
30 * The default timeout to use
31 *
32 * @var int
33 */
34 protected $_timeout = self::DEFAULT_TIMEOUT;
35
36 /**
37 * The prefix prepended to cache keys.
38 *
39 * If we are using the same redis instance for multiple CiviCRM
40 * installs, we must have a unique prefix for each install to prevent
41 * the keys from clobbering each other.
42 *
43 * @var string
44 */
45 protected $_prefix = self::DEFAULT_PREFIX;
46
47 /**
48 * The actual redis object
49 *
6db6aac0 50 * @var Redis
59e56021
H
51 */
52 protected $_cache;
53
9a38d080
TO
54 /**
55 * Create a connection. If a connection already exists, re-use it.
56 *
57 * @param array $config
58 * @return Redis
59 */
60 public static function connect($config) {
2e1f50d6
CW
61 $host = $config['host'] ?? self::DEFAULT_HOST;
62 $port = $config['port'] ?? self::DEFAULT_PORT;
6714d8d2
SL
63 // Ugh.
64 $pass = CRM_Utils_Constant::value('CIVICRM_DB_CACHE_PASSWORD');
9a38d080
TO
65 $id = implode(':', ['connect', $host, $port /* $pass is constant */]);
66 if (!isset(Civi::$statics[__CLASS__][$id])) {
67 // Ideally, we'd track the connection in the service-container, but the
68 // cache connection is boot-critical.
69 $redis = new Redis();
70 if (!$redis->connect($host, $port)) {
71 // dont use fatal here since we can go in an infinite loop
72 echo 'Could not connect to redisd server';
73 CRM_Utils_System::civiExit();
74 }
75 if ($pass) {
76 $redis->auth($pass);
77 }
78 Civi::$statics[__CLASS__][$id] = $redis;
79 }
80 return Civi::$statics[__CLASS__][$id];
81 }
82
59e56021
H
83 /**
84 * Constructor
85 *
4ca97b71
H
86 * @param array $config
87 * An array of configuration params.
59e56021
H
88 *
89 * @return \CRM_Utils_Cache_Redis
90 */
91 public function __construct($config) {
59e56021
H
92 if (isset($config['timeout'])) {
93 $this->_timeout = $config['timeout'];
94 }
95 if (isset($config['prefix'])) {
96 $this->_prefix = $config['prefix'];
97 }
98
9a38d080 99 $this->_cache = self::connect($config);
59e56021
H
100 }
101
102 /**
103 * @param $key
104 * @param $value
858451a9 105 * @param null|int|\DateInterval $ttl
59e56021
H
106 *
107 * @return bool
108 * @throws Exception
109 */
858451a9 110 public function set($key, $value, $ttl = NULL) {
26733eb5
TO
111 CRM_Utils_Cache::assertValidKey($key);
112 if (is_int($ttl) && $ttl <= 0) {
113 return $this->delete($key);
858451a9 114 }
26733eb5
TO
115 $ttl = CRM_Utils_Date::convertCacheTtl($ttl, self::DEFAULT_TIMEOUT);
116 if (!$this->_cache->setex($this->_prefix . $key, $ttl, serialize($value))) {
aed6cc22 117 if (PHP_SAPI === 'cli' || (Civi\Core\Container::isContainerBooted() && CRM_Core_Permission::check('view debug output'))) {
26733eb5 118 throw new CRM_Utils_Cache_CacheException("Redis set ($key) failed: " . $this->_cache->getLastError());
aed6cc22
TO
119 }
120 else {
121 Civi::log()->error("Redis set ($key) failed: " . $this->_cache->getLastError());
26733eb5 122 throw new CRM_Utils_Cache_CacheException("Redis set ($key) failed");
aed6cc22 123 }
59e56021
H
124 return FALSE;
125 }
126 return TRUE;
127 }
128
129 /**
130 * @param $key
2da67cc5 131 * @param mixed $default
59e56021
H
132 *
133 * @return mixed
134 */
2da67cc5 135 public function get($key, $default = NULL) {
26733eb5 136 CRM_Utils_Cache::assertValidKey($key);
59e56021 137 $result = $this->_cache->get($this->_prefix . $key);
2da67cc5 138 return ($result === FALSE) ? $default : unserialize($result);
59e56021
H
139 }
140
141 /**
142 * @param $key
143 *
eec321a4 144 * @return bool
59e56021
H
145 */
146 public function delete($key) {
26733eb5 147 CRM_Utils_Cache::assertValidKey($key);
53a48c5a 148 $this->_cache->del($this->_prefix . $key);
eec321a4 149 return TRUE;
59e56021
H
150 }
151
152 /**
124e5288 153 * @return bool
59e56021
H
154 */
155 public function flush() {
fbebc7a0
TO
156 // FIXME: Ideally, we'd map each prefix to a different 'hash' object in Redis,
157 // and this would be simpler. However, that needs to go in tandem with a
158 // more general rethink of cache expiration/TTL.
159
160 $keys = $this->_cache->keys($this->_prefix . '*');
124e5288
TO
161 $this->_cache->del($keys);
162 return TRUE;
59e56021 163 }
4ca97b71 164
c31de879
TO
165 public function clear() {
166 return $this->flush();
167 }
168
59e56021 169}