PageRenderTime 29ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/Cache.php

https://github.com/abirmmu/codeigniter-cache
PHP | 431 lines | 241 code | 64 blank | 126 comment | 35 complexity | 74957ddcbb5f9b277a161897d694c98c MD5 | raw file
  1. <?php if (!defined('BASEPATH')) exit('No direct script access allowed');
  2. /**
  3. * Cache Class
  4. *
  5. * Partial Caching library for CodeIgniter
  6. *
  7. * @category Libraries
  8. * @author Phil Sturgeon
  9. * @link http://philsturgeon.co.uk/code/codeigniter-cache
  10. * @license MIT
  11. * @version 2.1
  12. */
  13. class Cache
  14. {
  15. private $_ci;
  16. private $_path;
  17. private $_contents;
  18. private $_filename;
  19. private $_expires;
  20. private $_default_expires;
  21. private $_created;
  22. private $_dependencies;
  23. /**
  24. * Constructor - Initializes and references CI
  25. */
  26. function __construct()
  27. {
  28. log_message('debug', "Cache Class Initialized.");
  29. $this->_ci =& get_instance();
  30. $this->_reset();
  31. $this->_ci->load->config('cache');
  32. $this->_path = $this->_ci->config->item('cache_dir');
  33. $this->_default_expires = $this->_ci->config->item('cache_default_expires');
  34. if ( ! is_dir($this->_path))
  35. {
  36. show_error("Cache Path not found: $this->_path");
  37. }
  38. }
  39. /**
  40. * Initialize Cache object to empty
  41. *
  42. * @access private
  43. * @return void
  44. */
  45. private function _reset()
  46. {
  47. $this->_contents = NULL;
  48. $this->_filename = NULL;
  49. $this->_expires = NULL;
  50. $this->_created = NULL;
  51. $this->_dependencies = array();
  52. }
  53. /**
  54. * Call a library's cached result or create new cache
  55. *
  56. * @access public
  57. * @param string
  58. * @return array
  59. */
  60. public function library($library, $method, $arguments = array(), $expires = NULL)
  61. {
  62. if(!in_array(ucfirst($library), $this->_ci->load->_ci_classes))
  63. {
  64. $this->_ci->load->library($library);
  65. }
  66. return $this->_call($library, $method, $arguments, $expires);
  67. }
  68. /**
  69. * Call a model's cached result or create new cache
  70. *
  71. * @access public
  72. * @return array
  73. */
  74. public function model($model, $method, $arguments = array(), $expires = NULL)
  75. {
  76. if(!in_array(ucfirst($model), $this->_ci->load->_ci_classes))
  77. {
  78. $this->_ci->load->model($model);
  79. }
  80. return $this->_call($model, $method, $arguments, $expires);
  81. }
  82. // Depreciated, use model() or library()
  83. private function _call($property, $method, $arguments = array(), $expires = NULL)
  84. {
  85. $this->_ci->load->helper('security');
  86. if(!is_array($arguments))
  87. {
  88. $arguments = (array) $arguments;
  89. }
  90. // Clean given arguments to a 0-index array
  91. $arguments = array_values($arguments);
  92. $cache_file = $property.DIRECTORY_SEPARATOR.do_hash($method.serialize($arguments), 'sha1');
  93. // See if we have this cached or delete if $expires is negative
  94. if($expires >= 0)
  95. {
  96. $cached_response = $this->get($cache_file);
  97. }
  98. else
  99. {
  100. $this->delete($cache_file);
  101. return;
  102. }
  103. // Not FALSE? Return it
  104. if($cached_response !== FALSE && $cached_response !== NULL)
  105. {
  106. return $cached_response;
  107. }
  108. else
  109. {
  110. // Call the model or library with the method provided and the same arguments
  111. $new_response = call_user_func_array(array($this->_ci->$property, $method), $arguments);
  112. $this->write($new_response, $cache_file, $expires);
  113. return $new_response;
  114. }
  115. }
  116. /**
  117. * Helper functions for the dependencies property
  118. */
  119. function set_dependencies($dependencies)
  120. {
  121. if (is_array($dependencies))
  122. $this->_dependencies = $dependencies;
  123. else
  124. $this->_dependencies = array($dependencies);
  125. // Return $this to support chaining
  126. return $this;
  127. }
  128. function add_dependencies($dependencies)
  129. {
  130. if (is_array($dependencies))
  131. $this->_dependencies = array_merge($this->_dependencies, $dependencies);
  132. else
  133. $this->_dependencies[] = $dependencies;
  134. // Return $this to support chaining
  135. return $this;
  136. }
  137. function get_dependencies() { return $this->_dependencies; }
  138. /**
  139. * Helper function to get the cache creation date
  140. */
  141. function get_created($created) { return $this->_created; }
  142. /**
  143. * Retrieve Cache File
  144. *
  145. * @access public
  146. * @param string
  147. * @param boolean
  148. * @return mixed
  149. */
  150. function get($filename = NULL, $use_expires = true)
  151. {
  152. // Check if cache was requested with the function or uses this object
  153. if ($filename !== NULL)
  154. {
  155. $this->_reset();
  156. $this->_filename = $filename;
  157. }
  158. // Check directory permissions
  159. if ( ! is_dir($this->_path) OR ! is_really_writable($this->_path))
  160. {
  161. return FALSE;
  162. }
  163. // Build the file path.
  164. $filepath = $this->_path.$this->_filename.'.cache';
  165. // Check if the cache exists, if not return FALSE
  166. if ( ! @file_exists($filepath))
  167. {
  168. return FALSE;
  169. }
  170. // Check if the cache can be opened, if not return FALSE
  171. if ( ! $fp = @fopen($filepath, FOPEN_READ))
  172. {
  173. return FALSE;
  174. }
  175. // Lock the cache
  176. flock($fp, LOCK_SH);
  177. // If the file contains data return it, otherwise return NULL
  178. if (filesize($filepath) > 0)
  179. {
  180. $this->_contents = unserialize(fread($fp, filesize($filepath)));
  181. }
  182. else
  183. {
  184. $this->_contents = NULL;
  185. }
  186. // Unlock the cache and close the file
  187. flock($fp, LOCK_UN);
  188. fclose($fp);
  189. // Check cache expiration, delete and return FALSE when expired
  190. if ($use_expires && ! empty($this->_contents['__cache_expires']) && $this->_contents['__cache_expires'] < time())
  191. {
  192. $this->delete($filename);
  193. return FALSE;
  194. }
  195. // Check Cache dependencies
  196. if(isset($this->_contents['__cache_dependencies']))
  197. {
  198. foreach ($this->_contents['__cache_dependencies'] as $dep)
  199. {
  200. $cache_created = filemtime($this->_path.$this->_filename.'.cache');
  201. // If dependency doesn't exist or is newer than this cache, delete and return FALSE
  202. if (! file_exists($this->_path.$dep.'.cache') or filemtime($this->_path.$dep.'.cache') > $cache_created)
  203. {
  204. $this->delete($filename);
  205. return FALSE;
  206. }
  207. }
  208. }
  209. // Instantiate the object variables
  210. $this->_expires = isset($this->_contents['__cache_expires']) ? $this->_contents['__cache_expires'] : NULL;
  211. $this->_dependencies = isset($this->_contents['__cache_dependencies']) ? $this->_contents['__cache_dependencies'] : NULL;
  212. $this->_created = isset($this->_contents['__cache_created']) ? $this->_contents['__cache_created'] : NULL;
  213. // Cleanup the meta variables from the contents
  214. $this->_contents = @$this->_contents['__cache_contents'];
  215. // Return the cache
  216. log_message('debug', "Cache retrieved: ".$filename);
  217. return $this->_contents;
  218. }
  219. /**
  220. * Write Cache File
  221. *
  222. * @access public
  223. * @param mixed
  224. * @param string
  225. * @param int
  226. * @param array
  227. * @return void
  228. */
  229. function write($contents = NULL, $filename = NULL, $expires = NULL, $dependencies = array())
  230. {
  231. // Check if cache was passed with the function or uses this object
  232. if ($contents !== NULL)
  233. {
  234. $this->_reset();
  235. $this->_contents = $contents;
  236. $this->_filename = $filename;
  237. $this->_expires = $expires;
  238. $this->_dependencies = $dependencies;
  239. }
  240. // Put the contents in an array so additional meta variables
  241. // can be easily removed from the output
  242. $this->_contents = array('__cache_contents' => $this->_contents);
  243. // Check directory permissions
  244. if ( ! is_dir($this->_path) OR ! is_really_writable($this->_path))
  245. {
  246. return;
  247. }
  248. // check if filename contains dirs
  249. $subdirs = explode(DIRECTORY_SEPARATOR, $this->_filename);
  250. if (count($subdirs) > 1)
  251. {
  252. array_pop($subdirs);
  253. $test_path = $this->_path.implode(DIRECTORY_SEPARATOR, $subdirs);
  254. // check if specified subdir exists
  255. if ( ! @file_exists($test_path))
  256. {
  257. // create non existing dirs, asumes PHP5
  258. if ( ! @mkdir($test_path, DIR_WRITE_MODE, TRUE)) return FALSE;
  259. }
  260. }
  261. // Set the path to the cachefile which is to be created
  262. $cache_path = $this->_path.$this->_filename.'.cache';
  263. // Open the file and log if an error occures
  264. if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
  265. {
  266. log_message('error', "Unable to write Cache file: ".$cache_path);
  267. return;
  268. }
  269. // Meta variables
  270. $this->_contents['__cache_created'] = time();
  271. $this->_contents['__cache_dependencies'] = $this->_dependencies;
  272. // Add expires variable if its set...
  273. if (! empty($this->_expires))
  274. {
  275. $this->_contents['__cache_expires'] = $this->_expires + time();
  276. }
  277. // ...or add default expiration if its set
  278. elseif (! empty($this->_default_expires) )
  279. {
  280. $this->_contents['__cache_expires'] = $this->_default_expires + time();
  281. }
  282. // Lock the file before writing or log an error if it failes
  283. if (flock($fp, LOCK_EX))
  284. {
  285. fwrite($fp, serialize($this->_contents));
  286. flock($fp, LOCK_UN);
  287. }
  288. else
  289. {
  290. log_message('error', "Cache was unable to secure a file lock for file at: ".$cache_path);
  291. return;
  292. }
  293. fclose($fp);
  294. @chmod($cache_path, DIR_WRITE_MODE);
  295. // Log success
  296. log_message('debug', "Cache file written: ".$cache_path);
  297. // Reset values
  298. $this->_reset();
  299. }
  300. /**
  301. * Delete Cache File
  302. *
  303. * @access public
  304. * @param string
  305. * @return void
  306. */
  307. function delete($filename = NULL)
  308. {
  309. if ($filename !== NULL) $this->_filename = $filename;
  310. $file_path = $this->_path.$this->_filename.'.cache';
  311. if (file_exists($file_path)) unlink($file_path);
  312. // Reset values
  313. $this->_reset();
  314. }
  315. /**
  316. * Delete a group of cached files
  317. *
  318. * Allows you to pass a group to delete cache. Example:
  319. *
  320. * <code>
  321. * $this->cache->write($data, 'nav_title');
  322. * $this->cache->write($links, 'nav_links');
  323. * $this->cache->delete_group('nav_');
  324. * </code>
  325. *
  326. * @param string $group
  327. * @return void
  328. */
  329. public function delete_group($group = null)
  330. {
  331. if ($group === null)
  332. {
  333. return FALSE;
  334. }
  335. $this->_ci->load->helper('directory');
  336. $map = directory_map($this->_path, TRUE);
  337. foreach ($map AS $file)
  338. {
  339. if (strpos($file, $group) !== FALSE)
  340. {
  341. unlink($this->_path.$file);
  342. }
  343. }
  344. // Reset values
  345. $this->_reset();
  346. }
  347. /**
  348. * Delete Full Cache or Cache subdir
  349. *
  350. * @access public
  351. * @param string
  352. * @return void
  353. */
  354. function delete_all($dirname = '')
  355. {
  356. if (empty($this->_path))
  357. {
  358. return FALSE;
  359. }
  360. $this->_ci->load->helper('file');
  361. if (file_exists($this->_path.$dirname)) delete_files($this->_path.$dirname, TRUE);
  362. // Reset values
  363. $this->_reset();
  364. }
  365. }
  366. /* End of file Cache.php */
  367. /* Location: ./application/libraries/Cache.php */