SproutCMS

This is the code documentation for the SproutCMS project

source of /sprout/Helpers/Drivers/Cache/File.php

Copyright (C) 2017 Karmabunny Pty Ltd.

This file is a part of SproutCMS.

SproutCMS is free software: you can redistribute it and/or modify it under the terms
of the GNU General Public License as published by the Free Software Foundation, either
version 2 of the License, or (at your option) any later version.

For more information, visit <http://getsproutcms.com>.

This class was originally from Kohana 2.3.4
Copyright 2007-2008 Kohana Team
  1. <?php
  2. /**
  3.  * Copyright (C) 2017 Karmabunny Pty Ltd.
  4.  *
  5.  * This file is a part of SproutCMS.
  6.  *
  7.  * SproutCMS is free software: you can redistribute it and/or modify it under the terms
  8.  * of the GNU General Public License as published by the Free Software Foundation, either
  9.  * version 2 of the License, or (at your option) any later version.
  10.  *
  11.  * For more information, visit <http://getsproutcms.com>.
  12.  *
  13.  * This class was originally from Kohana 2.3.4
  14.  * Copyright 2007-2008 Kohana Team
  15.  */
  16. namespace Sprout\Helpers\Drivers\Cache;
  17.  
  18. use Kohana;
  19. use Kohana_Exception;
  20.  
  21. use Sprout\Helpers\Drivers\CacheDriver;
  22.  
  23.  
  24. /**
  25.  * File-based Cache driver.
  26.  */
  27. class File implements CacheDriver
  28. {
  29.  
  30. protected $directory = '';
  31.  
  32. /**
  33.   * Tests that the storage location is a directory and is writable.
  34.   */
  35. public function __construct($directory)
  36. {
  37. // Find the real path to the directory
  38. $directory = str_replace('\\', '/', realpath($directory)).'/';
  39.  
  40. // Make sure the cache directory is writable
  41. if ( ! is_dir($directory) OR ! is_writable($directory))
  42. throw new Kohana_Exception('cache.unwritable', $directory);
  43.  
  44. // Directory is valid
  45. $this->directory = $directory;
  46. }
  47.  
  48. /**
  49.   * Finds an array of files matching the given id or tag.
  50.   *
  51.   * @param string cache id or tag
  52.   * @param bool search for tags
  53.   * @return array of filenames matching the id or tag
  54.   */
  55. public function exists($id, $tag = FALSE)
  56. {
  57. if ($id === TRUE)
  58. {
  59. // Find all the files
  60. return glob($this->directory.'*~*~*');
  61. }
  62. elseif ($tag === TRUE)
  63. {
  64. // Find all the files that have the tag name
  65. $paths = glob($this->directory.'*~*'.$id.'*~*');
  66.  
  67. // Find all tags matching the given tag
  68. $files = array();
  69. foreach ($paths as $path)
  70. {
  71. // Split the files
  72. $tags = explode('~', basename($path));
  73.  
  74. // Find valid tags
  75. if (count($tags) !== 3 OR empty($tags[1]))
  76. continue;
  77.  
  78. // Split the tags by plus signs, used to separate tags
  79. $tags = explode('+', $tags[1]);
  80.  
  81. if (in_array($tag, $tags))
  82. {
  83. // Add the file to the array, it has the requested tag
  84. $files[] = $path;
  85. }
  86. }
  87.  
  88. return $files;
  89. }
  90. else
  91. {
  92. // Find the file matching the given id
  93. return glob($this->directory.$id.'~*');
  94. }
  95. }
  96.  
  97. /**
  98.   * Sets a cache item to the given data, tags, and lifetime.
  99.   *
  100.   * @param string cache id to set
  101.   * @param string data in the cache
  102.   * @param array cache tags
  103.   * @param integer lifetime
  104.   * @return bool
  105.   */
  106. public function set($id, $data, array $tags = NULL, $lifetime)
  107. {
  108. // Remove old cache files
  109. $this->delete($id);
  110.  
  111. // Cache File driver expects unix timestamp
  112. if ($lifetime !== 0)
  113. {
  114. $lifetime += time();
  115. }
  116.  
  117. if ( ! empty($tags))
  118. {
  119. // Convert the tags into a string list
  120. $tags = implode('+', $tags);
  121. }
  122.  
  123. // Write out a serialized cache
  124. return (bool) file_put_contents($this->directory.$id.'~'.$tags.'~'.$lifetime, json_encode($data));
  125. }
  126.  
  127. /**
  128.   * Finds an array of ids for a given tag.
  129.   *
  130.   * @param string tag name
  131.   * @return array of ids that match the tag
  132.   */
  133. public function find($tag)
  134. {
  135. // An array will always be returned
  136. $result = array();
  137.  
  138. if ($paths = $this->exists($tag, TRUE))
  139. {
  140. // Length of directory name
  141. $offset = strlen($this->directory);
  142.  
  143. // Find all the files with the given tag
  144. foreach ($paths as $path)
  145. {
  146. // Get the id from the filename
  147. list($id, $junk) = explode('~', basename($path), 2);
  148.  
  149. if (($data = $this->get($id)) !== FALSE)
  150. {
  151. // Add the result to the array
  152. $result[$id] = $data;
  153. }
  154. }
  155. }
  156.  
  157. return $result;
  158. }
  159.  
  160. /**
  161.   * Fetches a cache item. This will delete the item if it is expired or if
  162.   * the hash does not match the stored hash.
  163.   *
  164.   * @param string cache id
  165.   * @return mixed|NULL
  166.   */
  167. public function get($id)
  168. {
  169. if ($file = $this->exists($id))
  170. {
  171. // Use the first file
  172. $file = current($file);
  173.  
  174. // Validate that the cache has not expired
  175. if ($this->expired($file))
  176. {
  177. // Remove this cache, it has expired
  178. $this->delete($id);
  179. }
  180. else
  181. {
  182. // Turn off errors while reading the file
  183. $ER = error_reporting(0);
  184.  
  185. if (($data = file_get_contents($file)) !== FALSE)
  186. {
  187. // Unserialize the data
  188. $data = json_decode($data, true);
  189. }
  190. else
  191. {
  192. // Delete the data
  193. unset($data);
  194. }
  195.  
  196. // Turn errors back on
  197. }
  198. }
  199.  
  200. // Return NULL if there is no data
  201. return isset($data) ? $data : NULL;
  202. }
  203.  
  204. /**
  205.   * Deletes a cache item by id or tag
  206.   *
  207.   * @param string cache id or tag, or TRUE for "all items"
  208.   * @param boolean use tags
  209.   * @return boolean
  210.   */
  211. public function delete($id, $tag = FALSE)
  212. {
  213. $files = $this->exists($id, $tag);
  214.  
  215. if (empty($files))
  216. return FALSE;
  217.  
  218. // Disable all error reporting while deleting
  219. $ER = error_reporting(0);
  220.  
  221. foreach ($files as $file)
  222. {
  223. // Remove the cache file
  224. if ( ! unlink($file))
  225. Kohana::log('error', 'Cache: Unable to delete cache file: '.$file);
  226. }
  227.  
  228. // Turn on error reporting again
  229.  
  230. return TRUE;
  231. }
  232.  
  233. /**
  234.   * Deletes all cache files that are older than the current time.
  235.   *
  236.   * @return void
  237.   */
  238. public function deleteExpired()
  239. {
  240. if ($files = $this->exists(TRUE))
  241. {
  242. // Disable all error reporting while deleting
  243. $ER = error_reporting(0);
  244.  
  245. foreach ($files as $file)
  246. {
  247. if ($this->expired($file))
  248. {
  249. // The cache file has already expired, delete it
  250. if ( ! unlink($file))
  251. Kohana::log('error', 'Cache: Unable to delete cache file: '.$file);
  252. }
  253. }
  254.  
  255. // Turn on error reporting again
  256. }
  257. }
  258.  
  259. /**
  260.   * Check if a cache file has expired by filename.
  261.   *
  262.   * @param string filename
  263.   * @return bool
  264.   */
  265. protected function expired($file)
  266. {
  267. // Get the expiration time
  268. $expires = (int) substr($file, strrpos($file, '~') + 1);
  269.  
  270. // Expirations of 0 are "never expire"
  271. return ($expires !== 0 AND $expires <= time());
  272. }
  273.  
  274. } // End Cache File Driver
  275.