2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Cache definition class
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.
25 * @copyright 2012 Sam Hemelryk
26 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
29 defined('MOODLE_INTERNAL') || die();
32 * The cache definition class.
34 * Cache definitions need to be defined in db/caches.php files.
35 * They can be constructed with the following options.
39 * [int] Sets the mode for the definition. Must be one of cache_store::MODE_*
43 * [bool] Set to true if your cache will only use simple keys for its items.
44 * Simple keys consist of digits, underscores and the 26 chars of the english language. a-zA-Z0-9_
45 * If true the keys won't be hashed before being passed to the cache store for gets/sets/deletes. It will be
46 * better for performance and possible only becase we know the keys are safe.
48 * [bool] If set to true we know that the data is scalar or array of scalar.
49 * + requireidentifiers
50 * [array] An array of identifiers that must be provided to the cache when it is created.
51 * + requiredataguarantee
52 * [bool] If set to true then only stores that can guarantee data will remain available once set will be used.
53 * + requiremultipleidentifiers
54 * [bool] If set to true then only stores that support multiple identifiers will be used.
55 * + requirelockingread
56 * [bool] If set to true then a lock will be gained before reading from the cache store. It is recommended not to use
57 * this setting unless 100% absolutely positively required. Remember 99.9% of caches will NOT need this setting.
58 * This setting will only be used for application caches presently.
59 * + requirelockingwrite
60 * [bool] If set to true then a lock will be gained before writing to the cache store. As above this is not recommended
61 * unless truly needed. Please think about the order of your code and deal with race conditions there first.
62 * This setting will only be used for application caches presently.
64 * [int] If set this will be used as the maximum number of entries within the cache store for this definition.
65 * Its important to note that cache stores don't actually have to acknowledge this setting or maintain it as a hard limit.
67 * [string] A class to use as the loader for this cache. This is an advanced setting and will allow the developer of the
68 * definition to take 100% control of the caching solution.
69 * Any class used here must inherit the cache_loader interface and must extend default cache loader for the mode they are
72 * [string] Suplements the above setting indicated the file containing the class to be used. This file is included when
75 * [string] A class to use as the data loader for this definition.
76 * Any class used here must inherit the cache_data_loader interface.
78 * [string] Suplements the above setting indicated the file containing the class to be used. This file is included when
81 * [bool] This setting does two important things. First it tells the cache API to only instantiate the cache structure for
82 * this definition once, further requests will be given the original instance.
83 * Second the cache loader will keep an array of the items set and retrieved to the cache during the request.
84 * This has several advantages including better performance without needing to start passing the cache instance between
85 * function calls, the downside is that the cache instance + the items used stay within memory.
86 * Consider using this setting when you know that there are going to be many calls to the cache for the same information
87 * or when you are converting existing code to the cache and need to access the cache within functions but don't want
88 * to add it as an argument to the function.
90 * [int] This supplements the above setting by limiting the number of items in the caches persistent array of items.
91 * Tweaking this setting lower will allow you to minimise the memory implications above while hopefully still managing to
92 * offset calls to the cache store.
94 * [int] A time to live for the data (in seconds). It is strongly recommended that you don't make use of this and
95 * instead try to create an event driven invalidation system.
96 * Not all cache stores will support this natively and there are undesired performance impacts if the cache store does not.
98 * [bool] If set to true only the mapped cache store(s) will be used and the default mode store will not. This is a super
99 * advanced setting and should not be used unless absolutely required. It allows you to avoid the default stores for one
101 * + invalidationevents
102 * [array] An array of events that should cause this cache to invalidate some or all of the items within it.
104 * [int] The sharing options that are appropriate for this definition. Should be the sum of the possible options.
106 * [int] The default sharing option to use. It's highly recommended that you don't set this unless there is a very
107 * specific reason not to use the system default.
109 * For examples take a look at lib/db/caches.php
113 * @copyright 2012 Sam Hemelryk
114 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
116 class cache_definition {
118 /** The cache can be shared with everyone */
119 const SHARING_ALL = 1;
120 /** The cache can be shared with other sites using the same siteid. */
121 const SHARING_SITEID = 2;
122 /** The cache can be shared with other sites of the same version. */
123 const SHARING_VERSION = 4;
124 /** The cache can be shared with other sites using the same key */
125 const SHARING_INPUT = 8;
128 * The default sharing options available.
129 * All + SiteID + Version + Input.
131 const SHARING_DEFAULTOPTIONS = 15;
133 * The default sharing option that gets used if none have been selected.
134 * SiteID. It is the most restrictive.
136 const SHARING_DEFAULT = 2;
139 * The identifier for the definition
145 * The mode for the defintion. One of cache_store::MODE_*
151 * The component this definition is associated with.
154 protected $component;
157 * The area this definition is associated with.
163 * If set to true we know the keys are simple. a-zA-Z0-9_
166 protected $simplekeys = false;
169 * Set to true if we know the data is scalar or array of scalar.
172 protected $simpledata = false;
175 * An array of identifiers that must be provided when the definition is used to create a cache.
178 protected $requireidentifiers = array();
181 * If set to true then only stores that guarantee data may be used with this definition.
184 protected $requiredataguarantee = false;
187 * If set to true then only stores that support multple identifiers may be used with this definition.
190 protected $requiremultipleidentifiers = false;
193 * If set to true then we know that this definition requires the locking functionality.
194 * This gets set during construction based upon the settings requirelockingread and requirelockingwrite.
197 protected $requirelocking = false;
200 * Set to true if this definition requires read locking.
203 protected $requirelockingread = false;
206 * Gets set to true if this definition requires write locking.
209 protected $requirelockingwrite = false;
212 * Gets set to true if this definition requires searchable stores.
216 protected $requiresearchable = false;
219 * Sets the maximum number of items that can exist in the cache.
220 * Please note this isn't a hard limit, and doesn't need to be enforced by the caches. They can choose to do so optionally.
223 protected $maxsize = null;
226 * The class to use as the cache loader for this definition.
229 protected $overrideclass = null;
232 * The file in which the override class exists. This will be included if required.
233 * @var string Absolute path
235 protected $overrideclassfile = null;
238 * The data source class to use with this definition.
241 protected $datasource = null;
244 * The file in which the data source class exists. This will be included if required.
247 protected $datasourcefile = null;
250 * The data source class aggregate to use. This is a super advanced setting.
253 protected $datasourceaggregate = null;
256 * Set to true if the definitions cache should be persistent
259 protected $persistent = false;
262 * The persistent item array max size.
265 protected $persistentmaxsize = false;
268 * The TTL for data in this cache. Please don't use this, instead use event driven invalidation.
274 * Set to true if this cache should only use mapped cache stores and not the default mode cache store.
277 protected $mappingsonly = false;
280 * An array of events that should cause this cache to invalidate.
283 protected $invalidationevents = array();
286 * An array of identifiers provided to this cache when it was initialised.
289 protected $identifiers = array();
292 * Key prefix for use with single key cache stores
295 protected $keyprefixsingle = null;
298 * Key prefix to use with cache stores that support multi keys.
301 protected $keyprefixmulti = null;
304 * A hash identifier of this definition.
307 protected $definitionhash = null;
310 * The selected sharing mode for this definition.
313 protected $sharingoptions;
316 * The selected sharing option.
317 * @var int One of self::SHARING_*
319 protected $selectedsharingoption = self::SHARING_DEFAULT;
322 * The user input key to use if the SHARING_INPUT option has been selected.
323 * @var string Must be ALPHANUMEXT
325 protected $userinputsharingkey = '';
328 * Creates a cache definition given a definition from the cache configuration or from a caches.php file.
331 * @param array $definition
332 * @param string $datasourceaggregate
333 * @return cache_definition
334 * @throws coding_exception
336 public static function load($id, array $definition, $datasourceaggregate = null) {
339 if (!array_key_exists('mode', $definition)) {
340 throw new coding_exception('You must provide a mode when creating a cache definition');
342 if (!array_key_exists('component', $definition)) {
343 throw new coding_exception('You must provide a component when creating a cache definition');
345 if (!array_key_exists('area', $definition)) {
346 throw new coding_exception('You must provide an area when creating a cache definition');
348 $mode = (int)$definition['mode'];
349 $component = (string)$definition['component'];
350 $area = (string)$definition['area'];
355 $requireidentifiers = array();
356 $requiredataguarantee = false;
357 $requiremultipleidentifiers = false;
358 $requirelockingread = false;
359 $requirelockingwrite = false;
360 $requiresearchable = ($mode === cache_store::MODE_SESSION) ? true : false;
362 $overrideclass = null;
363 $overrideclassfile = null;
365 $datasourcefile = null;
367 $persistentmaxsize = false;
369 $mappingsonly = false;
370 $invalidationevents = array();
371 $sharingoptions = self::SHARING_DEFAULT;
372 $selectedsharingoption = self::SHARING_DEFAULT;
373 $userinputsharingkey = '';
375 if (array_key_exists('simplekeys', $definition)) {
376 $simplekeys = (bool)$definition['simplekeys'];
378 if (array_key_exists('simpledata', $definition)) {
379 $simpledata = (bool)$definition['simpledata'];
381 if (array_key_exists('requireidentifiers', $definition)) {
382 $requireidentifiers = (array)$definition['requireidentifiers'];
384 if (array_key_exists('requiredataguarantee', $definition)) {
385 $requiredataguarantee = (bool)$definition['requiredataguarantee'];
387 if (array_key_exists('requiremultipleidentifiers', $definition)) {
388 $requiremultipleidentifiers = (bool)$definition['requiremultipleidentifiers'];
391 if (array_key_exists('requirelockingread', $definition)) {
392 $requirelockingread = (bool)$definition['requirelockingread'];
394 if (array_key_exists('requirelockingwrite', $definition)) {
395 $requirelockingwrite = (bool)$definition['requirelockingwrite'];
397 $requirelocking = $requirelockingwrite || $requirelockingread;
399 if (array_key_exists('requiresearchable', $definition)) {
400 $requiresearchable = (bool)$definition['requiresearchable'];
403 if (array_key_exists('maxsize', $definition)) {
404 $maxsize = (int)$definition['maxsize'];
407 if (array_key_exists('overrideclass', $definition)) {
408 $overrideclass = $definition['overrideclass'];
410 if (array_key_exists('overrideclassfile', $definition)) {
411 $overrideclassfile = $definition['overrideclassfile'];
414 if (array_key_exists('datasource', $definition)) {
415 $datasource = $definition['datasource'];
417 if (array_key_exists('datasourcefile', $definition)) {
418 $datasourcefile = $definition['datasourcefile'];
421 if (array_key_exists('persistent', $definition)) {
422 $persistent = (bool)$definition['persistent'];
424 if (array_key_exists('persistentmaxsize', $definition)) {
425 $persistentmaxsize = (int)$definition['persistentmaxsize'];
427 if (array_key_exists('ttl', $definition)) {
428 $ttl = (int)$definition['ttl'];
430 if (array_key_exists('mappingsonly', $definition)) {
431 $mappingsonly = (bool)$definition['mappingsonly'];
433 if (array_key_exists('invalidationevents', $definition)) {
434 $invalidationevents = (array)$definition['invalidationevents'];
436 if (array_key_exists('sharingoptions', $definition)) {
437 $sharingoptions = (int)$definition['sharingoptions'];
439 if (array_key_exists('selectedsharingoption', $definition)) {
440 $selectedsharingoption = (int)$definition['selectedsharingoption'];
441 } else if (array_key_exists('defaultsharing', $definition)) {
442 $selectedsharingoption = (int)$definition['defaultsharing'];
443 } else if ($sharingoptions ^ $selectedsharingoption) {
444 if ($sharingoptions & self::SHARING_SITEID) {
445 $selectedsharingoption = self::SHARING_SITEID;
446 } else if ($sharingoptions & self::SHARING_VERSION) {
447 $selectedsharingoption = self::SHARING_VERSION;
449 $selectedsharingoption = self::SHARING_ALL;
453 if (array_key_exists('userinputsharingkey', $definition) && !empty($definition['userinputsharingkey'])) {
454 $userinputsharingkey = (string)$definition['userinputsharingkey'];
457 if (!is_null($overrideclass)) {
458 if (!is_null($overrideclassfile)) {
459 if (strpos($overrideclassfile, $CFG->dirroot) !== 0) {
460 $overrideclassfile = $CFG->dirroot.'/'.$overrideclassfile;
462 if (strpos($overrideclassfile, '../') !== false) {
463 throw new coding_exception('No path craziness allowed within override class file path.');
465 if (!file_exists($overrideclassfile)) {
466 throw new coding_exception('The override class file does not exist.');
468 require_once($overrideclassfile);
470 if (!class_exists($overrideclass)) {
471 throw new coding_exception('The override class does not exist.');
474 // Make sure that the provided class extends the default class for the mode.
475 if (get_parent_class($overrideclass) !== cache_helper::get_class_for_mode($mode)) {
476 throw new coding_exception('The override class does not immediately extend the relevant cache class.');
480 if (!is_null($datasource)) {
481 if (!is_null($datasourcefile)) {
482 if (strpos($datasourcefile, $CFG->dirroot) !== 0) {
483 $datasourcefile = $CFG->dirroot.'/'.$datasourcefile;
485 if (strpos($datasourcefile, '../') !== false) {
486 throw new coding_exception('No path craziness allowed within data source file path.');
488 if (!file_exists($datasourcefile)) {
489 throw new coding_exception('The data source class file does not exist.');
491 require_once($datasourcefile);
493 if (!class_exists($datasource)) {
494 throw new coding_exception('The data source class does not exist.');
496 if (!array_key_exists('cache_data_source', class_implements($datasource))) {
497 throw new coding_exception('Cache data source classes must implement the cache_data_source interface');
501 $cachedefinition = new cache_definition();
502 $cachedefinition->id = $id;
503 $cachedefinition->mode = $mode;
504 $cachedefinition->component = $component;
505 $cachedefinition->area = $area;
506 $cachedefinition->simplekeys = $simplekeys;
507 $cachedefinition->simpledata = $simpledata;
508 $cachedefinition->requireidentifiers = $requireidentifiers;
509 $cachedefinition->requiredataguarantee = $requiredataguarantee;
510 $cachedefinition->requiremultipleidentifiers = $requiremultipleidentifiers;
511 $cachedefinition->requirelocking = $requirelocking;
512 $cachedefinition->requirelockingread = $requirelockingread;
513 $cachedefinition->requirelockingwrite = $requirelockingwrite;
514 $cachedefinition->requiresearchable = $requiresearchable;
515 $cachedefinition->maxsize = $maxsize;
516 $cachedefinition->overrideclass = $overrideclass;
517 $cachedefinition->overrideclassfile = $overrideclassfile;
518 $cachedefinition->datasource = $datasource;
519 $cachedefinition->datasourcefile = $datasourcefile;
520 $cachedefinition->datasourceaggregate = $datasourceaggregate;
521 $cachedefinition->persistent = $persistent;
522 $cachedefinition->persistentmaxsize = $persistentmaxsize;
523 $cachedefinition->ttl = $ttl;
524 $cachedefinition->mappingsonly = $mappingsonly;
525 $cachedefinition->invalidationevents = $invalidationevents;
526 $cachedefinition->sharingoptions = $sharingoptions;
527 $cachedefinition->selectedsharingoption = $selectedsharingoption;
528 $cachedefinition->userinputsharingkey = $userinputsharingkey;
530 return $cachedefinition;
534 * Creates an ah-hoc cache definition given the required params.
536 * Please note that when using an adhoc definition you cannot set any of the optional params.
537 * This is because we cannot guarantee consistent access and we don't want to mislead people into thinking that.
539 * @param int $mode One of cache_store::MODE_*
540 * @param string $component The component this definition relates to.
541 * @param string $area The area this definition relates to.
542 * @param array $options An array of options, available options are:
543 * - simplekeys : Set to true if the keys you will use are a-zA-Z0-9_
544 * - simpledata : Set to true if the type of the data you are going to store is scalar, or an array of scalar vars
545 * - overrideclass : The class to use as the loader.
546 * - persistent : If set to true the cache will persist construction requests.
547 * @return cache_application|cache_session|cache_request
549 public static function load_adhoc($mode, $component, $area, array $options = array()) {
550 $id = 'adhoc/'.$component.'_'.$area;
553 'component' => $component,
556 if (!empty($options['simplekeys'])) {
557 $definition['simplekeys'] = $options['simplekeys'];
559 if (!empty($options['simpledata'])) {
560 $definition['simpledata'] = $options['simpledata'];
562 if (!empty($options['persistent'])) {
563 $definition['persistent'] = $options['persistent'];
565 if (!empty($options['overrideclass'])) {
566 $definition['overrideclass'] = $options['overrideclass'];
568 if (!empty($options['sharingoptions'])) {
569 $definition['sharingoptions'] = $options['sharingoptions'];
571 return self::load($id, $definition, null);
575 * Returns the cache loader class that should be used for this definition.
578 public function get_cache_class() {
579 if (!is_null($this->overrideclass)) {
580 return $this->overrideclass;
582 return cache_helper::get_class_for_mode($this->mode);
586 * Returns the id of this definition.
589 public function get_id() {
594 * Returns the name for this definition
597 public function get_name() {
598 $identifier = 'cachedef_'.clean_param($this->area, PARAM_STRINGID);
599 $component = $this->component;
600 if ($component === 'core') {
601 $component = 'cache';
603 return new lang_string($identifier, $component);
607 * Returns the mode of this definition
608 * @return int One more cache_store::MODE_
610 public function get_mode() {
615 * Returns the area this definition is associated with.
618 public function get_area() {
623 * Returns the component this definition is associated with.
626 public function get_component() {
627 return $this->component;
631 * Returns true if this definition is using simple keys.
633 * Simple keys contain only a-zA-Z0-9_
637 public function uses_simple_keys() {
638 return $this->simplekeys;
642 * Returns the identifiers that are being used for this definition.
645 public function get_identifiers() {
646 return $this->identifiers;
650 * Returns the ttl in seconds for this definition if there is one, or null if not.
653 public function get_ttl() {
658 * Returns the maximum number of items allowed in this cache.
661 public function get_maxsize() {
662 return $this->maxsize;
666 * Returns true if this definition should only be used with mappings.
669 public function is_for_mappings_only() {
670 return $this->mappingsonly;
674 * Returns true if the data is known to be scalar or array of scalar.
677 public function uses_simple_data() {
678 return $this->simpledata;
682 * Returns true if this definition requires a data guarantee from the cache stores being used.
685 public function require_data_guarantee() {
686 return $this->requiredataguarantee;
690 * Returns true if this definition requires that the cache stores support multiple identifiers
693 public function require_multiple_identifiers() {
694 return $this->requiremultipleidentifiers;
698 * Returns true if this definition requires locking functionality. Either read or write locking.
701 public function require_locking() {
702 return $this->requirelocking;
706 * Returns true if this definition requires read locking.
709 public function require_locking_read() {
710 return $this->requirelockingread;
714 * Returns true if this definition requires write locking.
717 public function require_locking_write() {
718 return $this->requirelockingwrite;
722 * Returns true if this definition requires a searchable cache.
726 public function require_searchable() {
727 return $this->requiresearchable;
731 * Returns true if this definition has an associated data source.
734 public function has_data_source() {
735 return !is_null($this->datasource);
739 * Returns an instance of the data source class used for this definition.
741 * @return cache_data_source
742 * @throws coding_exception
744 public function get_data_source() {
745 if (!$this->has_data_source()) {
746 throw new coding_exception('This cache does not use a data source.');
748 return forward_static_call(array($this->datasource, 'get_instance_for_cache'), $this);
752 * Sets the identifiers for this definition, or updates them if they have already been set.
754 * @param array $identifiers
755 * @throws coding_exception
757 public function set_identifiers(array $identifiers = array()) {
758 foreach ($this->requireidentifiers as $identifier) {
759 if (!isset($identifiers[$identifier])) {
760 throw new coding_exception('Identifier required for cache has not been provided: '.$identifier);
763 foreach ($identifiers as $name => $value) {
764 $this->identifiers[$name] = (string)$value;
766 // Reset the key prefix's they need updating now.
767 $this->keyprefixsingle = null;
768 $this->keyprefixmulti = null;
772 * Returns the requirements of this definition as a binary flag.
775 public function get_requirements_bin() {
777 if ($this->require_data_guarantee()) {
778 $requires += cache_store::SUPPORTS_DATA_GUARANTEE;
780 if ($this->require_multiple_identifiers()) {
781 $requires += cache_store::SUPPORTS_MULTIPLE_IDENTIFIERS;
783 if ($this->require_searchable()) {
784 $requires += cache_store::IS_SEARCHABLE;
790 * Returns true if this definitions cache should be made persistent.
793 public function should_be_persistent() {
794 return $this->persistent || $this->mode === cache_store::MODE_SESSION;
798 * Returns the max size for the persistent item array in the cache.
801 public function get_persistent_max_size() {
802 return $this->persistentmaxsize;
806 * Generates a hash of this definition and returns it.
809 public function generate_definition_hash() {
810 if ($this->definitionhash === null) {
811 $this->definitionhash = md5("{$this->mode} {$this->component} {$this->area}");
813 return $this->definitionhash;
817 * Generates a single key prefix for this definition
821 public function generate_single_key_prefix() {
822 if ($this->keyprefixsingle === null) {
823 $this->keyprefixsingle = $this->mode.'/'.$this->component.'/'.$this->area;
824 $this->keyprefixsingle .= '/'.$this->get_cache_identifier();
825 $identifiers = $this->get_identifiers();
827 foreach ($identifiers as $key => $value) {
828 $this->keyprefixsingle .= '/'.$key.'='.$value;
831 $this->keyprefixsingle = md5($this->keyprefixsingle);
833 return $this->keyprefixsingle;
837 * Generates a multi key prefix for this definition
841 public function generate_multi_key_parts() {
842 if ($this->keyprefixmulti === null) {
843 $this->keyprefixmulti = array(
844 'mode' => $this->mode,
845 'component' => $this->component,
846 'area' => $this->area,
847 'siteidentifier' => $this->get_cache_identifier()
849 if (!empty($this->identifiers)) {
850 $identifiers = array();
851 foreach ($this->identifiers as $key => $value) {
852 $identifiers[] = htmlentities($key, ENT_QUOTES, 'UTF-8').'='.htmlentities($value, ENT_QUOTES, 'UTF-8');
854 $this->keyprefixmulti['identifiers'] = join('&', $identifiers);
857 return $this->keyprefixmulti;
861 * Check if this definition should invalidate on the given event.
863 * @param string $event
864 * @return bool True if the definition should invalidate on the event. False otherwise.
866 public function invalidates_on_event($event) {
867 return (in_array($event, $this->invalidationevents));
871 * Check if the definition has any invalidation events.
873 * @return bool True if it does, false otherwise
875 public function has_invalidation_events() {
876 return !empty($this->invalidationevents);
880 * Returns all of the invalidation events for this definition.
884 public function get_invalidation_events() {
885 return $this->invalidationevents;
889 * Returns a cache identification string.
891 * @return string A string to be used as part of keys.
893 protected function get_cache_identifier() {
894 $identifiers = array();
895 if ($this->selectedsharingoption & self::SHARING_ALL) {
896 // Nothing to do here.
898 if ($this->selectedsharingoption & self::SHARING_SITEID) {
899 $identifiers[] = cache_helper::get_site_identifier();
901 if ($this->selectedsharingoption & self::SHARING_VERSION) {
902 $identifiers[] = cache_helper::get_site_version();
904 if ($this->selectedsharingoption & self::SHARING_INPUT && !empty($this->userinputsharingkey)) {
905 $identifiers[] = $this->userinputsharingkey;
908 return join('/', $identifiers);
912 * Returns true if this definition requires identifiers.
916 public function has_required_identifiers() {
917 return (count($this->requireidentifiers) > 0);