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