Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
bc77d7c0 | 4 | | Copyright CiviCRM LLC. All rights reserved. | |
6a488035 | 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 | | |
6a488035 | 9 | +--------------------------------------------------------------------+ |
d25dd0ee | 10 | */ |
6a488035 TO |
11 | |
12 | /** | |
13 | * | |
14 | * @package CRM | |
ca5cec67 | 15 | * @copyright CiviCRM LLC https://civicrm.org/licensing |
6a488035 | 16 | */ |
9f49773e | 17 | class CRM_Utils_Cache_Memcache implements CRM_Utils_Cache_Interface { |
0d64c8fa | 18 | |
6714d8d2 SL |
19 | // TODO Consider native implementation. |
20 | use CRM_Utils_Cache_NaiveMultipleTrait; | |
0d64c8fa | 21 | |
353ffa53 TO |
22 | const DEFAULT_HOST = 'localhost'; |
23 | const DEFAULT_PORT = 11211; | |
6a488035 | 24 | const DEFAULT_TIMEOUT = 3600; |
353ffa53 | 25 | const DEFAULT_PREFIX = ''; |
6a488035 | 26 | |
fbbfd6dd TO |
27 | /** |
28 | * If another process clears namespace, we'll find out in ~5 sec. | |
29 | */ | |
30 | const NS_LOCAL_TTL = 5; | |
31 | ||
6a488035 | 32 | /** |
fe482240 | 33 | * The host name of the memcached server. |
6a488035 TO |
34 | * |
35 | * @var string | |
36 | */ | |
37 | protected $_host = self::DEFAULT_HOST; | |
38 | ||
39 | /** | |
fe482240 | 40 | * The port on which to connect on. |
6a488035 TO |
41 | * |
42 | * @var int | |
43 | */ | |
44 | protected $_port = self::DEFAULT_PORT; | |
45 | ||
46 | /** | |
fe482240 | 47 | * The default timeout to use. |
6a488035 TO |
48 | * |
49 | * @var int | |
50 | */ | |
51 | protected $_timeout = self::DEFAULT_TIMEOUT; | |
52 | ||
53 | /** | |
54 | * The prefix prepended to cache keys. | |
55 | * | |
56 | * If we are using the same memcache instance for multiple CiviCRM | |
57 | * installs, we must have a unique prefix for each install to prevent | |
58 | * the keys from clobbering each other. | |
59 | * | |
60 | * @var string | |
61 | */ | |
62 | protected $_prefix = self::DEFAULT_PREFIX; | |
63 | ||
64 | /** | |
fe482240 | 65 | * The actual memcache object. |
6a488035 | 66 | * |
41e0c250 | 67 | * @var Memcache |
6a488035 TO |
68 | */ |
69 | protected $_cache; | |
70 | ||
fbbfd6dd | 71 | /** |
51dda21e | 72 | * @var null|array |
fbbfd6dd TO |
73 | * |
74 | * This is the effective prefix. It may be bumped up whenever the dataset is flushed. | |
75 | * | |
76 | * @see https://github.com/memcached/memcached/wiki/ProgrammingTricks#deleting-by-namespace | |
77 | */ | |
78 | protected $_truePrefix = NULL; | |
79 | ||
6a488035 | 80 | /** |
fe482240 | 81 | * Constructor. |
6a488035 | 82 | * |
77855840 TO |
83 | * @param array $config |
84 | * An array of configuration params. | |
6a488035 | 85 | * |
77b97be7 | 86 | * @return \CRM_Utils_Cache_Memcache |
6a488035 | 87 | */ |
00be9182 | 88 | public function __construct($config) { |
6a488035 TO |
89 | if (isset($config['host'])) { |
90 | $this->_host = $config['host']; | |
91 | } | |
92 | if (isset($config['port'])) { | |
93 | $this->_port = $config['port']; | |
94 | } | |
95 | if (isset($config['timeout'])) { | |
96 | $this->_timeout = $config['timeout']; | |
97 | } | |
98 | if (isset($config['prefix'])) { | |
99 | $this->_prefix = $config['prefix']; | |
100 | } | |
101 | ||
102 | $this->_cache = new Memcache(); | |
103 | ||
104 | if (!$this->_cache->connect($this->_host, $this->_port)) { | |
105 | // dont use fatal here since we can go in an infinite loop | |
106 | echo 'Could not connect to Memcached server'; | |
107 | CRM_Utils_System::civiExit(); | |
108 | } | |
109 | } | |
110 | ||
5bc392e6 EM |
111 | /** |
112 | * @param $key | |
113 | * @param $value | |
858451a9 | 114 | * @param null|int|\DateInterval $ttl |
5bc392e6 EM |
115 | * |
116 | * @return bool | |
117 | */ | |
858451a9 | 118 | public function set($key, $value, $ttl = NULL) { |
fbbfd6dd TO |
119 | CRM_Utils_Cache::assertValidKey($key); |
120 | if (is_int($ttl) && $ttl <= 0) { | |
121 | return $this->delete($key); | |
6a488035 | 122 | } |
fbbfd6dd TO |
123 | $expires = CRM_Utils_Date::convertCacheTtlToExpires($ttl, $this->_timeout); |
124 | return $this->_cache->set($this->getTruePrefix() . $key, serialize($value), FALSE, $expires); | |
6a488035 TO |
125 | } |
126 | ||
5bc392e6 EM |
127 | /** |
128 | * @param $key | |
2da67cc5 | 129 | * @param mixed $default |
5bc392e6 EM |
130 | * |
131 | * @return mixed | |
132 | */ | |
2da67cc5 | 133 | public function get($key, $default = NULL) { |
fbbfd6dd TO |
134 | CRM_Utils_Cache::assertValidKey($key); |
135 | $result = $this->_cache->get($this->getTruePrefix() . $key); | |
136 | return ($result === FALSE) ? $default : unserialize($result); | |
6a488035 TO |
137 | } |
138 | ||
fbbfd6dd TO |
139 | /** |
140 | * @param string $key | |
141 | * | |
142 | * @return bool | |
143 | * @throws \Psr\SimpleCache\CacheException | |
144 | */ | |
145 | public function has($key) { | |
146 | CRM_Utils_Cache::assertValidKey($key); | |
147 | $result = $this->_cache->get($this->getTruePrefix() . $key); | |
148 | return ($result !== FALSE); | |
149 | } | |
150 | ||
5bc392e6 EM |
151 | /** |
152 | * @param $key | |
153 | * | |
eec321a4 | 154 | * @return bool |
5bc392e6 | 155 | */ |
00be9182 | 156 | public function delete($key) { |
fbbfd6dd TO |
157 | CRM_Utils_Cache::assertValidKey($key); |
158 | $this->_cache->delete($this->getTruePrefix() . $key); | |
159 | return TRUE; | |
6a488035 TO |
160 | } |
161 | ||
5bc392e6 | 162 | /** |
124e5288 | 163 | * @return bool |
5bc392e6 | 164 | */ |
00be9182 | 165 | public function flush() { |
fbbfd6dd TO |
166 | $this->_truePrefix = NULL; |
167 | $this->_cache->delete($this->_prefix); | |
168 | return TRUE; | |
6a488035 | 169 | } |
96025800 | 170 | |
c31de879 TO |
171 | public function clear() { |
172 | return $this->flush(); | |
173 | } | |
174 | ||
fbbfd6dd TO |
175 | protected function getTruePrefix() { |
176 | if ($this->_truePrefix === NULL || $this->_truePrefix['expires'] < time()) { | |
177 | $key = $this->_prefix; | |
178 | $value = $this->_cache->get($key); | |
179 | if ($value === FALSE) { | |
180 | $value = uniqid(); | |
6714d8d2 SL |
181 | // Indefinite. |
182 | $this->_cache->set($key, $value, FALSE, 0); | |
fbbfd6dd TO |
183 | } |
184 | $this->_truePrefix = [ | |
185 | 'value' => $value, | |
186 | 'expires' => time() + self::NS_LOCAL_TTL, | |
187 | ]; | |
188 | } | |
189 | return $this->_prefix . $this->_truePrefix['value'] . '/'; | |
190 | } | |
191 | ||
6a488035 | 192 | } |