PageRenderTime 39ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/fuel/core/classes/cache/storage/file.php

https://bitbucket.org/codeyash/bootstrap
PHP | 333 lines | 197 code | 51 blank | 85 comment | 23 complexity | f93c6fda68cc3d3d9f3a4fad9620ffce MD5 | raw file
Possible License(s): MIT, Apache-2.0
  1. <?php
  2. /**
  3. * Part of the Fuel framework.
  4. *
  5. * @package Fuel
  6. * @version 1.5
  7. * @author Fuel Development Team
  8. * @license MIT License
  9. * @copyright 2010 - 2013 Fuel Development Team
  10. * @link http://fuelphp.com
  11. */
  12. namespace Fuel\Core;
  13. class Cache_Storage_File extends \Cache_Storage_Driver
  14. {
  15. /**
  16. * @const string Tag used for opening & closing cache properties
  17. */
  18. const PROPS_TAG = 'Fuel_Cache_Properties';
  19. /**
  20. * @var string File caching basepath
  21. */
  22. protected static $path = '';
  23. /**
  24. * @var array driver specific configuration
  25. */
  26. protected $config = array();
  27. public function __construct($identifier, $config)
  28. {
  29. parent::__construct($identifier, $config);
  30. $this->config = isset($config['file']) ? $config['file'] : array();
  31. // check for an expiration override
  32. $this->expiration = $this->_validate_config('expiration', isset($this->config['expiration'])
  33. ? $this->config['expiration'] : $this->expiration);
  34. // determine the file cache path
  35. static::$path = !empty($this->config['path'])
  36. ? $this->config['path'] : \Config::get('cache_dir', APPPATH.'cache'.DS);
  37. if ( ! is_dir(static::$path) || ! is_writable(static::$path))
  38. {
  39. throw new \FuelException('Cache directory does not exist or is not writable.');
  40. }
  41. }
  42. /**
  43. * Translates a given identifier to a valid path
  44. *
  45. * @param string
  46. * @return string
  47. */
  48. protected function identifier_to_path($identifier)
  49. {
  50. // replace dots with dashes
  51. $identifier = str_replace('.', DS, $identifier);
  52. return $identifier;
  53. }
  54. /**
  55. * Prepend the cache properties
  56. *
  57. * @return string
  58. */
  59. protected function prep_contents()
  60. {
  61. $properties = array(
  62. 'created' => $this->created,
  63. 'expiration' => $this->expiration,
  64. 'dependencies' => $this->dependencies,
  65. 'content_handler' => $this->content_handler
  66. );
  67. $properties = '{{'.self::PROPS_TAG.'}}'.json_encode($properties).'{{/'.self::PROPS_TAG.'}}';
  68. return $properties.$this->contents;
  69. }
  70. /**
  71. * Remove the prepended cache properties and save them in class properties
  72. *
  73. * @param string
  74. * @throws UnexpectedValueException
  75. */
  76. protected function unprep_contents($payload)
  77. {
  78. $properties_end = strpos($payload, '{{/'.self::PROPS_TAG.'}}');
  79. if ($properties_end === false)
  80. {
  81. throw new \UnexpectedValueException('Cache has bad formatting');
  82. }
  83. $this->contents = substr($payload, $properties_end + strlen('{{/'.self::PROPS_TAG.'}}'));
  84. $props = substr(substr($payload, 0, $properties_end), strlen('{{'.self::PROPS_TAG.'}}'));
  85. $props = json_decode($props, true);
  86. if ($props === null)
  87. {
  88. throw new \UnexpectedValueException('Cache properties retrieval failed');
  89. }
  90. $this->created = $props['created'];
  91. $this->expiration = is_null($props['expiration']) ? null : (int) ($props['expiration'] - time());
  92. $this->dependencies = $props['dependencies'];
  93. $this->content_handler = $props['content_handler'];
  94. }
  95. /**
  96. * Check if other caches or files have been changed since cache creation
  97. *
  98. * @param array
  99. * @return bool
  100. */
  101. public function check_dependencies(array $dependencies)
  102. {
  103. foreach($dependencies as $dep)
  104. {
  105. if (file_exists($file = static::$path.str_replace('.', DS, $dep).'.cache'))
  106. {
  107. $filemtime = filemtime($file);
  108. if ($filemtime === false || $filemtime > $this->created)
  109. {
  110. return false;
  111. }
  112. }
  113. elseif (file_exists($dep))
  114. {
  115. $filemtime = filemtime($file);
  116. if ($filemtime === false || $filemtime > $this->created)
  117. {
  118. return false;
  119. }
  120. }
  121. else
  122. {
  123. return false;
  124. }
  125. }
  126. return true;
  127. }
  128. /**
  129. * Delete Cache
  130. */
  131. public function delete()
  132. {
  133. if (file_exists($file = static::$path.$this->identifier_to_path($this->identifier).'.cache'))
  134. {
  135. unlink($file);
  136. $this->reset();
  137. }
  138. }
  139. // ---------------------------------------------------------------------
  140. /**
  141. * Purge all caches
  142. *
  143. * @param limit purge to subsection
  144. * @return bool
  145. */
  146. public function delete_all($section)
  147. {
  148. $path = rtrim(static::$path, '\\/').DS;
  149. $section = static::identifier_to_path($section);
  150. $files = \File::read_dir($path.$section, -1, array('\.cache$' => 'file'));
  151. $delete = function($path, $files) use(&$delete, &$section)
  152. {
  153. $path = rtrim($path, '\\/').DS;
  154. foreach ($files as $dir => $file)
  155. {
  156. if (is_numeric($dir))
  157. {
  158. if ( ! $result = \File::delete($path.$file))
  159. {
  160. return $result;
  161. }
  162. }
  163. else
  164. {
  165. if ( ! $result = ($delete($path.$dir, $file) and rmdir($path.$dir)))
  166. {
  167. return $result;
  168. }
  169. }
  170. }
  171. $section !== '' and rmdir($path);
  172. return true;
  173. };
  174. return $delete($path.$section, $files);
  175. }
  176. /**
  177. * Save a cache, this does the generic pre-processing
  178. *
  179. * @return bool success
  180. */
  181. protected function _set()
  182. {
  183. $payload = $this->prep_contents();
  184. $id_path = $this->identifier_to_path($this->identifier);
  185. // create directory if necessary
  186. $subdirs = explode(DS, $id_path);
  187. if (count($subdirs) > 1)
  188. {
  189. array_pop($subdirs);
  190. $test_path = static::$path.implode(DS, $subdirs);
  191. // check if specified subdir exists
  192. if ( ! @is_dir($test_path))
  193. {
  194. // create non existing dir
  195. if ( ! @mkdir($test_path, 0755, true))
  196. {
  197. return false;
  198. }
  199. }
  200. }
  201. // write the cache
  202. $file = static::$path.$id_path.'.cache';
  203. $handle = fopen($file, 'c');
  204. if ( ! $handle)
  205. {
  206. return false;
  207. }
  208. // wait for a lock
  209. while ( ! flock($handle, LOCK_EX));
  210. // truncate the file
  211. ftruncate($handle, 0);
  212. // write the session data
  213. fwrite($handle, $payload);
  214. //release the lock
  215. flock($handle, LOCK_UN);
  216. // close the file
  217. fclose($handle);
  218. return true;
  219. }
  220. /**
  221. * Load a cache, this does the generic post-processing
  222. *
  223. * @return bool success
  224. */
  225. protected function _get()
  226. {
  227. $id_path = $this->identifier_to_path( $this->identifier );
  228. $file = static::$path.$id_path.'.cache';
  229. if ( ! file_exists($file))
  230. {
  231. return false;
  232. }
  233. $handle = fopen($file, 'r');
  234. if ( ! $handle)
  235. {
  236. return false;
  237. }
  238. // wait for a lock
  239. while( ! flock($handle, LOCK_SH));
  240. // read the session data
  241. $payload = fread($handle, filesize($file));
  242. //release the lock
  243. flock($handle, LOCK_UN);
  244. // close the file
  245. fclose($handle);
  246. try
  247. {
  248. $this->unprep_contents($payload);
  249. }
  250. catch (\UnexpectedValueException $e)
  251. {
  252. return false;
  253. }
  254. return true;
  255. }
  256. /**
  257. * validate a driver config value
  258. *
  259. * @param string name of the config variable to validate
  260. * @param mixed value
  261. * @return mixed
  262. */
  263. private function _validate_config($name, $value)
  264. {
  265. switch ($name)
  266. {
  267. case 'cache_id':
  268. if (empty($value) or ! is_string($value))
  269. {
  270. $value = 'fuel';
  271. }
  272. break;
  273. case 'expiration':
  274. if (empty($value) or ! is_numeric($value))
  275. {
  276. $value = null;
  277. }
  278. break;
  279. }
  280. return $value;
  281. }
  282. }