You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

328 lines
7.5 KiB

7 years ago
  1. <?php
  2. /**
  3. * CodeIgniter
  4. *
  5. * An open source application development framework for PHP
  6. *
  7. * This content is released under the MIT License (MIT)
  8. *
  9. * Copyright (c) 2014 - 2017, British Columbia Institute of Technology
  10. *
  11. * 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:
  17. *
  18. * 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
  31. * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
  32. * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/)
  33. * @license http://opensource.org/licenses/MIT MIT License
  34. * @link https://codeigniter.com
  35. * @since Version 3.0.0
  36. * @filesource
  37. */
  38. defined('BASEPATH') OR exit('No direct script access allowed');
  39. /**
  40. * CodeIgniter Redis Caching Class
  41. *
  42. * @package CodeIgniter
  43. * @subpackage Libraries
  44. * @category Core
  45. * @author Anton Lindqvist <anton@qvister.se>
  46. * @link
  47. */
  48. class CI_Cache_redis extends CI_Driver
  49. {
  50. /**
  51. * Default config
  52. *
  53. * @static
  54. * @var array
  55. */
  56. protected static $_default_config = array(
  57. 'socket_type' => 'tcp',
  58. 'host' => '127.0.0.1',
  59. 'password' => NULL,
  60. 'port' => 6379,
  61. 'timeout' => 0
  62. );
  63. /**
  64. * Redis connection
  65. *
  66. * @var Redis
  67. */
  68. protected $_redis;
  69. /**
  70. * An internal cache for storing keys of serialized values.
  71. *
  72. * @var array
  73. */
  74. protected $_serialized = array();
  75. // ------------------------------------------------------------------------
  76. /**
  77. * Class constructor
  78. *
  79. * Setup Redis
  80. *
  81. * Loads Redis config file if present. Will halt execution
  82. * if a Redis connection can't be established.
  83. *
  84. * @return void
  85. * @see Redis::connect()
  86. */
  87. public function __construct()
  88. {
  89. if ( ! $this->is_supported())
  90. {
  91. log_message('error', 'Cache: Failed to create Redis object; extension not loaded?');
  92. return;
  93. }
  94. $CI =& get_instance();
  95. if ($CI->config->load('redis', TRUE, TRUE))
  96. {
  97. $config = array_merge(self::$_default_config, $CI->config->item('redis'));
  98. }
  99. else
  100. {
  101. $config = self::$_default_config;
  102. }
  103. $this->_redis = new Redis();
  104. try
  105. {
  106. if ($config['socket_type'] === 'unix')
  107. {
  108. $success = $this->_redis->connect($config['socket']);
  109. }
  110. else // tcp socket
  111. {
  112. $success = $this->_redis->connect($config['host'], $config['port'], $config['timeout']);
  113. }
  114. if ( ! $success)
  115. {
  116. log_message('error', 'Cache: Redis connection failed. Check your configuration.');
  117. }
  118. if (isset($config['password']) && ! $this->_redis->auth($config['password']))
  119. {
  120. log_message('error', 'Cache: Redis authentication failed.');
  121. }
  122. }
  123. catch (RedisException $e)
  124. {
  125. log_message('error', 'Cache: Redis connection refused ('.$e->getMessage().')');
  126. }
  127. // Initialize the index of serialized values.
  128. $serialized = $this->_redis->sMembers('_ci_redis_serialized');
  129. empty($serialized) OR $this->_serialized = array_flip($serialized);
  130. }
  131. // ------------------------------------------------------------------------
  132. /**
  133. * Get cache
  134. *
  135. * @param string $key Cache ID
  136. * @return mixed
  137. */
  138. public function get($key)
  139. {
  140. $value = $this->_redis->get($key);
  141. if ($value !== FALSE && isset($this->_serialized[$key]))
  142. {
  143. return unserialize($value);
  144. }
  145. return $value;
  146. }
  147. // ------------------------------------------------------------------------
  148. /**
  149. * Save cache
  150. *
  151. * @param string $id Cache ID
  152. * @param mixed $data Data to save
  153. * @param int $ttl Time to live in seconds
  154. * @param bool $raw Whether to store the raw value (unused)
  155. * @return bool TRUE on success, FALSE on failure
  156. */
  157. public function save($id, $data, $ttl = 60, $raw = FALSE)
  158. {
  159. if (is_array($data) OR is_object($data))
  160. {
  161. if ( ! $this->_redis->sIsMember('_ci_redis_serialized', $id) && ! $this->_redis->sAdd('_ci_redis_serialized', $id))
  162. {
  163. return FALSE;
  164. }
  165. isset($this->_serialized[$id]) OR $this->_serialized[$id] = TRUE;
  166. $data = serialize($data);
  167. }
  168. elseif (isset($this->_serialized[$id]))
  169. {
  170. $this->_serialized[$id] = NULL;
  171. $this->_redis->sRemove('_ci_redis_serialized', $id);
  172. }
  173. return $this->_redis->set($id, $data, $ttl);
  174. }
  175. // ------------------------------------------------------------------------
  176. /**
  177. * Delete from cache
  178. *
  179. * @param string $key Cache key
  180. * @return bool
  181. */
  182. public function delete($key)
  183. {
  184. if ($this->_redis->delete($key) !== 1)
  185. {
  186. return FALSE;
  187. }
  188. if (isset($this->_serialized[$key]))
  189. {
  190. $this->_serialized[$key] = NULL;
  191. $this->_redis->sRemove('_ci_redis_serialized', $key);
  192. }
  193. return TRUE;
  194. }
  195. // ------------------------------------------------------------------------
  196. /**
  197. * Increment a raw value
  198. *
  199. * @param string $id Cache ID
  200. * @param int $offset Step/value to add
  201. * @return mixed New value on success or FALSE on failure
  202. */
  203. public function increment($id, $offset = 1)
  204. {
  205. return $this->_redis->incr($id, $offset);
  206. }
  207. // ------------------------------------------------------------------------
  208. /**
  209. * Decrement a raw value
  210. *
  211. * @param string $id Cache ID
  212. * @param int $offset Step/value to reduce by
  213. * @return mixed New value on success or FALSE on failure
  214. */
  215. public function decrement($id, $offset = 1)
  216. {
  217. return $this->_redis->decr($id, $offset);
  218. }
  219. // ------------------------------------------------------------------------
  220. /**
  221. * Clean cache
  222. *
  223. * @return bool
  224. * @see Redis::flushDB()
  225. */
  226. public function clean()
  227. {
  228. return $this->_redis->flushDB();
  229. }
  230. // ------------------------------------------------------------------------
  231. /**
  232. * Get cache driver info
  233. *
  234. * @param string $type Not supported in Redis.
  235. * Only included in order to offer a
  236. * consistent cache API.
  237. * @return array
  238. * @see Redis::info()
  239. */
  240. public function cache_info($type = NULL)
  241. {
  242. return $this->_redis->info();
  243. }
  244. // ------------------------------------------------------------------------
  245. /**
  246. * Get cache metadata
  247. *
  248. * @param string $key Cache key
  249. * @return array
  250. */
  251. public function get_metadata($key)
  252. {
  253. $value = $this->get($key);
  254. if ($value !== FALSE)
  255. {
  256. return array(
  257. 'expire' => time() + $this->_redis->ttl($key),
  258. 'data' => $value
  259. );
  260. }
  261. return FALSE;
  262. }
  263. // ------------------------------------------------------------------------
  264. /**
  265. * Check if Redis driver is supported
  266. *
  267. * @return bool
  268. */
  269. public function is_supported()
  270. {
  271. return extension_loaded('redis');
  272. }
  273. // ------------------------------------------------------------------------
  274. /**
  275. * Class destructor
  276. *
  277. * Closes the connection to Redis if present.
  278. *
  279. * @return void
  280. */
  281. public function __destruct()
  282. {
  283. if ($this->_redis)
  284. {
  285. $this->_redis->close();
  286. }
  287. }
  288. }