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

/www/wiki/inc/cache.php

https://github.com/micz/elencode
PHP | 289 lines | 161 code | 57 blank | 71 comment | 29 complexity | c32b4f2c087bf457bbdaf2e5ced35aeb MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. /**
  3. * Generic class to handle caching
  4. *
  5. * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
  6. * @author Chris Smith <chris@jalakai.co.uk>
  7. */
  8. if(!defined('DOKU_INC')) die('meh.');
  9. require_once(DOKU_INC.'inc/io.php');
  10. require_once(DOKU_INC.'inc/pageutils.php');
  11. require_once(DOKU_INC.'inc/parserutils.php');
  12. class cache {
  13. var $key = ''; // primary identifier for this item
  14. var $ext = ''; // file ext for cache data, secondary identifier for this item
  15. var $cache = ''; // cache file name
  16. var $depends = array(); // array containing cache dependency information,
  17. // used by _useCache to determine cache validity
  18. var $_event = ''; // event to be triggered during useCache
  19. function cache($key,$ext) {
  20. $this->key = $key;
  21. $this->ext = $ext;
  22. $this->cache = getCacheName($key,$ext);
  23. }
  24. /**
  25. * public method to determine whether the cache can be used
  26. *
  27. * to assist in cetralisation of event triggering and calculation of cache statistics,
  28. * don't override this function override _useCache()
  29. *
  30. * @param array $depends array of cache dependencies, support dependecies:
  31. * 'age' => max age of the cache in seconds
  32. * 'files' => cache must be younger than mtime of each file
  33. * (nb. dependency passes if file doesn't exist)
  34. *
  35. * @return bool true if cache can be used, false otherwise
  36. */
  37. function useCache($depends=array()) {
  38. $this->depends = $depends;
  39. $this->_addDependencies();
  40. if ($this->_event) {
  41. return $this->_stats(trigger_event($this->_event,$this,array($this,'_useCache')));
  42. } else {
  43. return $this->_stats($this->_useCache());
  44. }
  45. }
  46. /**
  47. * private method containing cache use decision logic
  48. *
  49. * this function processes the following keys in the depends array
  50. * purge - force a purge on any non empty value
  51. * age - expire cache if older than age (seconds)
  52. * files - expire cache if any file in this array was updated more recently than the cache
  53. *
  54. * can be overridden
  55. *
  56. * @return bool see useCache()
  57. */
  58. function _useCache() {
  59. if (!empty($this->depends['purge'])) return false; // purge requested?
  60. if (!($this->_time = @filemtime($this->cache))) return false; // cache exists?
  61. // cache too old?
  62. if (!empty($this->depends['age']) && ((time() - $this->_time) > $this->depends['age'])) return false;
  63. if (!empty($this->depends['files'])) {
  64. foreach ($this->depends['files'] as $file) {
  65. if ($this->_time < @filemtime($file)) return false; // cache older than files it depends on?
  66. }
  67. }
  68. return true;
  69. }
  70. /**
  71. * add dependencies to the depends array
  72. *
  73. * this method should only add dependencies,
  74. * it should not remove any existing dependencies and
  75. * it should only overwrite a dependency when the new value is more stringent than the old
  76. */
  77. function _addDependencies() {
  78. if (isset($_REQUEST['purge'])) $this->depends['purge'] = true; // purge requested
  79. }
  80. /**
  81. * retrieve the cached data
  82. *
  83. * @param bool $clean true to clean line endings, false to leave line endings alone
  84. * @return string cache contents
  85. */
  86. function retrieveCache($clean=true) {
  87. return io_readFile($this->cache, $clean);
  88. }
  89. /**
  90. * cache $data
  91. *
  92. * @param string $data the data to be cached
  93. * @return bool true on success, false otherwise
  94. */
  95. function storeCache($data) {
  96. return io_savefile($this->cache, $data);
  97. }
  98. /**
  99. * remove any cached data associated with this cache instance
  100. */
  101. function removeCache() {
  102. @unlink($this->cache);
  103. }
  104. /**
  105. * Record cache hits statistics.
  106. * (Only when debugging allowed, to reduce overhead.)
  107. *
  108. * @param bool $success result of this cache use attempt
  109. * @return bool pass-thru $success value
  110. */
  111. function _stats($success) {
  112. global $conf;
  113. static $stats = NULL;
  114. static $file;
  115. if (!$conf['allowdebug']) { return $success; }
  116. if (is_null($stats)) {
  117. $file = $conf['cachedir'].'/cache_stats.txt';
  118. $lines = explode("\n",io_readFile($file));
  119. foreach ($lines as $line) {
  120. $i = strpos($line,',');
  121. $stats[substr($line,0,$i)] = $line;
  122. }
  123. }
  124. if (isset($stats[$this->ext])) {
  125. list($ext,$count,$hits) = explode(',',$stats[$this->ext]);
  126. } else {
  127. $ext = $this->ext;
  128. $count = 0;
  129. $hits = 0;
  130. }
  131. $count++;
  132. if ($success) $hits++;
  133. $stats[$this->ext] = "$ext,$count,$hits";
  134. io_saveFile($file,join("\n",$stats));
  135. return $success;
  136. }
  137. }
  138. class cache_parser extends cache {
  139. var $file = ''; // source file for cache
  140. var $mode = ''; // input mode (represents the processing the input file will undergo)
  141. var $_event = 'PARSER_CACHE_USE';
  142. function cache_parser($id, $file, $mode) {
  143. if ($id) $this->page = $id;
  144. $this->file = $file;
  145. $this->mode = $mode;
  146. parent::cache($file.$_SERVER['HTTP_HOST'].$_SERVER['SERVER_PORT'],'.'.$mode);
  147. }
  148. function _useCache() {
  149. if (!@file_exists($this->file)) return false; // source exists?
  150. return parent::_useCache();
  151. }
  152. function _addDependencies() {
  153. global $conf, $config_cascade;
  154. $this->depends['age'] = isset($this->depends['age']) ?
  155. min($this->depends['age'],$conf['cachetime']) : $conf['cachetime'];
  156. // parser cache file dependencies ...
  157. $files = array($this->file, // ... source
  158. DOKU_INC.'inc/parser/parser.php', // ... parser
  159. DOKU_INC.'inc/parser/handler.php', // ... handler
  160. );
  161. $files = array_merge($files, getConfigFiles('main')); // ... wiki settings
  162. $this->depends['files'] = !empty($this->depends['files']) ? array_merge($files, $this->depends['files']) : $files;
  163. parent::_addDependencies();
  164. }
  165. }
  166. class cache_renderer extends cache_parser {
  167. function useCache($depends=array()) {
  168. $use = parent::useCache($depends);
  169. // meta data needs to be kept in step with the cache
  170. if (!$use && isset($this->page)) {
  171. p_set_metadata($this->page,array(),true);
  172. }
  173. return $use;
  174. }
  175. function _useCache() {
  176. global $conf;
  177. if (!parent::_useCache()) return false;
  178. // for wiki pages, check metadata dependencies
  179. if (isset($this->page)) {
  180. $metadata = p_get_metadata($this->page);
  181. // check currnent link existence is consistent with cache version
  182. // first check the purgefile
  183. // - if the cache is more recent that the purgefile we know no links can have been updated
  184. if ($this->_time < @filemtime($conf['cachedir'].'/purgefile')) {
  185. # $links = p_get_metadata($this->page,"relation references");
  186. $links = $metadata['relation']['references'];
  187. if (!empty($links)) {
  188. foreach ($links as $id => $exists) {
  189. if ($exists != page_exists($id,'',false)) return false;
  190. }
  191. }
  192. }
  193. }
  194. return true;
  195. }
  196. function _addDependencies() {
  197. // renderer cache file dependencies ...
  198. $files = array(
  199. DOKU_INC.'inc/parser/'.$this->mode.'.php', // ... the renderer
  200. );
  201. // page implies metadata and possibly some other dependencies
  202. if (isset($this->page)) {
  203. $metafile = metaFN($this->page,'.meta');
  204. if (@file_exists($metafile)) {
  205. $files[] = $metafile; // ... the page's own metadata
  206. $files[] = DOKU_INC.'inc/parser/metadata.php'; // ... the metadata renderer
  207. $valid = p_get_metadata($this->page, 'date valid');
  208. if (!empty($valid['age'])) {
  209. $this->depends['age'] = isset($this->depends['age']) ?
  210. min($this->depends['age'],$valid['age']) : $valid['age'];
  211. }
  212. } else {
  213. $this->depends['purge'] = true; // ... purging cache will generate metadata
  214. return;
  215. }
  216. }
  217. $this->depends['files'] = !empty($this->depends['files']) ? array_merge($files, $this->depends['files']) : $files;
  218. parent::_addDependencies();
  219. }
  220. }
  221. class cache_instructions extends cache_parser {
  222. function cache_instructions($id, $file) {
  223. parent::cache_parser($id, $file, 'i');
  224. }
  225. function retrieveCache() {
  226. $contents = io_readFile($this->cache, false);
  227. return !empty($contents) ? unserialize($contents) : array();
  228. }
  229. function storeCache($instructions) {
  230. return io_savefile($this->cache,serialize($instructions));
  231. }
  232. }