PageRenderTime 25ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/cache/classes/kohana/cache/file.php

https://github.com/purplexcite/Purpled-Blog-Engine
PHP | 413 lines | 193 code | 38 blank | 182 comment | 22 complexity | c927155b2cc534689c44bedae1c57fb2 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * [Kohana Cache](api/Kohana_Cache) File driver. Provides a file based
  4. * driver for the Kohana Cache library. This is one of the slowest
  5. * caching methods.
  6. *
  7. * ### Configuration example
  8. *
  9. * Below is an example of a _file_ server configuration.
  10. *
  11. * return array(
  12. * 'file' => array( // File driver group
  13. * 'driver' => 'file', // using File driver
  14. * 'cache_dir' => APPPATH.'cache/.kohana_cache', // Cache location
  15. * ),
  16. * )
  17. *
  18. * In cases where only one cache group is required, if the group is named `default` there is
  19. * no need to pass the group name when instantiating a cache instance.
  20. *
  21. * #### General cache group configuration settings
  22. *
  23. * Below are the settings available to all types of cache driver.
  24. *
  25. * Name | Required | Description
  26. * -------------- | -------- | ---------------------------------------------------------------
  27. * driver | __YES__ | (_string_) The driver type to use
  28. * cache_dir | __NO__ | (_string_) The cache directory to use for this cache instance
  29. *
  30. * ### System requirements
  31. *
  32. * * Kohana 3.0.x
  33. * * PHP 5.2.4 or greater
  34. *
  35. * @package Kohana
  36. * @category Cache
  37. * @author Kohana Team
  38. * @copyright (c) 2009-2010 Kohana Team
  39. * @license http://kohanaphp.com/license
  40. */
  41. class Kohana_Cache_File extends Cache {
  42. // !!! NOTICE !!!
  43. // THIS CONSTANT IS USED BY THE FILE CACHE CLASS
  44. // INTERNALLY. USE THE CONFIGURATION FILE TO
  45. // REDEFINE THE CACHE DIRECTORY.
  46. const CACHE_DIR = 'cache/.kohana_cache';
  47. /**
  48. * Creates a hashed filename based on the string. This is used
  49. * to create shorter unique IDs for each cache filename.
  50. *
  51. * // Create the cache filename
  52. * $filename = Cache_File::filename($this->_sanitize_id($id));
  53. *
  54. * @param string string to hash into filename
  55. * @return string
  56. */
  57. protected static function filename($string)
  58. {
  59. return sha1($string).'.txt';
  60. }
  61. /**
  62. * @var string the caching directory
  63. */
  64. protected $_cache_dir;
  65. /**
  66. * Constructs the file cache driver. This method cannot be invoked externally. The file cache driver must
  67. * be instantiated using the `Cache::instance()` method.
  68. *
  69. * @param array config
  70. * @throws Kohana_Cache_Exception
  71. */
  72. protected function __construct(array $config)
  73. {
  74. // Setup parent
  75. parent::__construct($config);
  76. try
  77. {
  78. $directory = Arr::get($this->_config, 'cache_dir', APPPATH.Cache_File::CACHE_DIR);
  79. $this->_cache_dir = new RecursiveDirectoryIterator($directory);
  80. }
  81. catch (UnexpectedValueException $e)
  82. {
  83. if ( ! mkdir($directory, 0777, TRUE))
  84. {
  85. throw new Kohana_Cache_Exception('Failed to create the defined cache directory : :directory', array(':directory' => $directory));
  86. }
  87. chmod($directory, 0777);
  88. $this->_cache_dir = new RecursiveDirectoryIterator($directory);
  89. }
  90. // If the defined directory is a file, get outta here
  91. if ($this->_cache_dir->isFile())
  92. {
  93. throw new Kohana_Cache_Exception('Unable to create cache directory as a already file exists : :resource', array(':resource' => $this->_cache_dir->getRealPath()));
  94. }
  95. // Check the read status of the directory
  96. if ( ! $this->_cache_dir->isReadable())
  97. {
  98. throw new Kohana_Cache_Exception('Unable to read from the cache directory :resource', array(':resource' => $this->_cache_dir->getRealPath()));
  99. }
  100. // Check the write status of the directory
  101. if ( ! $this->_cache_dir->isWritable())
  102. {
  103. throw new Kohana_Cache_Exception('Unable to write to the cache directory :resource', array(':resource' => $this->_cache_dir->getRealPath()));
  104. }
  105. }
  106. /**
  107. * Retrieve a cached value entry by id.
  108. *
  109. * // Retrieve cache entry from file group
  110. * $data = Cache::instance('file')->get('foo');
  111. *
  112. * // Retrieve cache entry from file group and return 'bar' if miss
  113. * $data = Cache::instance('file')->get('foo', 'bar');
  114. *
  115. * @param string id of cache to entry
  116. * @param string default value to return if cache miss
  117. * @return mixed
  118. * @throws Kohana_Cache_Exception
  119. */
  120. public function get($id, $default = NULL)
  121. {
  122. $filename = Cache_File::filename($this->_sanitize_id($id));
  123. $directory = $this->_resolve_directory($filename);
  124. // Wrap operations in try/catch to handle notices
  125. try
  126. {
  127. // Open file
  128. $file = new SplFileInfo($directory.$filename);
  129. // If file does not exist
  130. if ( ! $file->getRealPath())
  131. {
  132. // Return default value
  133. return $default;
  134. }
  135. else
  136. {
  137. // Open the file and extract the json
  138. $json = $file->openFile()->current();
  139. // Decode the json into PHP object
  140. $data = json_decode($json);
  141. // Test the expiry
  142. if ($data->expiry < time())
  143. {
  144. // Delete the file
  145. $this->_delete_file($file, NULL, TRUE);
  146. // Return default value
  147. return $default;
  148. }
  149. else
  150. {
  151. return ($data->type === 'string') ? $data->payload : unserialize($data->payload);
  152. }
  153. }
  154. }
  155. catch (ErrorException $e)
  156. {
  157. // Handle ErrorException caused by failed unserialization
  158. if ($e->getCode() === E_NOTICE)
  159. {
  160. throw new Kohana_Cache_Exception(__METHOD__.' failed to unserialize cached object with message : '.$e->getMessage());
  161. }
  162. // Otherwise throw the exception
  163. throw $e;
  164. }
  165. }
  166. /**
  167. * Set a value to cache with id and lifetime
  168. *
  169. * $data = 'bar';
  170. *
  171. * // Set 'bar' to 'foo' in file group, using default expiry
  172. * Cache::instance('file')->set('foo', $data);
  173. *
  174. * // Set 'bar' to 'foo' in file group for 30 seconds
  175. * Cache::instance('file')->set('foo', $data, 30);
  176. *
  177. * @param string id of cache entry
  178. * @param string data to set to cache
  179. * @param integer lifetime in seconds
  180. * @return boolean
  181. */
  182. public function set($id, $data, $lifetime = NULL)
  183. {
  184. $filename = Cache_File::filename($this->_sanitize_id($id));
  185. $directory = $this->_resolve_directory($filename);
  186. // If lifetime is NULL
  187. if ($lifetime === NULL)
  188. {
  189. // Set to the default expiry
  190. $lifetime = Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE);
  191. }
  192. // Open directory
  193. $dir = new SplFileInfo($directory);
  194. // If the directory path is not a directory
  195. if ( ! $dir->isDir())
  196. {
  197. // Create the directory
  198. if ( ! mkdir($directory, 0777, TRUE))
  199. {
  200. throw new Kohana_Cache_Exception(__METHOD__.' unable to create directory : :directory', array(':directory' => $directory));
  201. }
  202. // chmod to solve potential umask issues
  203. chmod($directory, 0777);
  204. }
  205. // Open file to inspect
  206. $resouce = new SplFileInfo($directory.$filename);
  207. $file = $resouce->openFile('w');
  208. try
  209. {
  210. $type = gettype($data);
  211. // Serialize the data
  212. $data = json_encode((object) array(
  213. 'payload' => ($type === 'string') ? $data : serialize($data),
  214. 'expiry' => time() + $lifetime,
  215. 'type' => $type
  216. ));
  217. $size = strlen($data);
  218. }
  219. catch (ErrorException $e)
  220. {
  221. // If serialize through an error exception
  222. if ($e->getCode() === E_NOTICE)
  223. {
  224. // Throw a caching error
  225. throw new Kohana_Cache_Exception(__METHOD__.' failed to serialize data for caching with message : '.$e->getMessage());
  226. }
  227. // Else rethrow the error exception
  228. throw $e;
  229. }
  230. try
  231. {
  232. $file->fwrite($data, $size);
  233. return (bool) $file->fflush();
  234. }
  235. catch (Exception $e)
  236. {
  237. throw $e;
  238. }
  239. }
  240. /**
  241. * Delete a cache entry based on id
  242. *
  243. * // Delete 'foo' entry from the file group
  244. * Cache::instance('file')->delete('foo');
  245. *
  246. * @param string id to remove from cache
  247. * @return boolean
  248. */
  249. public function delete($id)
  250. {
  251. $filename = Cache_File::filename($this->_sanitize_id($id));
  252. $directory = $this->_resolve_directory($filename);
  253. return $this->_delete_file(new SplFileInfo($directory.$filename), NULL, TRUE);
  254. }
  255. /**
  256. * Delete all cache entries.
  257. *
  258. * Beware of using this method when
  259. * using shared memory cache systems, as it will wipe every
  260. * entry within the system for all clients.
  261. *
  262. * // Delete all cache entries in the file group
  263. * Cache::instance('file')->delete_all();
  264. *
  265. * @return boolean
  266. */
  267. public function delete_all()
  268. {
  269. return $this->_delete_file($this->_cache_dir, TRUE);
  270. }
  271. /**
  272. * Deletes files recursively and returns FALSE on any errors
  273. *
  274. * // Delete a file or folder whilst retaining parent directory and ignore all errors
  275. * $this->_delete_file($folder, TRUE, TRUE);
  276. *
  277. * @param SplFileInfo file
  278. * @param boolean retain the parent directory
  279. * @param boolean ignore_errors to prevent all exceptions interrupting exec
  280. * @return boolean
  281. * @throws Kohana_Cache_Exception
  282. */
  283. protected function _delete_file(SplFileInfo $file, $retain_parent_directory = FALSE, $ignore_errors = FALSE)
  284. {
  285. // Allow graceful error handling
  286. try
  287. {
  288. // If is file
  289. if ($file->isFile())
  290. {
  291. try
  292. {
  293. // Try to delete
  294. unlink($file->getRealPath());
  295. }
  296. catch (ErrorException $e)
  297. {
  298. // Catch any delete file warnings
  299. if ($e->getCode() === E_WARNING)
  300. {
  301. throw new Kohana_Cache_Exception(__METHOD__.' failed to delete file : :file', array(':file' => $file->getRealPath()));
  302. }
  303. }
  304. }
  305. // Else, is directory
  306. else if ($file->isDir())
  307. {
  308. // Create new DirectoryIterator
  309. $files = new DirectoryIterator($file->getPathname());
  310. // Iterate over each entry
  311. while ($files->valid())
  312. {
  313. // Extract the entry name
  314. $name = $files->getFilename();
  315. // If the name is not a dot
  316. if ($name != '.' and $name != '..')
  317. {
  318. // Create new file resource
  319. $fp = new SplFileInfo($files->getRealPath());
  320. // Delete the file
  321. $this->_delete_file($fp);
  322. }
  323. // Move the file pointer on
  324. $files->next();
  325. }
  326. // If set to retain parent directory, return now
  327. if ($retain_parent_directory)
  328. {
  329. return TRUE;
  330. }
  331. try
  332. {
  333. // Remove the files iterator
  334. // (fixes Windows PHP which has permission issues with open iterators)
  335. unset($files);
  336. // Try to remove the parent directory
  337. return rmdir($file->getRealPath());
  338. }
  339. catch (ErrorException $e)
  340. {
  341. // Catch any delete directory warnings
  342. if ($e->getCode() === E_WARNING)
  343. {
  344. throw new Kohana_Cache_Exception(__METHOD__.' failed to delete directory : :directory', array(':directory' => $file->getRealPath()));
  345. }
  346. }
  347. }
  348. }
  349. // Catch all exceptions
  350. catch (Exception $e)
  351. {
  352. // If ignore_errors is on
  353. if ($ignore_errors === TRUE)
  354. {
  355. // Return
  356. return FALSE;
  357. }
  358. // Throw exception
  359. throw $e;
  360. }
  361. }
  362. /**
  363. * Resolves the cache directory real path from the filename
  364. *
  365. * // Get the realpath of the cache folder
  366. * $realpath = $this->_resolve_directory($filename);
  367. *
  368. * @param string filename to resolve
  369. * @return string
  370. */
  371. protected function _resolve_directory($filename)
  372. {
  373. return $this->_cache_dir->getRealPath().DIRECTORY_SEPARATOR.$filename[0].$filename[1].DIRECTORY_SEPARATOR;
  374. }
  375. }