PageRenderTime 42ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://bitbucket.org/sriedel/iccrm-wip
PHP | 329 lines | 195 code | 49 blank | 85 comment | 23 complexity | 36617cb897a993e515359e5dd7b4614d MD5 | raw file
Possible License(s): MIT
  1. <?php
  2. /**
  3. * Part of the Fuel framework.
  4. *
  5. * @package Fuel
  6. * @version 1.0
  7. * @author Fuel Development Team
  8. * @license MIT License
  9. * @copyright 2010 - 2012 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)
  152. {
  153. foreach ($files as $dir => $file)
  154. {
  155. if (is_numeric($dir))
  156. {
  157. if ( ! $result = \File::delete($path.$file))
  158. {
  159. return $result;
  160. }
  161. }
  162. else
  163. {
  164. if ( ! $result = ($delete($path.$dir, $file) and rmdir($path.$dir)))
  165. {
  166. return $result;
  167. }
  168. }
  169. }
  170. return true;
  171. };
  172. return $delete($path.$section, $files);
  173. }
  174. /**
  175. * Save a cache, this does the generic pre-processing
  176. *
  177. * @return bool success
  178. */
  179. protected function _set()
  180. {
  181. $payload = $this->prep_contents();
  182. $id_path = $this->identifier_to_path($this->identifier);
  183. // create directory if necessary
  184. $subdirs = explode(DS, $id_path);
  185. if (count($subdirs) > 1)
  186. {
  187. array_pop($subdirs);
  188. $test_path = static::$path.implode(DS, $subdirs);
  189. // check if specified subdir exists
  190. if ( ! @is_dir($test_path))
  191. {
  192. // create non existing dir
  193. if ( ! @mkdir($test_path, 0755, true))
  194. {
  195. return false;
  196. }
  197. }
  198. }
  199. // write the cache
  200. $file = static::$path.$id_path.'.cache';
  201. $handle = fopen($file, 'c');
  202. if ( ! $handle)
  203. {
  204. return false;
  205. }
  206. // wait for a lock
  207. while ( ! flock($handle, LOCK_EX));
  208. // truncate the file
  209. ftruncate($handle, 0);
  210. // write the session data
  211. fwrite($handle, $payload);
  212. //release the lock
  213. flock($handle, LOCK_UN);
  214. // close the file
  215. fclose($handle);
  216. return true;
  217. }
  218. /**
  219. * Load a cache, this does the generic post-processing
  220. *
  221. * @return bool success
  222. */
  223. protected function _get()
  224. {
  225. $id_path = $this->identifier_to_path( $this->identifier );
  226. $file = static::$path.$id_path.'.cache';
  227. if ( ! file_exists($file))
  228. {
  229. return false;
  230. }
  231. $handle = fopen($file, 'r');
  232. if ( ! $handle)
  233. {
  234. return false;
  235. }
  236. // wait for a lock
  237. while( ! flock($handle, LOCK_SH));
  238. // read the session data
  239. $payload = fread($handle, filesize($file));
  240. //release the lock
  241. flock($handle, LOCK_UN);
  242. // close the file
  243. fclose($handle);
  244. try
  245. {
  246. $this->unprep_contents($payload);
  247. }
  248. catch (\UnexpectedValueException $e)
  249. {
  250. return false;
  251. }
  252. return true;
  253. }
  254. /**
  255. * validate a driver config value
  256. *
  257. * @param string name of the config variable to validate
  258. * @param mixed value
  259. * @return mixed
  260. */
  261. private function _validate_config($name, $value)
  262. {
  263. switch ($name)
  264. {
  265. case 'cache_id':
  266. if (empty($value) or ! is_string($value))
  267. {
  268. $value = 'fuel';
  269. }
  270. break;
  271. case 'expiration':
  272. if (empty($value) or ! is_numeric($value))
  273. {
  274. $value = null;
  275. }
  276. break;
  277. }
  278. return $value;
  279. }
  280. }