xref: /dokuwiki/inc/Cache/Cache.php (revision b4b0b31be159c157f279e565ddbe3ae02269246b)
10db5771eSMichael Große<?php
20db5771eSMichael Große
30db5771eSMichael Großenamespace dokuwiki\Cache;
40db5771eSMichael Große
5*b4b0b31bSMichael Großeuse \dokuwiki\Debug\PropertyDeprecationHelper;
6*b4b0b31bSMichael Große
70db5771eSMichael Große/**
80db5771eSMichael Große * Generic handling of caching
90db5771eSMichael Große */
100db5771eSMichael Großeclass Cache
110db5771eSMichael Große{
12*b4b0b31bSMichael Große    use PropertyDeprecationHelper;
13*b4b0b31bSMichael Große
140db5771eSMichael Große    public $key = '';          // primary identifier for this item
150db5771eSMichael Große    public $ext = '';          // file ext for cache data, secondary identifier for this item
160db5771eSMichael Große    public $cache = '';        // cache file name
170db5771eSMichael Große    public $depends = array(); // array containing cache dependency information,
1872c2bae8SMichael Große    //   used by makeDefaultCacheDecision to determine cache validity
190db5771eSMichael Große
20*b4b0b31bSMichael Große    // phpcs:disable
21*b4b0b31bSMichael Große    /**
22*b4b0b31bSMichael Große     * @deprecated since 2019-02-02 use the respective getters instead!
23*b4b0b31bSMichael Große     */
24*b4b0b31bSMichael Große    protected $_event = '';       // event to be triggered during useCache
25*b4b0b31bSMichael Große    protected $_time;
26*b4b0b31bSMichael Große    protected $_nocache = false;  // if set to true, cache will not be used or stored
27*b4b0b31bSMichael Große    // phpcs:enable
280db5771eSMichael Große
290db5771eSMichael Große    /**
300db5771eSMichael Große     * @param string $key primary identifier
310db5771eSMichael Große     * @param string $ext file extension
320db5771eSMichael Große     */
330db5771eSMichael Große    public function __construct($key, $ext)
340db5771eSMichael Große    {
350db5771eSMichael Große        $this->key = $key;
360db5771eSMichael Große        $this->ext = $ext;
370db5771eSMichael Große        $this->cache = getCacheName($key, $ext);
380db5771eSMichael Große
390db5771eSMichael Große        /**
40d2f1d7a1SMichael Große         * @deprecated since 2019-02-02 use the respective getters instead!
41d2f1d7a1SMichael Große         */
42*b4b0b31bSMichael Große        $this->deprecatePublicProperty('_event');
43*b4b0b31bSMichael Große        $this->deprecatePublicProperty('_time');
44*b4b0b31bSMichael Große        $this->deprecatePublicProperty('_nocache');
45d2f1d7a1SMichael Große    }
46d2f1d7a1SMichael Große
47d2f1d7a1SMichael Große    public function getTime()
48d2f1d7a1SMichael Große    {
49*b4b0b31bSMichael Große        return $this->_time;
50d2f1d7a1SMichael Große    }
51d2f1d7a1SMichael Große
52d2f1d7a1SMichael Große    public function getEvent()
53d2f1d7a1SMichael Große    {
54*b4b0b31bSMichael Große        return $this->_event;
55d2f1d7a1SMichael Große    }
56d2f1d7a1SMichael Große
57*b4b0b31bSMichael Große    public function setEvent($_event)
58d2f1d7a1SMichael Große    {
59*b4b0b31bSMichael Große        $this->_event = $_event;
60d2f1d7a1SMichael Große    }
61d2f1d7a1SMichael Große
62d2f1d7a1SMichael Große    /**
630db5771eSMichael Große     * public method to determine whether the cache can be used
640db5771eSMichael Große     *
650db5771eSMichael Große     * to assist in centralisation of event triggering and calculation of cache statistics,
6672c2bae8SMichael Große     * don't override this function override makeDefaultCacheDecision()
670db5771eSMichael Große     *
680db5771eSMichael Große     * @param  array $depends array of cache dependencies, support dependecies:
690db5771eSMichael Große     *                            'age'   => max age of the cache in seconds
700db5771eSMichael Große     *                            'files' => cache must be younger than mtime of each file
710db5771eSMichael Große     *                                       (nb. dependency passes if file doesn't exist)
720db5771eSMichael Große     *
730db5771eSMichael Große     * @return bool    true if cache can be used, false otherwise
740db5771eSMichael Große     */
750db5771eSMichael Große    public function useCache($depends = array())
760db5771eSMichael Große    {
770db5771eSMichael Große        $this->depends = $depends;
7842c00b45SMichael Große        $this->addDependencies();
790db5771eSMichael Große
80*b4b0b31bSMichael Große        if ($this->_event) {
81*b4b0b31bSMichael Große            return $this->stats(trigger_event($this->_event, $this, array($this, 'makeDefaultCacheDecision')));
820db5771eSMichael Große        } else {
8372c2bae8SMichael Große            return $this->stats($this->makeDefaultCacheDecision());
840db5771eSMichael Große        }
850db5771eSMichael Große    }
860db5771eSMichael Große
870db5771eSMichael Große    /**
8843ff1056SMichael Große     * internal method containing cache use decision logic
890db5771eSMichael Große     *
900db5771eSMichael Große     * this function processes the following keys in the depends array
910db5771eSMichael Große     *   purge - force a purge on any non empty value
920db5771eSMichael Große     *   age   - expire cache if older than age (seconds)
930db5771eSMichael Große     *   files - expire cache if any file in this array was updated more recently than the cache
940db5771eSMichael Große     *
950db5771eSMichael Große     * Note that this function needs to be public as it is used as callback for the event handler
960db5771eSMichael Große     *
970db5771eSMichael Große     * can be overridden
980db5771eSMichael Große     *
9942c00b45SMichael Große     * @internal This method may only be called by the event handler! Call \dokuwiki\Cache\Cache::useCache instead!
10042c00b45SMichael Große     *
1010db5771eSMichael Große     * @return bool               see useCache()
1020db5771eSMichael Große     */
10372c2bae8SMichael Große    public function makeDefaultCacheDecision()
1040db5771eSMichael Große    {
1050db5771eSMichael Große
106*b4b0b31bSMichael Große        if ($this->_nocache) {
1070db5771eSMichael Große            return false;
1080db5771eSMichael Große        }                              // caching turned off
1090db5771eSMichael Große        if (!empty($this->depends['purge'])) {
1100db5771eSMichael Große            return false;
1110db5771eSMichael Große        }              // purge requested?
112*b4b0b31bSMichael Große        if (!($this->_time = @filemtime($this->cache))) {
1130db5771eSMichael Große            return false;
1140db5771eSMichael Große        }   // cache exists?
1150db5771eSMichael Große
1160db5771eSMichael Große        // cache too old?
117*b4b0b31bSMichael Große        if (!empty($this->depends['age']) && ((time() - $this->_time) > $this->depends['age'])) {
1180db5771eSMichael Große            return false;
1190db5771eSMichael Große        }
1200db5771eSMichael Große
1210db5771eSMichael Große        if (!empty($this->depends['files'])) {
1220db5771eSMichael Große            foreach ($this->depends['files'] as $file) {
123*b4b0b31bSMichael Große                if ($this->_time <= @filemtime($file)) {
1240db5771eSMichael Große                    return false;
1250db5771eSMichael Große                }         // cache older than files it depends on?
1260db5771eSMichael Große            }
1270db5771eSMichael Große        }
1280db5771eSMichael Große
1290db5771eSMichael Große        return true;
1300db5771eSMichael Große    }
1310db5771eSMichael Große
1320db5771eSMichael Große    /**
1330db5771eSMichael Große     * add dependencies to the depends array
1340db5771eSMichael Große     *
1350db5771eSMichael Große     * this method should only add dependencies,
1360db5771eSMichael Große     * it should not remove any existing dependencies and
1370db5771eSMichael Große     * it should only overwrite a dependency when the new value is more stringent than the old
1380db5771eSMichael Große     */
13942c00b45SMichael Große    protected function addDependencies()
1400db5771eSMichael Große    {
1410db5771eSMichael Große        global $INPUT;
1420db5771eSMichael Große        if ($INPUT->has('purge')) {
1430db5771eSMichael Große            $this->depends['purge'] = true;
1440db5771eSMichael Große        }   // purge requested
1450db5771eSMichael Große    }
1460db5771eSMichael Große
1470db5771eSMichael Große    /**
1480db5771eSMichael Große     * retrieve the cached data
1490db5771eSMichael Große     *
1500db5771eSMichael Große     * @param   bool $clean true to clean line endings, false to leave line endings alone
1510db5771eSMichael Große     * @return  string          cache contents
1520db5771eSMichael Große     */
1530db5771eSMichael Große    public function retrieveCache($clean = true)
1540db5771eSMichael Große    {
1550db5771eSMichael Große        return io_readFile($this->cache, $clean);
1560db5771eSMichael Große    }
1570db5771eSMichael Große
1580db5771eSMichael Große    /**
1590db5771eSMichael Große     * cache $data
1600db5771eSMichael Große     *
1610db5771eSMichael Große     * @param   string $data the data to be cached
1620db5771eSMichael Große     * @return  bool           true on success, false otherwise
1630db5771eSMichael Große     */
1640db5771eSMichael Große    public function storeCache($data)
1650db5771eSMichael Große    {
166*b4b0b31bSMichael Große        if ($this->_nocache) {
1670db5771eSMichael Große            return false;
1680db5771eSMichael Große        }
1690db5771eSMichael Große
1700db5771eSMichael Große        return io_savefile($this->cache, $data);
1710db5771eSMichael Große    }
1720db5771eSMichael Große
1730db5771eSMichael Große    /**
1740db5771eSMichael Große     * remove any cached data associated with this cache instance
1750db5771eSMichael Große     */
1760db5771eSMichael Große    public function removeCache()
1770db5771eSMichael Große    {
1780db5771eSMichael Große        @unlink($this->cache);
1790db5771eSMichael Große    }
1800db5771eSMichael Große
1810db5771eSMichael Große    /**
1820db5771eSMichael Große     * Record cache hits statistics.
1830db5771eSMichael Große     * (Only when debugging allowed, to reduce overhead.)
1840db5771eSMichael Große     *
1850db5771eSMichael Große     * @param    bool $success result of this cache use attempt
1860db5771eSMichael Große     * @return   bool              pass-thru $success value
1870db5771eSMichael Große     */
18842c00b45SMichael Große    protected function stats($success)
1890db5771eSMichael Große    {
1900db5771eSMichael Große        global $conf;
1910db5771eSMichael Große        static $stats = null;
1920db5771eSMichael Große        static $file;
1930db5771eSMichael Große
1940db5771eSMichael Große        if (!$conf['allowdebug']) {
1950db5771eSMichael Große            return $success;
1960db5771eSMichael Große        }
1970db5771eSMichael Große
1980db5771eSMichael Große        if (is_null($stats)) {
1990db5771eSMichael Große            $file = $conf['cachedir'] . '/cache_stats.txt';
2000db5771eSMichael Große            $lines = explode("\n", io_readFile($file));
2010db5771eSMichael Große
2020db5771eSMichael Große            foreach ($lines as $line) {
2030db5771eSMichael Große                $i = strpos($line, ',');
2040db5771eSMichael Große                $stats[substr($line, 0, $i)] = $line;
2050db5771eSMichael Große            }
2060db5771eSMichael Große        }
2070db5771eSMichael Große
2080db5771eSMichael Große        if (isset($stats[$this->ext])) {
2090db5771eSMichael Große            list($ext, $count, $hits) = explode(',', $stats[$this->ext]);
2100db5771eSMichael Große        } else {
2110db5771eSMichael Große            $ext = $this->ext;
2120db5771eSMichael Große            $count = 0;
2130db5771eSMichael Große            $hits = 0;
2140db5771eSMichael Große        }
2150db5771eSMichael Große
2160db5771eSMichael Große        $count++;
2170db5771eSMichael Große        if ($success) {
2180db5771eSMichael Große            $hits++;
2190db5771eSMichael Große        }
2200db5771eSMichael Große        $stats[$this->ext] = "$ext,$count,$hits";
2210db5771eSMichael Große
2220db5771eSMichael Große        io_saveFile($file, join("\n", $stats));
2230db5771eSMichael Große
2240db5771eSMichael Große        return $success;
2250db5771eSMichael Große    }
226d2f1d7a1SMichael Große
227d2f1d7a1SMichael Große    /**
228d2f1d7a1SMichael Große     * @return bool
229d2f1d7a1SMichael Große     */
230d2f1d7a1SMichael Große    public function isNoCache()
231d2f1d7a1SMichael Große    {
232*b4b0b31bSMichael Große        return $this->_nocache;
233d2f1d7a1SMichael Große    }
2340db5771eSMichael Große}
235