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