1: <?php
2: /**
3: * Memcache storage engine for cache
4: *
5: *
6: * PHP 5
7: *
8: * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
9: * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
10: *
11: * Licensed under The MIT License
12: * Redistributions of files must retain the above copyright notice.
13: *
14: * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
15: * @link http://cakephp.org CakePHP(tm) Project
16: * @package Cake.Cache.Engine
17: * @since CakePHP(tm) v 1.2.0.4933
18: * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
19: */
20:
21: /**
22: * Memcache storage engine for cache. Memcache has some limitations in the amount of
23: * control you have over expire times far in the future. See MemcacheEngine::write() for
24: * more information.
25: *
26: * @package Cake.Cache.Engine
27: */
28: class MemcacheEngine extends CacheEngine {
29:
30: /**
31: * Memcache wrapper.
32: *
33: * @var Memcache
34: */
35: protected $_Memcache = null;
36:
37: /**
38: * Settings
39: *
40: * - servers = string or array of memcache servers, default => 127.0.0.1. If an
41: * array MemcacheEngine will use them as a pool.
42: * - compress = boolean, default => false
43: *
44: * @var array
45: */
46: public $settings = array();
47:
48: /**
49: * Initialize the Cache Engine
50: *
51: * Called automatically by the cache frontend
52: * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
53: *
54: * @param array $settings array of setting for the engine
55: * @return boolean True if the engine has been successfully initialized, false if not
56: */
57: public function init($settings = array()) {
58: if (!class_exists('Memcache')) {
59: return false;
60: }
61: parent::init(array_merge(array(
62: 'engine' => 'Memcache',
63: 'prefix' => Inflector::slug(APP_DIR) . '_',
64: 'servers' => array('127.0.0.1'),
65: 'compress' => false,
66: 'persistent' => true
67: ), $settings)
68: );
69:
70: if ($this->settings['compress']) {
71: $this->settings['compress'] = MEMCACHE_COMPRESSED;
72: }
73: if (!is_array($this->settings['servers'])) {
74: $this->settings['servers'] = array($this->settings['servers']);
75: }
76: if (!isset($this->_Memcache)) {
77: $return = false;
78: $this->_Memcache = new Memcache();
79: foreach ($this->settings['servers'] as $server) {
80: list($host, $port) = $this->_parseServerString($server);
81: if ($this->_Memcache->addServer($host, $port, $this->settings['persistent'])) {
82: $return = true;
83: }
84: }
85: return $return;
86: }
87: return true;
88: }
89:
90: /**
91: * Parses the server address into the host/port. Handles both IPv6 and IPv4
92: * addresses and Unix sockets
93: *
94: * @param string $server The server address string.
95: * @return array Array containing host, port
96: */
97: protected function _parseServerString($server) {
98: if ($server[0] == 'u') {
99: return array($server, 0);
100: }
101: if (substr($server, 0, 1) == '[') {
102: $position = strpos($server, ']:');
103: if ($position !== false) {
104: $position++;
105: }
106: } else {
107: $position = strpos($server, ':');
108: }
109: $port = 11211;
110: $host = $server;
111: if ($position !== false) {
112: $host = substr($server, 0, $position);
113: $port = substr($server, $position + 1);
114: }
115: return array($host, $port);
116: }
117:
118: /**
119: * Write data for key into cache. When using memcache as your cache engine
120: * remember that the Memcache pecl extension does not support cache expiry times greater
121: * than 30 days in the future. Any duration greater than 30 days will be treated as never expiring.
122: *
123: * @param string $key Identifier for the data
124: * @param mixed $value Data to be cached
125: * @param integer $duration How long to cache the data, in seconds
126: * @return boolean True if the data was successfully cached, false on failure
127: * @see http://php.net/manual/en/memcache.set.php
128: */
129: public function write($key, $value, $duration) {
130: if ($duration > 30 * DAY) {
131: $duration = 0;
132: }
133: return $this->_Memcache->set($key, $value, $this->settings['compress'], $duration);
134: }
135:
136: /**
137: * Read a key from the cache
138: *
139: * @param string $key Identifier for the data
140: * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
141: */
142: public function read($key) {
143: return $this->_Memcache->get($key);
144: }
145:
146: /**
147: * Increments the value of an integer cached key
148: *
149: * @param string $key Identifier for the data
150: * @param integer $offset How much to increment
151: * @return New incremented value, false otherwise
152: * @throws CacheException when you try to increment with compress = true
153: */
154: public function increment($key, $offset = 1) {
155: if ($this->settings['compress']) {
156: throw new CacheException(
157: __d('cake_dev', 'Method increment() not implemented for compressed cache in %s', __CLASS__)
158: );
159: }
160: return $this->_Memcache->increment($key, $offset);
161: }
162:
163: /**
164: * Decrements the value of an integer cached key
165: *
166: * @param string $key Identifier for the data
167: * @param integer $offset How much to subtract
168: * @return New decremented value, false otherwise
169: * @throws CacheException when you try to decrement with compress = true
170: */
171: public function decrement($key, $offset = 1) {
172: if ($this->settings['compress']) {
173: throw new CacheException(
174: __d('cake_dev', 'Method decrement() not implemented for compressed cache in %s', __CLASS__)
175: );
176: }
177: return $this->_Memcache->decrement($key, $offset);
178: }
179:
180: /**
181: * Delete a key from the cache
182: *
183: * @param string $key Identifier for the data
184: * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed
185: */
186: public function delete($key) {
187: return $this->_Memcache->delete($key);
188: }
189:
190: /**
191: * Delete all keys from the cache
192: *
193: * @param boolean $check
194: * @return boolean True if the cache was successfully cleared, false otherwise
195: */
196: public function clear($check) {
197: if ($check) {
198: return true;
199: }
200: foreach ($this->_Memcache->getExtendedStats('slabs') as $slabs) {
201: foreach (array_keys($slabs) as $slabId) {
202: if (!is_numeric($slabId)) {
203: continue;
204: }
205:
206: foreach ($this->_Memcache->getExtendedStats('cachedump', $slabId) as $stats) {
207: if (!is_array($stats)) {
208: continue;
209: }
210: foreach (array_keys($stats) as $key) {
211: if (strpos($key, $this->settings['prefix']) === 0) {
212: $this->_Memcache->delete($key);
213: }
214: }
215: }
216: }
217: }
218: return true;
219: }
220:
221: /**
222: * Connects to a server in connection pool
223: *
224: * @param string $host host ip address or name
225: * @param integer $port Server port
226: * @return boolean True if memcache server was connected
227: */
228: public function connect($host, $port = 11211) {
229: if ($this->_Memcache->getServerStatus($host, $port) === 0) {
230: if ($this->_Memcache->connect($host, $port)) {
231: return true;
232: }
233: return false;
234: }
235: return true;
236: }
237: }
238: