blob: 773d20c43b2f41865b6b9b3c73583dbb253f8143 [file] [log] [blame]
Andrey Andreevc5536aa2012-11-01 17:33:58 +02001<?php
Anton Lindqvist1e8be292012-01-21 12:25:08 +01002/**
3 * CodeIgniter
4 *
Andrey Andreevfe9309d2015-01-09 17:48:58 +02005 * An open source application development framework for PHP
Anton Lindqvist1e8be292012-01-21 12:25:08 +01006 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +02007 * This content is released under the MIT License (MIT)
Anton Lindqvist5a1d9532012-01-23 23:20:26 +01008 *
Andrey Andreevfe9309d2015-01-09 17:48:58 +02009 * Copyright (c) 2014 - 2015, British Columbia Institute of Technology
Anton Lindqvist5a1d9532012-01-23 23:20:26 +010010 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020011 * Permission is hereby granted, free of charge, to any person obtaining a copy
12 * of this software and associated documentation files (the "Software"), to deal
13 * in the Software without restriction, including without limitation the rights
14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 * copies of the Software, and to permit persons to whom the Software is
16 * furnished to do so, subject to the following conditions:
Anton Lindqvist5a1d9532012-01-23 23:20:26 +010017 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020018 * The above copyright notice and this permission notice shall be included in
19 * all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 * THE SOFTWARE.
28 *
29 * @package CodeIgniter
30 * @author EllisLab Dev Team
darwinel871754a2014-02-11 17:34:57 +010031 * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/)
Andrey Andreevfe9309d2015-01-09 17:48:58 +020032 * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/)
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020033 * @license http://opensource.org/licenses/MIT MIT License
34 * @link http://codeigniter.com
35 * @since Version 3.0.0
Anton Lindqvist1e8be292012-01-21 12:25:08 +010036 * @filesource
37 */
Andrey Andreevc5536aa2012-11-01 17:33:58 +020038defined('BASEPATH') OR exit('No direct script access allowed');
Anton Lindqvist1e8be292012-01-21 12:25:08 +010039
Anton Lindqvist1e8be292012-01-21 12:25:08 +010040/**
41 * CodeIgniter Redis Caching Class
42 *
43 * @package CodeIgniter
44 * @subpackage Libraries
45 * @category Core
46 * @author Anton Lindqvist <anton@qvister.se>
47 * @link
48 */
49class CI_Cache_redis extends CI_Driver
50{
Anton Lindqvist1e8be292012-01-21 12:25:08 +010051 /**
52 * Default config
53 *
Anton Lindqvist1e8be292012-01-21 12:25:08 +010054 * @static
Andrey Andreev9e674f72012-06-09 21:02:52 +030055 * @var array
Anton Lindqvist1e8be292012-01-21 12:25:08 +010056 */
Anton Lindqvist3573af82012-01-21 20:33:12 +010057 protected static $_default_config = array(
Kakysha80d663a2013-10-28 01:39:17 +040058 'socket_type' => 'tcp',
Anton Lindqvist1e8be292012-01-21 12:25:08 +010059 'host' => '127.0.0.1',
Anton Lindqvist5ccf5ce2012-06-08 11:47:17 +020060 'password' => NULL,
Anton Lindqvist1e8be292012-01-21 12:25:08 +010061 'port' => 6379,
62 'timeout' => 0
63 );
64
65 /**
66 * Redis connection
67 *
Andrey Andreev9e674f72012-06-09 21:02:52 +030068 * @var Redis
Anton Lindqvist1e8be292012-01-21 12:25:08 +010069 */
Anton Lindqvist3573af82012-01-21 20:33:12 +010070 protected $_redis;
Anton Lindqvist1e8be292012-01-21 12:25:08 +010071
Ivan Tcholakov927e5082014-08-14 04:07:39 +030072 /**
73 * An internal cache for storing keys of serialized values.
74 *
75 * @var array
76 */
Andrey Andreev2492b502014-08-18 16:20:05 +030077 protected $_serialized = array();
Ivan Tcholakov927e5082014-08-14 04:07:39 +030078
Andrey Andreev9e674f72012-06-09 21:02:52 +030079 // ------------------------------------------------------------------------
Anton Lindqvist1e8be292012-01-21 12:25:08 +010080
81 /**
82 * Get cache
83 *
Andrey Andreev43d7fa72014-01-09 17:29:45 +020084 * @param string Cache ID
Andrey Andreev9e674f72012-06-09 21:02:52 +030085 * @return mixed
Anton Lindqvist1e8be292012-01-21 12:25:08 +010086 */
87 public function get($key)
88 {
Ivan Tcholakov927e5082014-08-14 04:07:39 +030089 $value = $this->_redis->get($key);
90
Andrey Andreev2492b502014-08-18 16:20:05 +030091 if ($value !== FALSE && isset($this->_serialized[$key]))
Ivan Tcholakov927e5082014-08-14 04:07:39 +030092 {
93 return unserialize($value);
94 }
95
96 return $value;
Anton Lindqvist1e8be292012-01-21 12:25:08 +010097 }
98
Andrey Andreev9e674f72012-06-09 21:02:52 +030099 // ------------------------------------------------------------------------
100
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100101 /**
102 * Save cache
103 *
Andrey Andreev43d7fa72014-01-09 17:29:45 +0200104 * @param string $id Cache ID
105 * @param mixed $data Data to save
106 * @param int $ttl Time to live in seconds
107 * @param bool $raw Whether to store the raw value (unused)
108 * @return bool TRUE on success, FALSE on failure
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100109 */
Andrey Andreev43d7fa72014-01-09 17:29:45 +0200110 public function save($id, $data, $ttl = 60, $raw = FALSE)
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100111 {
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300112 if (is_array($data) OR is_object($data))
113 {
Mathew White01015d92015-03-22 12:46:49 +0000114 if ( ! $this->_redis->sIsMember('_ci_redis_serialized', $id) && ! $this->_redis->sAdd('_ci_redis_serialized', $id))
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300115 {
Andrey Andreev2492b502014-08-18 16:20:05 +0300116 return FALSE;
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300117 }
118
Andrey Andreev2492b502014-08-18 16:20:05 +0300119 isset($this->_serialized[$id]) OR $this->_serialized[$id] = TRUE;
120 $data = serialize($data);
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300121 }
Andrey Andreev2492b502014-08-18 16:20:05 +0300122 elseif (isset($this->_serialized[$id]))
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300123 {
Andrey Andreev2492b502014-08-18 16:20:05 +0300124 $this->_serialized[$id] = NULL;
Ivan Tcholakovd245f062014-08-18 13:52:44 +0300125 $this->_redis->sRemove('_ci_redis_serialized', $id);
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300126 }
127
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100128 return ($ttl)
Andrey Andreev43d7fa72014-01-09 17:29:45 +0200129 ? $this->_redis->setex($id, $ttl, $data)
130 : $this->_redis->set($id, $data);
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100131 }
132
Andrey Andreev9e674f72012-06-09 21:02:52 +0300133 // ------------------------------------------------------------------------
134
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100135 /**
136 * Delete from cache
137 *
Andrey Andreev9e674f72012-06-09 21:02:52 +0300138 * @param string Cache key
139 * @return bool
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100140 */
141 public function delete($key)
142 {
Andrey Andreev2492b502014-08-18 16:20:05 +0300143 if ($this->_redis->delete($key) !== 1)
Ivan Tcholakovd514d5c2014-08-18 12:04:27 +0300144 {
Andrey Andreev2492b502014-08-18 16:20:05 +0300145 return FALSE;
Ivan Tcholakovd514d5c2014-08-18 12:04:27 +0300146 }
Ivan Tcholakovbc417612014-08-18 11:11:39 +0300147
Andrey Andreev2492b502014-08-18 16:20:05 +0300148 if (isset($this->_serialized[$key]))
149 {
150 $this->_serialized[$key] = NULL;
151 $this->_redis->sRemove('_ci_redis_serialized', $key);
152 }
153
154 return TRUE;
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100155 }
156
Andrey Andreev9e674f72012-06-09 21:02:52 +0300157 // ------------------------------------------------------------------------
158
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100159 /**
Andrey Andreev43d7fa72014-01-09 17:29:45 +0200160 * Increment a raw value
161 *
162 * @param string $id Cache ID
163 * @param int $offset Step/value to add
164 * @return mixed New value on success or FALSE on failure
165 */
166 public function increment($id, $offset = 1)
167 {
Andrey Andreev170ae282015-05-07 10:43:28 +0300168 return ($offset == 1)
169 ? $this->_redis->incr($id)
170 : $this->_redis->incrBy($id, $offset);
Andrey Andreev43d7fa72014-01-09 17:29:45 +0200171 }
172
173 // ------------------------------------------------------------------------
174
175 /**
176 * Decrement a raw value
177 *
178 * @param string $id Cache ID
179 * @param int $offset Step/value to reduce by
180 * @return mixed New value on success or FALSE on failure
181 */
182 public function decrement($id, $offset = 1)
183 {
Andrey Andreev170ae282015-05-07 10:43:28 +0300184 return ($offset == 1)
185 ? $this->_redis->decr($id)
186 : $this->_redis->decrBy($id, $offset);
Andrey Andreev43d7fa72014-01-09 17:29:45 +0200187 }
188
189 // ------------------------------------------------------------------------
190
191 /**
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100192 * Clean cache
193 *
Andrey Andreev9e674f72012-06-09 21:02:52 +0300194 * @return bool
195 * @see Redis::flushDB()
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100196 */
197 public function clean()
198 {
199 return $this->_redis->flushDB();
200 }
201
Andrey Andreev9e674f72012-06-09 21:02:52 +0300202 // ------------------------------------------------------------------------
203
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100204 /**
205 * Get cache driver info
206 *
Andrey Andreev9e674f72012-06-09 21:02:52 +0300207 * @param string Not supported in Redis.
208 * Only included in order to offer a
209 * consistent cache API.
210 * @return array
211 * @see Redis::info()
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100212 */
213 public function cache_info($type = NULL)
214 {
215 return $this->_redis->info();
216 }
217
Andrey Andreev9e674f72012-06-09 21:02:52 +0300218 // ------------------------------------------------------------------------
219
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100220 /**
221 * Get cache metadata
222 *
Andrey Andreev9e674f72012-06-09 21:02:52 +0300223 * @param string Cache key
224 * @return array
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100225 */
226 public function get_metadata($key)
227 {
228 $value = $this->get($key);
229
ftwbzhaob2119a72015-04-09 13:27:01 +0800230 if ($value !== FALSE)
Andrey Andreev9e674f72012-06-09 21:02:52 +0300231 {
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100232 return array(
233 'expire' => time() + $this->_redis->ttl($key),
234 'data' => $value
235 );
236 }
Andrey Andreev9e674f72012-06-09 21:02:52 +0300237
238 return FALSE;
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100239 }
240
Andrey Andreev9e674f72012-06-09 21:02:52 +0300241 // ------------------------------------------------------------------------
242
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100243 /**
244 * Check if Redis driver is supported
245 *
Andrey Andreev9e674f72012-06-09 21:02:52 +0300246 * @return bool
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100247 */
248 public function is_supported()
249 {
Fieahbc834c32015-02-22 17:08:35 +0800250 if ( ! extension_loaded('redis'))
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100251 {
Tyler Brownelld967f722013-07-29 19:06:52 -0400252 log_message('debug', 'The Redis extension must be loaded to use Redis cache.');
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100253 return FALSE;
254 }
Fieahbc834c32015-02-22 17:08:35 +0800255
256 return $this->_setup_redis();
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100257 }
258
Andrey Andreev9e674f72012-06-09 21:02:52 +0300259 // ------------------------------------------------------------------------
260
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100261 /**
262 * Setup Redis config and connection
263 *
Andrey Andreev9e674f72012-06-09 21:02:52 +0300264 * Loads Redis config file if present. Will halt execution
265 * if a Redis connection can't be established.
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100266 *
Andrey Andreev9e674f72012-06-09 21:02:52 +0300267 * @return bool
268 * @see Redis::connect()
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100269 */
Anton Lindqvist5ccf5ce2012-06-08 11:47:17 +0200270 protected function _setup_redis()
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100271 {
272 $config = array();
273 $CI =& get_instance();
274
275 if ($CI->config->load('redis', TRUE, TRUE))
Anton Lindqvist5ccf5ce2012-06-08 11:47:17 +0200276 {
ftwbzhaoabd713f2015-04-09 13:11:51 +0800277 $config = $CI->config->item('redis');
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100278 }
279
280 $config = array_merge(self::$_default_config, $config);
281
282 $this->_redis = new Redis();
283
284 try
285 {
Kakysha80d663a2013-10-28 01:39:17 +0400286 if ($config['socket_type'] === 'unix')
287 {
Kakyshaffe21302013-10-28 21:30:05 +0400288 $success = $this->_redis->connect($config['socket']);
Kakysha8f3f1f92013-10-28 23:14:08 +0400289 }
290 else // tcp socket
Kakysha80d663a2013-10-28 01:39:17 +0400291 {
Kakyshaffe21302013-10-28 21:30:05 +0400292 $success = $this->_redis->connect($config['host'], $config['port'], $config['timeout']);
Kakysha2d146732013-10-28 20:20:24 +0400293 }
Andrey Andreev119d8a72014-01-08 15:27:53 +0200294
Kakyshaffe21302013-10-28 21:30:05 +0400295 if ( ! $success)
Kakysha2d146732013-10-28 20:20:24 +0400296 {
Kakysha333b69b2013-10-29 03:49:56 +0400297 log_message('debug', 'Cache: Redis connection refused. Check the config.');
Kakysha2d146732013-10-28 20:20:24 +0400298 return FALSE;
Kakysha80d663a2013-10-28 01:39:17 +0400299 }
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100300 }
301 catch (RedisException $e)
302 {
Kakysha333b69b2013-10-29 03:49:56 +0400303 log_message('debug', 'Cache: Redis connection refused ('.$e->getMessage().')');
Kakysha2d146732013-10-28 20:20:24 +0400304 return FALSE;
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100305 }
Anton Lindqvist210e6642012-04-23 10:13:46 +0200306
Anton Lindqvist5ccf5ce2012-06-08 11:47:17 +0200307 if (isset($config['password']))
308 {
ftwbzhao9b9a06c2015-04-07 18:36:51 +0800309 if ( ! $this->_redis->auth($config['password']))
310 {
311 log_message('debug', 'Cache: Redis authentication failed.');
312 return FALSE;
313 }
Anton Lindqvist210e6642012-04-23 10:13:46 +0200314 }
Andrey Andreev119d8a72014-01-08 15:27:53 +0200315
Ivan Tcholakovd245f062014-08-18 13:52:44 +0300316 // Initialize the index of serialized values.
Andrey Andreev2492b502014-08-18 16:20:05 +0300317 $serialized = $this->_redis->sMembers('_ci_redis_serialized');
318 if ( ! empty($serialized))
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300319 {
Andrey Andreev1b634f82014-08-18 16:21:11 +0300320 $this->_serialized = array_flip($serialized);
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300321 }
322
Kakysha2d146732013-10-28 20:20:24 +0400323 return TRUE;
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100324 }
325
Andrey Andreev9e674f72012-06-09 21:02:52 +0300326 // ------------------------------------------------------------------------
327
328 /**
Andrey Andreev9e674f72012-06-09 21:02:52 +0300329 * Class destructor
330 *
331 * Closes the connection to Redis if present.
332 *
333 * @return void
334 */
335 public function __destruct()
336 {
337 if ($this->_redis)
338 {
339 $this->_redis->close();
340 }
341 }
342
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100343}