PageRenderTime 53ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://github.com/enygma/fuel
PHP | 475 lines | 198 code | 68 blank | 209 comment | 23 complexity | 8d6a9d1b4fbf6d0207109fd379d76421 MD5 | raw file
  1. <?php
  2. /**
  3. * Fuel
  4. *
  5. * Fuel is a fast, lightweight, community driven PHP5 framework.
  6. *
  7. * @package Fuel
  8. * @version 1.0
  9. * @author Fuel Development Team
  10. * @license MIT License
  11. * @copyright 2010 Dan Horrigan
  12. * @link http://fuelphp.com
  13. */
  14. namespace Fuel\Core;
  15. abstract class Cache_Storage_Driver {
  16. /**
  17. * @var array defines which class properties are gettable with get_... in the __call() method
  18. */
  19. protected static $_gettable = array('created', 'expiration', 'dependencies', 'identifier');
  20. /**
  21. * @var array defines which class properties are settable with set_... in the __call() method
  22. */
  23. protected static $_settable = array('expiration', 'dependencies', 'identifier');
  24. /**
  25. * @var string name of the content handler driver
  26. */
  27. protected $content_handler = null;
  28. /**
  29. * @var Cache_Handler_Driver handles and formats the cache's contents
  30. */
  31. protected $handler_object = null;
  32. /**
  33. * @var string the cache's name, either string or md5'd serialization of something else
  34. */
  35. protected $identifier = null;
  36. /**
  37. * @var int timestamp of creation of the cache
  38. */
  39. protected $created = null;
  40. /**
  41. * @var int timestamp when this cache will expire
  42. */
  43. protected $expiration = null;
  44. /**
  45. * @var array contains identifiers of other caches this one depends on
  46. */
  47. protected $dependencies = array();
  48. /**
  49. * @var mixed the contents of this
  50. */
  51. protected $contents = null;
  52. /**
  53. * @var string loaded driver
  54. */
  55. protected $driver = null;
  56. // ---------------------------------------------------------------------
  57. /**
  58. * Abstract method that should take care of the storage engine specific reading. Needs to set the object properties:
  59. * - created
  60. * - expiration
  61. * - dependencies
  62. * - contents
  63. * - content_handler
  64. *
  65. * @access protected
  66. * @return bool success of the operation
  67. */
  68. abstract protected function _get();
  69. // ---------------------------------------------------------------------
  70. /**
  71. * Abstract method that should take care of the storage engine specific writing. Needs to write the object properties:
  72. * - created
  73. * - expiration
  74. * - dependencies
  75. * - contents
  76. * - content_handler
  77. *
  78. * @access protected
  79. */
  80. abstract protected function _set();
  81. // ---------------------------------------------------------------------
  82. /**
  83. * Should delete this cache instance, should also run reset() afterwards
  84. *
  85. * @access public
  86. */
  87. abstract public function delete();
  88. // ---------------------------------------------------------------------
  89. /**
  90. * Flushes the whole cache for a specific storage type or just a part of it when $section is set
  91. * (might not work with all storage drivers), defaults to the default storage type
  92. *
  93. * @access public
  94. * @param string
  95. */
  96. abstract public function delete_all($section);
  97. // ---------------------------------------------------------------------
  98. /**
  99. * Should check all dependencies against the creation timestamp.
  100. * This is static to make it possible in the future to check dependencies from other storages then the current one,
  101. * though I don't have a clue yet how to make that possible.
  102. *
  103. * @access protected
  104. * @return bool either true or false on any failure
  105. */
  106. abstract public function check_dependencies(Array $dependencies);
  107. // ---------------------------------------------------------------------
  108. /**
  109. * Default constructor, any extension should either load this first or act similar
  110. *
  111. * @param string the identifier for this cache
  112. * @param array additional config values
  113. */
  114. public function __construct($identifier, $config)
  115. {
  116. $this->identifier = $identifier;
  117. // fetch options from config and set them
  118. $this->expiration = array_key_exists('expiration', $config) ? $config['expiration'] : null;
  119. $this->dependencies = array_key_exists('dependencies', $config) ? $config['dependencies'] : array();
  120. $this->content_handler = array_key_exists('content_handler', $config) ? new $config['content_handler']() : null;
  121. $this->driver = array_key_exists('driver', $config) ? $config['driver'] : 'file';
  122. }
  123. // ---------------------------------------------------------------------
  124. /**
  125. * Allows for default getting and setting
  126. *
  127. * @access public
  128. * @param string
  129. * @param array
  130. * @return void|mixed
  131. */
  132. public function __call($method, $args = array())
  133. {
  134. // Allow getting any properties set in static::$_gettable
  135. if (substr($method, 0, 3) == 'get')
  136. {
  137. $name = substr($method, 4);
  138. if (in_array($name, static::$_gettable))
  139. {
  140. return $this->{$name};
  141. }
  142. else
  143. {
  144. throw new \Cache_Exception('This property doesn\'t exist or can\'t be read.');
  145. }
  146. }
  147. // Allow setting any properties set in static::$_settable
  148. elseif (substr($method, 0, 3) == 'set')
  149. {
  150. $name = substr($method, 4);
  151. if (in_array($name, static::$_settable))
  152. {
  153. $this->{$name} = @$args[0];
  154. }
  155. else
  156. {
  157. throw new \Cache_Exception('This property doesn\'t exist or can\'t be set.');
  158. }
  159. return $this;
  160. }
  161. else
  162. {
  163. throw new \Cache_Exception('Illigal method call');
  164. }
  165. }
  166. // ---------------------------------------------------------------------
  167. /**
  168. * Converts the identifier to a string when necessary:
  169. * A int is just converted to a string, all others are serialized and then md5'd
  170. *
  171. * @access public
  172. * @param mixed
  173. */
  174. public static function stringify_identifier($identifier)
  175. {
  176. // Identifier may not be empty, but can be false or 0
  177. if ($identifier === '' || $identifier === null)
  178. {
  179. throw new \Cache_Exception('The identifier cannot be empty, must contain a value of any kind other than null or an empty string.');
  180. }
  181. // In case of string or int just return it as a string
  182. if (is_string($identifier) || is_int($identifier))
  183. {
  184. // cleanup to only allow alphanum chars, dashes, dots & underscores
  185. if (preg_match('/^([a-z0-9_\.\-]*)$/iuD', $identifier) === 0)
  186. {
  187. throw new \Cache_Exception('Cache identifier can only contain alphanumeric characters, underscores, dashes & dots.');
  188. }
  189. return (string) $identifier;
  190. }
  191. // In case of array, bool or object return the md5 of the $identifier's serialization
  192. else
  193. {
  194. return '_hashes.'.md5(serialize($identifier));
  195. }
  196. }
  197. // ---------------------------------------------------------------------
  198. /**
  199. * Resets all properties except for the identifier, should be run by default when a delete() is triggered
  200. *
  201. * @access public
  202. */
  203. public function reset()
  204. {
  205. $this->contents = NULL;
  206. $this->created = NULL;
  207. $this->expiration = NULL;
  208. $this->dependencies = array();
  209. $this->content_handler = NULL;
  210. $this->handler_object = NULL;
  211. }
  212. // ---------------------------------------------------------------------
  213. /**
  214. * Front for writing the cache, ensures interchangebility of storage engines. Actual writing
  215. * is being done by the _set() method which needs to be extended.
  216. *
  217. * @access public
  218. * @param mixed The content to be cached
  219. * @param int The time in minutes until the cache will expire, =< 0 or null means no expiration
  220. * @param array Array of names on which this cache depends for
  221. * @return object The new request
  222. */
  223. final public function set($contents = null, $expiration = null, $dependencies = array())
  224. {
  225. // save the current expiration
  226. $current_expiration = $this->expiration;
  227. // Use either the given value or the class property
  228. if ( ! is_null($contents)) $this->set_contents($contents);
  229. $this->expiration = ( ! is_null($expiration)) ? $expiration : $this->expiration;
  230. $this->dependencies = ( ! empty($dependencies)) ? $dependencies : $this->dependencies;
  231. // Create expiration timestamp when other then null
  232. if ( ! is_null($this->expiration))
  233. {
  234. if ( ! is_numeric($this->expiration))
  235. {
  236. throw new \Cache_Exception('Expiration must be a valid number.');
  237. }
  238. $this->expiration = time() + intval($this->expiration) * 60;
  239. }
  240. // Convert dependency identifiers to string when set
  241. $this->dependencies = ( ! is_array($this->dependencies)) ? array($this->dependencies) : $this->dependencies;
  242. if ( ! empty( $this->dependencies ) )
  243. {
  244. foreach($this->dependencies as $key => $id)
  245. {
  246. $this->dependencies[$key] = $this->stringify_identifier($id);
  247. }
  248. }
  249. $this->created = time();
  250. // Turn everything over to the storage specific method
  251. $this->_set();
  252. // restore the expiration
  253. $this->expiration = $current_expiration;
  254. }
  255. // ---------------------------------------------------------------------
  256. /**
  257. * Front for reading the cache, ensures interchangebility of storage engines. Actual reading
  258. * is being done by the _get() method which needs to be extended.
  259. *
  260. * @access public
  261. * @param bool
  262. * @return mixed
  263. */
  264. final public function get($use_expiration = true)
  265. {
  266. if ( ! $this->_get())
  267. {
  268. throw new \Cache_Exception('not found');
  269. }
  270. if ($use_expiration)
  271. {
  272. if (! is_null($this->expiration) and $this->expiration < 0)
  273. {
  274. $this->delete();
  275. throw new \Cache_Exception('expired');
  276. }
  277. // Check dependencies and handle as expired on failure
  278. if ( ! $this->check_dependencies($this->dependencies))
  279. {
  280. $this->delete();
  281. throw new \Cache_Exception('expired');
  282. }
  283. }
  284. return $this->get_contents();
  285. }
  286. // ---------------------------------------------------------------------
  287. /**
  288. * Does get() & set() in one call that takes a callback and it's arguements to generate the contents
  289. *
  290. * @access public
  291. * @param string|array Valid PHP callback
  292. * @param array Arguements for the above function/method
  293. * @param int Cache expiration in minutes
  294. * @param array Contains the identifiers of caches this one will depend on
  295. */
  296. final public function call($callback, $args = array(), $expiration = null, $dependencies = array())
  297. {
  298. try
  299. {
  300. $this->get();
  301. }
  302. catch (Cache_Exception $e)
  303. {
  304. // Create the contents
  305. $contents = call_user_func_array($callback, $args);
  306. $this->set($contents, $expiration, $dependencies);
  307. }
  308. return $this->get_contents();
  309. }
  310. // ---------------------------------------------------------------------
  311. /**
  312. * Set the contents with optional handler instead of the default
  313. *
  314. * @access public
  315. * @param mixed
  316. * @param string
  317. */
  318. public function set_contents($contents, $handler = NULL)
  319. {
  320. $this->contents = $contents;
  321. $this->set_content_handler($handler);
  322. $this->contents = $this->handle_writing($contents);
  323. return $this;
  324. }
  325. // ---------------------------------------------------------------------
  326. /**
  327. * Fetches contents
  328. *
  329. * @access public
  330. * @return mixed
  331. */
  332. public function get_contents()
  333. {
  334. return $this->handle_reading($this->contents);
  335. }
  336. // ---------------------------------------------------------------------
  337. /**
  338. * Decides a content handler that makes it possible to write non-strings to a file
  339. *
  340. * @access protected
  341. * @param string
  342. */
  343. protected function set_content_handler($handler)
  344. {
  345. $this->handler_object = null;
  346. $this->content_handler = (string) $handler;
  347. return $this;
  348. }
  349. // ---------------------------------------------------------------------
  350. /**
  351. * Gets a specific content handler
  352. *
  353. * @access public
  354. * @param string
  355. * @return Cache_Handler_Driver
  356. */
  357. public function get_content_handler($handler = null)
  358. {
  359. if ( ! empty($this->handler_object))
  360. {
  361. return $this->handler_object;
  362. }
  363. // When not yet set, use $handler or detect the prefered handler (string = string, otherwise serialize)
  364. if (empty($this->content_handler) && empty($handler))
  365. {
  366. if ( ! empty($handler))
  367. {
  368. $this->content_handler = $handler;
  369. }
  370. if (is_string($this->contents))
  371. {
  372. $this->content_handler = \Config::get('cache.string_handler', 'string');
  373. }
  374. else
  375. {
  376. $type = is_object($this->contents) ? get_class($this->contents) : gettype($this->contents);
  377. $this->content_handler = \Config::get('cache.'.$type.'_handler', 'serialized');
  378. }
  379. }
  380. $class = 'Cache_Handler_'.ucfirst($this->content_handler);
  381. $this->handler_object = new $class();
  382. return $this->handler_object;
  383. }
  384. // ---------------------------------------------------------------------
  385. /**
  386. * Converts the contents the cachable format
  387. *
  388. * @access protected
  389. * @return string
  390. */
  391. protected function handle_writing($contents)
  392. {
  393. return $this->get_content_handler()->writable($contents);
  394. }
  395. // ---------------------------------------------------------------------
  396. /**
  397. * Converts the cachable format to the original value
  398. *
  399. * @access protected
  400. * @return mixed
  401. */
  402. protected function handle_reading($contents)
  403. {
  404. return $this->get_content_handler()->readable($contents);
  405. }
  406. }
  407. /* End of file driver.php */