pictcode / lib / Cake / Cache / Engine / MemcachedEngine.php @ 0b1b8047
履歴 | 表示 | アノテート | ダウンロード (10.185 KB)
1 | 635eef61 | spyder1211 | <?php
|
---|---|---|---|
2 | /**
|
||
3 | * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||
4 | * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||
5 | *
|
||
6 | * Licensed under The MIT License
|
||
7 | * For full copyright and license information, please see the LICENSE.txt
|
||
8 | * Redistributions of files must retain the above copyright notice.
|
||
9 | *
|
||
10 | * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||
11 | * @link http://cakephp.org CakePHP(tm) Project
|
||
12 | * @since CakePHP(tm) v 2.5.0
|
||
13 | * @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||
14 | */
|
||
15 | |||
16 | /**
|
||
17 | * Memcached storage engine for cache. Memcached has some limitations in the amount of
|
||
18 | * control you have over expire times far in the future. See MemcachedEngine::write() for
|
||
19 | * more information.
|
||
20 | *
|
||
21 | * Main advantage of this Memcached engine over the memcached engine is
|
||
22 | * support of binary protocol, and igbibnary serialization
|
||
23 | * (if memcached extension compiled with --enable-igbinary)
|
||
24 | * Compressed keys can also be incremented/decremented
|
||
25 | *
|
||
26 | * @package Cake.Cache.Engine
|
||
27 | */
|
||
28 | class MemcachedEngine extends CacheEngine { |
||
29 | |||
30 | /**
|
||
31 | * memcached wrapper.
|
||
32 | *
|
||
33 | * @var Memcache
|
||
34 | */
|
||
35 | protected $_Memcached = null; |
||
36 | |||
37 | /**
|
||
38 | * Settings
|
||
39 | *
|
||
40 | * - servers = string or array of memcached servers, default => 127.0.0.1. If an
|
||
41 | * array MemcacheEngine will use them as a pool.
|
||
42 | * - compress = boolean, default => false
|
||
43 | * - persistent = string The name of the persistent connection. All configurations using
|
||
44 | * the same persistent value will share a single underlying connection.
|
||
45 | * - serialize = string, default => php. The serializer engine used to serialize data.
|
||
46 | * Available engines are php, igbinary and json. Beside php, the memcached extension
|
||
47 | * must be compiled with the appropriate serializer support.
|
||
48 | * - options - Additional options for the memcached client. Should be an array of option => value.
|
||
49 | * Use the Memcached::OPT_* constants as keys.
|
||
50 | *
|
||
51 | * @var array
|
||
52 | */
|
||
53 | public $settings = array(); |
||
54 | |||
55 | /**
|
||
56 | * List of available serializer engines
|
||
57 | *
|
||
58 | * Memcached must be compiled with json and igbinary support to use these engines
|
||
59 | *
|
||
60 | * @var array
|
||
61 | */
|
||
62 | protected $_serializers = array( |
||
63 | 'igbinary' => Memcached::SERIALIZER_IGBINARY, |
||
64 | 'json' => Memcached::SERIALIZER_JSON, |
||
65 | 'php' => Memcached::SERIALIZER_PHP |
||
66 | ); |
||
67 | |||
68 | /**
|
||
69 | * Initialize the Cache Engine
|
||
70 | *
|
||
71 | * Called automatically by the cache frontend
|
||
72 | * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
|
||
73 | *
|
||
74 | * @param array $settings array of setting for the engine
|
||
75 | * @return bool True if the engine has been successfully initialized, false if not
|
||
76 | * @throws CacheException when you try use authentication without Memcached compiled with SASL support
|
||
77 | */
|
||
78 | public function init($settings = array()) { |
||
79 | if (!class_exists('Memcached')) { |
||
80 | return false; |
||
81 | } |
||
82 | if (!isset($settings['prefix'])) { |
||
83 | $settings['prefix'] = Inflector::slug(APP_DIR) . '_'; |
||
84 | } |
||
85 | |||
86 | if (defined('Memcached::HAVE_MSGPACK') && Memcached::HAVE_MSGPACK) { |
||
87 | $this->_serializers['msgpack'] = Memcached::SERIALIZER_MSGPACK; |
||
88 | } |
||
89 | |||
90 | $settings += array( |
||
91 | 'engine' => 'Memcached', |
||
92 | 'servers' => array('127.0.0.1'), |
||
93 | 'compress' => false, |
||
94 | 'persistent' => false, |
||
95 | 'login' => null, |
||
96 | 'password' => null, |
||
97 | 'serialize' => 'php', |
||
98 | 'options' => array() |
||
99 | ); |
||
100 | parent::init($settings); |
||
101 | |||
102 | if (!is_array($this->settings['servers'])) { |
||
103 | $this->settings['servers'] = array($this->settings['servers']); |
||
104 | } |
||
105 | |||
106 | if (isset($this->_Memcached)) { |
||
107 | return true; |
||
108 | } |
||
109 | |||
110 | if (!$this->settings['persistent']) { |
||
111 | $this->_Memcached = new Memcached(); |
||
112 | } else {
|
||
113 | $this->_Memcached = new Memcached((string)$this->settings['persistent']); |
||
114 | } |
||
115 | $this->_setOptions();
|
||
116 | |||
117 | if (count($this->_Memcached->getServerList())) { |
||
118 | return true; |
||
119 | } |
||
120 | |||
121 | $servers = array(); |
||
122 | foreach ($this->settings['servers'] as $server) { |
||
123 | $servers[] = $this->_parseServerString($server); |
||
124 | } |
||
125 | |||
126 | if (!$this->_Memcached->addServers($servers)) { |
||
127 | return false; |
||
128 | } |
||
129 | |||
130 | if ($this->settings['login'] !== null && $this->settings['password'] !== null) { |
||
131 | if (!method_exists($this->_Memcached, 'setSaslAuthData')) { |
||
132 | throw new CacheException( |
||
133 | __d('cake_dev', 'Memcached extension is not build with SASL support') |
||
134 | ); |
||
135 | } |
||
136 | $this->_Memcached->setOption(Memcached::OPT_BINARY_PROTOCOL, true); |
||
137 | $this->_Memcached->setSaslAuthData($this->settings['login'], $this->settings['password']); |
||
138 | } |
||
139 | if (is_array($this->settings['options'])) { |
||
140 | foreach ($this->settings['options'] as $opt => $value) { |
||
141 | $this->_Memcached->setOption($opt, $value); |
||
142 | } |
||
143 | } |
||
144 | |||
145 | return true; |
||
146 | } |
||
147 | |||
148 | /**
|
||
149 | * Settings the memcached instance
|
||
150 | *
|
||
151 | * @throws CacheException when the Memcached extension is not built with the desired serializer engine
|
||
152 | * @return void
|
||
153 | */
|
||
154 | protected function _setOptions() { |
||
155 | $this->_Memcached->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, true); |
||
156 | |||
157 | $serializer = strtolower($this->settings['serialize']); |
||
158 | if (!isset($this->_serializers[$serializer])) { |
||
159 | throw new CacheException( |
||
160 | __d('cake_dev', '%s is not a valid serializer engine for Memcached', $serializer) |
||
161 | ); |
||
162 | } |
||
163 | |||
164 | if ($serializer !== 'php' && !constant('Memcached::HAVE_' . strtoupper($serializer))) { |
||
165 | throw new CacheException( |
||
166 | __d('cake_dev', 'Memcached extension is not compiled with %s support', $serializer) |
||
167 | ); |
||
168 | } |
||
169 | |||
170 | $this->_Memcached->setOption(Memcached::OPT_SERIALIZER, $this->_serializers[$serializer]); |
||
171 | |||
172 | // Check for Amazon ElastiCache instance
|
||
173 | if (defined('Memcached::OPT_CLIENT_MODE') && defined('Memcached::DYNAMIC_CLIENT_MODE')) { |
||
174 | $this->_Memcached->setOption(Memcached::OPT_CLIENT_MODE, Memcached::DYNAMIC_CLIENT_MODE); |
||
175 | } |
||
176 | |||
177 | $this->_Memcached->setOption(Memcached::OPT_COMPRESSION, (bool)$this->settings['compress']); |
||
178 | } |
||
179 | |||
180 | /**
|
||
181 | * Parses the server address into the host/port. Handles both IPv6 and IPv4
|
||
182 | * addresses and Unix sockets
|
||
183 | *
|
||
184 | * @param string $server The server address string.
|
||
185 | * @return array Array containing host, port
|
||
186 | */
|
||
187 | protected function _parseServerString($server) { |
||
188 | $socketTransport = 'unix://'; |
||
189 | if (strpos($server, $socketTransport) === 0) { |
||
190 | return array(substr($server, strlen($socketTransport)), 0); |
||
191 | } |
||
192 | if (substr($server, 0, 1) === '[') { |
||
193 | $position = strpos($server, ']:'); |
||
194 | if ($position !== false) { |
||
195 | $position++;
|
||
196 | } |
||
197 | } else {
|
||
198 | $position = strpos($server, ':'); |
||
199 | } |
||
200 | $port = 11211; |
||
201 | $host = $server; |
||
202 | if ($position !== false) { |
||
203 | $host = substr($server, 0, $position); |
||
204 | $port = substr($server, $position + 1); |
||
205 | } |
||
206 | return array($host, (int)$port); |
||
207 | } |
||
208 | |||
209 | /**
|
||
210 | * Write data for key into cache. When using memcached as your cache engine
|
||
211 | * remember that the Memcached pecl extension does not support cache expiry times greater
|
||
212 | * than 30 days in the future. Any duration greater than 30 days will be treated as never expiring.
|
||
213 | *
|
||
214 | * @param string $key Identifier for the data
|
||
215 | * @param mixed $value Data to be cached
|
||
216 | * @param int $duration How long to cache the data, in seconds
|
||
217 | * @return bool True if the data was successfully cached, false on failure
|
||
218 | * @see http://php.net/manual/en/memcache.set.php
|
||
219 | */
|
||
220 | public function write($key, $value, $duration) { |
||
221 | if ($duration > 30 * DAY) { |
||
222 | $duration = 0; |
||
223 | } |
||
224 | |||
225 | return $this->_Memcached->set($key, $value, $duration); |
||
226 | } |
||
227 | |||
228 | /**
|
||
229 | * Read a key from the cache
|
||
230 | *
|
||
231 | * @param string $key Identifier for the data
|
||
232 | * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
|
||
233 | */
|
||
234 | public function read($key) { |
||
235 | return $this->_Memcached->get($key); |
||
236 | } |
||
237 | |||
238 | /**
|
||
239 | * Increments the value of an integer cached key
|
||
240 | *
|
||
241 | * @param string $key Identifier for the data
|
||
242 | * @param int $offset How much to increment
|
||
243 | * @return New incremented value, false otherwise
|
||
244 | * @throws CacheException when you try to increment with compress = true
|
||
245 | */
|
||
246 | public function increment($key, $offset = 1) { |
||
247 | return $this->_Memcached->increment($key, $offset); |
||
248 | } |
||
249 | |||
250 | /**
|
||
251 | * Decrements the value of an integer cached key
|
||
252 | *
|
||
253 | * @param string $key Identifier for the data
|
||
254 | * @param int $offset How much to subtract
|
||
255 | * @return New decremented value, false otherwise
|
||
256 | * @throws CacheException when you try to decrement with compress = true
|
||
257 | */
|
||
258 | public function decrement($key, $offset = 1) { |
||
259 | return $this->_Memcached->decrement($key, $offset); |
||
260 | } |
||
261 | |||
262 | /**
|
||
263 | * Delete a key from the cache
|
||
264 | *
|
||
265 | * @param string $key Identifier for the data
|
||
266 | * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed
|
||
267 | */
|
||
268 | public function delete($key) { |
||
269 | return $this->_Memcached->delete($key); |
||
270 | } |
||
271 | |||
272 | /**
|
||
273 | * Delete all keys from the cache
|
||
274 | *
|
||
275 | * @param bool $check If true no deletes will occur and instead CakePHP will rely
|
||
276 | * on key TTL values.
|
||
277 | * @return bool True if the cache was successfully cleared, false otherwise. Will
|
||
278 | * also return false if you are using a binary protocol.
|
||
279 | */
|
||
280 | public function clear($check) { |
||
281 | if ($check) { |
||
282 | return true; |
||
283 | } |
||
284 | |||
285 | $keys = $this->_Memcached->getAllKeys(); |
||
286 | if ($keys === false) { |
||
287 | return false; |
||
288 | } |
||
289 | |||
290 | foreach ($keys as $key) { |
||
291 | if (strpos($key, $this->settings['prefix']) === 0) { |
||
292 | $this->_Memcached->delete($key); |
||
293 | } |
||
294 | } |
||
295 | |||
296 | return true; |
||
297 | } |
||
298 | |||
299 | /**
|
||
300 | * Returns the `group value` for each of the configured groups
|
||
301 | * If the group initial value was not found, then it initializes
|
||
302 | * the group accordingly.
|
||
303 | *
|
||
304 | * @return array
|
||
305 | */
|
||
306 | public function groups() { |
||
307 | if (empty($this->_compiledGroupNames)) { |
||
308 | foreach ($this->settings['groups'] as $group) { |
||
309 | $this->_compiledGroupNames[] = $this->settings['prefix'] . $group; |
||
310 | } |
||
311 | } |
||
312 | |||
313 | $groups = $this->_Memcached->getMulti($this->_compiledGroupNames); |
||
314 | if (count($groups) !== count($this->settings['groups'])) { |
||
315 | foreach ($this->_compiledGroupNames as $group) { |
||
316 | if (!isset($groups[$group])) { |
||
317 | $this->_Memcached->set($group, 1, 0); |
||
318 | $groups[$group] = 1; |
||
319 | } |
||
320 | } |
||
321 | ksort($groups); |
||
322 | } |
||
323 | |||
324 | $result = array(); |
||
325 | $groups = array_values($groups); |
||
326 | foreach ($this->settings['groups'] as $i => $group) { |
||
327 | $result[] = $group . $groups[$i]; |
||
328 | } |
||
329 | |||
330 | return $result; |
||
331 | } |
||
332 | |||
333 | /**
|
||
334 | * Increments the group value to simulate deletion of all keys under a group
|
||
335 | * old values will remain in storage until they expire.
|
||
336 | *
|
||
337 | * @param string $group The group to clear.
|
||
338 | * @return bool success
|
||
339 | */
|
||
340 | public function clearGroup($group) { |
||
341 | return (bool)$this->_Memcached->increment($this->settings['prefix'] . $group); |
||
342 | } |
||
343 | } |