1: <?php
2: /**
3: * Redis storage engine for cache
4: *
5: *
6: * PHP 5
7: *
8: * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
9: * Copyright 2005-2012, 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-2012, 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 2.2
18: * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
19: */
20:
21: /**
22: * Redis storage engine for cache.
23: *
24: * @package Cake.Cache.Engine
25: */
26: class RedisEngine extends CacheEngine {
27:
28: /**
29: * Redis wrapper.
30: *
31: * @var Redis
32: */
33: protected $_Redis = null;
34:
35: /**
36: * Settings
37: *
38: * - server = string url or ip to the Redis server host
39: * - port = integer port number to the Redis server (default: 6379)
40: * - timeout = float timeout in seconds (default: 0)
41: * - persistent = bool Connects to the Redis server with a persistent connection (default: true)
42: *
43: * @var array
44: */
45: public $settings = array();
46:
47: /**
48: * Initialize the Cache Engine
49: *
50: * Called automatically by the cache frontend
51: * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
52: *
53: * @param array $settings array of setting for the engine
54: * @return boolean True if the engine has been successfully initialized, false if not
55: */
56: public function init($settings = array()) {
57: if (!class_exists('Redis')) {
58: return false;
59: }
60: parent::init(array_merge(array(
61: 'engine' => 'Redis',
62: 'prefix' => null,
63: 'server' => '127.0.0.1',
64: 'port' => 6379,
65: 'timeout' => 0,
66: 'persistent' => true
67: ), $settings)
68: );
69:
70: return $this->_connect();
71: }
72:
73: /**
74: * Connects to a Redis server
75: *
76: * @return boolean True if Redis server was connected
77: */
78: protected function _connect() {
79: $return = false;
80: try {
81: $this->_Redis = new Redis();
82: if (empty($this->settings['persistent'])) {
83: $return = $this->_Redis->connect($this->settings['server'], $this->settings['port'], $this->settings['timeout']);
84: } else {
85: $return = $this->_Redis->pconnect($this->settings['server'], $this->settings['port'], $this->settings['timeout']);
86: }
87: } catch (RedisException $e) {
88: return false;
89: }
90: return $return;
91: }
92:
93: /**
94: * Write data for key into cache.
95: *
96: * @param string $key Identifier for the data
97: * @param mixed $value Data to be cached
98: * @param integer $duration How long to cache the data, in seconds
99: * @return boolean True if the data was successfully cached, false on failure
100: */
101: public function write($key, $value, $duration) {
102: if (!is_int($value)) {
103: $value = serialize($value);
104: }
105: if ($duration === 0) {
106: return $this->_Redis->set($key, $value);
107: }
108:
109: return $this->_Redis->setex($key, $duration, $value);
110: }
111:
112: /**
113: * Read a key from the cache
114: *
115: * @param string $key Identifier for the data
116: * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
117: */
118: public function read($key) {
119: $value = $this->_Redis->get($key);
120: if (ctype_digit($value)) {
121: $value = (int)$value;
122: }
123: if ($value !== false && is_string($value)) {
124: $value = unserialize($value);
125: }
126: return $value;
127: }
128:
129: /**
130: * Increments the value of an integer cached key
131: *
132: * @param string $key Identifier for the data
133: * @param integer $offset How much to increment
134: * @return New incremented value, false otherwise
135: * @throws CacheException when you try to increment with compress = true
136: */
137: public function increment($key, $offset = 1) {
138: return (int)$this->_Redis->incrBy($key, $offset);
139: }
140:
141: /**
142: * Decrements the value of an integer cached key
143: *
144: * @param string $key Identifier for the data
145: * @param integer $offset How much to subtract
146: * @return New decremented value, false otherwise
147: * @throws CacheException when you try to decrement with compress = true
148: */
149: public function decrement($key, $offset = 1) {
150: return (int)$this->_Redis->decrBy($key, $offset);
151: }
152:
153: /**
154: * Delete a key from the cache
155: *
156: * @param string $key Identifier for the data
157: * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed
158: */
159: public function delete($key) {
160: return $this->_Redis->delete($key) > 0;
161: }
162:
163: /**
164: * Delete all keys from the cache
165: *
166: * @param boolean $check
167: * @return boolean True if the cache was successfully cleared, false otherwise
168: */
169: public function clear($check) {
170: if ($check) {
171: return true;
172: }
173: $keys = $this->_Redis->getKeys($this->settings['prefix'] . '*');
174: $this->_Redis->del($keys);
175:
176: return true;
177: }
178:
179: /**
180: * Returns the `group value` for each of the configured groups
181: * If the group initial value was not found, then it initializes
182: * the group accordingly.
183: *
184: * @return array
185: **/
186: public function groups() {
187: $result = array();
188: foreach ($this->settings['groups'] as $group) {
189: $value = $this->_Redis->get($this->settings['prefix'] . $group);
190: if (!$value) {
191: $value = 1;
192: $this->_Redis->set($this->settings['prefix'] . $group, $value);
193: }
194: $result[] = $group . $value;
195: }
196: return $result;
197: }
198:
199: /**
200: * Increments the group value to simulate deletion of all keys under a group
201: * old values will remain in storage until they expire.
202: *
203: * @return boolean success
204: **/
205: public function clearGroup($group) {
206: return (bool)$this->_Redis->incr($this->settings['prefix'] . $group);
207: }
208:
209: /**
210: * Disconnects from the redis server
211: *
212: * @return voind
213: **/
214: public function __destruct() {
215: if (!$this->settings['persistent']) {
216: $this->_Redis->close();
217: }
218: }
219: }
220: