Commit | Line | Data |
---|---|---|
8139ad13 SH |
1 | <?php |
2 | // This file is part of Moodle - http://moodle.org/ | |
3 | // | |
4 | // Moodle is free software: you can redistribute it and/or modify | |
5 | // it under the terms of the GNU General Public License as published by | |
6 | // the Free Software Foundation, either version 3 of the License, or | |
7 | // (at your option) any later version. | |
8 | // | |
9 | // Moodle is distributed in the hope that it will be useful, | |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | // GNU General Public License for more details. | |
13 | // | |
14 | // You should have received a copy of the GNU General Public License | |
15 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
17 | /** | |
18 | * Cache helper class | |
19 | * | |
20 | * This file is part of Moodle's cache API, affectionately called MUC. | |
21 | * It contains the components that are requried in order to use caching. | |
22 | * | |
23 | * @package core | |
24 | * @category cache | |
25 | * @copyright 2012 Sam Hemelryk | |
26 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
27 | */ | |
28 | ||
29 | defined('MOODLE_INTERNAL') || die(); | |
30 | ||
31 | /** | |
32 | * The cache helper class. | |
33 | * | |
34 | * The cache helper class provides common functionality to the cache API and is useful to developers within to interact with | |
35 | * the cache API in a general way. | |
36 | * | |
37 | * @package core | |
38 | * @category cache | |
39 | * @copyright 2012 Sam Hemelryk | |
40 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
41 | */ | |
42 | class cache_helper { | |
43 | ||
44 | /** | |
45 | * Statistics gathered by the cache API during its operation will be used here. | |
46 | * @static | |
47 | * @var array | |
48 | */ | |
49 | protected static $stats = array(); | |
50 | ||
51 | /** | |
52 | * The instance of the cache helper. | |
53 | * @var cache_helper | |
54 | */ | |
55 | protected static $instance; | |
56 | ||
7634410b SH |
57 | /** |
58 | * The site identifier used by the cache. | |
59 | * Set the first time get_site_identifier is called. | |
60 | * @var string | |
61 | */ | |
62 | protected static $siteidentifier = null; | |
63 | ||
8139ad13 SH |
64 | /** |
65 | * Returns true if the cache API can be initialised before Moodle has finished initialising itself. | |
66 | * | |
67 | * This check is essential when trying to cache the likes of configuration information. It checks to make sure that the cache | |
68 | * configuration file has been created which allows use to set up caching when ever is required. | |
69 | * | |
70 | * @return bool | |
71 | */ | |
72 | public static function ready_for_early_init() { | |
73 | return cache_config::config_file_exists(); | |
74 | } | |
75 | ||
76 | /** | |
77 | * Returns an instance of the cache_helper. | |
78 | * | |
79 | * This is designed for internal use only and acts as a static store. | |
80 | * @staticvar null $instance | |
81 | * @return cache_helper | |
82 | */ | |
83 | protected static function instance() { | |
84 | if (is_null(self::$instance)) { | |
85 | self::$instance = new cache_helper(); | |
86 | } | |
87 | return self::$instance; | |
88 | } | |
89 | ||
90 | /** | |
91 | * Constructs an instance of the cache_helper class. Again for internal use only. | |
92 | */ | |
93 | protected function __construct() { | |
94 | // Nothing to do here, just making sure you can't get an instance of this. | |
95 | } | |
96 | ||
97 | /** | |
98 | * Used as a data store for initialised definitions. | |
99 | * @var array | |
100 | */ | |
101 | protected $definitions = array(); | |
102 | ||
103 | /** | |
104 | * Used as a data store for initialised cache stores | |
105 | * We use this because we want to avoid establishing multiple instances of a single store. | |
106 | * @var array | |
107 | */ | |
108 | protected $stores = array(); | |
109 | ||
110 | /** | |
111 | * Returns the class for use as a cache loader for the given mode. | |
112 | * | |
113 | * @param int $mode One of cache_store::MODE_ | |
114 | * @return string | |
115 | * @throws coding_exception | |
116 | */ | |
117 | public static function get_class_for_mode($mode) { | |
118 | switch ($mode) { | |
119 | case cache_store::MODE_APPLICATION : | |
120 | return 'cache_application'; | |
121 | case cache_store::MODE_REQUEST : | |
122 | return 'cache_request'; | |
123 | case cache_store::MODE_SESSION : | |
124 | return 'cache_session'; | |
125 | } | |
126 | throw new coding_exception('Unknown cache mode passed. Must be one of cache_store::MODE_*'); | |
127 | } | |
128 | ||
129 | /** | |
130 | * Returns the cache stores to be used with the given definition. | |
131 | * @param cache_definition $definition | |
132 | * @return array | |
133 | */ | |
134 | public static function get_cache_stores(cache_definition $definition) { | |
135 | $instance = cache_config::instance(); | |
136 | $stores = $instance->get_stores_for_definition($definition); | |
6fec1820 | 137 | $stores = self::initialise_cachestore_instances($stores, $definition); |
8139ad13 SH |
138 | return $stores; |
139 | } | |
140 | ||
141 | /** | |
142 | * Internal function for initialising an array of stores against a given cache definition. | |
143 | * | |
144 | * @param array $stores | |
145 | * @param cache_definition $definition | |
146 | * @return array | |
147 | */ | |
6fec1820 | 148 | protected static function initialise_cachestore_instances(array $stores, cache_definition $definition) { |
8139ad13 SH |
149 | $return = array(); |
150 | $factory = cache_factory::instance(); | |
151 | foreach ($stores as $name => $details) { | |
152 | $store = $factory->create_store_from_config($name, $details, $definition); | |
153 | if ($store !== false) { | |
154 | $return[] = $store; | |
155 | } | |
156 | } | |
157 | return $return; | |
158 | } | |
159 | ||
160 | /** | |
34c84c72 SH |
161 | * Returns a cache_lock instance suitable for use with the store. |
162 | * | |
163 | * @param cache_store $store | |
164 | * @return cache_lock_interface | |
8139ad13 | 165 | */ |
34c84c72 | 166 | public static function get_cachelock_for_store(cache_store $store) { |
8139ad13 | 167 | $instance = cache_config::instance(); |
34c84c72 SH |
168 | $lockconf = $instance->get_lock_for_store($store->my_name()); |
169 | $factory = cache_factory::instance(); | |
170 | return $factory->create_lock_instance($lockconf); | |
8139ad13 SH |
171 | } |
172 | ||
173 | /** | |
174 | * Returns an array of plugins without using core methods. | |
175 | * | |
176 | * This function explicitly does NOT use core functions as it will in some circumstances be called before Moodle has | |
177 | * finished initialising. This happens when loading configuration for instance. | |
178 | * | |
179 | * @return string | |
180 | */ | |
181 | public static function early_get_cache_plugins() { | |
182 | global $CFG; | |
183 | $result = array(); | |
184 | $ignored = array('CVS', '_vti_cnf', 'simpletest', 'db', 'yui', 'tests'); | |
185 | $fulldir = $CFG->dirroot.'/cache/stores'; | |
186 | $items = new DirectoryIterator($fulldir); | |
187 | foreach ($items as $item) { | |
188 | if ($item->isDot() or !$item->isDir()) { | |
189 | continue; | |
190 | } | |
191 | $pluginname = $item->getFilename(); | |
192 | if (in_array($pluginname, $ignored)) { | |
193 | continue; | |
194 | } | |
5f850735 | 195 | if (!is_valid_plugin_name($pluginname)) { |
170f821b | 196 | // Better ignore plugins with problematic names here. |
8139ad13 SH |
197 | continue; |
198 | } | |
199 | $result[$pluginname] = $fulldir.'/'.$pluginname; | |
200 | unset($item); | |
201 | } | |
202 | unset($items); | |
203 | return $result; | |
204 | } | |
205 | ||
206 | /** | |
207 | * Invalidates a given set of keys from a given definition. | |
208 | * | |
209 | * @todo Invalidating by definition should also add to the event cache so that sessions can be invalidated (when required). | |
210 | * | |
211 | * @param string $component | |
212 | * @param string $area | |
213 | * @param array $identifiers | |
214 | * @param array $keys | |
215 | * @return boolean | |
216 | */ | |
217 | public static function invalidate_by_definition($component, $area, array $identifiers = array(), $keys = array()) { | |
218 | $cache = cache::make($component, $area, $identifiers); | |
219 | if (is_array($keys)) { | |
220 | $cache->delete_many($keys); | |
221 | } else if (is_scalar($keys)) { | |
222 | $cache->delete($keys); | |
223 | } else { | |
224 | throw new coding_exception('cache_helper::invalidate_by_definition only accepts $keys as array, or scalar.'); | |
225 | } | |
226 | return true; | |
227 | } | |
228 | ||
229 | /** | |
230 | * Invalidates a given set of keys by means of an event. | |
231 | * | |
232 | * @todo add support for identifiers to be supplied and utilised. | |
233 | * | |
234 | * @param string $event | |
235 | * @param array $keys | |
236 | */ | |
237 | public static function invalidate_by_event($event, array $keys) { | |
238 | $instance = cache_config::instance(); | |
239 | $invalidationeventset = false; | |
240 | $factory = cache_factory::instance(); | |
fb0eaa3f | 241 | $inuse = $factory->get_caches_in_use(); |
8139ad13 SH |
242 | foreach ($instance->get_definitions() as $name => $definitionarr) { |
243 | $definition = cache_definition::load($name, $definitionarr); | |
244 | if ($definition->invalidates_on_event($event)) { | |
fb0eaa3f SH |
245 | // First up check if there is a cache loader for this definition already. |
246 | // If there is we need to invalidate the keys from there. | |
247 | $definitionkey = $definition->get_component().'/'.$definition->get_area(); | |
248 | if (isset($inuse[$definitionkey])) { | |
249 | $inuse[$definitionkey]->delete_many($keys); | |
8139ad13 SH |
250 | } |
251 | ||
fb0eaa3f SH |
252 | // We should only log events for application and session caches. |
253 | // Request caches shouldn't have events as all data is lost at the end of the request. | |
254 | // Events should only be logged once of course and likely several definitions are watching so we | |
255 | // track its logging with $invalidationeventset. | |
256 | $logevent = ($invalidationeventset === false && $definition->get_mode() !== cache_store::MODE_REQUEST); | |
257 | ||
258 | if ($logevent) { | |
8139ad13 SH |
259 | // Get the event invalidation cache. |
260 | $cache = cache::make('core', 'eventinvalidation'); | |
261 | // Get any existing invalidated keys for this cache. | |
262 | $data = $cache->get($event); | |
263 | if ($data === false) { | |
264 | // There are none. | |
265 | $data = array(); | |
266 | } | |
267 | // Add our keys to them with the current cache timestamp. | |
268 | foreach ($keys as $key) { | |
269 | $data[$key] = cache::now(); | |
270 | } | |
271 | // Set that data back to the cache. | |
272 | $cache->set($event, $data); | |
273 | // This only needs to occur once. | |
274 | $invalidationeventset = true; | |
275 | } | |
276 | } | |
277 | } | |
278 | } | |
279 | ||
280 | /** | |
281 | * Purges the cache for a specific definition. | |
282 | * | |
5f5776c1 SH |
283 | * If you need to purge a definition that requires identifiers or an aggregate and you don't |
284 | * know the details of those please use cache_helper::purge_stores_used_by_definition instead. | |
285 | * It is a more aggressive purge and will purge all data within the store, not just the data | |
286 | * belonging to the given definition. | |
287 | * | |
942be4a5 MS |
288 | * @todo MDL-36660: Change the signature: $aggregate must be added. |
289 | * | |
8139ad13 SH |
290 | * @param string $component |
291 | * @param string $area | |
292 | * @param array $identifiers | |
293 | * @return bool | |
294 | */ | |
295 | public static function purge_by_definition($component, $area, array $identifiers = array()) { | |
170f821b | 296 | // Create the cache. |
8139ad13 | 297 | $cache = cache::make($component, $area, $identifiers); |
942be4a5 MS |
298 | // Initialise, in case of a store. |
299 | if ($cache instanceof cache_store) { | |
300 | $factory = cache_factory::instance(); | |
301 | // TODO MDL-36660: Providing $aggregate is required for purging purposes: $definition->get_id() | |
302 | $definition = $factory->create_definition($component, $area, null); | |
303 | $definition->set_identifiers($identifiers); | |
304 | $cache->initialise($definition); | |
305 | } | |
8139ad13 SH |
306 | // Purge baby, purge. |
307 | $cache->purge(); | |
308 | return true; | |
309 | } | |
310 | ||
311 | /** | |
312 | * Purges a cache of all information on a given event. | |
313 | * | |
314 | * @param string $event | |
315 | */ | |
316 | public static function purge_by_event($event) { | |
317 | $instance = cache_config::instance(); | |
318 | $invalidationeventset = false; | |
319 | $factory = cache_factory::instance(); | |
fb0eaa3f | 320 | $inuse = $factory->get_caches_in_use(); |
8139ad13 SH |
321 | foreach ($instance->get_definitions() as $name => $definitionarr) { |
322 | $definition = cache_definition::load($name, $definitionarr); | |
323 | if ($definition->invalidates_on_event($event)) { | |
fb0eaa3f SH |
324 | // First up check if there is a cache loader for this definition already. |
325 | // If there is we need to invalidate the keys from there. | |
326 | $definitionkey = $definition->get_component().'/'.$definition->get_area(); | |
327 | if (isset($inuse[$definitionkey])) { | |
328 | $inuse[$definitionkey]->purge(); | |
942be4a5 | 329 | } |
fb0eaa3f SH |
330 | |
331 | // We should only log events for application and session caches. | |
332 | // Request caches shouldn't have events as all data is lost at the end of the request. | |
333 | // Events should only be logged once of course and likely several definitions are watching so we | |
334 | // track its logging with $invalidationeventset. | |
335 | $logevent = ($invalidationeventset === false && $definition->get_mode() !== cache_store::MODE_REQUEST); | |
336 | ||
8139ad13 | 337 | // We need to flag the event in the "Event invalidation" cache if it hasn't already happened. |
fb0eaa3f | 338 | if ($logevent && $invalidationeventset === false) { |
8139ad13 SH |
339 | // Get the event invalidation cache. |
340 | $cache = cache::make('core', 'eventinvalidation'); | |
170f821b | 341 | // Create a key to invalidate all. |
8139ad13 SH |
342 | $data = array( |
343 | 'purged' => cache::now() | |
344 | ); | |
345 | // Set that data back to the cache. | |
346 | $cache->set($event, $data); | |
347 | // This only needs to occur once. | |
348 | $invalidationeventset = true; | |
349 | } | |
350 | } | |
351 | } | |
352 | } | |
353 | ||
354 | /** | |
355 | * Ensure that the stats array is ready to collect information for the given store and definition. | |
356 | * @param string $store | |
357 | * @param string $definition | |
358 | */ | |
359 | protected static function ensure_ready_for_stats($store, $definition) { | |
8af5bb54 | 360 | // This function is performance-sensitive, so exit as quickly as possible |
361 | // if we do not need to do anything. | |
362 | if (isset(self::$stats[$definition][$store])) { | |
363 | return; | |
364 | } | |
8139ad13 SH |
365 | if (!array_key_exists($definition, self::$stats)) { |
366 | self::$stats[$definition] = array( | |
367 | $store => array( | |
368 | 'hits' => 0, | |
369 | 'misses' => 0, | |
370 | 'sets' => 0, | |
371 | ) | |
372 | ); | |
373 | } else if (!array_key_exists($store, self::$stats[$definition])) { | |
374 | self::$stats[$definition][$store] = array( | |
375 | 'hits' => 0, | |
376 | 'misses' => 0, | |
377 | 'sets' => 0, | |
378 | ); | |
379 | } | |
380 | } | |
381 | ||
382 | /** | |
383 | * Record a cache hit in the stats for the given store and definition. | |
384 | * | |
385 | * @param string $store | |
386 | * @param string $definition | |
387 | */ | |
388 | public static function record_cache_hit($store, $definition) { | |
389 | self::ensure_ready_for_stats($store, $definition); | |
390 | self::$stats[$definition][$store]['hits']++; | |
391 | } | |
392 | ||
393 | /** | |
394 | * Record a cache miss in the stats for the given store and definition. | |
395 | * | |
396 | * @param string $store | |
397 | * @param string $definition | |
398 | */ | |
399 | public static function record_cache_miss($store, $definition) { | |
400 | self::ensure_ready_for_stats($store, $definition); | |
401 | self::$stats[$definition][$store]['misses']++; | |
402 | } | |
403 | ||
404 | /** | |
405 | * Record a cache set in the stats for the given store and definition. | |
406 | * | |
407 | * @param string $store | |
408 | * @param string $definition | |
409 | */ | |
410 | public static function record_cache_set($store, $definition) { | |
411 | self::ensure_ready_for_stats($store, $definition); | |
412 | self::$stats[$definition][$store]['sets']++; | |
413 | } | |
414 | ||
415 | /** | |
416 | * Return the stats collected so far. | |
417 | * @return array | |
418 | */ | |
419 | public static function get_stats() { | |
420 | return self::$stats; | |
421 | } | |
422 | ||
423 | /** | |
424 | * Purge all of the cache stores of all of their data. | |
34c84c72 SH |
425 | * |
426 | * Think twice before calling this method. It will purge **ALL** caches regardless of whether they have been used recently or | |
427 | * anything. This will involve full setup of the cache + the purge operation. On a site using caching heavily this WILL be | |
428 | * painful. | |
b0dd08dd SH |
429 | * |
430 | * @param bool $usewriter If set to true the cache_config_writer class is used. This class is special as it avoids | |
431 | * it is still usable when caches have been disabled. | |
432 | * Please use this option only if you really must. It's purpose is to allow the cache to be purged when it would be | |
433 | * otherwise impossible. | |
8139ad13 | 434 | */ |
b0dd08dd SH |
435 | public static function purge_all($usewriter = false) { |
436 | $factory = cache_factory::instance(); | |
437 | $config = $factory->create_config_instance($usewriter); | |
fb8305de | 438 | foreach ($config->get_all_stores() as $store) { |
b0dd08dd | 439 | self::purge_store($store['name'], $config); |
8139ad13 SH |
440 | } |
441 | } | |
442 | ||
170f821b SH |
443 | /** |
444 | * Purges a store given its name. | |
445 | * | |
446 | * @param string $storename | |
b0dd08dd | 447 | * @param cache_config $config |
170f821b SH |
448 | * @return bool |
449 | */ | |
b0dd08dd SH |
450 | public static function purge_store($storename, cache_config $config = null) { |
451 | if ($config === null) { | |
452 | $config = cache_config::instance(); | |
453 | } | |
fb8305de MS |
454 | |
455 | $stores = $config->get_all_stores(); | |
456 | if (!array_key_exists($storename, $stores)) { | |
457 | // The store does not exist. | |
458 | return false; | |
459 | } | |
460 | ||
461 | $store = $stores[$storename]; | |
462 | $class = $store['class']; | |
463 | ||
464 | // Found the store: is it ready? | |
465 | $instance = new $class($store['name'], $store['configuration']); | |
466 | if (!$instance->is_ready()) { | |
467 | unset($instance); | |
468 | return false; | |
469 | } | |
470 | ||
471 | foreach ($config->get_definitions_by_store($storename) as $id => $definition) { | |
472 | $definition = cache_definition::load($id, $definition); | |
b0dd08dd SH |
473 | $definitioninstance = clone($instance); |
474 | $definitioninstance->initialise($definition); | |
475 | $definitioninstance->purge(); | |
476 | unset($definitioninstance); | |
170f821b | 477 | } |
fb8305de MS |
478 | |
479 | return true; | |
170f821b SH |
480 | } |
481 | ||
5f5776c1 SH |
482 | /** |
483 | * Purges all of the stores used by a definition. | |
484 | * | |
485 | * Unlike cache_helper::purge_by_definition this purges all of the data from the stores not | |
486 | * just the data relating to the definition. | |
487 | * This function is useful when you must purge a definition that requires setup but you don't | |
488 | * want to set it up. | |
489 | * | |
490 | * @param string $component | |
491 | * @param string $area | |
492 | */ | |
493 | public static function purge_stores_used_by_definition($component, $area) { | |
494 | $factory = cache_factory::instance(); | |
495 | $config = $factory->create_config_instance(); | |
496 | $definition = $factory->create_definition($component, $area); | |
497 | $stores = $config->get_stores_for_definition($definition); | |
498 | foreach ($stores as $store) { | |
499 | self::purge_store($store['name']); | |
500 | } | |
501 | } | |
502 | ||
8139ad13 SH |
503 | /** |
504 | * Returns the translated name of the definition. | |
505 | * | |
506 | * @param cache_definition $definition | |
507 | * @return lang_string | |
508 | */ | |
509 | public static function get_definition_name($definition) { | |
510 | if ($definition instanceof cache_definition) { | |
511 | return $definition->get_name(); | |
512 | } | |
513 | $identifier = 'cachedef_'.clean_param($definition['area'], PARAM_STRINGID); | |
514 | $component = $definition['component']; | |
515 | if ($component === 'core') { | |
516 | $component = 'cache'; | |
517 | } | |
518 | return new lang_string($identifier, $component); | |
519 | } | |
520 | ||
521 | /** | |
47834bcd SH |
522 | * Hashes a descriptive key to make it shorter and still unique. |
523 | * @param string|int $key | |
524 | * @param cache_definition $definition | |
8139ad13 SH |
525 | * @return string |
526 | */ | |
47834bcd SH |
527 | public static function hash_key($key, cache_definition $definition) { |
528 | if ($definition->uses_simple_keys()) { | |
3d164e1e | 529 | if (debugging() && preg_match('#[^a-zA-Z0-9_]#', $key)) { |
5dd68a75 SH |
530 | throw new coding_exception('Cache definition '.$definition->get_id().' requires simple keys. Invalid key provided.', $key); |
531 | } | |
702651c7 SH |
532 | // We put the key first so that we can be sure the start of the key changes. |
533 | return (string)$key . '-' . $definition->generate_single_key_prefix(); | |
47834bcd SH |
534 | } |
535 | $key = $definition->generate_single_key_prefix() . '-' . $key; | |
536 | return sha1($key); | |
8139ad13 | 537 | } |
75af47ee SH |
538 | |
539 | /** | |
540 | * Finds all definitions and updates them within the cache config file. | |
541 | * | |
542 | * @param bool $coreonly If set to true only core definitions will be updated. | |
543 | */ | |
544 | public static function update_definitions($coreonly = false) { | |
545 | global $CFG; | |
7866b094 | 546 | // Include locallib. |
75af47ee SH |
547 | require_once($CFG->dirroot.'/cache/locallib.php'); |
548 | // First update definitions | |
549 | cache_config_writer::update_definitions($coreonly); | |
550 | // Second reset anything we have already initialised to ensure we're all up to date. | |
551 | cache_factory::reset(); | |
552 | } | |
e0d9b7c0 SH |
553 | |
554 | /** | |
555 | * Update the site identifier stored by the cache API. | |
556 | * | |
557 | * @param string $siteidentifier | |
fe86ebfa | 558 | * @return string The new site identifier. |
e0d9b7c0 SH |
559 | */ |
560 | public static function update_site_identifier($siteidentifier) { | |
561 | global $CFG; | |
7866b094 | 562 | // Include locallib. |
e0d9b7c0 SH |
563 | require_once($CFG->dirroot.'/cache/locallib.php'); |
564 | $factory = cache_factory::instance(); | |
565 | $factory->updating_started(); | |
566 | $config = $factory->create_config_instance(true); | |
fe86ebfa | 567 | $siteidentifier = $config->update_site_identifier($siteidentifier); |
e0d9b7c0 SH |
568 | $factory->updating_finished(); |
569 | cache_factory::reset(); | |
fe86ebfa | 570 | return $siteidentifier; |
e0d9b7c0 | 571 | } |
7634410b SH |
572 | |
573 | /** | |
574 | * Returns the site identifier. | |
575 | * | |
576 | * @return string | |
577 | */ | |
578 | public static function get_site_identifier() { | |
fe86ebfa SH |
579 | global $CFG; |
580 | if (!is_null(self::$siteidentifier)) { | |
581 | return self::$siteidentifier; | |
582 | } | |
583 | // If site identifier hasn't been collected yet attempt to get it from the cache config. | |
584 | $factory = cache_factory::instance(); | |
585 | // If the factory is initialising then we don't want to try to get it from the config or we risk | |
586 | // causing the cache to enter an infinite initialisation loop. | |
587 | if (!$factory->is_initialising()) { | |
7634410b SH |
588 | $config = $factory->create_config_instance(); |
589 | self::$siteidentifier = $config->get_site_identifier(); | |
590 | } | |
fe86ebfa SH |
591 | if (is_null(self::$siteidentifier)) { |
592 | // If the site identifier is still null then config isn't aware of it yet. | |
593 | // We'll see if the CFG is loaded, and if not we will just use unknown. | |
594 | // It's very important here that we don't use get_config. We don't want an endless cache loop! | |
a3b63be7 | 595 | if (!empty($CFG->siteidentifier)) { |
fe86ebfa SH |
596 | self::$siteidentifier = self::update_site_identifier($CFG->siteidentifier); |
597 | } else { | |
598 | // It's not being recorded in MUC's config and the config data hasn't been loaded yet. | |
599 | // Likely we are initialising. | |
600 | return 'unknown'; | |
601 | } | |
602 | } | |
7634410b SH |
603 | return self::$siteidentifier; |
604 | } | |
605 | ||
606 | /** | |
607 | * Returns the site version. | |
608 | * | |
609 | * @return string | |
610 | */ | |
611 | public static function get_site_version() { | |
612 | global $CFG; | |
613 | return (string)$CFG->version; | |
614 | } | |
059102e7 SH |
615 | |
616 | /** | |
617 | * Runs cron routines for MUC. | |
618 | */ | |
619 | public static function cron() { | |
620 | self::clean_old_session_data(true); | |
621 | } | |
622 | ||
623 | /** | |
624 | * Cleans old session data from cache stores used for session based definitions. | |
625 | * | |
626 | * @param bool $output If set to true output will be given. | |
627 | */ | |
628 | public static function clean_old_session_data($output = false) { | |
629 | global $CFG; | |
630 | if ($output) { | |
631 | mtrace('Cleaning up stale session data from cache stores.'); | |
632 | } | |
633 | $factory = cache_factory::instance(); | |
634 | $config = $factory->create_config_instance(); | |
635 | $definitions = $config->get_definitions(); | |
636 | $purgetime = time() - $CFG->sessiontimeout; | |
637 | foreach ($definitions as $definitionarray) { | |
638 | // We are only interested in session caches. | |
639 | if (!($definitionarray['mode'] & cache_store::MODE_SESSION)) { | |
640 | continue; | |
641 | } | |
642 | $definition = $factory->create_definition($definitionarray['component'], $definitionarray['area']); | |
643 | $stores = $config->get_stores_for_definition($definition); | |
5cba0c4b SH |
644 | // Turn them into store instances. |
645 | $stores = self::initialise_cachestore_instances($stores, $definition); | |
059102e7 | 646 | // Initialise all of the stores used for that definition. |
5cba0c4b | 647 | foreach ($stores as $store) { |
059102e7 SH |
648 | // If the store doesn't support searching we can skip it. |
649 | if (!($store instanceof cache_is_searchable)) { | |
650 | debugging('Cache stores used for session definitions should ideally be searchable.', DEBUG_DEVELOPER); | |
651 | continue; | |
652 | } | |
653 | // Get all of the keys. | |
654 | $keys = $store->find_by_prefix(cache_session::KEY_PREFIX); | |
655 | $todelete = array(); | |
656 | foreach ($store->get_many($keys) as $key => $value) { | |
657 | if (strpos($key, cache_session::KEY_PREFIX) !== 0 || !is_array($value) || !isset($value['lastaccess'])) { | |
658 | continue; | |
659 | } | |
660 | if ((int)$value['lastaccess'] < $purgetime || true) { | |
661 | $todelete[] = $key; | |
662 | } | |
663 | } | |
664 | if (count($todelete)) { | |
665 | $outcome = (int)$store->delete_many($todelete); | |
666 | if ($output) { | |
667 | $strdef = s($definition->get_id()); | |
668 | $strstore = s($store->my_name()); | |
669 | mtrace("- Removed {$outcome} old {$strdef} sessions from the '{$strstore}' cache store."); | |
670 | } | |
671 | } | |
672 | } | |
673 | } | |
674 | } | |
7866b094 | 675 | } |