blob: e9194a4997b7c214d0c11bf74908f17f587eb773 [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 Andreev125ef472016-01-11 12:33:00 +02009 * Copyright (c) 2014 - 2016, 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
Andrey Andreev1924e872016-01-11 12:55:34 +020031 * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
Andrey Andreev125ef472016-01-11 12:33:00 +020032 * @copyright Copyright (c) 2014 - 2016, British Columbia Institute of Technology (http://bcit.ca/)
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020033 * @license http://opensource.org/licenses/MIT MIT License
Andrey Andreevbd202c92016-01-11 12:50:18 +020034 * @link https://codeigniter.com
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020035 * @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 /**
Andrey Andreev9aade1c2015-06-22 13:19:45 +030082 * Class constructor
83 *
84 * Setup Redis
85 *
86 * Loads Redis config file if present. Will halt execution
87 * if a Redis connection can't be established.
88 *
89 * @return void
90 * @see Redis::connect()
91 */
92 public function __construct()
93 {
Andrey Andreev1be89872016-03-11 18:12:57 +020094 if ( ! $this->is_supported())
95 {
96 log_message('error', 'Cache: Failed to create Redis object; extension not loaded?');
97 return;
98 }
99
Andrey Andreev9aade1c2015-06-22 13:19:45 +0300100 $config = array();
101 $CI =& get_instance();
102
103 if ($CI->config->load('redis', TRUE, TRUE))
104 {
105 $config = $CI->config->item('redis');
106 }
107
108 $config = array_merge(self::$_default_config, $config);
109 $this->_redis = new Redis();
110
111 try
112 {
113 if ($config['socket_type'] === 'unix')
114 {
115 $success = $this->_redis->connect($config['socket']);
116 }
117 else // tcp socket
118 {
119 $success = $this->_redis->connect($config['host'], $config['port'], $config['timeout']);
120 }
121
122 if ( ! $success)
123 {
Andrey Andreev24a4a6a2015-08-31 15:11:47 +0300124 log_message('error', 'Cache: Redis connection failed. Check your configuration.');
Andrey Andreev9aade1c2015-06-22 13:19:45 +0300125 }
Andrey Andreev24ff6db2015-09-14 13:56:41 +0300126
127 if (isset($config['password']) && ! $this->_redis->auth($config['password']))
128 {
129 log_message('error', 'Cache: Redis authentication failed.');
130 }
Andrey Andreev9aade1c2015-06-22 13:19:45 +0300131 }
132 catch (RedisException $e)
133 {
Andrey Andreev24a4a6a2015-08-31 15:11:47 +0300134 log_message('error', 'Cache: Redis connection refused ('.$e->getMessage().')');
Andrey Andreev9aade1c2015-06-22 13:19:45 +0300135 }
136
Andrey Andreev9aade1c2015-06-22 13:19:45 +0300137 // Initialize the index of serialized values.
138 $serialized = $this->_redis->sMembers('_ci_redis_serialized');
139 empty($serialized) OR $this->_serialized = array_flip($serialized);
140 }
141
142 // ------------------------------------------------------------------------
143
144 /**
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100145 * Get cache
146 *
Andrey Andreev43d7fa72014-01-09 17:29:45 +0200147 * @param string Cache ID
Andrey Andreev9e674f72012-06-09 21:02:52 +0300148 * @return mixed
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100149 */
150 public function get($key)
151 {
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300152 $value = $this->_redis->get($key);
153
Andrey Andreev2492b502014-08-18 16:20:05 +0300154 if ($value !== FALSE && isset($this->_serialized[$key]))
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300155 {
156 return unserialize($value);
157 }
158
159 return $value;
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100160 }
161
Andrey Andreev9e674f72012-06-09 21:02:52 +0300162 // ------------------------------------------------------------------------
163
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100164 /**
165 * Save cache
166 *
Andrey Andreev43d7fa72014-01-09 17:29:45 +0200167 * @param string $id Cache ID
168 * @param mixed $data Data to save
169 * @param int $ttl Time to live in seconds
170 * @param bool $raw Whether to store the raw value (unused)
171 * @return bool TRUE on success, FALSE on failure
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100172 */
Andrey Andreev43d7fa72014-01-09 17:29:45 +0200173 public function save($id, $data, $ttl = 60, $raw = FALSE)
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100174 {
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300175 if (is_array($data) OR is_object($data))
176 {
Mathew White01015d92015-03-22 12:46:49 +0000177 if ( ! $this->_redis->sIsMember('_ci_redis_serialized', $id) && ! $this->_redis->sAdd('_ci_redis_serialized', $id))
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300178 {
Andrey Andreev2492b502014-08-18 16:20:05 +0300179 return FALSE;
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300180 }
181
Andrey Andreev2492b502014-08-18 16:20:05 +0300182 isset($this->_serialized[$id]) OR $this->_serialized[$id] = TRUE;
183 $data = serialize($data);
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300184 }
Andrey Andreev2492b502014-08-18 16:20:05 +0300185 elseif (isset($this->_serialized[$id]))
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300186 {
Andrey Andreev2492b502014-08-18 16:20:05 +0300187 $this->_serialized[$id] = NULL;
Ivan Tcholakovd245f062014-08-18 13:52:44 +0300188 $this->_redis->sRemove('_ci_redis_serialized', $id);
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300189 }
190
ftwbzhaob4f36302015-05-13 17:47:41 +0800191 return $this->_redis->set($id, $data, $ttl);
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100192 }
193
Andrey Andreev9e674f72012-06-09 21:02:52 +0300194 // ------------------------------------------------------------------------
195
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100196 /**
197 * Delete from cache
198 *
Andrey Andreev9e674f72012-06-09 21:02:52 +0300199 * @param string Cache key
200 * @return bool
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100201 */
202 public function delete($key)
203 {
Andrey Andreev2492b502014-08-18 16:20:05 +0300204 if ($this->_redis->delete($key) !== 1)
Ivan Tcholakovd514d5c2014-08-18 12:04:27 +0300205 {
Andrey Andreev2492b502014-08-18 16:20:05 +0300206 return FALSE;
Ivan Tcholakovd514d5c2014-08-18 12:04:27 +0300207 }
Ivan Tcholakovbc417612014-08-18 11:11:39 +0300208
Andrey Andreev2492b502014-08-18 16:20:05 +0300209 if (isset($this->_serialized[$key]))
210 {
211 $this->_serialized[$key] = NULL;
212 $this->_redis->sRemove('_ci_redis_serialized', $key);
213 }
214
215 return TRUE;
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100216 }
217
Andrey Andreev9e674f72012-06-09 21:02:52 +0300218 // ------------------------------------------------------------------------
219
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100220 /**
Andrey Andreev43d7fa72014-01-09 17:29:45 +0200221 * Increment a raw value
222 *
223 * @param string $id Cache ID
224 * @param int $offset Step/value to add
225 * @return mixed New value on success or FALSE on failure
226 */
227 public function increment($id, $offset = 1)
228 {
Andrey Andreev70f738a2015-05-07 12:16:33 +0300229 return $this->_redis->incr($id, $offset);
Andrey Andreev43d7fa72014-01-09 17:29:45 +0200230 }
231
232 // ------------------------------------------------------------------------
233
234 /**
235 * Decrement a raw value
236 *
237 * @param string $id Cache ID
238 * @param int $offset Step/value to reduce by
239 * @return mixed New value on success or FALSE on failure
240 */
241 public function decrement($id, $offset = 1)
242 {
Andrey Andreev70f738a2015-05-07 12:16:33 +0300243 return $this->_redis->decr($id, $offset);
Andrey Andreev43d7fa72014-01-09 17:29:45 +0200244 }
245
246 // ------------------------------------------------------------------------
247
248 /**
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100249 * Clean cache
250 *
Andrey Andreev9e674f72012-06-09 21:02:52 +0300251 * @return bool
252 * @see Redis::flushDB()
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100253 */
254 public function clean()
255 {
256 return $this->_redis->flushDB();
257 }
258
Andrey Andreev9e674f72012-06-09 21:02:52 +0300259 // ------------------------------------------------------------------------
260
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100261 /**
262 * Get cache driver info
263 *
Andrey Andreev9e674f72012-06-09 21:02:52 +0300264 * @param string Not supported in Redis.
265 * Only included in order to offer a
266 * consistent cache API.
267 * @return array
268 * @see Redis::info()
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100269 */
270 public function cache_info($type = NULL)
271 {
272 return $this->_redis->info();
273 }
274
Andrey Andreev9e674f72012-06-09 21:02:52 +0300275 // ------------------------------------------------------------------------
276
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100277 /**
278 * Get cache metadata
279 *
Andrey Andreev9e674f72012-06-09 21:02:52 +0300280 * @param string Cache key
281 * @return array
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100282 */
283 public function get_metadata($key)
284 {
285 $value = $this->get($key);
286
ftwbzhaob2119a72015-04-09 13:27:01 +0800287 if ($value !== FALSE)
Andrey Andreev9e674f72012-06-09 21:02:52 +0300288 {
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100289 return array(
290 'expire' => time() + $this->_redis->ttl($key),
291 'data' => $value
292 );
293 }
Andrey Andreev9e674f72012-06-09 21:02:52 +0300294
295 return FALSE;
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100296 }
297
Andrey Andreev9e674f72012-06-09 21:02:52 +0300298 // ------------------------------------------------------------------------
299
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100300 /**
301 * Check if Redis driver is supported
302 *
Andrey Andreev9e674f72012-06-09 21:02:52 +0300303 * @return bool
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100304 */
305 public function is_supported()
306 {
Andrey Andreev24a4a6a2015-08-31 15:11:47 +0300307 return extension_loaded('redis');
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100308 }
309
Andrey Andreev9e674f72012-06-09 21:02:52 +0300310 // ------------------------------------------------------------------------
311
312 /**
Andrey Andreev9e674f72012-06-09 21:02:52 +0300313 * Class destructor
314 *
315 * Closes the connection to Redis if present.
316 *
317 * @return void
318 */
319 public function __destruct()
320 {
321 if ($this->_redis)
322 {
323 $this->_redis->close();
324 }
325 }
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100326}