xref: /dokuwiki/inc/Cache/Cache.php (revision 42c00b4522b22bb210a8dd2d76c87dfcda07bc7d)
10db5771eSMichael Große<?php
20db5771eSMichael Große
30db5771eSMichael Großenamespace dokuwiki\Cache;
40db5771eSMichael Große
50db5771eSMichael Große/**
60db5771eSMichael Große * Generic handling of caching
70db5771eSMichael Große */
80db5771eSMichael Großeclass Cache
90db5771eSMichael Große{
100db5771eSMichael Große    public $key = '';          // primary identifier for this item
110db5771eSMichael Große    public $ext = '';          // file ext for cache data, secondary identifier for this item
120db5771eSMichael Große    public $cache = '';        // cache file name
130db5771eSMichael Große    public $depends = array(); // array containing cache dependency information,
140db5771eSMichael Große    //   used by _useCache to determine cache validity
150db5771eSMichael Große
160db5771eSMichael Große    public $_event = '';       // event to be triggered during useCache
170db5771eSMichael Große    public $_time;
180db5771eSMichael Große    public $_nocache = false;  // if set to true, cache will not be used or stored
190db5771eSMichael Große
200db5771eSMichael Große    /**
210db5771eSMichael Große     * @param string $key primary identifier
220db5771eSMichael Große     * @param string $ext file extension
230db5771eSMichael Große     */
240db5771eSMichael Große    public function __construct($key, $ext)
250db5771eSMichael Große    {
260db5771eSMichael Große        $this->key = $key;
270db5771eSMichael Große        $this->ext = $ext;
280db5771eSMichael Große        $this->cache = getCacheName($key, $ext);
290db5771eSMichael Große    }
300db5771eSMichael Große
310db5771eSMichael Große    /**
320db5771eSMichael Große     * public method to determine whether the cache can be used
330db5771eSMichael Große     *
340db5771eSMichael Große     * to assist in centralisation of event triggering and calculation of cache statistics,
350db5771eSMichael Große     * don't override this function override _useCache()
360db5771eSMichael Große     *
370db5771eSMichael Große     * @param  array $depends array of cache dependencies, support dependecies:
380db5771eSMichael Große     *                            'age'   => max age of the cache in seconds
390db5771eSMichael Große     *                            'files' => cache must be younger than mtime of each file
400db5771eSMichael Große     *                                       (nb. dependency passes if file doesn't exist)
410db5771eSMichael Große     *
420db5771eSMichael Große     * @return bool    true if cache can be used, false otherwise
430db5771eSMichael Große     */
440db5771eSMichael Große    public function useCache($depends = array())
450db5771eSMichael Große    {
460db5771eSMichael Große        $this->depends = $depends;
47*42c00b45SMichael Große        $this->addDependencies();
480db5771eSMichael Große
490db5771eSMichael Große        if ($this->_event) {
50*42c00b45SMichael Große            return $this->stats(trigger_event($this->_event, $this, array($this, 'makdeDefaultCacheDecision')));
510db5771eSMichael Große        } else {
52*42c00b45SMichael Große            return $this->stats($this->makdeDefaultCacheDecision());
530db5771eSMichael Große        }
540db5771eSMichael Große    }
550db5771eSMichael Große
560db5771eSMichael Große    /**
570db5771eSMichael Große     * private method containing cache use decision logic
580db5771eSMichael Große     *
590db5771eSMichael Große     * this function processes the following keys in the depends array
600db5771eSMichael Große     *   purge - force a purge on any non empty value
610db5771eSMichael Große     *   age   - expire cache if older than age (seconds)
620db5771eSMichael Große     *   files - expire cache if any file in this array was updated more recently than the cache
630db5771eSMichael Große     *
640db5771eSMichael Große     * Note that this function needs to be public as it is used as callback for the event handler
650db5771eSMichael Große     *
660db5771eSMichael Große     * can be overridden
670db5771eSMichael Große     *
68*42c00b45SMichael Große     * @internal This method may only be called by the event handler! Call \dokuwiki\Cache\Cache::useCache instead!
69*42c00b45SMichael Große     *
700db5771eSMichael Große     * @return bool               see useCache()
710db5771eSMichael Große     */
72*42c00b45SMichael Große    public function makdeDefaultCacheDecision()
730db5771eSMichael Große    {
740db5771eSMichael Große
750db5771eSMichael Große        if ($this->_nocache) {
760db5771eSMichael Große            return false;
770db5771eSMichael Große        }                              // caching turned off
780db5771eSMichael Große        if (!empty($this->depends['purge'])) {
790db5771eSMichael Große            return false;
800db5771eSMichael Große        }              // purge requested?
810db5771eSMichael Große        if (!($this->_time = @filemtime($this->cache))) {
820db5771eSMichael Große            return false;
830db5771eSMichael Große        }   // cache exists?
840db5771eSMichael Große
850db5771eSMichael Große        // cache too old?
860db5771eSMichael Große        if (!empty($this->depends['age']) && ((time() - $this->_time) > $this->depends['age'])) {
870db5771eSMichael Große            return false;
880db5771eSMichael Große        }
890db5771eSMichael Große
900db5771eSMichael Große        if (!empty($this->depends['files'])) {
910db5771eSMichael Große            foreach ($this->depends['files'] as $file) {
920db5771eSMichael Große                if ($this->_time <= @filemtime($file)) {
930db5771eSMichael Große                    return false;
940db5771eSMichael Große                }         // cache older than files it depends on?
950db5771eSMichael Große            }
960db5771eSMichael Große        }
970db5771eSMichael Große
980db5771eSMichael Große        return true;
990db5771eSMichael Große    }
1000db5771eSMichael Große
1010db5771eSMichael Große    /**
1020db5771eSMichael Große     * add dependencies to the depends array
1030db5771eSMichael Große     *
1040db5771eSMichael Große     * this method should only add dependencies,
1050db5771eSMichael Große     * it should not remove any existing dependencies and
1060db5771eSMichael Große     * it should only overwrite a dependency when the new value is more stringent than the old
1070db5771eSMichael Große     */
108*42c00b45SMichael Große    protected function addDependencies()
1090db5771eSMichael Große    {
1100db5771eSMichael Große        global $INPUT;
1110db5771eSMichael Große        if ($INPUT->has('purge')) {
1120db5771eSMichael Große            $this->depends['purge'] = true;
1130db5771eSMichael Große        }   // purge requested
1140db5771eSMichael Große    }
1150db5771eSMichael Große
1160db5771eSMichael Große    /**
1170db5771eSMichael Große     * retrieve the cached data
1180db5771eSMichael Große     *
1190db5771eSMichael Große     * @param   bool $clean true to clean line endings, false to leave line endings alone
1200db5771eSMichael Große     * @return  string          cache contents
1210db5771eSMichael Große     */
1220db5771eSMichael Große    public function retrieveCache($clean = true)
1230db5771eSMichael Große    {
1240db5771eSMichael Große        return io_readFile($this->cache, $clean);
1250db5771eSMichael Große    }
1260db5771eSMichael Große
1270db5771eSMichael Große    /**
1280db5771eSMichael Große     * cache $data
1290db5771eSMichael Große     *
1300db5771eSMichael Große     * @param   string $data the data to be cached
1310db5771eSMichael Große     * @return  bool           true on success, false otherwise
1320db5771eSMichael Große     */
1330db5771eSMichael Große    public function storeCache($data)
1340db5771eSMichael Große    {
1350db5771eSMichael Große        if ($this->_nocache) {
1360db5771eSMichael Große            return false;
1370db5771eSMichael Große        }
1380db5771eSMichael Große
1390db5771eSMichael Große        return io_savefile($this->cache, $data);
1400db5771eSMichael Große    }
1410db5771eSMichael Große
1420db5771eSMichael Große    /**
1430db5771eSMichael Große     * remove any cached data associated with this cache instance
1440db5771eSMichael Große     */
1450db5771eSMichael Große    public function removeCache()
1460db5771eSMichael Große    {
1470db5771eSMichael Große        @unlink($this->cache);
1480db5771eSMichael Große    }
1490db5771eSMichael Große
1500db5771eSMichael Große    /**
1510db5771eSMichael Große     * Record cache hits statistics.
1520db5771eSMichael Große     * (Only when debugging allowed, to reduce overhead.)
1530db5771eSMichael Große     *
1540db5771eSMichael Große     * @param    bool $success result of this cache use attempt
1550db5771eSMichael Große     * @return   bool              pass-thru $success value
1560db5771eSMichael Große     */
157*42c00b45SMichael Große    protected function stats($success)
1580db5771eSMichael Große    {
1590db5771eSMichael Große        global $conf;
1600db5771eSMichael Große        static $stats = null;
1610db5771eSMichael Große        static $file;
1620db5771eSMichael Große
1630db5771eSMichael Große        if (!$conf['allowdebug']) {
1640db5771eSMichael Große            return $success;
1650db5771eSMichael Große        }
1660db5771eSMichael Große
1670db5771eSMichael Große        if (is_null($stats)) {
1680db5771eSMichael Große            $file = $conf['cachedir'] . '/cache_stats.txt';
1690db5771eSMichael Große            $lines = explode("\n", io_readFile($file));
1700db5771eSMichael Große
1710db5771eSMichael Große            foreach ($lines as $line) {
1720db5771eSMichael Große                $i = strpos($line, ',');
1730db5771eSMichael Große                $stats[substr($line, 0, $i)] = $line;
1740db5771eSMichael Große            }
1750db5771eSMichael Große        }
1760db5771eSMichael Große
1770db5771eSMichael Große        if (isset($stats[$this->ext])) {
1780db5771eSMichael Große            list($ext, $count, $hits) = explode(',', $stats[$this->ext]);
1790db5771eSMichael Große        } else {
1800db5771eSMichael Große            $ext = $this->ext;
1810db5771eSMichael Große            $count = 0;
1820db5771eSMichael Große            $hits = 0;
1830db5771eSMichael Große        }
1840db5771eSMichael Große
1850db5771eSMichael Große        $count++;
1860db5771eSMichael Große        if ($success) {
1870db5771eSMichael Große            $hits++;
1880db5771eSMichael Große        }
1890db5771eSMichael Große        $stats[$this->ext] = "$ext,$count,$hits";
1900db5771eSMichael Große
1910db5771eSMichael Große        io_saveFile($file, join("\n", $stats));
1920db5771eSMichael Große
1930db5771eSMichael Große        return $success;
1940db5771eSMichael Große    }
1950db5771eSMichael Große}
196