See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]
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 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 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); 137 $stores = self::initialise_cachestore_instances($stores, $definition); 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 cache_store[] 147 */ 148 protected static function initialise_cachestore_instances(array $stores, cache_definition $definition) { 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 /** 161 * Returns a cache_lock instance suitable for use with the store. 162 * 163 * @param cache_store $store 164 * @return cache_lock_interface 165 */ 166 public static function get_cachelock_for_store(cache_store $store) { 167 $instance = cache_config::instance(); 168 $lockconf = $instance->get_lock_for_store($store->my_name()); 169 $factory = cache_factory::instance(); 170 return $factory->create_lock_instance($lockconf); 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 } 195 if (!is_valid_plugin_name($pluginname)) { 196 // Better ignore plugins with problematic names here. 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 * Events cannot determine what identifiers might need to be cleared. Event based purge and invalidation 233 * are only supported on caches without identifiers. 234 * 235 * @param string $event 236 * @param array $keys 237 */ 238 public static function invalidate_by_event($event, array $keys) { 239 $instance = cache_config::instance(); 240 $invalidationeventset = false; 241 $factory = cache_factory::instance(); 242 $inuse = $factory->get_caches_in_use(); 243 $purgetoken = null; 244 foreach ($instance->get_definitions() as $name => $definitionarr) { 245 $definition = cache_definition::load($name, $definitionarr); 246 if ($definition->invalidates_on_event($event)) { 247 // First up check if there is a cache loader for this definition already. 248 // If there is we need to invalidate the keys from there. 249 $definitionkey = $definition->get_component().'/'.$definition->get_area(); 250 if (isset($inuse[$definitionkey])) { 251 $inuse[$definitionkey]->delete_many($keys); 252 } 253 254 // We should only log events for application and session caches. 255 // Request caches shouldn't have events as all data is lost at the end of the request. 256 // Events should only be logged once of course and likely several definitions are watching so we 257 // track its logging with $invalidationeventset. 258 $logevent = ($invalidationeventset === false && $definition->get_mode() !== cache_store::MODE_REQUEST); 259 260 if ($logevent) { 261 // Get the event invalidation cache. 262 $cache = cache::make('core', 'eventinvalidation'); 263 // Get any existing invalidated keys for this cache. 264 $data = $cache->get($event); 265 if ($data === false) { 266 // There are none. 267 $data = array(); 268 } 269 // Add our keys to them with the current cache timestamp. 270 if (null === $purgetoken) { 271 $purgetoken = cache::get_purge_token(true); 272 } 273 foreach ($keys as $key) { 274 $data[$key] = $purgetoken; 275 } 276 // Set that data back to the cache. 277 $cache->set($event, $data); 278 // This only needs to occur once. 279 $invalidationeventset = true; 280 } 281 } 282 } 283 } 284 285 /** 286 * Purges the cache for a specific definition. 287 * 288 * @param string $component 289 * @param string $area 290 * @param array $identifiers 291 * @return bool 292 */ 293 public static function purge_by_definition($component, $area, array $identifiers = array()) { 294 // Create the cache. 295 $cache = cache::make($component, $area, $identifiers); 296 // Initialise, in case of a store. 297 if ($cache instanceof cache_store) { 298 $factory = cache_factory::instance(); 299 $definition = $factory->create_definition($component, $area, null); 300 $cacheddefinition = clone $definition; 301 $cacheddefinition->set_identifiers($identifiers); 302 $cache->initialise($cacheddefinition); 303 } 304 // Purge baby, purge. 305 $cache->purge(); 306 return true; 307 } 308 309 /** 310 * Purges a cache of all information on a given event. 311 * 312 * Events cannot determine what identifiers might need to be cleared. Event based purge and invalidation 313 * are only supported on caches without identifiers. 314 * 315 * @param string $event 316 */ 317 public static function purge_by_event($event) { 318 $instance = cache_config::instance(); 319 $invalidationeventset = false; 320 $factory = cache_factory::instance(); 321 $inuse = $factory->get_caches_in_use(); 322 $purgetoken = null; 323 foreach ($instance->get_definitions() as $name => $definitionarr) { 324 $definition = cache_definition::load($name, $definitionarr); 325 if ($definition->invalidates_on_event($event)) { 326 // First up check if there is a cache loader for this definition already. 327 // If there is we need to invalidate the keys from there. 328 $definitionkey = $definition->get_component().'/'.$definition->get_area(); 329 if (isset($inuse[$definitionkey])) { 330 $inuse[$definitionkey]->purge(); 331 } else { 332 cache::make($definition->get_component(), $definition->get_area())->purge(); 333 } 334 335 // We should only log events for application and session caches. 336 // Request caches shouldn't have events as all data is lost at the end of the request. 337 // Events should only be logged once of course and likely several definitions are watching so we 338 // track its logging with $invalidationeventset. 339 $logevent = ($invalidationeventset === false && $definition->get_mode() !== cache_store::MODE_REQUEST); 340 341 // We need to flag the event in the "Event invalidation" cache if it hasn't already happened. 342 if ($logevent && $invalidationeventset === false) { 343 // Get the event invalidation cache. 344 $cache = cache::make('core', 'eventinvalidation'); 345 // Create a key to invalidate all. 346 if (null === $purgetoken) { 347 $purgetoken = cache::get_purge_token(true); 348 } 349 $data = array( 350 'purged' => $purgetoken, 351 ); 352 // Set that data back to the cache. 353 $cache->set($event, $data); 354 // This only needs to occur once. 355 $invalidationeventset = true; 356 } 357 } 358 } 359 } 360 361 /** 362 * Ensure that the stats array is ready to collect information for the given store and definition. 363 * @param string $store 364 * @param string $storeclass 365 * @param string $definition A string that identifies the definition. 366 * @param int $mode One of cache_store::MODE_*. Since 2.9. 367 */ 368 protected static function ensure_ready_for_stats($store, $storeclass, $definition, $mode = cache_store::MODE_APPLICATION) { 369 // This function is performance-sensitive, so exit as quickly as possible 370 // if we do not need to do anything. 371 if (isset(self::$stats[$definition]['stores'][$store])) { 372 return; 373 } 374 375 if (!array_key_exists($definition, self::$stats)) { 376 self::$stats[$definition] = array( 377 'mode' => $mode, 378 'stores' => array( 379 $store => array( 380 'class' => $storeclass, 381 'hits' => 0, 382 'misses' => 0, 383 'sets' => 0, 384 'iobytes' => cache_store::IO_BYTES_NOT_SUPPORTED, 385 'locks' => 0, 386 ) 387 ) 388 ); 389 } else if (!array_key_exists($store, self::$stats[$definition]['stores'])) { 390 self::$stats[$definition]['stores'][$store] = array( 391 'class' => $storeclass, 392 'hits' => 0, 393 'misses' => 0, 394 'sets' => 0, 395 'iobytes' => cache_store::IO_BYTES_NOT_SUPPORTED, 396 'locks' => 0, 397 ); 398 } 399 } 400 401 /** 402 * Returns a string to describe the definition. 403 * 404 * This method supports the definition as a string due to legacy requirements. 405 * It is backwards compatible when a string is passed but is not accurate. 406 * 407 * @since 2.9 408 * @param cache_definition|string $definition 409 * @return string 410 */ 411 protected static function get_definition_stat_id_and_mode($definition) { 412 if (!($definition instanceof cache_definition)) { 413 // All core calls to this method have been updated, this is the legacy state. 414 // We'll use application as the default as that is the most common, really this is not accurate of course but 415 // at this point we can only guess and as it only affects calls to cache stat outside of core (of which there should 416 // be none) I think that is fine. 417 debugging('Please update you cache stat calls to pass the definition rather than just its ID.', DEBUG_DEVELOPER); 418 return array((string)$definition, cache_store::MODE_APPLICATION); 419 } 420 return array($definition->get_id(), $definition->get_mode()); 421 } 422 423 /** 424 * Record a cache hit in the stats for the given store and definition. 425 * 426 * In Moodle 2.9 the $definition argument changed from accepting only a string to accepting a string or a 427 * cache_definition instance. It is preferable to pass a cache definition instance. 428 * 429 * In Moodle 3.9 the first argument changed to also accept a cache_store. 430 * 431 * @internal 432 * @param string|cache_store $store 433 * @param cache_definition $definition You used to be able to pass a string here, however that is deprecated please pass the 434 * actual cache_definition object now. 435 * @param int $hits The number of hits to record (by default 1) 436 * @param int $readbytes Number of bytes read from the cache or cache_store::IO_BYTES_NOT_SUPPORTED 437 */ 438 public static function record_cache_hit($store, $definition, int $hits = 1, int $readbytes = cache_store::IO_BYTES_NOT_SUPPORTED): void { 439 $storeclass = ''; 440 if ($store instanceof cache_store) { 441 $storeclass = get_class($store); 442 $store = $store->my_name(); 443 } 444 list($definitionstr, $mode) = self::get_definition_stat_id_and_mode($definition); 445 self::ensure_ready_for_stats($store, $storeclass, $definitionstr, $mode); 446 self::$stats[$definitionstr]['stores'][$store]['hits'] += $hits; 447 if ($readbytes !== cache_store::IO_BYTES_NOT_SUPPORTED) { 448 if (self::$stats[$definitionstr]['stores'][$store]['iobytes'] === cache_store::IO_BYTES_NOT_SUPPORTED) { 449 self::$stats[$definitionstr]['stores'][$store]['iobytes'] = $readbytes; 450 } else { 451 self::$stats[$definitionstr]['stores'][$store]['iobytes'] += $readbytes; 452 } 453 } 454 } 455 456 /** 457 * Record a cache miss in the stats for the given store and definition. 458 * 459 * In Moodle 2.9 the $definition argument changed from accepting only a string to accepting a string or a 460 * cache_definition instance. It is preferable to pass a cache definition instance. 461 * 462 * In Moodle 3.9 the first argument changed to also accept a cache_store. 463 * 464 * @internal 465 * @param string|cache_store $store 466 * @param cache_definition $definition You used to be able to pass a string here, however that is deprecated please pass the 467 * actual cache_definition object now. 468 * @param int $misses The number of misses to record (by default 1) 469 */ 470 public static function record_cache_miss($store, $definition, $misses = 1) { 471 $storeclass = ''; 472 if ($store instanceof cache_store) { 473 $storeclass = get_class($store); 474 $store = $store->my_name(); 475 } 476 list($definitionstr, $mode) = self::get_definition_stat_id_and_mode($definition); 477 self::ensure_ready_for_stats($store, $storeclass, $definitionstr, $mode); 478 self::$stats[$definitionstr]['stores'][$store]['misses'] += $misses; 479 } 480 481 /** 482 * Record a cache set in the stats for the given store and definition. 483 * 484 * In Moodle 2.9 the $definition argument changed from accepting only a string to accepting a string or a 485 * cache_definition instance. It is preferable to pass a cache definition instance. 486 * 487 * In Moodle 3.9 the first argument changed to also accept a cache_store. 488 * 489 * @internal 490 * @param string|cache_store $store 491 * @param cache_definition $definition You used to be able to pass a string here, however that is deprecated please pass the 492 * actual cache_definition object now. 493 * @param int $sets The number of sets to record (by default 1) 494 * @param int $writebytes Number of bytes written to the cache or cache_store::IO_BYTES_NOT_SUPPORTED 495 */ 496 public static function record_cache_set($store, $definition, int $sets = 1, 497 int $writebytes = cache_store::IO_BYTES_NOT_SUPPORTED) { 498 $storeclass = ''; 499 if ($store instanceof cache_store) { 500 $storeclass = get_class($store); 501 $store = $store->my_name(); 502 } 503 list($definitionstr, $mode) = self::get_definition_stat_id_and_mode($definition); 504 self::ensure_ready_for_stats($store, $storeclass, $definitionstr, $mode); 505 self::$stats[$definitionstr]['stores'][$store]['sets'] += $sets; 506 if ($writebytes !== cache_store::IO_BYTES_NOT_SUPPORTED) { 507 if (self::$stats[$definitionstr]['stores'][$store]['iobytes'] === cache_store::IO_BYTES_NOT_SUPPORTED) { 508 self::$stats[$definitionstr]['stores'][$store]['iobytes'] = $writebytes; 509 } else { 510 self::$stats[$definitionstr]['stores'][$store]['iobytes'] += $writebytes; 511 } 512 } 513 } 514 515 /** 516 * Return the stats collected so far. 517 * @return array 518 */ 519 public static function get_stats() { 520 return self::$stats; 521 } 522 523 /** 524 * Purge all of the cache stores of all of their data. 525 * 526 * Think twice before calling this method. It will purge **ALL** caches regardless of whether they have been used recently or 527 * anything. This will involve full setup of the cache + the purge operation. On a site using caching heavily this WILL be 528 * painful. 529 * 530 * @param bool $usewriter If set to true the cache_config_writer class is used. This class is special as it avoids 531 * it is still usable when caches have been disabled. 532 * Please use this option only if you really must. It's purpose is to allow the cache to be purged when it would be 533 * otherwise impossible. 534 */ 535 public static function purge_all($usewriter = false) { 536 $factory = cache_factory::instance(); 537 $config = $factory->create_config_instance($usewriter); 538 foreach ($config->get_all_stores() as $store) { 539 self::purge_store($store['name'], $config); 540 } 541 foreach ($factory->get_adhoc_caches_in_use() as $cache) { 542 $cache->purge(); 543 } 544 } 545 546 /** 547 * Purges a store given its name. 548 * 549 * @param string $storename 550 * @param cache_config $config 551 * @return bool 552 */ 553 public static function purge_store($storename, cache_config $config = null) { 554 if ($config === null) { 555 $config = cache_config::instance(); 556 } 557 558 $stores = $config->get_all_stores(); 559 if (!array_key_exists($storename, $stores)) { 560 // The store does not exist. 561 return false; 562 } 563 564 $store = $stores[$storename]; 565 $class = $store['class']; 566 567 568 // We check are_requirements_met although we expect is_ready is going to check as well. 569 if (!$class::are_requirements_met()) { 570 return false; 571 } 572 // Found the store: is it ready? 573 /* @var cache_store $instance */ 574 $instance = new $class($store['name'], $store['configuration']); 575 if (!$instance->is_ready()) { 576 unset($instance); 577 return false; 578 } 579 foreach ($config->get_definitions_by_store($storename) as $id => $definition) { 580 $definition = cache_definition::load($id, $definition); 581 $definitioninstance = clone($instance); 582 $definitioninstance->initialise($definition); 583 $definitioninstance->purge(); 584 unset($definitioninstance); 585 } 586 587 return true; 588 } 589 590 /** 591 * Purges all of the stores used by a definition. 592 * 593 * Unlike cache_helper::purge_by_definition this purges all of the data from the stores not 594 * just the data relating to the definition. 595 * This function is useful when you must purge a definition that requires setup but you don't 596 * want to set it up. 597 * 598 * @param string $component 599 * @param string $area 600 */ 601 public static function purge_stores_used_by_definition($component, $area) { 602 $factory = cache_factory::instance(); 603 $config = $factory->create_config_instance(); 604 $definition = $factory->create_definition($component, $area); 605 $stores = $config->get_stores_for_definition($definition); 606 foreach ($stores as $store) { 607 self::purge_store($store['name']); 608 } 609 } 610 611 /** 612 * Returns the translated name of the definition. 613 * 614 * @param cache_definition $definition 615 * @return lang_string 616 */ 617 public static function get_definition_name($definition) { 618 if ($definition instanceof cache_definition) { 619 return $definition->get_name(); 620 } 621 $identifier = 'cachedef_'.clean_param($definition['area'], PARAM_STRINGID); 622 $component = $definition['component']; 623 if ($component === 'core') { 624 $component = 'cache'; 625 } 626 return new lang_string($identifier, $component); 627 } 628 629 /** 630 * Hashes a descriptive key to make it shorter and still unique. 631 * @param string|int $key 632 * @param cache_definition $definition 633 * @return string 634 */ 635 public static function hash_key($key, cache_definition $definition) { 636 if ($definition->uses_simple_keys()) { 637 if (debugging() && preg_match('#[^a-zA-Z0-9_]#', $key ?? '')) { 638 throw new coding_exception('Cache definition '.$definition->get_id().' requires simple keys. Invalid key provided.', $key); 639 } 640 // We put the key first so that we can be sure the start of the key changes. 641 return (string)$key . '-' . $definition->generate_single_key_prefix(); 642 } 643 $key = $definition->generate_single_key_prefix() . '-' . $key; 644 return sha1($key); 645 } 646 647 /** 648 * Finds all definitions and updates them within the cache config file. 649 * 650 * @param bool $coreonly If set to true only core definitions will be updated. 651 */ 652 public static function update_definitions($coreonly = false) { 653 global $CFG; 654 // Include locallib. 655 require_once($CFG->dirroot.'/cache/locallib.php'); 656 // First update definitions 657 cache_config_writer::update_definitions($coreonly); 658 // Second reset anything we have already initialised to ensure we're all up to date. 659 cache_factory::reset(); 660 } 661 662 /** 663 * Update the site identifier stored by the cache API. 664 * 665 * @param string $siteidentifier 666 * @return string The new site identifier. 667 */ 668 public static function update_site_identifier($siteidentifier) { 669 global $CFG; 670 // Include locallib. 671 require_once($CFG->dirroot.'/cache/locallib.php'); 672 $factory = cache_factory::instance(); 673 $factory->updating_started(); 674 $config = $factory->create_config_instance(true); 675 $siteidentifier = $config->update_site_identifier($siteidentifier); 676 $factory->updating_finished(); 677 cache_factory::reset(); 678 return $siteidentifier; 679 } 680 681 /** 682 * Returns the site identifier. 683 * 684 * @return string 685 */ 686 public static function get_site_identifier() { 687 global $CFG; 688 if (!is_null(self::$siteidentifier)) { 689 return self::$siteidentifier; 690 } 691 // If site identifier hasn't been collected yet attempt to get it from the cache config. 692 $factory = cache_factory::instance(); 693 // If the factory is initialising then we don't want to try to get it from the config or we risk 694 // causing the cache to enter an infinite initialisation loop. 695 if (!$factory->is_initialising()) { 696 $config = $factory->create_config_instance(); 697 self::$siteidentifier = $config->get_site_identifier(); 698 } 699 if (is_null(self::$siteidentifier)) { 700 // If the site identifier is still null then config isn't aware of it yet. 701 // We'll see if the CFG is loaded, and if not we will just use unknown. 702 // It's very important here that we don't use get_config. We don't want an endless cache loop! 703 if (!empty($CFG->siteidentifier)) { 704 self::$siteidentifier = self::update_site_identifier($CFG->siteidentifier); 705 } else { 706 // It's not being recorded in MUC's config and the config data hasn't been loaded yet. 707 // Likely we are initialising. 708 return 'unknown'; 709 } 710 } 711 return self::$siteidentifier; 712 } 713 714 /** 715 * Returns the site version. 716 * 717 * @return string 718 */ 719 public static function get_site_version() { 720 global $CFG; 721 return (string)$CFG->version; 722 } 723 724 /** 725 * Runs cron routines for MUC. 726 */ 727 public static function cron() { 728 self::clean_old_session_data(true); 729 } 730 731 /** 732 * Cleans old session data from cache stores used for session based definitions. 733 * 734 * @param bool $output If set to true output will be given. 735 */ 736 public static function clean_old_session_data($output = false) { 737 global $CFG; 738 if ($output) { 739 mtrace('Cleaning up stale session data from cache stores.'); 740 } 741 $factory = cache_factory::instance(); 742 $config = $factory->create_config_instance(); 743 $definitions = $config->get_definitions(); 744 $purgetime = time() - $CFG->sessiontimeout; 745 foreach ($definitions as $definitionarray) { 746 // We are only interested in session caches. 747 if (!($definitionarray['mode'] & cache_store::MODE_SESSION)) { 748 continue; 749 } 750 $definition = $factory->create_definition($definitionarray['component'], $definitionarray['area']); 751 $stores = $config->get_stores_for_definition($definition); 752 // Turn them into store instances. 753 $stores = self::initialise_cachestore_instances($stores, $definition); 754 // Initialise all of the stores used for that definition. 755 foreach ($stores as $store) { 756 // If the store doesn't support searching we can skip it. 757 if (!($store instanceof cache_is_searchable)) { 758 debugging('Cache stores used for session definitions should ideally be searchable.', DEBUG_DEVELOPER); 759 continue; 760 } 761 // Get all of the keys. 762 $keys = $store->find_by_prefix(cache_session::KEY_PREFIX); 763 $todelete = array(); 764 foreach ($store->get_many($keys) as $key => $value) { 765 if (strpos($key, cache_session::KEY_PREFIX) !== 0 || !is_array($value) || !isset($value['lastaccess'])) { 766 continue; 767 } 768 if ((int)$value['lastaccess'] < $purgetime || true) { 769 $todelete[] = $key; 770 } 771 } 772 if (count($todelete)) { 773 $outcome = (int)$store->delete_many($todelete); 774 if ($output) { 775 $strdef = s($definition->get_id()); 776 $strstore = s($store->my_name()); 777 mtrace("- Removed {$outcome} old {$strdef} sessions from the '{$strstore}' cache store."); 778 } 779 } 780 } 781 } 782 } 783 784 /** 785 * Returns an array of stores that would meet the requirements for every definition. 786 * 787 * These stores would be 100% suitable to map as defaults for cache modes. 788 * 789 * @return array[] An array of stores, keys are the store names. 790 */ 791 public static function get_stores_suitable_for_mode_default() { 792 $factory = cache_factory::instance(); 793 $config = $factory->create_config_instance(); 794 $requirements = 0; 795 foreach ($config->get_definitions() as $definition) { 796 $definition = cache_definition::load($definition['component'].'/'.$definition['area'], $definition); 797 $requirements = $requirements | $definition->get_requirements_bin(); 798 } 799 $stores = array(); 800 foreach ($config->get_all_stores() as $name => $store) { 801 if (!empty($store['features']) && ($store['features'] & $requirements)) { 802 $stores[$name] = $store; 803 } 804 } 805 return $stores; 806 } 807 808 /** 809 * Returns stores suitable for use with a given definition. 810 * 811 * @param cache_definition $definition 812 * @return cache_store[] 813 */ 814 public static function get_stores_suitable_for_definition(cache_definition $definition) { 815 $factory = cache_factory::instance(); 816 $stores = array(); 817 if ($factory->is_initialising() || $factory->stores_disabled()) { 818 // No suitable stores here. 819 return $stores; 820 } else { 821 $stores = self::get_cache_stores($definition); 822 // If mappingsonly is set, having 0 stores is ok. 823 if ((count($stores) === 0) && (!$definition->is_for_mappings_only())) { 824 // No suitable stores we found for the definition. We need to come up with a sensible default. 825 // If this has happened we can be sure that the user has mapped custom stores to either the 826 // mode of the definition. The first alternative to try is the system default for the mode. 827 // e.g. the default file store instance for application definitions. 828 $config = $factory->create_config_instance(); 829 foreach ($config->get_stores($definition->get_mode()) as $name => $details) { 830 if (!empty($details['default'])) { 831 $stores[] = $factory->create_store_from_config($name, $details, $definition); 832 break; 833 } 834 } 835 } 836 } 837 return $stores; 838 } 839 840 /** 841 * Returns an array of warnings from the cache API. 842 * 843 * The warning returned here are for things like conflicting store instance configurations etc. 844 * These get shown on the admin notifications page for example. 845 * 846 * @param array|null $stores An array of stores to get warnings for, or null for all. 847 * @return string[] 848 */ 849 public static function warnings(array $stores = null) { 850 global $CFG; 851 if ($stores === null) { 852 require_once($CFG->dirroot.'/cache/locallib.php'); 853 $stores = core_cache\administration_helper::get_store_instance_summaries(); 854 } 855 $warnings = array(); 856 foreach ($stores as $store) { 857 if (!empty($store['warnings'])) { 858 $warnings = array_merge($warnings, $store['warnings']); 859 } 860 } 861 return $warnings; 862 } 863 864 /** 865 * A helper to determine whether a result was found. 866 * 867 * This has been deemed required after people have been confused by the fact that [] == false. 868 * 869 * @param mixed $value 870 * @return bool 871 */ 872 public static function result_found($value): bool { 873 return $value !== false; 874 } 875 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body