1 | <?php |
||||||
2 | /** |
||||||
3 | * @link https://www.yiiframework.com/ |
||||||
4 | * @copyright Copyright (c) 2008 Yii Software LLC |
||||||
5 | * @license https://www.yiiframework.com/license/ |
||||||
6 | */ |
||||||
7 | |||||||
8 | namespace yii\caching; |
||||||
9 | |||||||
10 | use Yii; |
||||||
11 | use yii\base\InvalidConfigException; |
||||||
12 | |||||||
13 | /** |
||||||
14 | * MemCache implements a cache application component based on [memcache](https://pecl.php.net/package/memcache) |
||||||
15 | * and [memcached](https://pecl.php.net/package/memcached). |
||||||
16 | * |
||||||
17 | * MemCache supports both [memcache](https://pecl.php.net/package/memcache) and |
||||||
18 | * [memcached](https://pecl.php.net/package/memcached). By setting [[useMemcached]] to be true or false, |
||||||
19 | * one can let MemCache to use either memcached or memcache, respectively. |
||||||
20 | * |
||||||
21 | * MemCache can be configured with a list of memcache servers by settings its [[servers]] property. |
||||||
22 | * By default, MemCache assumes there is a memcache server running on localhost at port 11211. |
||||||
23 | * |
||||||
24 | * See [[Cache]] for common cache operations that MemCache supports. |
||||||
25 | * |
||||||
26 | * Note, there is no security measure to protected data in memcache. |
||||||
27 | * All data in memcache can be accessed by any process running in the system. |
||||||
28 | * |
||||||
29 | * To use MemCache as the cache application component, configure the application as follows, |
||||||
30 | * |
||||||
31 | * ```php |
||||||
32 | * [ |
||||||
33 | * 'components' => [ |
||||||
34 | * 'cache' => [ |
||||||
35 | * 'class' => 'yii\caching\MemCache', |
||||||
36 | * 'servers' => [ |
||||||
37 | * [ |
||||||
38 | * 'host' => 'server1', |
||||||
39 | * 'port' => 11211, |
||||||
40 | * 'weight' => 60, |
||||||
41 | * ], |
||||||
42 | * [ |
||||||
43 | * 'host' => 'server2', |
||||||
44 | * 'port' => 11211, |
||||||
45 | * 'weight' => 40, |
||||||
46 | * ], |
||||||
47 | * ], |
||||||
48 | * ], |
||||||
49 | * ], |
||||||
50 | * ] |
||||||
51 | * ``` |
||||||
52 | * |
||||||
53 | * In the above, two memcache servers are used: server1 and server2. You can configure more properties of |
||||||
54 | * each server, such as `persistent`, `weight`, `timeout`. Please see [[MemCacheServer]] for available options. |
||||||
55 | * |
||||||
56 | * For more details and usage information on Cache, see the [guide article on caching](guide:caching-overview). |
||||||
57 | * |
||||||
58 | * @property-read \Memcache|\Memcached $memcache The memcache (or memcached) object used by this cache |
||||||
59 | * component. |
||||||
60 | * @property MemCacheServer[] $servers List of memcache server configurations. Note that the type of this |
||||||
61 | * property differs in getter and setter. See [[getServers()]] and [[setServers()]] for details. |
||||||
62 | * |
||||||
63 | * @author Qiang Xue <[email protected]> |
||||||
64 | * @since 2.0 |
||||||
65 | */ |
||||||
66 | class MemCache extends Cache |
||||||
67 | { |
||||||
68 | /** |
||||||
69 | * @var bool whether to use memcached or memcache as the underlying caching extension. |
||||||
70 | * If true, [memcached](https://pecl.php.net/package/memcached) will be used. |
||||||
71 | * If false, [memcache](https://pecl.php.net/package/memcache) will be used. |
||||||
72 | * Defaults to false. |
||||||
73 | */ |
||||||
74 | public $useMemcached = false; |
||||||
75 | /** |
||||||
76 | * @var string an ID that identifies a Memcached instance. This property is used only when [[useMemcached]] is true. |
||||||
77 | * By default the Memcached instances are destroyed at the end of the request. To create an instance that |
||||||
78 | * persists between requests, you may specify a unique ID for the instance. All instances created with the |
||||||
79 | * same ID will share the same connection. |
||||||
80 | * @see https://www.php.net/manual/en/memcached.construct.php |
||||||
81 | */ |
||||||
82 | public $persistentId; |
||||||
83 | /** |
||||||
84 | * @var array options for Memcached. This property is used only when [[useMemcached]] is true. |
||||||
85 | * @see https://www.php.net/manual/en/memcached.setoptions.php |
||||||
86 | */ |
||||||
87 | public $options; |
||||||
88 | /** |
||||||
89 | * @var string memcached sasl username. This property is used only when [[useMemcached]] is true. |
||||||
90 | * @see https://www.php.net/manual/en/memcached.setsaslauthdata.php |
||||||
91 | */ |
||||||
92 | public $username; |
||||||
93 | /** |
||||||
94 | * @var string memcached sasl password. This property is used only when [[useMemcached]] is true. |
||||||
95 | * @see https://www.php.net/manual/en/memcached.setsaslauthdata.php |
||||||
96 | */ |
||||||
97 | public $password; |
||||||
98 | |||||||
99 | /** |
||||||
100 | * @var \Memcache|\Memcached the Memcache instance |
||||||
101 | */ |
||||||
102 | private $_cache; |
||||||
103 | /** |
||||||
104 | * @var array list of memcache server configurations |
||||||
105 | */ |
||||||
106 | private $_servers = []; |
||||||
107 | |||||||
108 | |||||||
109 | /** |
||||||
110 | * Initializes this application component. |
||||||
111 | * It creates the memcache instance and adds memcache servers. |
||||||
112 | */ |
||||||
113 | public function init() |
||||||
114 | { |
||||||
115 | parent::init(); |
||||||
116 | $this->addServers($this->getMemcache(), $this->getServers()); |
||||||
117 | } |
||||||
118 | |||||||
119 | /** |
||||||
120 | * Add servers to the server pool of the cache specified. |
||||||
121 | * |
||||||
122 | * @param \Memcache|\Memcached $cache |
||||||
123 | * @param MemCacheServer[] $servers |
||||||
124 | * @throws InvalidConfigException |
||||||
125 | */ |
||||||
126 | protected function addServers($cache, $servers) |
||||||
127 | { |
||||||
128 | if (empty($servers)) { |
||||||
129 | $servers = [new MemCacheServer([ |
||||||
130 | 'host' => '127.0.0.1', |
||||||
131 | 'port' => 11211, |
||||||
132 | ])]; |
||||||
133 | } else { |
||||||
134 | foreach ($servers as $server) { |
||||||
135 | if ($server->host === null) { |
||||||
136 | throw new InvalidConfigException("The 'host' property must be specified for every memcache server."); |
||||||
137 | } |
||||||
138 | } |
||||||
139 | } |
||||||
140 | if ($this->useMemcached) { |
||||||
141 | $this->addMemcachedServers($cache, $servers); |
||||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||||
142 | } else { |
||||||
143 | $this->addMemcacheServers($cache, $servers); |
||||||
0 ignored issues
–
show
It seems like
$cache can also be of type Memcached ; however, parameter $cache of yii\caching\MemCache::addMemcacheServers() does only seem to accept Memcache , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
144 | } |
||||||
145 | } |
||||||
146 | |||||||
147 | /** |
||||||
148 | * Add servers to the server pool of the cache specified |
||||||
149 | * Used for memcached PECL extension. |
||||||
150 | * |
||||||
151 | * @param \Memcached $cache |
||||||
152 | * @param MemCacheServer[] $servers |
||||||
153 | */ |
||||||
154 | protected function addMemcachedServers($cache, $servers) |
||||||
155 | { |
||||||
156 | $existingServers = []; |
||||||
157 | if ($this->persistentId !== null) { |
||||||
158 | foreach ($cache->getServerList() as $s) { |
||||||
159 | $existingServers[$s['host'] . ':' . $s['port']] = true; |
||||||
160 | } |
||||||
161 | } |
||||||
162 | foreach ($servers as $server) { |
||||||
163 | if (empty($existingServers) || !isset($existingServers[$server->host . ':' . $server->port])) { |
||||||
164 | $cache->addServer($server->host, $server->port, $server->weight); |
||||||
165 | } |
||||||
166 | } |
||||||
167 | } |
||||||
168 | |||||||
169 | /** |
||||||
170 | * Add servers to the server pool of the cache specified |
||||||
171 | * Used for memcache PECL extension. |
||||||
172 | * |
||||||
173 | * @param \Memcache $cache |
||||||
174 | * @param MemCacheServer[] $servers |
||||||
175 | */ |
||||||
176 | protected function addMemcacheServers($cache, $servers) |
||||||
177 | { |
||||||
178 | $class = new \ReflectionClass($cache); |
||||||
179 | $paramCount = $class->getMethod('addServer')->getNumberOfParameters(); |
||||||
180 | foreach ($servers as $server) { |
||||||
181 | // $timeout is used for memcache versions that do not have $timeoutms parameter |
||||||
182 | $timeout = (int) ($server->timeout / 1000) + (($server->timeout % 1000 > 0) ? 1 : 0); |
||||||
183 | if ($paramCount === 9) { |
||||||
184 | $cache->addserver( |
||||||
185 | $server->host, |
||||||
186 | $server->port, |
||||||
187 | $server->persistent, |
||||||
188 | $server->weight, |
||||||
189 | $timeout, |
||||||
190 | $server->retryInterval, |
||||||
191 | $server->status, |
||||||
192 | $server->failureCallback, |
||||||
193 | $server->timeout |
||||||
194 | ); |
||||||
195 | } else { |
||||||
196 | $cache->addserver( |
||||||
197 | $server->host, |
||||||
198 | $server->port, |
||||||
199 | $server->persistent, |
||||||
200 | $server->weight, |
||||||
201 | $timeout, |
||||||
202 | $server->retryInterval, |
||||||
203 | $server->status, |
||||||
204 | $server->failureCallback |
||||||
205 | ); |
||||||
206 | } |
||||||
207 | } |
||||||
208 | } |
||||||
209 | |||||||
210 | /** |
||||||
211 | * Returns the underlying memcache (or memcached) object. |
||||||
212 | * @return \Memcache|\Memcached the memcache (or memcached) object used by this cache component. |
||||||
213 | * @throws InvalidConfigException if memcache or memcached extension is not loaded |
||||||
214 | */ |
||||||
215 | public function getMemcache() |
||||||
216 | { |
||||||
217 | if ($this->_cache === null) { |
||||||
218 | $extension = $this->useMemcached ? 'memcached' : 'memcache'; |
||||||
219 | if (!extension_loaded($extension)) { |
||||||
220 | throw new InvalidConfigException("MemCache requires PHP $extension extension to be loaded."); |
||||||
221 | } |
||||||
222 | |||||||
223 | if ($this->useMemcached) { |
||||||
224 | $this->_cache = $this->persistentId !== null ? new \Memcached($this->persistentId) : new \Memcached(); |
||||||
225 | if ($this->username !== null || $this->password !== null) { |
||||||
226 | $this->_cache->setOption(\Memcached::OPT_BINARY_PROTOCOL, true); |
||||||
227 | $this->_cache->setSaslAuthData($this->username, $this->password); |
||||||
228 | } |
||||||
229 | if (!empty($this->options)) { |
||||||
230 | $this->_cache->setOptions($this->options); |
||||||
231 | } |
||||||
232 | } else { |
||||||
233 | $this->_cache = new \Memcache(); |
||||||
234 | } |
||||||
235 | } |
||||||
236 | |||||||
237 | return $this->_cache; |
||||||
238 | } |
||||||
239 | |||||||
240 | /** |
||||||
241 | * Returns the memcache or memcached server configurations. |
||||||
242 | * @return MemCacheServer[] list of memcache server configurations. |
||||||
243 | */ |
||||||
244 | public function getServers() |
||||||
245 | { |
||||||
246 | return $this->_servers; |
||||||
247 | } |
||||||
248 | |||||||
249 | /** |
||||||
250 | * @param array $config list of memcache or memcached server configurations. Each element must be an array |
||||||
251 | * with the following keys: host, port, persistent, weight, timeout, retryInterval, status. |
||||||
252 | * @see https://www.php.net/manual/en/memcache.addserver.php |
||||||
253 | * @see https://www.php.net/manual/en/memcached.addserver.php |
||||||
254 | */ |
||||||
255 | public function setServers($config) |
||||||
256 | { |
||||||
257 | foreach ($config as $c) { |
||||||
258 | $this->_servers[] = new MemCacheServer($c); |
||||||
259 | } |
||||||
260 | } |
||||||
261 | |||||||
262 | /** |
||||||
263 | * Retrieves a value from cache with a specified key. |
||||||
264 | * This is the implementation of the method declared in the parent class. |
||||||
265 | * @param string $key a unique key identifying the cached value |
||||||
266 | * @return mixed|false the value stored in cache, false if the value is not in the cache or expired. |
||||||
267 | */ |
||||||
268 | protected function getValue($key) |
||||||
269 | { |
||||||
270 | return $this->_cache->get($key); |
||||||
271 | } |
||||||
272 | |||||||
273 | /** |
||||||
274 | * Retrieves multiple values from cache with the specified keys. |
||||||
275 | * @param array $keys a list of keys identifying the cached values |
||||||
276 | * @return array a list of cached values indexed by the keys |
||||||
277 | */ |
||||||
278 | protected function getValues($keys) |
||||||
279 | { |
||||||
280 | return $this->useMemcached ? $this->_cache->getMulti($keys) : $this->_cache->get($keys); |
||||||
0 ignored issues
–
show
The method
getMulti() does not exist on Memcache .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() $keys of type array is incompatible with the type string expected by parameter $key of Memcached::get() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
281 | } |
||||||
282 | |||||||
283 | /** |
||||||
284 | * Stores a value identified by a key in cache. |
||||||
285 | * This is the implementation of the method declared in the parent class. |
||||||
286 | * |
||||||
287 | * @param string $key the key identifying the value to be cached |
||||||
288 | * @param mixed $value the value to be cached. |
||||||
289 | * @see [Memcache::set()](https://www.php.net/manual/en/memcache.set.php) |
||||||
290 | * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire. |
||||||
291 | * @return bool true if the value is successfully stored into cache, false otherwise |
||||||
292 | */ |
||||||
293 | protected function setValue($key, $value, $duration) |
||||||
294 | { |
||||||
295 | $expire = $this->normalizeDuration($duration); |
||||||
296 | return $this->useMemcached ? $this->_cache->set($key, $value, $expire) : $this->_cache->set($key, $value, 0, $expire); |
||||||
297 | } |
||||||
298 | |||||||
299 | /** |
||||||
300 | * Stores multiple key-value pairs in cache. |
||||||
301 | * @param array $data array where key corresponds to cache key while value is the value stored |
||||||
302 | * @param int $duration the number of seconds in which the cached values will expire. 0 means never expire. |
||||||
303 | * @return array array of failed keys. |
||||||
304 | */ |
||||||
305 | protected function setValues($data, $duration) |
||||||
306 | { |
||||||
307 | if ($this->useMemcached) { |
||||||
308 | $expire = $this->normalizeDuration($duration); |
||||||
309 | |||||||
310 | // Memcached::setMulti() returns boolean |
||||||
311 | // @see https://www.php.net/manual/en/memcached.setmulti.php |
||||||
312 | return $this->_cache->setMulti($data, $expire) ? [] : array_keys($data); |
||||||
0 ignored issues
–
show
The method
setMulti() does not exist on Memcache .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||||||
313 | } |
||||||
314 | |||||||
315 | return parent::setValues($data, $duration); |
||||||
316 | } |
||||||
317 | |||||||
318 | /** |
||||||
319 | * Stores a value identified by a key into cache if the cache does not contain this key. |
||||||
320 | * This is the implementation of the method declared in the parent class. |
||||||
321 | * |
||||||
322 | * @param string $key the key identifying the value to be cached |
||||||
323 | * @param mixed $value the value to be cached |
||||||
324 | * @see [Memcache::set()](https://www.php.net/manual/en/memcache.set.php) |
||||||
325 | * @param int $duration the number of seconds in which the cached value will expire. 0 means never expire. |
||||||
326 | * @return bool true if the value is successfully stored into cache, false otherwise |
||||||
327 | */ |
||||||
328 | protected function addValue($key, $value, $duration) |
||||||
329 | { |
||||||
330 | $expire = $this->normalizeDuration($duration); |
||||||
331 | return $this->useMemcached ? $this->_cache->add($key, $value, $expire) : $this->_cache->add($key, $value, 0, $expire); |
||||||
332 | } |
||||||
333 | |||||||
334 | /** |
||||||
335 | * Deletes a value with the specified key from cache |
||||||
336 | * This is the implementation of the method declared in the parent class. |
||||||
337 | * @param string $key the key of the value to be deleted |
||||||
338 | * @return bool if no error happens during deletion |
||||||
339 | */ |
||||||
340 | protected function deleteValue($key) |
||||||
341 | { |
||||||
342 | return $this->_cache->delete($key, 0); |
||||||
343 | } |
||||||
344 | |||||||
345 | /** |
||||||
346 | * Deletes all values from cache. |
||||||
347 | * This is the implementation of the method declared in the parent class. |
||||||
348 | * @return bool whether the flush operation was successful. |
||||||
349 | */ |
||||||
350 | protected function flushValues() |
||||||
351 | { |
||||||
352 | return $this->_cache->flush(); |
||||||
353 | } |
||||||
354 | |||||||
355 | /** |
||||||
356 | * Normalizes duration value |
||||||
357 | * |
||||||
358 | * @see https://github.com/yiisoft/yii2/issues/17710 |
||||||
359 | * @see https://www.php.net/manual/en/memcache.set.php |
||||||
360 | * @see https://www.php.net/manual/en/memcached.expiration.php |
||||||
361 | * |
||||||
362 | * @since 2.0.31 |
||||||
363 | * @param int $duration |
||||||
364 | * @return int |
||||||
365 | */ |
||||||
366 | protected function normalizeDuration($duration) |
||||||
367 | { |
||||||
368 | if ($duration < 0) { |
||||||
369 | return 0; |
||||||
370 | } |
||||||
371 | |||||||
372 | if ($duration < 2592001) { |
||||||
373 | return $duration; |
||||||
374 | } |
||||||
375 | |||||||
376 | return $duration + time(); |
||||||
377 | } |
||||||
378 | } |
||||||
379 |