1
|
|
|
<?php |
|
|
|
|
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file contains functions that deal with getting and setting cache values. |
5
|
|
|
* |
6
|
|
|
* @name ElkArte Forum |
7
|
|
|
* @copyright ElkArte Forum contributors |
8
|
|
|
* @license BSD http://opensource.org/licenses/BSD-3-Clause |
9
|
|
|
* |
10
|
|
|
* This software is a derived product, based on: |
11
|
|
|
* |
12
|
|
|
* Simple Machines Forum (SMF) |
13
|
|
|
* copyright: 2011 Simple Machines (http://www.simplemachines.org) |
14
|
|
|
* license: BSD, See included LICENSE.TXT for terms and conditions. |
15
|
|
|
* |
16
|
|
|
* @version 1.1 dev Release Candidate 1 |
17
|
|
|
* |
18
|
|
|
*/ |
19
|
|
|
|
20
|
|
|
if (!defined('ELK')) |
21
|
|
|
die('No access...'); |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Class Cache - Methods that deal with getting and setting cache values. |
25
|
|
|
*/ |
26
|
|
|
class Cache |
27
|
|
|
{ |
28
|
|
|
/** |
29
|
|
|
* Holds our static instance of the class |
30
|
|
|
* @var object |
31
|
|
|
*/ |
32
|
|
|
protected static $_instance = null; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Array of options for the methods (if needed) |
36
|
|
|
* @var mixed[] |
37
|
|
|
*/ |
38
|
|
|
protected $_options = array(); |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* If the cache is enabled or not. |
42
|
|
|
* @var bool |
43
|
|
|
*/ |
44
|
|
|
protected $enabled = false; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* The caching level |
48
|
|
|
* @var int |
49
|
|
|
*/ |
50
|
|
|
protected $level = 0; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* The prefix to append to the cache key |
54
|
|
|
* @var string |
55
|
|
|
*/ |
56
|
|
|
protected $_key_prefix = null; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* The caching object |
60
|
|
|
* @var object |
61
|
|
|
*/ |
62
|
|
|
protected $_cache_obj = null; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Initialize the class, defines the options and the caching method to use |
66
|
|
|
* |
67
|
|
|
* @param int $level The level of caching |
68
|
|
|
* @param string $accelerator The accelerator used |
69
|
|
|
* @param mixed[] $options Any setting necessary to the caching engine |
70
|
|
|
*/ |
71
|
|
|
public function __construct($level, $accelerator, $options) |
72
|
|
|
{ |
73
|
|
|
$this->setLevel($level); |
74
|
|
|
|
75
|
|
|
if ($level > 0) |
76
|
|
|
{ |
77
|
|
|
$this->enable(true); |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
// If the cache is disabled just go out |
81
|
|
|
if (!$this->isEnabled()) |
82
|
|
|
return; |
83
|
|
|
|
84
|
|
|
$this->_options = $options; |
85
|
|
|
|
86
|
|
|
if (empty($accelerator)) |
87
|
|
|
$accelerator = 'filebased'; |
88
|
|
|
|
89
|
|
|
$cache_class = '\\ElkArte\\sources\\subs\\CacheMethod\\' . ucfirst($accelerator); |
90
|
|
|
$this->_cache_obj = new $cache_class($this->_options); |
91
|
|
|
|
92
|
|
|
if ($this->_cache_obj !== null) |
93
|
|
|
$this->_cache_enable = $this->_cache_obj->init(); |
|
|
|
|
94
|
|
|
|
95
|
|
|
$this->_build_prefix(); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* Try to retrieve a cache entry. On failure, call the appropriate function. |
100
|
|
|
* This callback is sent as $file to include, and $function to call, with |
101
|
|
|
* $params parameters. |
102
|
|
|
* |
103
|
|
|
* @param string $key cache entry key |
104
|
|
|
* @param string $file file to include |
105
|
|
|
* @param string $function function to call |
106
|
|
|
* @param mixed[] $params parameters sent to the function |
107
|
|
|
* @param int $level = 1 |
108
|
|
|
* @return string |
109
|
|
|
*/ |
110
|
|
|
public function quick_get($key, $file, $function, $params, $level = 1) |
111
|
|
|
{ |
112
|
|
|
if (!$this->isEnabled()) |
113
|
|
|
return; |
114
|
|
|
|
115
|
|
|
call_integration_hook('pre_cache_quick_get', array(&$key, &$file, &$function, &$params, &$level)); |
116
|
|
|
|
117
|
|
|
/* Refresh the cache if either: |
118
|
|
|
1. Caching is disabled. |
119
|
|
|
2. The cache level isn't high enough. |
120
|
|
|
3. The item has not been cached or the cached item expired. |
121
|
|
|
4. The cached item has a custom expiration condition evaluating to true. |
122
|
|
|
5. The expire time set in the cache item has passed (needed for Zend). |
123
|
|
|
*/ |
124
|
|
|
if ($this->level < $level || !is_array($cache_block = $this->get($key, 3600)) || (!empty($cache_block['refresh_eval']) && eval($cache_block['refresh_eval'])) || (!empty($cache_block['expires']) && $cache_block['expires'] < time())) |
|
|
|
|
125
|
|
|
{ |
126
|
|
|
require_once(SOURCEDIR . '/' . $file); |
127
|
|
|
$cache_block = call_user_func_array($function, $params); |
128
|
|
|
|
129
|
|
|
if ($this->_cache_enable >= $level) |
130
|
|
|
$this->put($key, $cache_block, $cache_block['expires'] - time()); |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
// Some cached data may need a freshening up after retrieval. |
134
|
|
|
if (!empty($cache_block['post_retri_eval'])) |
135
|
|
|
eval($cache_block['post_retri_eval']); |
|
|
|
|
136
|
|
|
|
137
|
|
|
call_integration_hook('post_cache_quick_get', array($cache_block)); |
138
|
|
|
|
139
|
|
|
return $cache_block['data']; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Puts value in the cache under key for ttl seconds. |
144
|
|
|
* |
145
|
|
|
* - It may "miss" so shouldn't be depended on |
146
|
|
|
* - Uses the cache engine chosen in the ACP and saved in settings.php |
147
|
|
|
* - It supports: |
148
|
|
|
* - Turck MMCache: http://turck-mmcache.sourceforge.net/index_old.html#api |
149
|
|
|
* - Xcache: http://xcache.lighttpd.net/wiki/XcacheApi |
150
|
|
|
* - memcache: http://www.php.net/memcache |
151
|
|
|
* - APC: http://www.php.net/apc |
152
|
|
|
* - eAccelerator: http://bart.eaccelerator.net/doc/phpdoc/ |
153
|
|
|
* - Zend: http://files.zend.com/help/Zend-Platform/output_cache_functions.htm |
154
|
|
|
* - Zend: http://files.zend.com/help/Zend-Platform/zend_cache_functions.htm |
155
|
|
|
* |
156
|
|
|
* @param string $key |
157
|
|
|
* @param string|int|mixed[]|null $value |
158
|
|
|
* @param int $ttl = 120 |
159
|
|
|
*/ |
160
|
14 |
|
public function put($key, $value, $ttl = 120) |
161
|
|
|
{ |
162
|
14 |
|
global $db_show_debug; |
163
|
|
|
|
164
|
14 |
|
if (!$this->isEnabled()) |
165
|
14 |
|
return; |
166
|
|
|
|
167
|
|
|
if ($db_show_debug === true) |
168
|
|
|
{ |
169
|
|
|
$cache_hit = array( |
170
|
|
|
'k' => $key, |
171
|
|
|
'd' => 'put', |
172
|
|
|
's' => $value === null ? 0 : strlen(serialize($value)) |
173
|
|
|
); |
174
|
|
|
$st = microtime(true); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
$key = $this->_key($key); |
178
|
|
|
$value = $value === null ? null : serialize($value); |
179
|
|
|
|
180
|
|
|
$this->_cache_obj->put($key, $value, $ttl); |
181
|
|
|
|
182
|
|
|
call_integration_hook('Cache::instance()->put', array($key, $value, $ttl)); |
183
|
|
|
|
184
|
|
|
if ($db_show_debug === true) |
185
|
|
|
{ |
186
|
|
|
$cache_hit['t'] = microtime(true) - $st; |
|
|
|
|
187
|
|
|
Debug::get()->cache($cache_hit); |
188
|
|
|
} |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* Gets the value from the cache specified by key, so long as it is not older than ttl seconds. |
193
|
|
|
* |
194
|
|
|
* - It may often "miss", so shouldn't be depended on. |
195
|
|
|
* - It supports the same as cache::put(). |
196
|
|
|
* |
197
|
|
|
* @param string $key |
198
|
|
|
* @param int $ttl = 120 |
199
|
|
|
*/ |
200
|
9 |
|
public function get($key, $ttl = 120) |
201
|
|
|
{ |
202
|
9 |
|
global $db_show_debug; |
203
|
|
|
|
204
|
9 |
|
if (!$this->isEnabled()) |
205
|
9 |
|
return; |
206
|
|
|
|
207
|
|
|
if ($db_show_debug === true) |
208
|
|
|
{ |
209
|
|
|
$cache_hit = array( |
210
|
|
|
'k' => $key, |
211
|
|
|
'd' => 'get' |
212
|
|
|
); |
213
|
|
|
$st = microtime(true); |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
$key = $this->_key($key); |
217
|
|
|
$value = $this->_cache_obj->get($key, $ttl); |
218
|
|
|
|
219
|
|
|
if ($db_show_debug === true) |
220
|
|
|
{ |
221
|
|
|
$cache_hit['t'] = microtime(true) - $st; |
|
|
|
|
222
|
|
|
$cache_hit['s'] = isset($value) ? strlen($value) : 0; |
223
|
|
|
Debug::get()->cache($cache_hit); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
call_integration_hook('Cache::instance()->get', array($key, $ttl, $value)); |
227
|
|
|
|
228
|
|
|
return empty($value) ? null : @unserialize($value); |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* Same as $this->get but sets $var to the result and return if it was a hit |
233
|
|
|
* |
234
|
|
|
* @param mixed $var The variable to be assigned the result |
235
|
|
|
* @param string $key |
236
|
|
|
* @param int $ttl |
237
|
|
|
* @return bool if it was a hit |
238
|
|
|
*/ |
239
|
8 |
|
public function getVar(&$var, $key, $ttl = 120) |
240
|
|
|
{ |
241
|
8 |
|
$var = $this->get($key, $ttl); |
242
|
8 |
|
return !$this->isMiss(); |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* Empty out the cache in use as best it can |
247
|
|
|
* |
248
|
|
|
* It may only remove the files of a certain type (if the $type parameter is given) |
249
|
|
|
* Type can be user, data or left blank |
250
|
|
|
* - user clears out user data |
251
|
|
|
* - data clears out system / opcode data |
252
|
|
|
* - If no type is specified will perform a complete cache clearing |
253
|
|
|
* For cache engines that do not distinguish on types, a full cache flush will be done |
254
|
|
|
* |
255
|
|
|
* @param string $type = '' |
256
|
|
|
*/ |
257
|
|
|
public function clean($type = '') |
258
|
|
|
{ |
259
|
|
|
if (!$this->isEnabled()) |
260
|
|
|
return; |
261
|
|
|
|
262
|
|
|
$this->_cache_obj->clean($type); |
263
|
|
|
|
264
|
|
|
// Invalidate cache, to be sure! |
265
|
|
|
// ... as long as CACHEDIR/index.php can be modified, anyway. |
266
|
|
|
@touch(CACHEDIR . '/index.php'); |
|
|
|
|
267
|
|
|
|
268
|
|
|
// Give addons a way to trigger cache cleaning. |
269
|
|
|
call_integration_hook('integrate_clean_cache'); |
270
|
|
|
|
271
|
|
|
clearstatcache(); |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* Enable or disable caching |
276
|
|
|
* |
277
|
|
|
* @param bool $enable |
278
|
|
|
* @return $this |
279
|
|
|
*/ |
280
|
|
|
public function enable($enable) |
281
|
|
|
{ |
282
|
|
|
$this->enabled = (bool) $enable; |
283
|
|
|
|
284
|
|
|
return $this; |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* Check if caching is enabled |
289
|
|
|
* @return bool |
290
|
|
|
*/ |
291
|
15 |
|
public function isEnabled() |
292
|
|
|
{ |
293
|
15 |
|
return $this->enabled; |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
/** |
297
|
|
|
* Set the caching level. Setting it to <= 0 disables caching |
298
|
|
|
* |
299
|
|
|
* @param int $level |
300
|
|
|
* @return $this |
301
|
|
|
*/ |
302
|
|
|
public function setLevel($level) |
303
|
|
|
{ |
304
|
|
|
$this->level = (int) $level; |
305
|
|
|
|
306
|
|
|
if ($this->level <= 0) |
307
|
|
|
{ |
308
|
|
|
$this->enable(false); |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
return $this; |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
/** |
315
|
|
|
* @return int |
316
|
|
|
*/ |
317
|
|
|
public function getLevel() |
318
|
|
|
{ |
319
|
|
|
return $this->level; |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
public function checkLevel($level) |
323
|
|
|
{ |
324
|
|
|
return $this->isEnabled() && $this->level >= $level; |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* @var bool If the result of the last get was a miss |
329
|
|
|
*/ |
330
|
8 |
|
public function isMiss() |
331
|
|
|
{ |
332
|
8 |
|
return $this->isEnabled() ? $this->_cache_obj->isMiss() : true; |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* Get the key for the cache. |
337
|
|
|
* |
338
|
|
|
* @param string $key |
339
|
|
|
* @return string |
340
|
|
|
*/ |
341
|
|
|
protected function _key($key) |
342
|
|
|
{ |
343
|
|
|
return $this->_key_prefix . $this->_cache_obj->fixkey($key); |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
/** |
347
|
|
|
* Set $_key_prefix to a "unique" value based on timestamp of a file |
348
|
|
|
*/ |
349
|
|
|
protected function _build_prefix() |
350
|
|
|
{ |
351
|
|
|
global $boardurl; |
352
|
|
|
|
353
|
|
|
$this->_key_prefix = md5($boardurl . filemtime(CACHEDIR . '/index.php')) . '-ELK-'; |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
/** |
357
|
|
|
* Find and return the instance of the Cache class if it exists, |
358
|
|
|
* or create it if it doesn't exist |
359
|
|
|
*/ |
360
|
15 |
|
public static function instance() |
361
|
|
|
{ |
362
|
15 |
|
if (self::$_instance === null) |
363
|
15 |
|
{ |
364
|
|
|
global $cache_accelerator, $cache_enable, $cache_uid, $cache_password; |
365
|
|
|
|
366
|
|
|
$options = array(); |
367
|
|
|
if ($cache_accelerator === 'xcache') |
368
|
|
|
{ |
369
|
|
|
$options = array( |
370
|
|
|
'cache_uid' => $cache_uid, |
371
|
|
|
'cache_password' => $cache_password, |
372
|
|
|
); |
373
|
|
|
} |
374
|
|
|
Elk_Autoloader::getInstance()->register(SUBSDIR . '/CacheMethod', '\\ElkArte\\sources\\subs\\CacheMethod'); |
375
|
|
|
|
376
|
|
|
self::$_instance = new Cache($cache_enable, $cache_accelerator, $options); |
377
|
|
|
} |
378
|
|
|
|
379
|
15 |
|
return self::$_instance; |
380
|
|
|
} |
381
|
|
|
} |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.