Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
16591 anikendra 1
<?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
		if (strpos($server, 'unix://') === 0) {
189
			return array($server, 0);
190
		}
191
		if (substr($server, 0, 1) === '[') {
192
			$position = strpos($server, ']:');
193
			if ($position !== false) {
194
				$position++;
195
			}
196
		} else {
197
			$position = strpos($server, ':');
198
		}
199
		$port = 11211;
200
		$host = $server;
201
		if ($position !== false) {
202
			$host = substr($server, 0, $position);
203
			$port = substr($server, $position + 1);
204
		}
205
		return array($host, (int)$port);
206
	}
207
 
208
/**
209
 * Write data for key into cache. When using memcached as your cache engine
210
 * remember that the Memcached pecl extension does not support cache expiry times greater
211
 * than 30 days in the future. Any duration greater than 30 days will be treated as never expiring.
212
 *
213
 * @param string $key Identifier for the data
214
 * @param mixed $value Data to be cached
215
 * @param int $duration How long to cache the data, in seconds
216
 * @return bool True if the data was successfully cached, false on failure
217
 * @see http://php.net/manual/en/memcache.set.php
218
 */
219
	public function write($key, $value, $duration) {
220
		if ($duration > 30 * DAY) {
221
			$duration = 0;
222
		}
223
 
224
		return $this->_Memcached->set($key, $value, $duration);
225
	}
226
 
227
/**
228
 * Read a key from the cache
229
 *
230
 * @param string $key Identifier for the data
231
 * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
232
 */
233
	public function read($key) {
234
		return $this->_Memcached->get($key);
235
	}
236
 
237
/**
238
 * Increments the value of an integer cached key
239
 *
240
 * @param string $key Identifier for the data
241
 * @param int $offset How much to increment
242
 * @return New incremented value, false otherwise
243
 * @throws CacheException when you try to increment with compress = true
244
 */
245
	public function increment($key, $offset = 1) {
246
		return $this->_Memcached->increment($key, $offset);
247
	}
248
 
249
/**
250
 * Decrements the value of an integer cached key
251
 *
252
 * @param string $key Identifier for the data
253
 * @param int $offset How much to subtract
254
 * @return New decremented value, false otherwise
255
 * @throws CacheException when you try to decrement with compress = true
256
 */
257
	public function decrement($key, $offset = 1) {
258
		return $this->_Memcached->decrement($key, $offset);
259
	}
260
 
261
/**
262
 * Delete a key from the cache
263
 *
264
 * @param string $key Identifier for the data
265
 * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed
266
 */
267
	public function delete($key) {
268
		return $this->_Memcached->delete($key);
269
	}
270
 
271
/**
272
 * Delete all keys from the cache
273
 *
274
 * @param bool $check If true no deletes will occur and instead CakePHP will rely
275
 *   on key TTL values.
276
 * @return bool True if the cache was successfully cleared, false otherwise
277
 */
278
	public function clear($check) {
279
		if ($check) {
280
			return true;
281
		}
282
 
283
		$keys = $this->_Memcached->getAllKeys();
284
 
285
		foreach ($keys as $key) {
286
			if (strpos($key, $this->settings['prefix']) === 0) {
287
				$this->_Memcached->delete($key);
288
			}
289
		}
290
 
291
		return true;
292
	}
293
 
294
/**
295
 * Returns the `group value` for each of the configured groups
296
 * If the group initial value was not found, then it initializes
297
 * the group accordingly.
298
 *
299
 * @return array
300
 */
301
	public function groups() {
302
		if (empty($this->_compiledGroupNames)) {
303
			foreach ($this->settings['groups'] as $group) {
304
				$this->_compiledGroupNames[] = $this->settings['prefix'] . $group;
305
			}
306
		}
307
 
308
		$groups = $this->_Memcached->getMulti($this->_compiledGroupNames);
309
		if (count($groups) !== count($this->settings['groups'])) {
310
			foreach ($this->_compiledGroupNames as $group) {
311
				if (!isset($groups[$group])) {
312
					$this->_Memcached->set($group, 1, 0);
313
					$groups[$group] = 1;
314
				}
315
			}
316
			ksort($groups);
317
		}
318
 
319
		$result = array();
320
		$groups = array_values($groups);
321
		foreach ($this->settings['groups'] as $i => $group) {
322
			$result[] = $group . $groups[$i];
323
		}
324
 
325
		return $result;
326
	}
327
 
328
/**
329
 * Increments the group value to simulate deletion of all keys under a group
330
 * old values will remain in storage until they expire.
331
 *
332
 * @param string $group The group to clear.
333
 * @return bool success
334
 */
335
	public function clearGroup($group) {
336
		return (bool)$this->_Memcached->increment($this->settings['prefix'] . $group);
337
	}
338
}