1 | <?php |
||||
2 | /** |
||||
3 | * @link http://www.yiiframework.com/ |
||||
4 | * @copyright Copyright (c) 2008 Yii Software LLC |
||||
5 | * @license http://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. This property is read-only. |
||||
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://secure.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://secure.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://secure.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://secure.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 | 32 | public function init() |
|||
114 | { |
||||
115 | 32 | parent::init(); |
|||
116 | 32 | $this->addServers($this->getMemcache(), $this->getServers()); |
|||
117 | 32 | } |
|||
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 | 32 | protected function addServers($cache, $servers) |
|||
127 | { |
||||
128 | 32 | if (empty($servers)) { |
|||
129 | 32 | $servers = [new MemCacheServer([ |
|||
130 | 32 | '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 | 32 | if ($this->useMemcached) { |
|||
141 | 16 | $this->addMemcachedServers($cache, $servers); |
|||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
142 | } else { |
||||
143 | 16 | $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
Loading history...
|
|||||
144 | } |
||||
145 | 32 | } |
|||
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 | 16 | protected function addMemcachedServers($cache, $servers) |
|||
155 | { |
||||
156 | 16 | $existingServers = []; |
|||
157 | 16 | if ($this->persistentId !== null) { |
|||
158 | foreach ($cache->getServerList() as $s) { |
||||
159 | $existingServers[$s['host'] . ':' . $s['port']] = true; |
||||
160 | } |
||||
161 | } |
||||
162 | 16 | foreach ($servers as $server) { |
|||
163 | 16 | if (empty($existingServers) || !isset($existingServers[$server->host . ':' . $server->port])) { |
|||
164 | 16 | $cache->addServer($server->host, $server->port, $server->weight); |
|||
165 | } |
||||
166 | } |
||||
167 | 16 | } |
|||
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 | 16 | protected function addMemcacheServers($cache, $servers) |
|||
177 | { |
||||
178 | 16 | $class = new \ReflectionClass($cache); |
|||
179 | 16 | $paramCount = $class->getMethod('addServer')->getNumberOfParameters(); |
|||
180 | 16 | foreach ($servers as $server) { |
|||
181 | // $timeout is used for memcache versions that do not have $timeoutms parameter |
||||
182 | 16 | $timeout = (int) ($server->timeout / 1000) + (($server->timeout % 1000 > 0) ? 1 : 0); |
|||
183 | 16 | 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 | 16 | $cache->addserver( |
|||
197 | 16 | $server->host, |
|||
198 | 16 | $server->port, |
|||
199 | 16 | $server->persistent, |
|||
200 | 16 | $server->weight, |
|||
201 | 16 | $timeout, |
|||
202 | 16 | $server->retryInterval, |
|||
203 | 16 | $server->status, |
|||
204 | 16 | $server->failureCallback |
|||
205 | ); |
||||
206 | } |
||||
207 | } |
||||
208 | 16 | } |
|||
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 | 32 | public function getMemcache() |
|||
216 | { |
||||
217 | 32 | if ($this->_cache === null) { |
|||
218 | 32 | $extension = $this->useMemcached ? 'memcached' : 'memcache'; |
|||
219 | 32 | if (!extension_loaded($extension)) { |
|||
220 | throw new InvalidConfigException("MemCache requires PHP $extension extension to be loaded."); |
||||
221 | } |
||||
222 | |||||
223 | 32 | if ($this->useMemcached) { |
|||
224 | 16 | $this->_cache = $this->persistentId !== null ? new \Memcached($this->persistentId) : new \Memcached(); |
|||
225 | 16 | 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 | 16 | if (!empty($this->options)) { |
|||
230 | 16 | $this->_cache->setOptions($this->options); |
|||
231 | } |
||||
232 | } else { |
||||
233 | 16 | $this->_cache = new \Memcache(); |
|||
234 | } |
||||
235 | } |
||||
236 | |||||
237 | 32 | return $this->_cache; |
|||
238 | } |
||||
239 | |||||
240 | /** |
||||
241 | * Returns the memcache or memcached server configurations. |
||||
242 | * @return MemCacheServer[] list of memcache server configurations. |
||||
243 | */ |
||||
244 | 32 | public function getServers() |
|||
245 | { |
||||
246 | 32 | 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://secure.php.net/manual/en/memcache.addserver.php |
||||
253 | * @see https://secure.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 | 26 | protected function getValue($key) |
|||
269 | { |
||||
270 | 26 | 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 | 4 | protected function getValues($keys) |
|||
279 | { |
||||
280 | 4 | return $this->useMemcached ? $this->_cache->getMulti($keys) : $this->_cache->get($keys); |
|||
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://secure.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 | 26 | protected function setValue($key, $value, $duration) |
|||
294 | { |
||||
295 | 26 | $expire = $this->normalizeDuration($duration); |
|||
296 | 26 | 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 | 6 | protected function setValues($data, $duration) |
|||
306 | { |
||||
307 | 6 | if ($this->useMemcached) { |
|||
308 | 3 | $expire = $this->normalizeDuration($duration); |
|||
309 | |||||
310 | // Memcached::setMulti() returns boolean |
||||
311 | // @see https://secure.php.net/manual/en/memcached.setmulti.php |
||||
312 | 3 | return $this->_cache->setMulti($data, $expire) ? [] : array_keys($data); |
|||
313 | } |
||||
314 | |||||
315 | 3 | 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://secure.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 | 4 | protected function addValue($key, $value, $duration) |
|||
329 | { |
||||
330 | 4 | $expire = $this->normalizeDuration($duration); |
|||
331 | 4 | 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 | 2 | protected function deleteValue($key) |
|||
341 | { |
||||
342 | 2 | 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 | 22 | protected function flushValues() |
|||
351 | { |
||||
352 | 22 | return $this->_cache->flush(); |
|||
353 | } |
||||
354 | |||||
355 | /** |
||||
356 | * Normalizes duration value |
||||
357 | * |
||||
358 | * @see https://github.com/yiisoft/yii2/issues/17710 |
||||
359 | * @see https://secure.php.net/manual/en/memcache.set.php |
||||
360 | * @see https://secure.php.net/manual/en/memcached.expiration.php |
||||
361 | * |
||||
362 | * @since 2.0.31 |
||||
363 | * @param int $duration |
||||
364 | * @return int |
||||
365 | */ |
||||
366 | 28 | protected function normalizeDuration($duration) |
|||
367 | { |
||||
368 | 28 | if ($duration < 0) { |
|||
369 | return 0; |
||||
370 | } |
||||
371 | |||||
372 | 28 | if ($duration < 2592001) { |
|||
373 | 28 | return $duration; |
|||
374 | } |
||||
375 | |||||
376 | return $duration + time(); |
||||
377 | } |
||||
378 | } |
||||
379 |