Container.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | PEAR :: Cache |
  4. // +----------------------------------------------------------------------+
  5. // | Copyright (c) 1997-2003 The PHP Group |
  6. // +----------------------------------------------------------------------+
  7. // | This source file is subject to version 2.0 of the PHP license, |
  8. // | that is bundled with this package in the file LICENSE, and is |
  9. // | available at through the world-wide-web at |
  10. // | http://www.php.net/license/2_02.txt. |
  11. // | If you did not receive a copy of the PHP license and are unable to |
  12. // | obtain it through the world-wide-web, please send a note to |
  13. // | license@php.net so we can mail you a copy immediately. |
  14. // +----------------------------------------------------------------------+
  15. // | Authors: Ulf Wendel <ulf.wendel@phpdoc.de> |
  16. // | Sebastian Bergmann <sb@sebastian-bergmann.de> |
  17. // | Christian Stocker <chregu@phant.ch> |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: Container.php,v 1.4 2003/01/04 11:54:45 mj Exp $
  21. require_once 'Cache/Error.php';
  22. /**
  23. * Common base class of all cache storage container.
  24. *
  25. * To speed up things we do a preload you should know about, otherwise it might
  26. * play you a trick. The Cache controller classes (Cache/Cache, Cache/Output, ...)
  27. * usually do something like is (isCached($id) && !isExpired($id)) return $container->load($id).
  28. * if you implement isCached(), isExpired() and load() straight ahead, each of this
  29. * functions will result in a storage medium (db, file,...) access. This generates too much load.
  30. * Now, a simple speculative preload should saves time in most cases. Whenever
  31. * one of the mentioned methods is invoked we preload the cached dataset into class variables.
  32. * That means that we have only one storage medium access for the sequence
  33. * (isCached($id) && !isExpired($id)) return $container->load($id).
  34. * The bad thing is that the preloaded data might be outdated meanwhile, which is
  35. * unlikely but for you power users, be warned. If you do not want the preload
  36. * you should switch it off by setting the class variable $preload to false. Anyway, this is
  37. * not recommended!
  38. *
  39. * @author Ulf Wendel <ulf.wendel@phpdoc.de>
  40. * @version $Id: Container.php,v 1.4 2003/01/04 11:54:45 mj Exp $
  41. * @package Cache
  42. * @access public
  43. * @abstract
  44. */
  45. class Cache_Container {
  46. /**
  47. * Flag indicating wheter to preload datasets.
  48. *
  49. * See the class description for more details.
  50. *
  51. * @var boolean
  52. */
  53. var $preload = true;
  54. /**
  55. * ID of a preloaded dataset
  56. *
  57. * @var string
  58. */
  59. var $id = '';
  60. /**
  61. * Cache group of a preloaded dataset
  62. *
  63. * @var string
  64. */
  65. var $group = '';
  66. /**
  67. * Expiration timestamp of a preloaded dataset.
  68. *
  69. * @var integer 0 means never, endless
  70. */
  71. var $expires = 0;
  72. /**
  73. * Value of a preloaded dataset.
  74. *
  75. * @var string
  76. */
  77. var $cachedata = '';
  78. /**
  79. * Preloaded userdata field.
  80. *
  81. * @var string
  82. */
  83. var $userdata = '';
  84. /**
  85. * Flag indicating that the dataset requested for preloading is unknown.
  86. *
  87. * @var boolean
  88. */
  89. var $unknown = true;
  90. /**
  91. * Encoding mode for cache data: base64 or addslashes() (slash).
  92. *
  93. * @var string base64 or slash
  94. */
  95. var $encoding_mode = 'base64';
  96. /**
  97. * Highwater mark - maximum space required by all cache entries.
  98. *
  99. * Whenever the garbage collection runs it checks the amount of space
  100. * required by all cache entries. If it's more than n (highwater) bytes
  101. * the garbage collection deletes as many entries as necessary to reach the
  102. * lowwater mark.
  103. *
  104. * @var int
  105. * @see lowwater
  106. */
  107. var $highwater = 2048000;
  108. /**
  109. * Lowwater mark
  110. *
  111. * @var int
  112. * @see highwater
  113. */
  114. var $lowwater = 1536000;
  115. /**
  116. * Options that can be set in every derived class using it's constructor.
  117. *
  118. * @var array
  119. */
  120. var $allowed_options = array('encoding_mode', 'highwater', 'lowwater');
  121. /**
  122. * Loads a dataset from the cache.
  123. *
  124. * @param string dataset ID
  125. * @param string cache group
  126. * @return mixed dataset value or NULL on failure
  127. * @access public
  128. */
  129. function load($id, $group) {
  130. if ($this->preload) {
  131. if ($this->id != $id || $this->group != $group)
  132. $this->preload($id, $group);
  133. return $this->cachedata;
  134. } else {
  135. list( , $data, ) = $this->fetch($id, $group);
  136. return $data;
  137. }
  138. } // end func load
  139. /**
  140. * Returns the userdata field of a cached data set.
  141. *
  142. * @param string dataset ID
  143. * @param string cache group
  144. * @return string userdata
  145. * @access public
  146. */
  147. function getUserdata($id, $group) {
  148. if ($this->preload) {
  149. if ($this->id != $id || $this->group != $group)
  150. $this->preload($id, $group);
  151. return $this->userdata;
  152. } else {
  153. list( , , $userdata) = $this->fetch($id, $group);
  154. return $userdata;
  155. }
  156. } // end func getUserdata
  157. /**
  158. * Checks if a dataset is expired.
  159. *
  160. * @param string dataset ID
  161. * @param string cache group
  162. * @param integer maximum age timestamp
  163. * @return boolean
  164. * @access public
  165. */
  166. function isExpired($id, $group, $max_age) {
  167. if ($this->preload) {
  168. if ($this->id != $id || $this->group != $group)
  169. $this->preload($id, $group);
  170. if ($this->unknown)
  171. return false;
  172. } else {
  173. // check if at all it is cached
  174. if (!$this->isCached($id, $group))
  175. return false;
  176. // I'm lazy...
  177. list($this->expires, , ) = $this->fetch($id, $group);
  178. }
  179. // endless
  180. if (0 == $this->expires)
  181. return false;
  182. // you feel fine, Ulf?
  183. if ($expired = ($this->expires <= time() || ($max_age && ($this->expires <= $max_age))) ) {
  184. $this->remove($id, $group);
  185. $this->flushPreload();
  186. }
  187. return $expired;
  188. } // end func isExpired
  189. /**
  190. * Checks if a dataset is cached.
  191. *
  192. * @param string dataset ID
  193. * @param string cache group
  194. * @return boolean
  195. */
  196. function isCached($id, $group) {
  197. if ($this->preload) {
  198. if ($this->id != $id || $this->group != $group)
  199. $this->preload($id, $group);
  200. return !($this->unknown);
  201. } else {
  202. return $this->idExists($id, $group);
  203. }
  204. } // end func isCached
  205. //
  206. // abstract methods
  207. //
  208. /**
  209. * Fetches a dataset from the storage medium.
  210. *
  211. * @param string dataset ID
  212. * @param string cache group
  213. * @return array format: [expire date, cached data, user data]
  214. * @throws Cache_Error
  215. * @abstract
  216. */
  217. function fetch($id, $group) {
  218. return array(NULL, NULL, NULL);
  219. } // end func fetch
  220. /**
  221. * Stores a dataset.
  222. *
  223. * @param string dataset ID
  224. * @param mixed data to store
  225. * @param mixed userdefined expire date
  226. * @param string cache group
  227. * @param string additional userdefined data
  228. * @return boolean
  229. * @throws Cache_Error
  230. * @access public
  231. * @abstract
  232. */
  233. function save($id, $data, $expire, $group, $userdata) {
  234. // QUESTION: Should we update the preload buffer instead?
  235. // Don't think so as the sequence save()/load() is unlikely.
  236. $this->flushPreload($id, $group);
  237. return NULL;
  238. } // end func save
  239. /**
  240. * Removes a dataset.
  241. *
  242. * @param string dataset ID
  243. * @param string cache group
  244. * @return boolean
  245. * @access public
  246. * @abstract
  247. */
  248. function remove($id, $group) {
  249. $this->flushPreload($id, $group);
  250. return NULL;
  251. } // end func remove
  252. /**
  253. * Flushes the cache - removes all caches datasets from the cache.
  254. *
  255. * @param string If a cache group is given only the group will be flushed
  256. * @return integer Number of removed datasets, -1 on failure
  257. * @access public
  258. * @abstract
  259. */
  260. function flush($group) {
  261. $this->flushPreload();
  262. return NULL;
  263. } // end func flush
  264. /**
  265. * Checks if a dataset exists.
  266. *
  267. * @param string dataset ID
  268. * @param string cache group
  269. * @return boolean
  270. * @access public
  271. * @abstract
  272. */
  273. function idExists($id, $group) {
  274. return NULL;
  275. } // end func idExists
  276. /**
  277. * Starts the garbage collection.
  278. *
  279. * @access public
  280. * @abstract
  281. */
  282. function garbageCollection() {
  283. $this->flushPreload();
  284. } // end func garbageCollection
  285. /**
  286. * Does a speculative preload of a dataset
  287. *
  288. * @param string dataset ID
  289. * @param string cache group
  290. * @return boolean
  291. */
  292. function preload($id, $group) {
  293. // whatever happens, remember the preloaded ID
  294. $this->id = $id;
  295. $this->group = $group;
  296. list($this->expires, $this->cachedata, $this->userdata) = $this->fetch($id, $group);
  297. if (NULL === $this->expires) {
  298. // Uuups, unknown ID
  299. $this->flushPreload();
  300. return false;
  301. }
  302. $this->unknown = false;
  303. return true;
  304. } // end func preload
  305. /**
  306. * Flushes the internal preload buffer.
  307. *
  308. * save(), remove() and flush() must call this method
  309. * to preevent differences between the preloaded values and
  310. * the real cache contents.
  311. *
  312. * @param string dataset ID, if left out the preloaded values will be flushed.
  313. * If given the preloaded values will only be flushed if they are
  314. * equal to the given id and group
  315. * @param string cache group
  316. * @see preload()
  317. */
  318. function flushPreload($id = '', $group = 'default') {
  319. if (!$id || ($this->id == $id && $this->group == $group)) {
  320. // clear the internal preload values
  321. $this->id = '';
  322. $this->group = '';
  323. $this->cachedata = '';
  324. $this->userdata = '';
  325. $this->expires = -1;
  326. $this->unknown = true;
  327. }
  328. } // end func flushPreload
  329. /**
  330. * Imports the requested datafields as object variables if allowed
  331. *
  332. * @param array List of fields to be imported as object variables
  333. * @param array List of allowed datafields
  334. */
  335. function setOptions($requested, $allowed) {
  336. foreach ($allowed as $k => $field)
  337. if (isset($requested[$field]))
  338. $this->$field = $requested[$field];
  339. } // end func setOptions
  340. /**
  341. * Encodes the data for the storage container.
  342. *
  343. * @var mixed data to encode
  344. */
  345. function encode($data) {
  346. if ('base64' == $this->encoding_mode)
  347. return base64_encode(serialize($data));
  348. else
  349. return serialize($data);
  350. } // end func encode
  351. /**
  352. * Decodes the data from the storage container.
  353. *
  354. * @var mixed
  355. */
  356. function decode($data) {
  357. if ('base64' == $this->encoding_mode)
  358. return unserialize(base64_decode($data));
  359. else
  360. return unserialize($data);
  361. } // end func decode
  362. /**
  363. * Translates human readable/relative times in unixtime
  364. *
  365. * @param mixed can be in the following formats:
  366. * human readable : yyyymmddhhmm[ss]] eg: 20010308095100
  367. * relative in seconds (1) : +xx eg: +10
  368. * relative in seconds (2) : x < 946681200 eg: 10
  369. * absolute unixtime : x < 2147483648 eg: 2147483648
  370. * see comments in code for details
  371. * @return integer unix timestamp
  372. */
  373. function getExpiresAbsolute($expires)
  374. {
  375. if (!$expires)
  376. return 0;
  377. //for api-compatibility, one has not to provide a "+",
  378. // if integer is < 946681200 (= Jan 01 2000 00:00:00)
  379. if ('+' == $expires[0] || $expires < 946681200)
  380. {
  381. return(time() + $expires);
  382. }
  383. //if integer is < 100000000000 (= in 3140 years),
  384. // it must be an absolut unixtime
  385. // (since the "human readable" definition asks for a higher number)
  386. elseif ($expires < 100000000000)
  387. {
  388. return $expires;
  389. }
  390. // else it's "human readable";
  391. else
  392. {
  393. $year = substr($expires, 0, 4);
  394. $month = substr($expires, 4, 2);
  395. $day = substr($expires, 6, 2);
  396. $hour = substr($expires, 8, 2);
  397. $minute = substr($expires, 10, 2);
  398. $second = substr($expires, 12, 2);
  399. return mktime($hour, $minute, $second, $month, $day, $year);
  400. }
  401. } // end func getExpireAbsolute
  402. } // end class Container
  403. ?>