PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/system/classes/filecache.php

https://github.com/HabariMag/habarimag-old
PHP | 332 lines | 207 code | 43 blank | 82 comment | 39 complexity | 50794351d6bf82efbe40e5c0e025332a MD5 | raw file
Possible License(s): Apache-2.0
  1. <?php
  2. /**
  3. * @package Habari
  4. *
  5. */
  6. /**
  7. * Contains the FileCache class
  8. *
  9. * Stores cache data in local files
  10. */
  11. class FileCache extends Cache
  12. {
  13. protected $cache_location;
  14. protected $enabled = false;
  15. protected $cache_files = array();
  16. protected $cache_data = array();
  17. protected $index_file;
  18. /**
  19. * Constructor for FileCache
  20. *
  21. * Sets up paths etc. and reads cache index, if it exists.
  22. */
  23. public function __construct()
  24. {
  25. if ( !defined( 'FILE_CACHE_LOCATION' ) ) {
  26. define( 'FILE_CACHE_LOCATION', HABARI_PATH . '/user/cache/' );
  27. }
  28. $this->cache_location = FILE_CACHE_LOCATION;
  29. $this->index_file = $this->cache_location . md5( 'index' . Options::get( 'GUID' ) ) . '.data';
  30. $this->enabled = is_writeable( $this->cache_location );
  31. if ( $this->enabled ) {
  32. if ( file_exists( $this->index_file ) ) {
  33. $this->cache_files = unserialize( file_get_contents( $this->index_file ) );
  34. }
  35. }
  36. else {
  37. Session::error( sprintf( _t( "The cache directory '%s' is not writable - the cache is disabled. The user, or group, which your web server is running as, needs to have read, write, and execute permissions on this directory." ), $this->cache_location ), 'filecache' );
  38. EventLog::log( sprintf( _t( "The cache directory '%s' is not writable - the cache is disabled." ), $this->cache_location ), 'notice', 'cache', 'habari' );
  39. }
  40. }
  41. /**
  42. * Is record with $name in the cache?
  43. *
  44. * @param string $name name of the cached item
  45. * @return boolean true if item is cached, false if not
  46. */
  47. protected function _has( $name, $group )
  48. {
  49. if ( !$this->enabled ) {
  50. return false;
  51. }
  52. $hash = $this->get_name_hash( $name );
  53. $ghash = $this->get_group_hash( $group );
  54. return isset( $this->cache_files[$ghash][$hash] ) && ( $this->cache_files[$ghash][$hash]['keep'] || $this->cache_files[$ghash][$hash]['expires'] > time() ) && file_exists( $this->cache_files[$ghash][$hash]['file'] );
  55. }
  56. /**
  57. * Is group in the cache?
  58. *
  59. * @param string $name name of the cached item
  60. * @return boolean true if item is cached, false if not
  61. */
  62. protected function _has_group( $group )
  63. {
  64. if ( !$this->enabled ) {
  65. return false;
  66. }
  67. $ghash = $this->get_group_hash( $group );
  68. $valid = true;
  69. $now = time();
  70. foreach ( $this->cache_files[$ghash] as $hash => $record ) {
  71. if ( ! file_exists( $record['file'] ) || $record['expires'] <= $now ) {
  72. $valid = false;
  73. break;
  74. }
  75. }
  76. return ( isset( $this->cache_files[$ghash] ) && count( $this->cache_files[$ghash] ) > 1 ) && $valid;
  77. }
  78. /**
  79. * Returns the group from the cache.
  80. *
  81. * @param string $name The name of the cached item
  82. * @return mixed The group or array() if it doesn't exist in cache
  83. */
  84. protected function _get_group( $group )
  85. {
  86. if ( !$this->enabled ) {
  87. return null;
  88. }
  89. $ghash = $this->get_group_hash( $group );
  90. if ( !isset( $this->cache_data[$group] ) ) {
  91. $this->cache_data[$group] = array();
  92. if ( isset( $this->cache_files[$ghash] ) ) {
  93. foreach ( $this->cache_files[$ghash] as $hash => $record ) {
  94. $this->cache_data[$group][$record['name']] = unserialize(
  95. file_get_contents( $record['file'] )
  96. );
  97. }
  98. }
  99. }
  100. return $this->cache_data[$group];
  101. }
  102. /**
  103. * Returns the named value from the cache.
  104. *
  105. * @param string $name The name of the cached item
  106. * @return mixed The item value or null if it doesn't exist in cache
  107. */
  108. protected function _get( $name, $group )
  109. {
  110. if ( !$this->enabled ) {
  111. return null;
  112. }
  113. $hash = $this->get_name_hash( $name );
  114. $ghash = $this->get_group_hash( $group );
  115. if ( !isset( $this->cache_data[$group][$name] ) ) {
  116. $this->cache_data[$group][$name] = null;
  117. if ( isset( $this->cache_files[$ghash][$hash] ) && ($this->cache_files[$ghash][$hash]['keep'] || $this->cache_files[$ghash][$hash]['expires'] > time()) && file_exists( $this->cache_files[$ghash][$hash]['file'] ) ) {
  118. $this->cache_data[$group][$name] = unserialize( file_get_contents( $this->cache_files[$ghash][$hash]['file'] ) );
  119. }
  120. }
  121. return $this->cache_data[$group][$name];
  122. }
  123. protected function _set( $name, $value, $expiry, $group, $keep )
  124. {
  125. if ( !$this->enabled ) {
  126. return null;
  127. }
  128. Plugins::act( 'cache_set_before', $name, $group, $value, $expiry );
  129. $hash = $this->get_name_hash( $name );
  130. $ghash = $this->get_group_hash( $group );
  131. if ( !isset( $this->cache_data[$group] ) ) {
  132. // prime our cache so the local version is up-to-date and complete
  133. $this->_get_group( $group );
  134. }
  135. $this->cache_data[$group][$name] = $value;
  136. file_put_contents( $this->cache_location . $ghash . $hash, serialize( $value ) );
  137. $this->cache_files[$ghash][$hash] = array( 'file' => $this->cache_location . $ghash . $hash, 'expires' => time() + $expiry, 'name' => $name, 'keep' => $keep );
  138. $this->clear_expired();
  139. file_put_contents( $this->index_file, serialize( $this->cache_files ) );
  140. Plugins::act( 'cache_set_after', $name, $group, $value, $expiry );
  141. return true;
  142. }
  143. /**
  144. * Expires the named value from the cache.
  145. *
  146. * @param string $name The name of the cached item
  147. * @param string $group The name of the cache group
  148. * @param string $match_mode (optional) how to match bucket names ('strict', 'regex', 'glob') (default 'strict')
  149. */
  150. protected function _expire( $name, $group, $match_mode = 'strict' )
  151. {
  152. if ( !$this->enabled ) {
  153. return null;
  154. }
  155. // prime the variable cache.
  156. // dirty, dirty hack. we should *never* load all the data in, especially when we only care about the expirey
  157. // alas, this crappy code requires it
  158. $this->_get_group($group);
  159. $keys = array();
  160. switch ( strtolower( $match_mode ) ) {
  161. case 'glob':
  162. if ( array_key_exists( $group, $this->cache_data ) ) {
  163. $keys = preg_grep( Utils::glob_to_regex( $name ), array_keys( $this->cache_data[$group] ) );
  164. }
  165. break;
  166. case 'regex':
  167. if ( array_key_exists( $group, $this->cache_data ) ) {
  168. $keys = preg_grep( $name, array_keys( $this->cache_data[$group] ) );
  169. }
  170. break;
  171. case 'strict':
  172. default:
  173. $keys = array( $name );
  174. break;
  175. }
  176. $ghash = $this->get_group_hash( $group );
  177. foreach ( $keys as $key ) {
  178. Plugins::act( 'cache_expire_before', $name, $group );
  179. $hash = $this->get_name_hash( $key );
  180. if ( isset( $this->cache_files[$ghash][$hash] ) && file_exists( $this->cache_files[$ghash][$hash]['file'] ) ) {
  181. unlink( $this->cache_files[$ghash][$hash]['file'] );
  182. unset( $this->cache_files[$ghash][$hash] );
  183. }
  184. Plugins::act( 'cache_expire_after', $name, $group );
  185. }
  186. $this->clear_expired();
  187. file_put_contents( $this->index_file, serialize( $this->cache_files ) );
  188. }
  189. /**
  190. * Return whether a named cache value has expired
  191. *
  192. * @param string $name The name of the cached item
  193. * @param string $group The group of the cached item
  194. * @return boolean true if the stored value has expired
  195. */
  196. protected function _expired( $name, $group )
  197. {
  198. if ( !$this->enabled ) {
  199. return null;
  200. }
  201. $hash = $this->get_name_hash( $name );
  202. $ghash = $this->get_group_hash( $group );
  203. // Do not check cached data, since we can return (and cache in this object) data if the cache is set to 'keep'
  204. if ( isset( $this->cache_files[$ghash][$hash] ) && $this->cache_files[$ghash][$hash]['expires'] > time() && file_exists( $this->cache_files[$ghash][$hash]['file'] ) ) {
  205. return false;
  206. }
  207. else {
  208. return true;
  209. }
  210. }
  211. /**
  212. * Extend the expiration of the named cached value.
  213. *
  214. * @param string $name The name of the cached item
  215. * @param integer $expiry The duration in seconds to extend the cache expiration by
  216. */
  217. protected function _extend( $name, $expiry, $group )
  218. {
  219. if ( !$this->enabled ) {
  220. return null;
  221. }
  222. Plugins::act( 'cache_extend_before', $name, $group, $expiry );
  223. $hash = $this->get_name_hash( $name );
  224. $ghash = $this->get_group_hash( $group );
  225. if ( isset( $this->cache_files[$ghash][$hash] ) ) {
  226. $this->cache_files[$ghash][$hash]['expires'] = time() + $expiry;
  227. $this->clear_expired();
  228. file_put_contents( $this->index_file, serialize( $this->cache_files ) );
  229. }
  230. Plugins::act( 'cache_extend_after', $name, $group, $expiry );
  231. }
  232. /**
  233. * Remove all cache files
  234. */
  235. protected function _purge()
  236. {
  237. Plugins::act( 'cache_purge_before' );
  238. $glob = Utils::glob( FILE_CACHE_LOCATION . '*.data' );
  239. foreach ( $glob as $file ) {
  240. unlink( $file );
  241. }
  242. $glob = Utils::glob( FILE_CACHE_LOCATION . '*.cache' );
  243. foreach ( $glob as $file ) {
  244. unlink( $file );
  245. }
  246. Plugins::act( 'cache_purge_after' );
  247. }
  248. /**
  249. * Get the unique hash for a given key.
  250. *
  251. * @param string $name The name of the cached item.
  252. */
  253. private function get_name_hash( $name )
  254. {
  255. return md5( $name . Options::get( 'GUID' ) ) . '.cache';
  256. }
  257. /**
  258. * Get the unique hash for a given key.
  259. *
  260. * @param string $name The name of the cached group.
  261. */
  262. private function get_group_hash( $group )
  263. {
  264. return md5( $group . Options::get( 'GUID' ) ) . '.';
  265. }
  266. /**
  267. * Check whether a given record is still fresh (e.g. has not expired).
  268. */
  269. private function record_fresh( $record )
  270. {
  271. if ( $record['expires'] > time() || $record['keep'] ) {
  272. return true;
  273. }
  274. elseif ( file_exists( $record['file'] ) ) {
  275. unlink( $record['file'] );
  276. }
  277. return false;
  278. }
  279. /**
  280. * Purge expired items from the cache.
  281. */
  282. private function clear_expired()
  283. {
  284. foreach ( $this->cache_files as $ghash => $records ) {
  285. $this->cache_files[$ghash] = array_filter( $records, array( $this, 'record_fresh' ) );
  286. }
  287. }
  288. }
  289. ?>