PageRenderTime 69ms CodeModel.GetById 41ms RepoModel.GetById 0ms app.codeStats 0ms

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

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