blob: bfd62043694620235ea193cfd89bb2ef685b22bf [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 Andreevcce6bd12018-01-09 11:32:02 +02009 * Copyright (c) 2014 - 2018, 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 Andreevcce6bd12018-01-09 11:32:02 +020032 * @copyright Copyright (c) 2014 - 2018, 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 $CI =& get_instance();
101
102 if ($CI->config->load('redis', TRUE, TRUE))
103 {
Andrey Andreeva0556f12016-03-12 13:05:38 +0200104 $config = array_merge(self::$_default_config, $CI->config->item('redis'));
105 }
106 else
107 {
108 $config = self::$_default_config;
Andrey Andreev9aade1c2015-06-22 13:19:45 +0300109 }
110
Andrey Andreev9aade1c2015-06-22 13:19:45 +0300111 $this->_redis = new Redis();
112
113 try
114 {
115 if ($config['socket_type'] === 'unix')
116 {
117 $success = $this->_redis->connect($config['socket']);
118 }
119 else // tcp socket
120 {
121 $success = $this->_redis->connect($config['host'], $config['port'], $config['timeout']);
122 }
123
124 if ( ! $success)
125 {
Andrey Andreev24a4a6a2015-08-31 15:11:47 +0300126 log_message('error', 'Cache: Redis connection failed. Check your configuration.');
Andrey Andreev9aade1c2015-06-22 13:19:45 +0300127 }
Andrey Andreev24ff6db2015-09-14 13:56:41 +0300128
129 if (isset($config['password']) && ! $this->_redis->auth($config['password']))
130 {
131 log_message('error', 'Cache: Redis authentication failed.');
132 }
Andrey Andreev9aade1c2015-06-22 13:19:45 +0300133 }
134 catch (RedisException $e)
135 {
Andrey Andreev24a4a6a2015-08-31 15:11:47 +0300136 log_message('error', 'Cache: Redis connection refused ('.$e->getMessage().')');
Andrey Andreev9aade1c2015-06-22 13:19:45 +0300137 }
138
Andrey Andreev9aade1c2015-06-22 13:19:45 +0300139 // Initialize the index of serialized values.
140 $serialized = $this->_redis->sMembers('_ci_redis_serialized');
141 empty($serialized) OR $this->_serialized = array_flip($serialized);
142 }
143
144 // ------------------------------------------------------------------------
145
146 /**
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100147 * Get cache
148 *
Andrey Andreev51f00ca2016-03-12 12:27:14 +0200149 * @param string $key Cache ID
Andrey Andreev9e674f72012-06-09 21:02:52 +0300150 * @return mixed
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100151 */
152 public function get($key)
153 {
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300154 $value = $this->_redis->get($key);
155
Andrey Andreev2492b502014-08-18 16:20:05 +0300156 if ($value !== FALSE && isset($this->_serialized[$key]))
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300157 {
158 return unserialize($value);
159 }
160
161 return $value;
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100162 }
163
Andrey Andreev9e674f72012-06-09 21:02:52 +0300164 // ------------------------------------------------------------------------
165
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100166 /**
167 * Save cache
168 *
Andrey Andreev43d7fa72014-01-09 17:29:45 +0200169 * @param string $id Cache ID
170 * @param mixed $data Data to save
171 * @param int $ttl Time to live in seconds
172 * @param bool $raw Whether to store the raw value (unused)
173 * @return bool TRUE on success, FALSE on failure
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100174 */
Andrey Andreev43d7fa72014-01-09 17:29:45 +0200175 public function save($id, $data, $ttl = 60, $raw = FALSE)
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100176 {
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300177 if (is_array($data) OR is_object($data))
178 {
Mathew White01015d92015-03-22 12:46:49 +0000179 if ( ! $this->_redis->sIsMember('_ci_redis_serialized', $id) && ! $this->_redis->sAdd('_ci_redis_serialized', $id))
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300180 {
Andrey Andreev2492b502014-08-18 16:20:05 +0300181 return FALSE;
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300182 }
183
Andrey Andreev2492b502014-08-18 16:20:05 +0300184 isset($this->_serialized[$id]) OR $this->_serialized[$id] = TRUE;
185 $data = serialize($data);
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300186 }
Andrey Andreev2492b502014-08-18 16:20:05 +0300187 elseif (isset($this->_serialized[$id]))
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300188 {
Andrey Andreev2492b502014-08-18 16:20:05 +0300189 $this->_serialized[$id] = NULL;
Ivan Tcholakovd245f062014-08-18 13:52:44 +0300190 $this->_redis->sRemove('_ci_redis_serialized', $id);
Ivan Tcholakov927e5082014-08-14 04:07:39 +0300191 }
192
ftwbzhaob4f36302015-05-13 17:47:41 +0800193 return $this->_redis->set($id, $data, $ttl);
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100194 }
195
Andrey Andreev9e674f72012-06-09 21:02:52 +0300196 // ------------------------------------------------------------------------
197
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100198 /**
199 * Delete from cache
200 *
Andrey Andreev51f00ca2016-03-12 12:27:14 +0200201 * @param string $key Cache key
Andrey Andreev9e674f72012-06-09 21:02:52 +0300202 * @return bool
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100203 */
204 public function delete($key)
205 {
Andrey Andreev2492b502014-08-18 16:20:05 +0300206 if ($this->_redis->delete($key) !== 1)
Ivan Tcholakovd514d5c2014-08-18 12:04:27 +0300207 {
Andrey Andreev2492b502014-08-18 16:20:05 +0300208 return FALSE;
Ivan Tcholakovd514d5c2014-08-18 12:04:27 +0300209 }
Ivan Tcholakovbc417612014-08-18 11:11:39 +0300210
Andrey Andreev2492b502014-08-18 16:20:05 +0300211 if (isset($this->_serialized[$key]))
212 {
213 $this->_serialized[$key] = NULL;
214 $this->_redis->sRemove('_ci_redis_serialized', $key);
215 }
216
217 return TRUE;
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100218 }
219
Andrey Andreev9e674f72012-06-09 21:02:52 +0300220 // ------------------------------------------------------------------------
221
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100222 /**
Andrey Andreev43d7fa72014-01-09 17:29:45 +0200223 * Increment a raw value
224 *
225 * @param string $id Cache ID
226 * @param int $offset Step/value to add
227 * @return mixed New value on success or FALSE on failure
228 */
229 public function increment($id, $offset = 1)
230 {
Andrey Andreev70f738a2015-05-07 12:16:33 +0300231 return $this->_redis->incr($id, $offset);
Andrey Andreev43d7fa72014-01-09 17:29:45 +0200232 }
233
234 // ------------------------------------------------------------------------
235
236 /**
237 * Decrement a raw value
238 *
239 * @param string $id Cache ID
240 * @param int $offset Step/value to reduce by
241 * @return mixed New value on success or FALSE on failure
242 */
243 public function decrement($id, $offset = 1)
244 {
Andrey Andreev70f738a2015-05-07 12:16:33 +0300245 return $this->_redis->decr($id, $offset);
Andrey Andreev43d7fa72014-01-09 17:29:45 +0200246 }
247
248 // ------------------------------------------------------------------------
249
250 /**
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100251 * Clean cache
252 *
Andrey Andreev9e674f72012-06-09 21:02:52 +0300253 * @return bool
254 * @see Redis::flushDB()
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100255 */
256 public function clean()
257 {
258 return $this->_redis->flushDB();
259 }
260
Andrey Andreev9e674f72012-06-09 21:02:52 +0300261 // ------------------------------------------------------------------------
262
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100263 /**
264 * Get cache driver info
265 *
Andrey Andreev51f00ca2016-03-12 12:27:14 +0200266 * @param string $type Not supported in Redis.
267 * Only included in order to offer a
268 * consistent cache API.
Andrey Andreev9e674f72012-06-09 21:02:52 +0300269 * @return array
270 * @see Redis::info()
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100271 */
272 public function cache_info($type = NULL)
273 {
274 return $this->_redis->info();
275 }
276
Andrey Andreev9e674f72012-06-09 21:02:52 +0300277 // ------------------------------------------------------------------------
278
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100279 /**
280 * Get cache metadata
281 *
Andrey Andreev51f00ca2016-03-12 12:27:14 +0200282 * @param string $key Cache key
Andrey Andreev9e674f72012-06-09 21:02:52 +0300283 * @return array
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100284 */
285 public function get_metadata($key)
286 {
287 $value = $this->get($key);
288
ftwbzhaob2119a72015-04-09 13:27:01 +0800289 if ($value !== FALSE)
Andrey Andreev9e674f72012-06-09 21:02:52 +0300290 {
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100291 return array(
292 'expire' => time() + $this->_redis->ttl($key),
293 'data' => $value
294 );
295 }
Andrey Andreev9e674f72012-06-09 21:02:52 +0300296
297 return FALSE;
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100298 }
299
Andrey Andreev9e674f72012-06-09 21:02:52 +0300300 // ------------------------------------------------------------------------
301
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100302 /**
303 * Check if Redis driver is supported
304 *
Andrey Andreev9e674f72012-06-09 21:02:52 +0300305 * @return bool
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100306 */
307 public function is_supported()
308 {
Andrey Andreev24a4a6a2015-08-31 15:11:47 +0300309 return extension_loaded('redis');
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100310 }
311
Andrey Andreev9e674f72012-06-09 21:02:52 +0300312 // ------------------------------------------------------------------------
313
314 /**
Andrey Andreev9e674f72012-06-09 21:02:52 +0300315 * Class destructor
316 *
317 * Closes the connection to Redis if present.
318 *
319 * @return void
320 */
321 public function __destruct()
322 {
323 if ($this->_redis)
324 {
325 $this->_redis->close();
326 }
327 }
Anton Lindqvist1e8be292012-01-21 12:25:08 +0100328}