xref: /dokuwiki/inc/Cache/Cache.php (revision 0db5771e6b5f779df34a039ad49d4652eaf21893)
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