1*0db5771eSMichael Große<?php 2*0db5771eSMichael Große 3*0db5771eSMichael Großenamespace dokuwiki\Cache; 4*0db5771eSMichael Große 5*0db5771eSMichael Große/** 6*0db5771eSMichael Große * Generic handling of caching 7*0db5771eSMichael Große */ 8*0db5771eSMichael Großeclass Cache 9*0db5771eSMichael Große{ 10*0db5771eSMichael Große public $key = ''; // primary identifier for this item 11*0db5771eSMichael Große public $ext = ''; // file ext for cache data, secondary identifier for this item 12*0db5771eSMichael Große public $cache = ''; // cache file name 13*0db5771eSMichael Große public $depends = array(); // array containing cache dependency information, 14*0db5771eSMichael Große // used by _useCache to determine cache validity 15*0db5771eSMichael Große 16*0db5771eSMichael Große public $_event = ''; // event to be triggered during useCache 17*0db5771eSMichael Große public $_time; 18*0db5771eSMichael Große public $_nocache = false; // if set to true, cache will not be used or stored 19*0db5771eSMichael Große 20*0db5771eSMichael Große /** 21*0db5771eSMichael Große * @param string $key primary identifier 22*0db5771eSMichael Große * @param string $ext file extension 23*0db5771eSMichael Große */ 24*0db5771eSMichael Große public function __construct($key, $ext) 25*0db5771eSMichael Große { 26*0db5771eSMichael Große $this->key = $key; 27*0db5771eSMichael Große $this->ext = $ext; 28*0db5771eSMichael Große $this->cache = getCacheName($key, $ext); 29*0db5771eSMichael Große } 30*0db5771eSMichael Große 31*0db5771eSMichael Große /** 32*0db5771eSMichael Große * public method to determine whether the cache can be used 33*0db5771eSMichael Große * 34*0db5771eSMichael Große * to assist in centralisation of event triggering and calculation of cache statistics, 35*0db5771eSMichael Große * don't override this function override _useCache() 36*0db5771eSMichael Große * 37*0db5771eSMichael Große * @param array $depends array of cache dependencies, support dependecies: 38*0db5771eSMichael Große * 'age' => max age of the cache in seconds 39*0db5771eSMichael Große * 'files' => cache must be younger than mtime of each file 40*0db5771eSMichael Große * (nb. dependency passes if file doesn't exist) 41*0db5771eSMichael Große * 42*0db5771eSMichael Große * @return bool true if cache can be used, false otherwise 43*0db5771eSMichael Große */ 44*0db5771eSMichael Große public function useCache($depends = array()) 45*0db5771eSMichael Große { 46*0db5771eSMichael Große $this->depends = $depends; 47*0db5771eSMichael Große $this->_addDependencies(); 48*0db5771eSMichael Große 49*0db5771eSMichael Große if ($this->_event) { 50*0db5771eSMichael Große return $this->_stats(trigger_event($this->_event, $this, array($this, '_useCache'))); 51*0db5771eSMichael Große } else { 52*0db5771eSMichael Große return $this->_stats($this->_useCache()); 53*0db5771eSMichael Große } 54*0db5771eSMichael Große } 55*0db5771eSMichael Große 56*0db5771eSMichael Große /** 57*0db5771eSMichael Große * private method containing cache use decision logic 58*0db5771eSMichael Große * 59*0db5771eSMichael Große * this function processes the following keys in the depends array 60*0db5771eSMichael Große * purge - force a purge on any non empty value 61*0db5771eSMichael Große * age - expire cache if older than age (seconds) 62*0db5771eSMichael Große * files - expire cache if any file in this array was updated more recently than the cache 63*0db5771eSMichael Große * 64*0db5771eSMichael Große * Note that this function needs to be public as it is used as callback for the event handler 65*0db5771eSMichael Große * 66*0db5771eSMichael Große * can be overridden 67*0db5771eSMichael Große * 68*0db5771eSMichael Große * @return bool see useCache() 69*0db5771eSMichael Große */ 70*0db5771eSMichael Große public function _useCache() 71*0db5771eSMichael Große { 72*0db5771eSMichael Große 73*0db5771eSMichael Große if ($this->_nocache) { 74*0db5771eSMichael Große return false; 75*0db5771eSMichael Große } // caching turned off 76*0db5771eSMichael Große if (!empty($this->depends['purge'])) { 77*0db5771eSMichael Große return false; 78*0db5771eSMichael Große } // purge requested? 79*0db5771eSMichael Große if (!($this->_time = @filemtime($this->cache))) { 80*0db5771eSMichael Große return false; 81*0db5771eSMichael Große } // cache exists? 82*0db5771eSMichael Große 83*0db5771eSMichael Große // cache too old? 84*0db5771eSMichael Große if (!empty($this->depends['age']) && ((time() - $this->_time) > $this->depends['age'])) { 85*0db5771eSMichael Große return false; 86*0db5771eSMichael Große } 87*0db5771eSMichael Große 88*0db5771eSMichael Große if (!empty($this->depends['files'])) { 89*0db5771eSMichael Große foreach ($this->depends['files'] as $file) { 90*0db5771eSMichael Große if ($this->_time <= @filemtime($file)) { 91*0db5771eSMichael Große return false; 92*0db5771eSMichael Große } // cache older than files it depends on? 93*0db5771eSMichael Große } 94*0db5771eSMichael Große } 95*0db5771eSMichael Große 96*0db5771eSMichael Große return true; 97*0db5771eSMichael Große } 98*0db5771eSMichael Große 99*0db5771eSMichael Große /** 100*0db5771eSMichael Große * add dependencies to the depends array 101*0db5771eSMichael Große * 102*0db5771eSMichael Große * this method should only add dependencies, 103*0db5771eSMichael Große * it should not remove any existing dependencies and 104*0db5771eSMichael Große * it should only overwrite a dependency when the new value is more stringent than the old 105*0db5771eSMichael Große */ 106*0db5771eSMichael Große protected function _addDependencies() 107*0db5771eSMichael Große { 108*0db5771eSMichael Große global $INPUT; 109*0db5771eSMichael Große if ($INPUT->has('purge')) { 110*0db5771eSMichael Große $this->depends['purge'] = true; 111*0db5771eSMichael Große } // purge requested 112*0db5771eSMichael Große } 113*0db5771eSMichael Große 114*0db5771eSMichael Große /** 115*0db5771eSMichael Große * retrieve the cached data 116*0db5771eSMichael Große * 117*0db5771eSMichael Große * @param bool $clean true to clean line endings, false to leave line endings alone 118*0db5771eSMichael Große * @return string cache contents 119*0db5771eSMichael Große */ 120*0db5771eSMichael Große public function retrieveCache($clean = true) 121*0db5771eSMichael Große { 122*0db5771eSMichael Große return io_readFile($this->cache, $clean); 123*0db5771eSMichael Große } 124*0db5771eSMichael Große 125*0db5771eSMichael Große /** 126*0db5771eSMichael Große * cache $data 127*0db5771eSMichael Große * 128*0db5771eSMichael Große * @param string $data the data to be cached 129*0db5771eSMichael Große * @return bool true on success, false otherwise 130*0db5771eSMichael Große */ 131*0db5771eSMichael Große public function storeCache($data) 132*0db5771eSMichael Große { 133*0db5771eSMichael Große if ($this->_nocache) { 134*0db5771eSMichael Große return false; 135*0db5771eSMichael Große } 136*0db5771eSMichael Große 137*0db5771eSMichael Große return io_savefile($this->cache, $data); 138*0db5771eSMichael Große } 139*0db5771eSMichael Große 140*0db5771eSMichael Große /** 141*0db5771eSMichael Große * remove any cached data associated with this cache instance 142*0db5771eSMichael Große */ 143*0db5771eSMichael Große public function removeCache() 144*0db5771eSMichael Große { 145*0db5771eSMichael Große @unlink($this->cache); 146*0db5771eSMichael Große } 147*0db5771eSMichael Große 148*0db5771eSMichael Große /** 149*0db5771eSMichael Große * Record cache hits statistics. 150*0db5771eSMichael Große * (Only when debugging allowed, to reduce overhead.) 151*0db5771eSMichael Große * 152*0db5771eSMichael Große * @param bool $success result of this cache use attempt 153*0db5771eSMichael Große * @return bool pass-thru $success value 154*0db5771eSMichael Große */ 155*0db5771eSMichael Große protected function _stats($success) 156*0db5771eSMichael Große { 157*0db5771eSMichael Große global $conf; 158*0db5771eSMichael Große static $stats = null; 159*0db5771eSMichael Große static $file; 160*0db5771eSMichael Große 161*0db5771eSMichael Große if (!$conf['allowdebug']) { 162*0db5771eSMichael Große return $success; 163*0db5771eSMichael Große } 164*0db5771eSMichael Große 165*0db5771eSMichael Große if (is_null($stats)) { 166*0db5771eSMichael Große $file = $conf['cachedir'] . '/cache_stats.txt'; 167*0db5771eSMichael Große $lines = explode("\n", io_readFile($file)); 168*0db5771eSMichael Große 169*0db5771eSMichael Große foreach ($lines as $line) { 170*0db5771eSMichael Große $i = strpos($line, ','); 171*0db5771eSMichael Große $stats[substr($line, 0, $i)] = $line; 172*0db5771eSMichael Große } 173*0db5771eSMichael Große } 174*0db5771eSMichael Große 175*0db5771eSMichael Große if (isset($stats[$this->ext])) { 176*0db5771eSMichael Große list($ext, $count, $hits) = explode(',', $stats[$this->ext]); 177*0db5771eSMichael Große } else { 178*0db5771eSMichael Große $ext = $this->ext; 179*0db5771eSMichael Große $count = 0; 180*0db5771eSMichael Große $hits = 0; 181*0db5771eSMichael Große } 182*0db5771eSMichael Große 183*0db5771eSMichael Große $count++; 184*0db5771eSMichael Große if ($success) { 185*0db5771eSMichael Große $hits++; 186*0db5771eSMichael Große } 187*0db5771eSMichael Große $stats[$this->ext] = "$ext,$count,$hits"; 188*0db5771eSMichael Große 189*0db5771eSMichael Große io_saveFile($file, join("\n", $stats)); 190*0db5771eSMichael Große 191*0db5771eSMichael Große return $success; 192*0db5771eSMichael Große } 193*0db5771eSMichael Große} 194