PageRenderTime 56ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/classes/cache/storage/driver.php

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