Failed Conditions
Push — refactorCachePSR2 ( 43ff10...fec08c )
by Michael
03:13
created

Cache::setEvent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace dokuwiki\Cache;
4
5
/**
6
 * Generic handling of caching
7
 */
8
class Cache
9
{
10
    public $key = '';          // primary identifier for this item
11
    public $ext = '';          // file ext for cache data, secondary identifier for this item
12
    public $cache = '';        // cache file name
13
    public $depends = array(); // array containing cache dependency information,
14
    //   used by makeDefaultCacheDecision to determine cache validity
15
16
    protected $event = '';       // event to be triggered during useCache
17
    protected $time;
18
    protected $nocache = false;  // if set to true, cache will not be used or stored
19
20
    /**
21
     * @param string $key primary identifier
22
     * @param string $ext file extension
23
     */
24
    public function __construct($key, $ext)
25
    {
26
        $this->key = $key;
27
        $this->ext = $ext;
28
        $this->cache = getCacheName($key, $ext);
29
    }
30
31
    /**
32
     * @deprecated since 2019-02-02 use the respective getters instead!
33
     */
34
    public function __get($key)
35
    {
36
        if ($key === '_event') {
37
            trigger_error(
38
                '\dokuwiki\Cache\Cache::_event is deprecated since 2019-02-02. Use \dokuwiki\Cache\Cache::getEvent()',
39
                E_USER_DEPRECATED
40
            );
41
            dbg_deprecated('\dokuwiki\Cache\Cache::getEvent()');
42
            return $this->getEvent();
43
        }
44
45
        if ($key === '_time') {
46
            trigger_error(
47
                '\dokuwiki\Cache\Cache::_time is deprecated since 2019-02-02. Use \dokuwiki\Cache\Cache::getTime()',
48
                E_USER_DEPRECATED
49
            );
50
            dbg_deprecated('\dokuwiki\Cache\Cache::getTime()');
51
            return $this->getTime();
52
        }
53
        return $this->$$key;
54
    }
55
56
    public function __set($name, $value)
57
    {
58
        if ($name === '_event') {
59
            trigger_error(
60
                '\dokuwiki\Cache\Cache::_event is deprecated since 2019-02-02. Use \dokuwiki\Cache\Cache::getEvent()',
61
                E_USER_DEPRECATED
62
            );
63
            dbg_deprecated('\dokuwiki\Cache\Cache::getEvent()');
64
            $this->setEvent($value);
65
        }
66
        $this->$$name = $value;
67
    }
68
69
    public function getTime()
70
    {
71
        return $this->time;
72
    }
73
74
    public function getEvent()
75
    {
76
        return $this->event;
77
    }
78
79
    public function setEvent($event)
80
    {
81
        $this->event = $event;
82
    }
83
84
    /**
85
     * public method to determine whether the cache can be used
86
     *
87
     * to assist in centralisation of event triggering and calculation of cache statistics,
88
     * don't override this function override makeDefaultCacheDecision()
89
     *
90
     * @param  array $depends array of cache dependencies, support dependecies:
91
     *                            'age'   => max age of the cache in seconds
92
     *                            'files' => cache must be younger than mtime of each file
93
     *                                       (nb. dependency passes if file doesn't exist)
94
     *
95
     * @return bool    true if cache can be used, false otherwise
96
     */
97
    public function useCache($depends = array())
98
    {
99
        $this->depends = $depends;
100
        $this->addDependencies();
101
102
        if ($this->event) {
103
            return $this->stats(trigger_event($this->event, $this, array($this, 'makeDefaultCacheDecision')));
104
        } else {
105
            return $this->stats($this->makeDefaultCacheDecision());
106
        }
107
    }
108
109
    /**
110
     * internal method containing cache use decision logic
111
     *
112
     * this function processes the following keys in the depends array
113
     *   purge - force a purge on any non empty value
114
     *   age   - expire cache if older than age (seconds)
115
     *   files - expire cache if any file in this array was updated more recently than the cache
116
     *
117
     * Note that this function needs to be public as it is used as callback for the event handler
118
     *
119
     * can be overridden
120
     *
121
     * @internal This method may only be called by the event handler! Call \dokuwiki\Cache\Cache::useCache instead!
122
     *
123
     * @return bool               see useCache()
124
     */
125
    public function makeDefaultCacheDecision()
126
    {
127
128
        if ($this->nocache) {
129
            return false;
130
        }                              // caching turned off
131
        if (!empty($this->depends['purge'])) {
132
            return false;
133
        }              // purge requested?
134
        if (!($this->time = @filemtime($this->cache))) {
135
            return false;
136
        }   // cache exists?
137
138
        // cache too old?
139
        if (!empty($this->depends['age']) && ((time() - $this->time) > $this->depends['age'])) {
140
            return false;
141
        }
142
143
        if (!empty($this->depends['files'])) {
144
            foreach ($this->depends['files'] as $file) {
145
                if ($this->time <= @filemtime($file)) {
146
                    return false;
147
                }         // cache older than files it depends on?
148
            }
149
        }
150
151
        return true;
152
    }
153
154
    /**
155
     * add dependencies to the depends array
156
     *
157
     * this method should only add dependencies,
158
     * it should not remove any existing dependencies and
159
     * it should only overwrite a dependency when the new value is more stringent than the old
160
     */
161
    protected function addDependencies()
162
    {
163
        global $INPUT;
164
        if ($INPUT->has('purge')) {
165
            $this->depends['purge'] = true;
166
        }   // purge requested
167
    }
168
169
    /**
170
     * retrieve the cached data
171
     *
172
     * @param   bool $clean true to clean line endings, false to leave line endings alone
173
     * @return  string          cache contents
174
     */
175
    public function retrieveCache($clean = true)
176
    {
177
        return io_readFile($this->cache, $clean);
178
    }
179
180
    /**
181
     * cache $data
182
     *
183
     * @param   string $data the data to be cached
184
     * @return  bool           true on success, false otherwise
185
     */
186
    public function storeCache($data)
187
    {
188
        if ($this->nocache) {
189
            return false;
190
        }
191
192
        return io_savefile($this->cache, $data);
193
    }
194
195
    /**
196
     * remove any cached data associated with this cache instance
197
     */
198
    public function removeCache()
199
    {
200
        @unlink($this->cache);
201
    }
202
203
    /**
204
     * Record cache hits statistics.
205
     * (Only when debugging allowed, to reduce overhead.)
206
     *
207
     * @param    bool $success result of this cache use attempt
208
     * @return   bool              pass-thru $success value
209
     */
210
    protected function stats($success)
211
    {
212
        global $conf;
213
        static $stats = null;
214
        static $file;
215
216
        if (!$conf['allowdebug']) {
217
            return $success;
218
        }
219
220
        if (is_null($stats)) {
221
            $file = $conf['cachedir'] . '/cache_stats.txt';
222
            $lines = explode("\n", io_readFile($file));
223
224
            foreach ($lines as $line) {
225
                $i = strpos($line, ',');
226
                $stats[substr($line, 0, $i)] = $line;
227
            }
228
        }
229
230
        if (isset($stats[$this->ext])) {
231
            list($ext, $count, $hits) = explode(',', $stats[$this->ext]);
232
        } else {
233
            $ext = $this->ext;
234
            $count = 0;
235
            $hits = 0;
236
        }
237
238
        $count++;
239
        if ($success) {
240
            $hits++;
241
        }
242
        $stats[$this->ext] = "$ext,$count,$hits";
243
244
        io_saveFile($file, join("\n", $stats));
245
246
        return $success;
247
    }
248
249
    /**
250
     * @return bool
251
     */
252
    public function isNoCache()
253
    {
254
        return $this->nocache;
255
    }
256
}
257