diff --git a/cache/classes/application_cache.php b/cache/classes/application_cache.php
new file mode 100644
index 00000000000..ee7a3b62f5e
--- /dev/null
+++ b/cache/classes/application_cache.php
@@ -0,0 +1,330 @@
+.
+
+/**
+ * An application cache.
+ *
+ * This class is used for application caches returned by the cache::make methods.
+ * On top of the standard functionality it also allows locking to be required and or manually operated.
+ *
+ * This cache class should never be interacted with directly. Instead you should always use the cache::make methods.
+ * It is technically possible to call those methods through this class however there is no guarantee that you will get an
+ * instance of this class back again.
+ *
+ * @internal don't use me directly.
+ *
+ * @package core
+ * @category cache
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cache_application extends cache implements cache_loader_with_locking {
+
+ /**
+ * Lock identifier.
+ * This is used to ensure the lock belongs to the cache instance + definition + user.
+ * @var string
+ */
+ protected $lockidentifier;
+
+ /**
+ * Gets set to true if the cache's primary store natively supports locking.
+ * If it does then we use that, otherwise we need to instantiate a second store to use for locking.
+ * @var cache_store
+ */
+ protected $nativelocking = null;
+
+ /**
+ * Gets set to true if the cache is going to be using locking.
+ * This isn't a requirement, it doesn't need to use locking (most won't) and this bool is used to quickly check things.
+ * If required then locking will be forced for the get|set|delete operation.
+ * @var bool
+ */
+ protected $requirelocking = false;
+
+ /**
+ * Gets set to true if the cache writes (set|delete) must have a manual lock created first
+ * @var bool
+ */
+ protected $requirelockingbeforewrite = false;
+
+ /**
+ * Gets set to a cache_store to use for locking if the caches primary store doesn't support locking natively.
+ * @var cache_lock_interface
+ */
+ protected $cachelockinstance;
+
+ /**
+ * Store a list of locks acquired by this process.
+ * @var array
+ */
+ protected $locks;
+
+ /**
+ * Overrides the cache construct method.
+ *
+ * You should not call this method from your code, instead you should use the cache::make methods.
+ *
+ * @param cache_definition $definition
+ * @param cache_store $store
+ * @param cache_loader|cache_data_source $loader
+ */
+ public function __construct(cache_definition $definition, cache_store $store, $loader = null) {
+ parent::__construct($definition, $store, $loader);
+ $this->nativelocking = $this->store_supports_native_locking();
+ if ($definition->require_locking()) {
+ $this->requirelocking = true;
+ $this->requirelockingbeforewrite = $definition->require_locking_before_write();
+ }
+
+ $this->handle_invalidation_events();
+ }
+
+ /**
+ * Returns the identifier to use
+ *
+ * @staticvar int $instances Counts the number of instances. Used as part of the lock identifier.
+ * @return string
+ */
+ public function get_identifier() {
+ static $instances = 0;
+ if ($this->lockidentifier === null) {
+ $this->lockidentifier = md5(
+ $this->get_definition()->generate_definition_hash() .
+ sesskey() .
+ $instances++ .
+ 'cache_application'
+ );
+ }
+ return $this->lockidentifier;
+ }
+
+ /**
+ * Fixes the instance up after a clone.
+ */
+ public function __clone() {
+ // Force a new idenfitier.
+ $this->lockidentifier = null;
+ }
+
+ /**
+ * Acquires a lock on the given key.
+ *
+ * This is done automatically if the definition requires it.
+ * It is recommended to use a definition if you want to have locking although it is possible to do locking without having
+ * it required by the definition.
+ * The problem with such an approach is that you cannot ensure that code will consistently use locking. You will need to
+ * rely on the integrators review skills.
+ *
+ * @param string|int $key The key as given to get|set|delete
+ * @return bool Always returns true
+ * @throws moodle_exception If the lock cannot be obtained
+ */
+ public function acquire_lock($key) {
+ $releaseparent = false;
+ try {
+ if ($this->get_loader() !== false) {
+ $this->get_loader()->acquire_lock($key);
+ // We need to release this lock later if the lock is not successful.
+ $releaseparent = true;
+ }
+ $hashedkey = cache_helper::hash_key($key, $this->get_definition());
+ $before = microtime(true);
+ if ($this->nativelocking) {
+ $lock = $this->get_store()->acquire_lock($hashedkey, $this->get_identifier());
+ } else {
+ $this->ensure_cachelock_available();
+ $lock = $this->cachelockinstance->lock($hashedkey, $this->get_identifier());
+ }
+ $after = microtime(true);
+ if ($lock) {
+ $this->locks[$hashedkey] = $lock;
+ if (MDL_PERF || $this->perfdebug) {
+ \core\lock\timing_wrapper_lock_factory::record_lock_data($after, $before,
+ $this->get_definition()->get_id(), $hashedkey, $lock, $this->get_identifier() . $hashedkey);
+ }
+ $releaseparent = false;
+ return true;
+ } else {
+ throw new moodle_exception('ex_unabletolock', 'cache', '', null,
+ 'store: ' . get_class($this->get_store()) . ', lock: ' . $hashedkey);
+ }
+ } finally {
+ // Release the parent lock if we acquired it, then threw an exception.
+ if ($releaseparent) {
+ $this->get_loader()->release_lock($key);
+ }
+ }
+ }
+
+ /**
+ * Checks if this cache has a lock on the given key.
+ *
+ * @param string|int $key The key as given to get|set|delete
+ * @return bool|null Returns true if there is a lock and this cache has it, null if no one has a lock on that key, false if
+ * someone else has the lock.
+ */
+ public function check_lock_state($key) {
+ $key = cache_helper::hash_key($key, $this->get_definition());
+ if (!empty($this->locks[$key])) {
+ return true; // Shortcut to save having to make a call to the cache store if the lock is held by this process.
+ }
+ if ($this->nativelocking) {
+ return $this->get_store()->check_lock_state($key, $this->get_identifier());
+ } else {
+ $this->ensure_cachelock_available();
+ return $this->cachelockinstance->check_state($key, $this->get_identifier());
+ }
+ }
+
+ /**
+ * Releases the lock this cache has on the given key
+ *
+ * @param string|int $key
+ * @return bool True if the operation succeeded, false otherwise.
+ */
+ public function release_lock($key) {
+ $loaderkey = $key;
+ $key = cache_helper::hash_key($key, $this->get_definition());
+ if ($this->nativelocking) {
+ $released = $this->get_store()->release_lock($key, $this->get_identifier());
+ } else {
+ $this->ensure_cachelock_available();
+ $released = $this->cachelockinstance->unlock($key, $this->get_identifier());
+ }
+ if ($released && array_key_exists($key, $this->locks)) {
+ unset($this->locks[$key]);
+ if (MDL_PERF || $this->perfdebug) {
+ \core\lock\timing_wrapper_lock_factory::record_lock_released_data($this->get_identifier() . $key);
+ }
+ }
+ if ($this->get_loader() !== false) {
+ $this->get_loader()->release_lock($loaderkey);
+ }
+ return $released;
+ }
+
+ /**
+ * Ensure that the dedicated lock store is ready to go.
+ *
+ * This should only happen if the cache store doesn't natively support it.
+ */
+ protected function ensure_cachelock_available() {
+ if ($this->cachelockinstance === null) {
+ $this->cachelockinstance = cache_helper::get_cachelock_for_store($this->get_store());
+ }
+ }
+
+ /**
+ * Sends a key => value pair to the cache.
+ *
+ *
+ * // This code will add four entries to the cache, one for each url.
+ * $cache->set('main', 'http://moodle.org');
+ * $cache->set('docs', 'http://docs.moodle.org');
+ * $cache->set('tracker', 'http://tracker.moodle.org');
+ * $cache->set('qa', 'http://qa.moodle.net');
+ *
+ *
+ * @param string|int $key The key for the data being requested.
+ * @param int $version Version number
+ * @param mixed $data The data to set against the key.
+ * @param bool $setparents If true, sets all parent loaders, otherwise only this one
+ * @return bool True on success, false otherwise.
+ * @throws coding_exception If a required lock has not beeen acquired
+ */
+ protected function set_implementation($key, int $version, $data, bool $setparents = true): bool {
+ if ($this->requirelockingbeforewrite && !$this->check_lock_state($key)) {
+ throw new coding_exception('Attempted to set cache key "' . $key . '" without a lock. '
+ . 'Locking before writes is required for ' . $this->get_definition()->get_id());
+ }
+ return parent::set_implementation($key, $version, $data, $setparents);
+ }
+
+ /**
+ * Sends several key => value pairs to the cache.
+ *
+ * Using this function comes with potential performance implications.
+ * Not all cache stores will support get_many/set_many operations and in order to replicate this functionality will call
+ * the equivalent singular method for each item provided.
+ * This should not deter you from using this function as there is a performance benefit in situations where the cache store
+ * does support it, but you should be aware of this fact.
+ *
+ *
+ * // This code will add four entries to the cache, one for each url.
+ * $cache->set_many(array(
+ * 'main' => 'http://moodle.org',
+ * 'docs' => 'http://docs.moodle.org',
+ * 'tracker' => 'http://tracker.moodle.org',
+ * 'qa' => ''http://qa.moodle.net'
+ * ));
+ *
+ *
+ * @param array $keyvaluearray An array of key => value pairs to send to the cache.
+ * @return int The number of items successfully set. It is up to the developer to check this matches the number of items.
+ * ... if they care that is.
+ * @throws coding_exception If a required lock has not beeen acquired
+ */
+ public function set_many(array $keyvaluearray) {
+ if ($this->requirelockingbeforewrite) {
+ foreach ($keyvaluearray as $key => $value) {
+ if (!$this->check_lock_state($key)) {
+ throw new coding_exception('Attempted to set cache key "' . $key . '" without a lock. '
+ . 'Locking before writes is required for ' . $this->get_definition()->get_id());
+ }
+ }
+ }
+ return parent::set_many($keyvaluearray);
+ }
+
+ /**
+ * Delete the given key from the cache.
+ *
+ * @param string|int $key The key to delete.
+ * @param bool $recurse When set to true the key will also be deleted from all stacked cache loaders and their stores.
+ * This happens by default and ensure that all the caches are consistent. It is NOT recommended to change this.
+ * @return bool True of success, false otherwise.
+ * @throws coding_exception If a required lock has not beeen acquired
+ */
+ public function delete($key, $recurse = true) {
+ if ($this->requirelockingbeforewrite && !$this->check_lock_state($key)) {
+ throw new coding_exception('Attempted to delete cache key "' . $key . '" without a lock. '
+ . 'Locking before writes is required for ' . $this->get_definition()->get_id());
+ }
+ return parent::delete($key, $recurse);
+ }
+
+ /**
+ * Delete all of the given keys from the cache.
+ *
+ * @param array $keys The key to delete.
+ * @param bool $recurse When set to true the key will also be deleted from all stacked cache loaders and their stores.
+ * This happens by default and ensure that all the caches are consistent. It is NOT recommended to change this.
+ * @return int The number of items successfully deleted.
+ * @throws coding_exception If a required lock has not beeen acquired
+ */
+ public function delete_many(array $keys, $recurse = true) {
+ if ($this->requirelockingbeforewrite) {
+ foreach ($keys as $key) {
+ if (!$this->check_lock_state($key)) {
+ throw new coding_exception('Attempted to delete cache key "' . $key . '" without a lock. '
+ . 'Locking before writes is required for ' . $this->get_definition()->get_id());
+ }
+ }
+ }
+ return parent::delete_many($keys, $recurse);
+ }
+}
diff --git a/cache/classes/loaders.php b/cache/classes/cache.php
similarity index 62%
rename from cache/classes/loaders.php
rename to cache/classes/cache.php
index 36fdb4da40e..72469ff9552 100644
--- a/cache/classes/loaders.php
+++ b/cache/classes/cache.php
@@ -14,20 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
-/**
- * Cache loaders
- *
- * This file is part of Moodle's cache API, affectionately called MUC.
- * It contains the components that are required in order to use caching.
- *
- * @package core
- * @category cache
- * @copyright 2012 Sam Hemelryk
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
/**
* The main cache class.
*
@@ -1563,899 +1549,3 @@ class cache implements cache_loader {
public function purge_current_user() {
}
}
-
-/**
- * An application cache.
- *
- * This class is used for application caches returned by the cache::make methods.
- * On top of the standard functionality it also allows locking to be required and or manually operated.
- *
- * This cache class should never be interacted with directly. Instead you should always use the cache::make methods.
- * It is technically possible to call those methods through this class however there is no guarantee that you will get an
- * instance of this class back again.
- *
- * @internal don't use me directly.
- *
- * @package core
- * @category cache
- * @copyright 2012 Sam Hemelryk
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class cache_application extends cache implements cache_loader_with_locking {
-
- /**
- * Lock identifier.
- * This is used to ensure the lock belongs to the cache instance + definition + user.
- * @var string
- */
- protected $lockidentifier;
-
- /**
- * Gets set to true if the cache's primary store natively supports locking.
- * If it does then we use that, otherwise we need to instantiate a second store to use for locking.
- * @var cache_store
- */
- protected $nativelocking = null;
-
- /**
- * Gets set to true if the cache is going to be using locking.
- * This isn't a requirement, it doesn't need to use locking (most won't) and this bool is used to quickly check things.
- * If required then locking will be forced for the get|set|delete operation.
- * @var bool
- */
- protected $requirelocking = false;
-
- /**
- * Gets set to true if the cache writes (set|delete) must have a manual lock created first
- * @var bool
- */
- protected $requirelockingbeforewrite = false;
-
- /**
- * Gets set to a cache_store to use for locking if the caches primary store doesn't support locking natively.
- * @var cache_lock_interface
- */
- protected $cachelockinstance;
-
- /**
- * Store a list of locks acquired by this process.
- * @var array
- */
- protected $locks;
-
- /**
- * Overrides the cache construct method.
- *
- * You should not call this method from your code, instead you should use the cache::make methods.
- *
- * @param cache_definition $definition
- * @param cache_store $store
- * @param cache_loader|cache_data_source $loader
- */
- public function __construct(cache_definition $definition, cache_store $store, $loader = null) {
- parent::__construct($definition, $store, $loader);
- $this->nativelocking = $this->store_supports_native_locking();
- if ($definition->require_locking()) {
- $this->requirelocking = true;
- $this->requirelockingbeforewrite = $definition->require_locking_before_write();
- }
-
- $this->handle_invalidation_events();
- }
-
- /**
- * Returns the identifier to use
- *
- * @staticvar int $instances Counts the number of instances. Used as part of the lock identifier.
- * @return string
- */
- public function get_identifier() {
- static $instances = 0;
- if ($this->lockidentifier === null) {
- $this->lockidentifier = md5(
- $this->get_definition()->generate_definition_hash() .
- sesskey() .
- $instances++ .
- 'cache_application'
- );
- }
- return $this->lockidentifier;
- }
-
- /**
- * Fixes the instance up after a clone.
- */
- public function __clone() {
- // Force a new idenfitier.
- $this->lockidentifier = null;
- }
-
- /**
- * Acquires a lock on the given key.
- *
- * This is done automatically if the definition requires it.
- * It is recommended to use a definition if you want to have locking although it is possible to do locking without having
- * it required by the definition.
- * The problem with such an approach is that you cannot ensure that code will consistently use locking. You will need to
- * rely on the integrators review skills.
- *
- * @param string|int $key The key as given to get|set|delete
- * @return bool Always returns true
- * @throws moodle_exception If the lock cannot be obtained
- */
- public function acquire_lock($key) {
- $releaseparent = false;
- try {
- if ($this->get_loader() !== false) {
- $this->get_loader()->acquire_lock($key);
- // We need to release this lock later if the lock is not successful.
- $releaseparent = true;
- }
- $hashedkey = cache_helper::hash_key($key, $this->get_definition());
- $before = microtime(true);
- if ($this->nativelocking) {
- $lock = $this->get_store()->acquire_lock($hashedkey, $this->get_identifier());
- } else {
- $this->ensure_cachelock_available();
- $lock = $this->cachelockinstance->lock($hashedkey, $this->get_identifier());
- }
- $after = microtime(true);
- if ($lock) {
- $this->locks[$hashedkey] = $lock;
- if (MDL_PERF || $this->perfdebug) {
- \core\lock\timing_wrapper_lock_factory::record_lock_data($after, $before,
- $this->get_definition()->get_id(), $hashedkey, $lock, $this->get_identifier() . $hashedkey);
- }
- $releaseparent = false;
- return true;
- } else {
- throw new moodle_exception('ex_unabletolock', 'cache', '', null,
- 'store: ' . get_class($this->get_store()) . ', lock: ' . $hashedkey);
- }
- } finally {
- // Release the parent lock if we acquired it, then threw an exception.
- if ($releaseparent) {
- $this->get_loader()->release_lock($key);
- }
- }
- }
-
- /**
- * Checks if this cache has a lock on the given key.
- *
- * @param string|int $key The key as given to get|set|delete
- * @return bool|null Returns true if there is a lock and this cache has it, null if no one has a lock on that key, false if
- * someone else has the lock.
- */
- public function check_lock_state($key) {
- $key = cache_helper::hash_key($key, $this->get_definition());
- if (!empty($this->locks[$key])) {
- return true; // Shortcut to save having to make a call to the cache store if the lock is held by this process.
- }
- if ($this->nativelocking) {
- return $this->get_store()->check_lock_state($key, $this->get_identifier());
- } else {
- $this->ensure_cachelock_available();
- return $this->cachelockinstance->check_state($key, $this->get_identifier());
- }
- }
-
- /**
- * Releases the lock this cache has on the given key
- *
- * @param string|int $key
- * @return bool True if the operation succeeded, false otherwise.
- */
- public function release_lock($key) {
- $loaderkey = $key;
- $key = cache_helper::hash_key($key, $this->get_definition());
- if ($this->nativelocking) {
- $released = $this->get_store()->release_lock($key, $this->get_identifier());
- } else {
- $this->ensure_cachelock_available();
- $released = $this->cachelockinstance->unlock($key, $this->get_identifier());
- }
- if ($released && array_key_exists($key, $this->locks)) {
- unset($this->locks[$key]);
- if (MDL_PERF || $this->perfdebug) {
- \core\lock\timing_wrapper_lock_factory::record_lock_released_data($this->get_identifier() . $key);
- }
- }
- if ($this->get_loader() !== false) {
- $this->get_loader()->release_lock($loaderkey);
- }
- return $released;
- }
-
- /**
- * Ensure that the dedicated lock store is ready to go.
- *
- * This should only happen if the cache store doesn't natively support it.
- */
- protected function ensure_cachelock_available() {
- if ($this->cachelockinstance === null) {
- $this->cachelockinstance = cache_helper::get_cachelock_for_store($this->get_store());
- }
- }
-
- /**
- * Sends a key => value pair to the cache.
- *
- *
- * // This code will add four entries to the cache, one for each url.
- * $cache->set('main', 'http://moodle.org');
- * $cache->set('docs', 'http://docs.moodle.org');
- * $cache->set('tracker', 'http://tracker.moodle.org');
- * $cache->set('qa', 'http://qa.moodle.net');
- *
- *
- * @param string|int $key The key for the data being requested.
- * @param int $version Version number
- * @param mixed $data The data to set against the key.
- * @param bool $setparents If true, sets all parent loaders, otherwise only this one
- * @return bool True on success, false otherwise.
- * @throws coding_exception If a required lock has not beeen acquired
- */
- protected function set_implementation($key, int $version, $data, bool $setparents = true): bool {
- if ($this->requirelockingbeforewrite && !$this->check_lock_state($key)) {
- throw new coding_exception('Attempted to set cache key "' . $key . '" without a lock. '
- . 'Locking before writes is required for ' . $this->get_definition()->get_id());
- }
- return parent::set_implementation($key, $version, $data, $setparents);
- }
-
- /**
- * Sends several key => value pairs to the cache.
- *
- * Using this function comes with potential performance implications.
- * Not all cache stores will support get_many/set_many operations and in order to replicate this functionality will call
- * the equivalent singular method for each item provided.
- * This should not deter you from using this function as there is a performance benefit in situations where the cache store
- * does support it, but you should be aware of this fact.
- *
- *
- * // This code will add four entries to the cache, one for each url.
- * $cache->set_many(array(
- * 'main' => 'http://moodle.org',
- * 'docs' => 'http://docs.moodle.org',
- * 'tracker' => 'http://tracker.moodle.org',
- * 'qa' => ''http://qa.moodle.net'
- * ));
- *
- *
- * @param array $keyvaluearray An array of key => value pairs to send to the cache.
- * @return int The number of items successfully set. It is up to the developer to check this matches the number of items.
- * ... if they care that is.
- * @throws coding_exception If a required lock has not beeen acquired
- */
- public function set_many(array $keyvaluearray) {
- if ($this->requirelockingbeforewrite) {
- foreach ($keyvaluearray as $key => $value) {
- if (!$this->check_lock_state($key)) {
- throw new coding_exception('Attempted to set cache key "' . $key . '" without a lock. '
- . 'Locking before writes is required for ' . $this->get_definition()->get_id());
- }
- }
- }
- return parent::set_many($keyvaluearray);
- }
-
- /**
- * Delete the given key from the cache.
- *
- * @param string|int $key The key to delete.
- * @param bool $recurse When set to true the key will also be deleted from all stacked cache loaders and their stores.
- * This happens by default and ensure that all the caches are consistent. It is NOT recommended to change this.
- * @return bool True of success, false otherwise.
- * @throws coding_exception If a required lock has not beeen acquired
- */
- public function delete($key, $recurse = true) {
- if ($this->requirelockingbeforewrite && !$this->check_lock_state($key)) {
- throw new coding_exception('Attempted to delete cache key "' . $key . '" without a lock. '
- . 'Locking before writes is required for ' . $this->get_definition()->get_id());
- }
- return parent::delete($key, $recurse);
- }
-
- /**
- * Delete all of the given keys from the cache.
- *
- * @param array $keys The key to delete.
- * @param bool $recurse When set to true the key will also be deleted from all stacked cache loaders and their stores.
- * This happens by default and ensure that all the caches are consistent. It is NOT recommended to change this.
- * @return int The number of items successfully deleted.
- * @throws coding_exception If a required lock has not beeen acquired
- */
- public function delete_many(array $keys, $recurse = true) {
- if ($this->requirelockingbeforewrite) {
- foreach ($keys as $key) {
- if (!$this->check_lock_state($key)) {
- throw new coding_exception('Attempted to delete cache key "' . $key . '" without a lock. '
- . 'Locking before writes is required for ' . $this->get_definition()->get_id());
- }
- }
- }
- return parent::delete_many($keys, $recurse);
- }
-}
-
-/**
- * A session cache.
- *
- * This class is used for session caches returned by the cache::make methods.
- *
- * It differs from the application loader in a couple of noteable ways:
- * 1. Sessions are always expected to exist.
- * Because of this we don't ever use the static acceleration array.
- * 2. Session data for a loader instance (store + definition) is consolidate into a
- * single array for storage within the store.
- * Along with this we embed a lastaccessed time with the data. This way we can
- * check sessions for a last access time.
- * 3. Session stores are required to support key searching and must
- * implement cache_is_searchable. This ensures stores used for the cache can be
- * targetted for garbage collection of session data.
- *
- * This cache class should never be interacted with directly. Instead you should always use the cache::make methods.
- * It is technically possible to call those methods through this class however there is no guarantee that you will get an
- * instance of this class back again.
- *
- * @todo we should support locking in the session as well. Should be pretty simple to set up.
- *
- * @internal don't use me directly.
- * @method cache_store|cache_is_searchable get_store() Returns the cache store which must implement both cache_is_searchable.
- *
- * @package core
- * @category cache
- * @copyright 2012 Sam Hemelryk
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class cache_session extends cache {
- /**
- * The user the session has been established for.
- * @var int
- */
- protected static $loadeduserid = null;
-
- /**
- * The userid this cache is currently using.
- * @var int
- */
- protected $currentuserid = null;
-
- /**
- * The session id we are currently using.
- * @var array
- */
- protected $sessionid = null;
-
- /**
- * The session data for the above session id.
- * @var array
- */
- protected $session = null;
-
- /**
- * Constant used to prefix keys.
- */
- const KEY_PREFIX = 'sess_';
-
- /**
- * This is the key used to track last access.
- */
- const LASTACCESS = '__lastaccess__';
-
- /**
- * Override the cache::construct method.
- *
- * This function gets overriden so that we can process any invalidation events if need be.
- * If the definition doesn't have any invalidation events then this occurs exactly as it would for the cache class.
- * Otherwise we look at the last invalidation time and then check the invalidation data for events that have occured
- * between then now.
- *
- * You should not call this method from your code, instead you should use the cache::make methods.
- *
- * @param cache_definition $definition
- * @param cache_store $store
- * @param cache_loader|cache_data_source $loader
- */
- public function __construct(cache_definition $definition, cache_store $store, $loader = null) {
- // First up copy the loadeduserid to the current user id.
- $this->currentuserid = self::$loadeduserid;
- $this->set_session_id();
- parent::__construct($definition, $store, $loader);
-
- // This will trigger check tracked user. If this gets removed a call to that will need to be added here in its place.
- $this->set(self::LASTACCESS, cache::now());
-
- $this->handle_invalidation_events();
- }
-
- /**
- * Sets the session id for the loader.
- */
- protected function set_session_id() {
- $this->sessionid = preg_replace('#[^a-zA-Z0-9_]#', '_', session_id());
- }
-
- /**
- * Returns the prefix used for all keys.
- * @return string
- */
- protected function get_key_prefix() {
- return 'u'.$this->currentuserid.'_'.$this->sessionid;
- }
-
- /**
- * Parses the key turning it into a string (or array is required) suitable to be passed to the cache store.
- *
- * This function is called for every operation that uses keys. For this reason we use this function to also check
- * that the current user is the same as the user who last used this cache.
- *
- * On top of that if prepends the string 'sess_' to the start of all keys. The _ ensures things are easily identifiable.
- *
- * @param string|int $key As passed to get|set|delete etc.
- * @return string|array String unless the store supports multi-identifiers in which case an array if returned.
- */
- protected function parse_key($key) {
- $prefix = $this->get_key_prefix();
- if ($key === self::LASTACCESS) {
- return $key.$prefix;
- }
- return $prefix.'_'.parent::parse_key($key);
- }
-
- /**
- * Check that this cache instance is tracking the current user.
- */
- protected function check_tracked_user() {
- if (isset($_SESSION['USER']->id) && $_SESSION['USER']->id !== null) {
- // Get the id of the current user.
- $new = $_SESSION['USER']->id;
- } else {
- // No user set up yet.
- $new = 0;
- }
- if ($new !== self::$loadeduserid) {
- // The current user doesn't match the tracked userid for this request.
- if (!is_null(self::$loadeduserid)) {
- // Purge the data we have for the old user.
- // This way we don't bloat the session.
- $this->purge();
- }
- self::$loadeduserid = $new;
- $this->currentuserid = $new;
- } else if ($new !== $this->currentuserid) {
- // The current user matches the loaded user but not the user last used by this cache.
- $this->purge_current_user();
- $this->currentuserid = $new;
- }
- }
-
- /**
- * Purges the session cache of all data belonging to the current user.
- */
- public function purge_current_user() {
- $keys = $this->get_store()->find_by_prefix($this->get_key_prefix());
- $this->get_store()->delete_many($keys);
- }
-
- /**
- * Retrieves the value for the given key from the cache.
- *
- * @param string|int $key The key for the data being requested.
- * It can be any structure although using a scalar string or int is recommended in the interests of performance.
- * In advanced cases an array may be useful such as in situations requiring the multi-key functionality.
- * @param int $requiredversion Minimum required version of the data or cache::VERSION_NONE
- * @param int $strictness One of IGNORE_MISSING | MUST_EXIST
- * @param mixed &$actualversion If specified, will be set to the actual version number retrieved
- * @return mixed|false The data from the cache or false if the key did not exist within the cache.
- * @throws coding_exception
- */
- protected function get_implementation($key, int $requiredversion, int $strictness, &$actualversion = null) {
- // Check the tracked user.
- $this->check_tracked_user();
-
- // Use parent code.
- return parent::get_implementation($key, $requiredversion, $strictness, $actualversion);
- }
-
- /**
- * Sends a key => value pair to the cache.
- *
- *
- * // This code will add four entries to the cache, one for each url.
- * $cache->set('main', 'http://moodle.org');
- * $cache->set('docs', 'http://docs.moodle.org');
- * $cache->set('tracker', 'http://tracker.moodle.org');
- * $cache->set('qa', 'http://qa.moodle.net');
- *
- *
- * @param string|int $key The key for the data being requested.
- * It can be any structure although using a scalar string or int is recommended in the interests of performance.
- * In advanced cases an array may be useful such as in situations requiring the multi-key functionality.
- * @param mixed $data The data to set against the key.
- * @return bool True on success, false otherwise.
- */
- public function set($key, $data) {
- $this->check_tracked_user();
- $loader = $this->get_loader();
- if ($loader !== false) {
- // We have a loader available set it there as well.
- // We have to let the loader do its own parsing of data as it may be unique.
- $loader->set($key, $data);
- }
- if (is_object($data) && $data instanceof cacheable_object) {
- $data = new cache_cached_object($data);
- } else if (!$this->get_store()->supports_dereferencing_objects() && !is_scalar($data)) {
- // If data is an object it will be a reference.
- // If data is an array if may contain references.
- // We want to break references so that the cache cannot be modified outside of itself.
- // Call the function to unreference it (in the best way possible).
- $data = $this->unref($data);
- }
- // We dont' support native TTL here as we consolidate data for sessions.
- if ($this->has_a_ttl() && !$this->store_supports_native_ttl()) {
- $data = new cache_ttl_wrapper($data, $this->get_definition()->get_ttl());
- }
- $success = $this->get_store()->set($this->parse_key($key), $data);
- if ($this->perfdebug) {
- cache_helper::record_cache_set($this->get_store(), $this->get_definition(), 1,
- $this->get_store()->get_last_io_bytes());
- }
- return $success;
- }
-
- /**
- * Delete the given key from the cache.
- *
- * @param string|int $key The key to delete.
- * @param bool $recurse When set to true the key will also be deleted from all stacked cache loaders and their stores.
- * This happens by default and ensure that all the caches are consistent. It is NOT recommended to change this.
- * @return bool True of success, false otherwise.
- */
- public function delete($key, $recurse = true) {
- $parsedkey = $this->parse_key($key);
- if ($recurse && $this->get_loader() !== false) {
- // Delete from the bottom of the stack first.
- $this->get_loader()->delete($key, $recurse);
- }
- return $this->get_store()->delete($parsedkey);
- }
-
- /**
- * Retrieves an array of values for an array of keys.
- *
- * Using this function comes with potential performance implications.
- * Not all cache stores will support get_many/set_many operations and in order to replicate this functionality will call
- * the equivalent singular method for each item provided.
- * This should not deter you from using this function as there is a performance benefit in situations where the cache store
- * does support it, but you should be aware of this fact.
- *
- * @param array $keys The keys of the data being requested.
- * Each key can be any structure although using a scalar string or int is recommended in the interests of performance.
- * In advanced cases an array may be useful such as in situations requiring the multi-key functionality.
- * @param int $strictness One of IGNORE_MISSING or MUST_EXIST.
- * @return array An array of key value pairs for the items that could be retrieved from the cache.
- * If MUST_EXIST was used and not all keys existed within the cache then an exception will be thrown.
- * Otherwise any key that did not exist will have a data value of false within the results.
- * @throws coding_exception
- */
- public function get_many(array $keys, $strictness = IGNORE_MISSING) {
- $this->check_tracked_user();
- $parsedkeys = array();
- $keymap = array();
- foreach ($keys as $key) {
- $parsedkey = $this->parse_key($key);
- $parsedkeys[$key] = $parsedkey;
- $keymap[$parsedkey] = $key;
- }
- $result = $this->get_store()->get_many($parsedkeys);
- if ($this->perfdebug) {
- $readbytes = $this->get_store()->get_last_io_bytes();
- }
- $return = array();
- $missingkeys = array();
- $hasmissingkeys = false;
- foreach ($result as $parsedkey => $value) {
- $key = $keymap[$parsedkey];
- if ($value instanceof cache_ttl_wrapper) {
- /* @var cache_ttl_wrapper $value */
- if ($value->has_expired()) {
- $this->delete($keymap[$parsedkey]);
- $value = false;
- } else {
- $value = $value->data;
- }
- }
- if ($value instanceof cache_cached_object) {
- /* @var cache_cached_object $value */
- $value = $value->restore_object();
- } else if (!$this->get_store()->supports_dereferencing_objects() && !is_scalar($value)) {
- // If data is an object it will be a reference.
- // If data is an array if may contain references.
- // We want to break references so that the cache cannot be modified outside of itself.
- // Call the function to unreference it (in the best way possible).
- $value = $this->unref($value);
- }
- $return[$key] = $value;
- if ($value === false) {
- $hasmissingkeys = true;
- $missingkeys[$parsedkey] = $key;
- }
- }
- if ($hasmissingkeys) {
- // We've got missing keys - we've got to check any loaders or data sources.
- $loader = $this->get_loader();
- $datasource = $this->get_datasource();
- if ($loader !== false) {
- foreach ($loader->get_many($missingkeys) as $key => $value) {
- if ($value !== false) {
- $return[$key] = $value;
- unset($missingkeys[$parsedkeys[$key]]);
- }
- }
- }
- $hasmissingkeys = count($missingkeys) > 0;
- if ($datasource !== false && $hasmissingkeys) {
- // We're still missing keys but we've got a datasource.
- foreach ($datasource->load_many_for_cache($missingkeys) as $key => $value) {
- if ($value !== false) {
- $return[$key] = $value;
- unset($missingkeys[$parsedkeys[$key]]);
- }
- }
- $hasmissingkeys = count($missingkeys) > 0;
- }
- }
- if ($hasmissingkeys && $strictness === MUST_EXIST) {
- throw new coding_exception('Requested key did not exist in any cache stores and could not be loaded.');
- }
- if ($this->perfdebug) {
- $hits = 0;
- $misses = 0;
- foreach ($return as $value) {
- if ($value === false) {
- $misses++;
- } else {
- $hits++;
- }
- }
- cache_helper::record_cache_hit($this->get_store(), $this->get_definition(), $hits, $readbytes);
- cache_helper::record_cache_miss($this->get_store(), $this->get_definition(), $misses);
- }
- return $return;
-
- }
-
- /**
- * Delete all of the given keys from the cache.
- *
- * @param array $keys The key to delete.
- * @param bool $recurse When set to true the key will also be deleted from all stacked cache loaders and their stores.
- * This happens by default and ensure that all the caches are consistent. It is NOT recommended to change this.
- * @return int The number of items successfully deleted.
- */
- public function delete_many(array $keys, $recurse = true) {
- $parsedkeys = array_map(array($this, 'parse_key'), $keys);
- if ($recurse && $this->get_loader() !== false) {
- // Delete from the bottom of the stack first.
- $this->get_loader()->delete_many($keys, $recurse);
- }
- return $this->get_store()->delete_many($parsedkeys);
- }
-
- /**
- * Sends several key => value pairs to the cache.
- *
- * Using this function comes with potential performance implications.
- * Not all cache stores will support get_many/set_many operations and in order to replicate this functionality will call
- * the equivalent singular method for each item provided.
- * This should not deter you from using this function as there is a performance benefit in situations where the cache store
- * does support it, but you should be aware of this fact.
- *
- *
- * // This code will add four entries to the cache, one for each url.
- * $cache->set_many(array(
- * 'main' => 'http://moodle.org',
- * 'docs' => 'http://docs.moodle.org',
- * 'tracker' => 'http://tracker.moodle.org',
- * 'qa' => ''http://qa.moodle.net'
- * ));
- *
- *
- * @param array $keyvaluearray An array of key => value pairs to send to the cache.
- * @return int The number of items successfully set. It is up to the developer to check this matches the number of items.
- * ... if they care that is.
- */
- public function set_many(array $keyvaluearray) {
- $this->check_tracked_user();
- $loader = $this->get_loader();
- if ($loader !== false) {
- // We have a loader available set it there as well.
- // We have to let the loader do its own parsing of data as it may be unique.
- $loader->set_many($keyvaluearray);
- }
- $data = array();
- $definitionid = $this->get_definition()->get_ttl();
- $simulatettl = $this->has_a_ttl() && !$this->store_supports_native_ttl();
- foreach ($keyvaluearray as $key => $value) {
- if (is_object($value) && $value instanceof cacheable_object) {
- $value = new cache_cached_object($value);
- } else if (!$this->get_store()->supports_dereferencing_objects() && !is_scalar($value)) {
- // If data is an object it will be a reference.
- // If data is an array if may contain references.
- // We want to break references so that the cache cannot be modified outside of itself.
- // Call the function to unreference it (in the best way possible).
- $value = $this->unref($value);
- }
- if ($simulatettl) {
- $value = new cache_ttl_wrapper($value, $definitionid);
- }
- $data[$key] = array(
- 'key' => $this->parse_key($key),
- 'value' => $value
- );
- }
- $successfullyset = $this->get_store()->set_many($data);
- if ($this->perfdebug && $successfullyset) {
- cache_helper::record_cache_set($this->get_store(), $this->get_definition(), $successfullyset,
- $this->get_store()->get_last_io_bytes());
- }
- return $successfullyset;
- }
-
- /**
- * Purges the cache store, and loader if there is one.
- *
- * @return bool True on success, false otherwise
- */
- public function purge() {
- $this->get_store()->purge();
- if ($this->get_loader()) {
- $this->get_loader()->purge();
- }
- return true;
- }
-
- /**
- * Test is a cache has a key.
- *
- * The use of the has methods is strongly discouraged. In a high load environment the cache may well change between the
- * test and any subsequent action (get, set, delete etc).
- * Instead it is recommended to write your code in such a way they it performs the following steps:
- *
- * - Attempt to retrieve the information.
- * - Generate the information.
- * - Attempt to set the information
- *
- *
- * Its also worth mentioning that not all stores support key tests.
- * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
- * Just one more reason you should not use these methods unless you have a very good reason to do so.
- *
- * @param string|int $key
- * @param bool $tryloadifpossible If set to true, the cache doesn't contain the key, and there is another cache loader or
- * data source then the code will try load the key value from the next item in the chain.
- * @return bool True if the cache has the requested key, false otherwise.
- */
- public function has($key, $tryloadifpossible = false) {
- $this->check_tracked_user();
- $parsedkey = $this->parse_key($key);
- $store = $this->get_store();
- if ($this->has_a_ttl() && !$this->store_supports_native_ttl()) {
- // The data has a TTL and the store doesn't support it natively.
- // We must fetch the data and expect a ttl wrapper.
- $data = $store->get($parsedkey);
- $has = ($data instanceof cache_ttl_wrapper && !$data->has_expired());
- } else if (!$this->store_supports_key_awareness()) {
- // The store doesn't support key awareness, get the data and check it manually... puke.
- // Either no TTL is set of the store supports its handling natively.
- $data = $store->get($parsedkey);
- $has = ($data !== false);
- } else {
- // The store supports key awareness, this is easy!
- // Either no TTL is set of the store supports its handling natively.
- /* @var cache_store|cache_is_key_aware $store */
- $has = $store->has($parsedkey);
- }
- if (!$has && $tryloadifpossible) {
- $result = null;
- if ($this->get_loader() !== false) {
- $result = $this->get_loader()->get($parsedkey);
- } else if ($this->get_datasource() !== null) {
- $result = $this->get_datasource()->load_for_cache($key);
- }
- $has = ($result !== null);
- if ($has) {
- $this->set($key, $result);
- }
- }
- return $has;
- }
-
- /**
- * Test is a cache has all of the given keys.
- *
- * It is strongly recommended to avoid the use of this function if not absolutely required.
- * In a high load environment the cache may well change between the test and any subsequent action (get, set, delete etc).
- *
- * Its also worth mentioning that not all stores support key tests.
- * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
- * Just one more reason you should not use these methods unless you have a very good reason to do so.
- *
- * @param array $keys
- * @return bool True if the cache has all of the given keys, false otherwise.
- */
- public function has_all(array $keys) {
- $this->check_tracked_user();
- if (($this->has_a_ttl() && !$this->store_supports_native_ttl()) || !$this->store_supports_key_awareness()) {
- foreach ($keys as $key) {
- if (!$this->has($key)) {
- return false;
- }
- }
- return true;
- }
- // The cache must be key aware and if support native ttl if it a ttl is set.
- /* @var cache_store|cache_is_key_aware $store */
- $store = $this->get_store();
- return $store->has_all(array_map(array($this, 'parse_key'), $keys));
- }
-
- /**
- * Test if a cache has at least one of the given keys.
- *
- * It is strongly recommended to avoid the use of this function if not absolutely required.
- * In a high load environment the cache may well change between the test and any subsequent action (get, set, delete etc).
- *
- * Its also worth mentioning that not all stores support key tests.
- * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
- * Just one more reason you should not use these methods unless you have a very good reason to do so.
- *
- * @param array $keys
- * @return bool True if the cache has at least one of the given keys
- */
- public function has_any(array $keys) {
- if (($this->has_a_ttl() && !$this->store_supports_native_ttl()) || !$this->store_supports_key_awareness()) {
- foreach ($keys as $key) {
- if ($this->has($key)) {
- return true;
- }
- }
- return false;
- }
- /* @var cache_store|cache_is_key_aware $store */
- $store = $this->get_store();
- return $store->has_any(array_map(array($this, 'parse_key'), $keys));
- }
-
- /**
- * The session loader never uses static acceleration.
- * Instead it stores things in the static $session variable. Shared between all session loaders.
- *
- * @return bool
- */
- protected function use_static_acceleration() {
- return false;
- }
-}
-
-/**
- * An request cache.
- *
- * This class is used for request caches returned by the cache::make methods.
- *
- * This cache class should never be interacted with directly. Instead you should always use the cache::make methods.
- * It is technically possible to call those methods through this class however there is no guarantee that you will get an
- * instance of this class back again.
- *
- * @internal don't use me directly.
- *
- * @package core
- * @category cache
- * @copyright 2012 Sam Hemelryk
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class cache_request extends cache {
- // This comment appeases code pre-checker ;) !
-}
diff --git a/cache/classes/cache_lock_interface.php b/cache/classes/cache_lock_interface.php
new file mode 100644
index 00000000000..fc7f8400380
--- /dev/null
+++ b/cache/classes/cache_lock_interface.php
@@ -0,0 +1,74 @@
+.
+
+/**
+ * Cache lock interface
+ *
+ * This interface needs to be inherited by all cache lock plugins.
+ */
+interface cache_lock_interface {
+ /**
+ * Constructs an instance of the cache lock given its name and its configuration data
+ *
+ * @param string $name The unique name of the lock instance
+ * @param array $configuration
+ */
+ public function __construct($name, array $configuration = array());
+
+ /**
+ * Acquires a lock on a given key.
+ *
+ * @param string $key The key to acquire a lock for.
+ * @param string $ownerid An unique identifier for the owner of this lock. It is entirely optional for the cache lock plugin
+ * to use this. Each implementation can decide for themselves.
+ * @param bool $block If set to true the application will wait until a lock can be acquired
+ * @return bool True if the lock can be acquired false otherwise.
+ */
+ public function lock($key, $ownerid, $block = false);
+
+ /**
+ * Releases the lock held on a certain key.
+ *
+ * @param string $key The key to release the lock for.
+ * @param string $ownerid An unique identifier for the owner of this lock. It is entirely optional for the cache lock plugin
+ * to use this. Each implementation can decide for themselves.
+ * @param bool $forceunlock If set to true the lock will be removed if it exists regardless of whether or not we own it.
+ */
+ public function unlock($key, $ownerid, $forceunlock = false);
+
+ /**
+ * Checks the state of the given key.
+ *
+ * Returns true if the key is locked and belongs to the ownerid.
+ * Returns false if the key is locked but does not belong to the ownerid.
+ * Returns null if there is no lock
+ *
+ * @param string $key The key we are checking for.
+ * @param string $ownerid The identifier so we can check if we have the lock or if it is someone else.
+ * @return bool True if this code has the lock, false if there is a lock but this code doesn't have it, null if there
+ * is no lock.
+ */
+ public function check_state($key, $ownerid);
+
+ /**
+ * Cleans up any left over locks.
+ *
+ * This function MUST clean up any locks that have been acquired and not released during processing.
+ * Although the situation of acquiring a lock and not releasing it should be insanely rare we need to deal with it.
+ * Things such as unfortunate timeouts etc could cause this situation.
+ */
+ public function __destruct();
+}
diff --git a/cache/classes/cacheable_object_array.php b/cache/classes/cacheable_object_array.php
new file mode 100644
index 00000000000..0267291337d
--- /dev/null
+++ b/cache/classes/cacheable_object_array.php
@@ -0,0 +1,92 @@
+.
+
+/**
+ * An array of cacheable objects.
+ *
+ * This class allows a developer to create an array of cacheable objects and store that.
+ * The cache API doesn't check items within an array to see whether they are cacheable. Such a check would be very costly to both
+ * arrays using cacheable object and those that don't.
+ * Instead the developer must explicitly use a cacheable_object_array instance.
+ *
+ * The following is one example of how this class can be used.
+ *
+ * $data = array();
+ * $data[] = new cacheable_object('one');
+ * $data[] = new cacheable_object('two');
+ * $data[] = new cacheable_object('three');
+ * $cache->set(new cacheable_object_array($data));
+ *
+ * Another example would be
+ *
+ * $data = new cacheable_object_array();
+ * $data[] = new cacheable_object('one');
+ * $data[] = new cacheable_object('two');
+ * $data[] = new cacheable_object('three');
+ * $cache->set($data);
+ *
+ *
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cacheable_object_array extends ArrayObject implements cacheable_object {
+
+ /**
+ * Constructs a new array object instance.
+ * @param array $items
+ */
+ final public function __construct(array $items = array()) {
+ parent::__construct($items, ArrayObject::STD_PROP_LIST);
+ }
+
+ /**
+ * Returns the data to cache for this object.
+ *
+ * @return array An array of cache_cached_object instances.
+ * @throws coding_exception
+ */
+ final public function prepare_to_cache() {
+ $result = array();
+ foreach ($this as $key => $value) {
+ if ($value instanceof cacheable_object) {
+ $value = new cache_cached_object($value);
+ } else {
+ throw new coding_exception('Only cacheable_object instances can be added to a cacheable_array');
+ }
+ $result[$key] = $value;
+ }
+ return $result;
+ }
+
+ /**
+ * Returns the cacheable_object_array that was originally sent to the cache.
+ *
+ * @param array $data
+ * @return cacheable_object_array
+ * @throws coding_exception
+ */
+ final public static function wake_from_cache($data) {
+ if (!is_array($data)) {
+ throw new coding_exception('Invalid data type when reviving cacheable_array data');
+ }
+ $result = array();
+ foreach ($data as $key => $value) {
+ $result[$key] = $value->restore_object();
+ }
+ $class = __CLASS__;
+ return new $class($result);
+ }
+}
diff --git a/cache/classes/cacheable_object_interface.php b/cache/classes/cacheable_object_interface.php
new file mode 100644
index 00000000000..a4fd5e9a419
--- /dev/null
+++ b/cache/classes/cacheable_object_interface.php
@@ -0,0 +1,43 @@
+.
+
+/**
+ * Cacheable object.
+ *
+ * This interface can be implemented by any class that is going to be passed into a cache and allows it to take control of the
+ * structure and the information about to be cached, as well as how to deal with it when it is retrieved from a cache.
+ * Think of it like serialisation and the __sleep and __wakeup methods.
+ * This is used because cache stores are responsible for how they interact with data and what they do when storing it. This
+ * interface ensures there is always a guaranteed action.
+ */
+interface cacheable_object {
+
+ /**
+ * Prepares the object for caching. Works like the __sleep method.
+ *
+ * @return mixed The data to cache, can be anything except a class that implements the cacheable_object... that would
+ * be dumb.
+ */
+ public function prepare_to_cache();
+
+ /**
+ * Takes the data provided by prepare_to_cache and reinitialises an instance of the associated from it.
+ *
+ * @param mixed $data
+ * @return object The instance for the given data.
+ */
+ public static function wake_from_cache($data);
+}
diff --git a/cache/classes/cached_object.php b/cache/classes/cached_object.php
new file mode 100644
index 00000000000..aec2f847d85
--- /dev/null
+++ b/cache/classes/cached_object.php
@@ -0,0 +1,58 @@
+.
+
+/**
+ * A cached object wrapper.
+ *
+ * This class gets used when the data is an object that has implemented the cacheable_object interface.
+ *
+ * @package core
+ * @category cache
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cache_cached_object {
+
+ /**
+ * The class of the cacheable object
+ * @var string
+ */
+ protected $class;
+
+ /**
+ * The data returned by the cacheable_object prepare_to_cache method.
+ * @var mixed
+ */
+ protected $data;
+
+ /**
+ * Constructs a cached object wrapper.
+ * @param cacheable_object $obj
+ */
+ public function __construct(cacheable_object $obj) {
+ $this->class = get_class($obj);
+ $this->data = $obj->prepare_to_cache();
+ }
+
+ /**
+ * Restores the data as an instance of the cacheable_object class.
+ * @return object
+ */
+ public function restore_object() {
+ $class = $this->class;
+ return $class::wake_from_cache($this->data);
+ }
+}
diff --git a/cache/classes/config.php b/cache/classes/config.php
index ad192cae74b..d51e9322713 100644
--- a/cache/classes/config.php
+++ b/cache/classes/config.php
@@ -14,20 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
-/**
- * Cache configuration reader
- *
- * This file is part of Moodle's cache API, affectionately called MUC.
- * It contains the components that are requried in order to use caching.
- *
- * @package core
- * @category cache
- * @copyright 2012 Sam Hemelryk
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
/**
* Cache configuration reader.
*
@@ -478,10 +464,10 @@ class cache_config {
/**
* Gets all of the stores that are to be used for the given definition.
*
- * @param cache_definition $definition
- * @return array
+ * @param definition $definition
+ * @return array
*/
- public function get_stores_for_definition(cache_definition $definition) {
+ public function get_stores_for_definition(definition $definition) {
// Check if MUC has been disabled.
$factory = cache_factory::instance();
if ($factory->stores_disabled()) {
diff --git a/cache/classes/configurable_cache_interface.php b/cache/classes/configurable_cache_interface.php
new file mode 100644
index 00000000000..4cc554382d9
--- /dev/null
+++ b/cache/classes/configurable_cache_interface.php
@@ -0,0 +1,44 @@
+.
+
+/**
+ * Cache store feature: configurable.
+ *
+ * This feature should be implemented by all cache stores that are configurable when adding an instance.
+ * It requires the implementation of methods required to convert form data into the a configuration array for the
+ * store instance, and then the reverse converting configuration data into an array that can be used to set the
+ * data for the edit form.
+ *
+ * Can be implemented by classes already implementing cache_store.
+ */
+interface cache_is_configurable {
+
+ /**
+ * Given the data from the add instance form this function creates a configuration array.
+ *
+ * @param stdClass $data
+ * @return array
+ */
+ public static function config_get_configuration_array($data);
+
+ /**
+ * Allows the cache store to set its data against the edit form before it is shown to the user.
+ *
+ * @param moodleform $editform
+ * @param array $config
+ */
+ public static function config_set_edit_form_data(moodleform $editform, array $config);
+}
diff --git a/cache/classes/data_source_interface.php b/cache/classes/data_source_interface.php
new file mode 100644
index 00000000000..a96a8d277b4
--- /dev/null
+++ b/cache/classes/data_source_interface.php
@@ -0,0 +1,60 @@
+.
+
+/**
+ * Cache Data Source.
+ *
+ * The cache data source interface can be implemented by any class within Moodle.
+ * If implemented then the class can be reference in a cache definition and will be used to load information that cannot be
+ * retrieved from the cache. As part of its retrieval that information will also be loaded into the cache.
+ *
+ * This allows developers to created a complete cache solution that can be used through code ensuring consistent cache
+ * interaction and loading. Allowing them in turn to centralise code and help keeps things more easily maintainable.
+ *
+ * Can be implemented by any class.
+ *
+ * @package core
+ * @category cache
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+interface cache_data_source {
+
+ /**
+ * Returns an instance of the data source class that the cache can use for loading data using the other methods
+ * specified by this interface.
+ *
+ * @param cache_definition $definition
+ * @return object
+ */
+ public static function get_instance_for_cache(cache_definition $definition);
+
+ /**
+ * Loads the data for the key provided ready formatted for caching.
+ *
+ * @param string|int $key The key to load.
+ * @return mixed What ever data should be returned, or false if it can't be loaded.
+ */
+ public function load_for_cache($key);
+
+ /**
+ * Loads several keys for the cache.
+ *
+ * @param array $keys An array of keys each of which will be string|int.
+ * @return array An array of matching data items.
+ */
+ public function load_many_for_cache(array $keys);
+}
diff --git a/cache/classes/exception/cache_exception.php b/cache/classes/exception/cache_exception.php
new file mode 100644
index 00000000000..3e1ae88c865
--- /dev/null
+++ b/cache/classes/exception/cache_exception.php
@@ -0,0 +1,39 @@
+.
+
+/**
+ * A cache exception class. Just allows people to catch cache exceptions.
+ *
+ * @package core
+ * @category cache
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cache_exception extends moodle_exception {
+ /**
+ * Constructs a new exception
+ *
+ * @param string $errorcode
+ * @param string $module
+ * @param string $link
+ * @param mixed $a
+ * @param mixed $debuginfo
+ */
+ public function __construct($errorcode, $module = 'cache', $link = '', $a = null, $debuginfo = null) {
+ // This may appear like a useless override but you will notice that we have set a MUCH more useful default for $module.
+ parent::__construct($errorcode, $module, $link, $a, $debuginfo);
+ }
+}
diff --git a/cache/classes/factory.php b/cache/classes/factory.php
index 116b1b36258..d2991a5d7f7 100644
--- a/cache/classes/factory.php
+++ b/cache/classes/factory.php
@@ -14,20 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
-/**
- * This file contains the cache factory class.
- *
- * This file is part of Moodle's cache API, affectionately called MUC.
- * It contains the components that are requried in order to use caching.
- *
- * @package core
- * @category cache
- * @copyright 2012 Sam Hemelryk
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
/**
* The cache factory class.
*
diff --git a/cache/classes/helper.php b/cache/classes/helper.php
index a88372e77e1..689e5475c3d 100644
--- a/cache/classes/helper.php
+++ b/cache/classes/helper.php
@@ -14,20 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see .
-/**
- * Cache helper class
- *
- * This file is part of Moodle's cache API, affectionately called MUC.
- * It contains the components that are requried in order to use caching.
- *
- * @package core
- * @category cache
- * @copyright 2012 Sam Hemelryk
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
/**
* The cache helper class.
*
diff --git a/cache/classes/interfaces.php b/cache/classes/interfaces.php
deleted file mode 100644
index e0dff287f64..00000000000
--- a/cache/classes/interfaces.php
+++ /dev/null
@@ -1,605 +0,0 @@
-.
-
-/**
- * Cache API interfaces
- *
- * This file is part of Moodle's cache API, affectionately called MUC.
- * It contains the components that are requried in order to use caching.
- *
- * @package core
- * @category cache
- * @copyright 2012 Sam Hemelryk
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-/**
- * Cache Loader.
- *
- * This cache loader interface provides the required structure for classes that wish to be interacted with as a
- * means of accessing and interacting with a cache.
- *
- * Can be implemented by any class wishing to be a cache loader.
- */
-interface cache_loader {
-
- /**
- * Retrieves the value for the given key from the cache.
- *
- * @param string|int $key The key for the data being requested.
- * @param int $strictness One of IGNORE_MISSING or MUST_EXIST.
- * @return mixed The data retrieved from the cache, or false if the key did not exist within the cache.
- * If MUST_EXIST was used then an exception will be thrown if the key does not exist within the cache.
- */
- public function get($key, $strictness = IGNORE_MISSING);
-
- /**
- * Retrieves the value and actual version for the given key, with at least the required version.
- *
- * If there is no value for the key, or there is a value but it doesn't have the required
- * version, then this function will return false (or throw an exception if you set strictness
- * to MUST_EXIST).
- *
- * This function can be used to make it easier to support localisable caches (where the cache
- * could be stored on a local server as well as a shared cache). Specifying the version means
- * that it will automatically retrieve the correct version if available, either from the local
- * server or [if that has an older version] from the shared server.
- *
- * If the cached version is newer than specified version, it will be returned regardless. For
- * example, if you request version 4, but the locally cached version is 5, it will be returned.
- * If you request version 6, and the locally cached version is 5, then the system will look in
- * higher-level caches (if any); if there still isn't a version 6 or greater, it will return
- * null.
- *
- * You must use this function if you use set_versioned.
- *
- * @param string|int $key The key for the data being requested.
- * @param int $requiredversion Minimum required version of the data
- * @param int $strictness One of IGNORE_MISSING or MUST_EXIST.
- * @param mixed $actualversion If specified, will be set to the actual version number retrieved
- * @return mixed Data from the cache, or false if the key did not exist or was too old
- */
- public function get_versioned($key, int $requiredversion, int $strictness = IGNORE_MISSING, &$actualversion = null);
-
- /**
- * Retrieves an array of values for an array of keys.
- *
- * Using this function comes with potential performance implications.
- * Not all cache stores will support get_many/set_many operations and in order to replicate this functionality will call
- * the equivalent singular method for each item provided.
- * This should not deter you from using this function as there is a performance benefit in situations where the cache
- * store does support it, but you should be aware of this fact.
- *
- * @param array $keys The keys of the data being requested.
- * @param int $strictness One of IGNORE_MISSING or MUST_EXIST.
- * @return array An array of key value pairs for the items that could be retrieved from the cache.
- * If MUST_EXIST was used and not all keys existed within the cache then an exception will be thrown.
- * Otherwise any key that did not exist will have a data value of false within the results.
- */
- public function get_many(array $keys, $strictness = IGNORE_MISSING);
-
- /**
- * Sends a key => value pair to the cache.
- *
- *
- * // This code will add four entries to the cache, one for each url.
- * $cache->set('main', 'http://moodle.org');
- * $cache->set('docs', 'http://docs.moodle.org');
- * $cache->set('tracker', 'http://tracker.moodle.org');
- * $cache->set('qa', 'http://qa.moodle.net');
- *
- *
- * @param string|int $key The key for the data being requested.
- * @param mixed $data The data to set against the key.
- * @return bool True on success, false otherwise.
- */
- public function set($key, $data);
-
- /**
- * Sets the value for the given key with the given version.
- *
- * The cache does not store multiple versions - any existing version will be overwritten with
- * this one. This function should only be used if there is a known 'current version' (e.g.
- * stored in a database table). It only ensures that the cache does not return outdated data.
- *
- * This function can be used to help implement localisable caches (where the cache could be
- * stored on a local server as well as a shared cache). The version will be recorded alongside
- * the item and get_versioned will always return the correct version.
- *
- * The version number must be an integer that always increases. This could be based on the
- * current time, or a stored value that increases by 1 each time it changes, etc.
- *
- * If you use this function you must use get_versioned to retrieve the data.
- *
- * @param string|int $key The key for the data being set.
- * @param int $version Integer for the version of the data
- * @param mixed $data The data to set against the key.
- * @return bool True on success, false otherwise.
- */
- public function set_versioned($key, int $version, $data): bool;
-
- /**
- * Sends several key => value pairs to the cache.
- *
- * Using this function comes with potential performance implications.
- * Not all cache stores will support get_many/set_many operations and in order to replicate this functionality will call
- * the equivalent singular method for each item provided.
- * This should not deter you from using this function as there is a performance benefit in situations where the cache store
- * does support it, but you should be aware of this fact.
- *
- *
- * // This code will add four entries to the cache, one for each url.
- * $cache->set_many(array(
- * 'main' => 'http://moodle.org',
- * 'docs' => 'http://docs.moodle.org',
- * 'tracker' => 'http://tracker.moodle.org',
- * 'qa' => ''http://qa.moodle.net'
- * ));
- *
- *
- * @param array $keyvaluearray An array of key => value pairs to send to the cache.
- * @return int The number of items successfully set. It is up to the developer to check this matches the number of items.
- * ... if they care that is.
- */
- public function set_many(array $keyvaluearray);
-
- /**
- * Test is a cache has a key.
- *
- * The use of the has methods is strongly discouraged. In a high load environment the cache may well change between the
- * test and any subsequent action (get, set, delete etc).
- * Instead it is recommended to write your code in such a way they it performs the following steps:
- *
- * - Attempt to retrieve the information.
- * - Generate the information.
- * - Attempt to set the information
- *
- *
- * Its also worth mentioning that not all stores support key tests.
- * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
- * Just one more reason you should not use these methods unless you have a very good reason to do so.
- *
- * @param string|int $key
- * @return bool True if the cache has the requested key, false otherwise.
- */
- public function has($key);
-
- /**
- * Test if a cache has at least one of the given keys.
- *
- * It is strongly recommended to avoid the use of this function if not absolutely required.
- * In a high load environment the cache may well change between the test and any subsequent action (get, set, delete etc).
- *
- * Its also worth mentioning that not all stores support key tests.
- * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
- * Just one more reason you should not use these methods unless you have a very good reason to do so.
- *
- * @param array $keys
- * @return bool True if the cache has at least one of the given keys
- */
- public function has_any(array $keys);
-
- /**
- * Test is a cache has all of the given keys.
- *
- * It is strongly recommended to avoid the use of this function if not absolutely required.
- * In a high load environment the cache may well change between the test and any subsequent action (get, set, delete etc).
- *
- * Its also worth mentioning that not all stores support key tests.
- * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
- * Just one more reason you should not use these methods unless you have a very good reason to do so.
- *
- * @param array $keys
- * @return bool True if the cache has all of the given keys, false otherwise.
- */
- public function has_all(array $keys);
-
- /**
- * Delete the given key from the cache.
- *
- * @param string|int $key The key to delete.
- * @param bool $recurse When set to true the key will also be deleted from all stacked cache loaders and their stores.
- * This happens by default and ensure that all the caches are consistent. It is NOT recommended to change this.
- * @return bool True of success, false otherwise.
- */
- public function delete($key, $recurse = true);
-
- /**
- * Delete all of the given keys from the cache.
- *
- * @param array $keys The key to delete.
- * @param bool $recurse When set to true the key will also be deleted from all stacked cache loaders and their stores.
- * This happens by default and ensure that all the caches are consistent. It is NOT recommended to change this.
- * @return int The number of items successfully deleted.
- */
- public function delete_many(array $keys, $recurse = true);
-}
-
-/**
- * Cache Loader supporting locking.
- *
- * This interface should be given to classes already implementing cache_loader that also wish to support locking.
- * It outlines the required structure for utilising locking functionality when using a cache.
- *
- * Can be implemented by any class already implementing the cache_loader interface.
- */
-interface cache_loader_with_locking {
-
- /**
- * Acquires a lock for the given key.
- *
- * Please note that this happens automatically if the cache definition requires locking.
- * it is still made a public method so that adhoc caches can use it if they choose.
- * However this doesn't guarantee consistent access. It will become the responsibility of the calling code to ensure
- * locks are acquired, checked, and released.
- *
- * Prior to Moodle 4,3 this function used to return false if the lock cannot be obtained. It
- * now always returns true, and throws an exception if the lock cannot be obtained.
- *
- * @param string|int $key
- * @return bool Always returns true (for backwards compatibility)
- * @throws moodle_exception If the lock cannot be obtained after a timeout
- */
- public function acquire_lock($key);
-
- /**
- * Checks if the cache loader owns the lock for the given key.
- *
- * Please note that this happens automatically if the cache definition requires locking.
- * it is still made a public method so that adhoc caches can use it if they choose.
- * However this doesn't guarantee consistent access. It will become the responsibility of the calling code to ensure
- * locks are acquired, checked, and released.
- *
- * @param string|int $key
- * @return bool True if this code has the lock, false if there is a lock but this code doesn't have it,
- * null if there is no lock.
- */
- public function check_lock_state($key);
-
- /**
- * Releases the lock for the given key.
- *
- * Please note that this happens automatically if the cache definition requires locking.
- * it is still made a public method so that adhoc caches can use it if they choose.
- * However this doesn't guarantee consistent access. It will become the responsibility of the calling code to ensure
- * locks are acquired, checked, and released.
- *
- * @param string|int $key
- * @return bool True if the lock has been released, false if there was a problem releasing the lock.
- */
- public function release_lock($key);
-}
-
-/**
- * Cache store feature: locking
- *
- * This is a feature that cache stores can implement if they wish to support locking themselves rather
- * than having the cache loader handle it for them.
- *
- * Can be implemented by classes already implementing cache_store.
- */
-interface cache_is_lockable {
-
- /**
- * Acquires a lock on the given key for the given identifier.
- *
- * @param string $key The key we are locking.
- * @param string $ownerid The identifier so we can check if we have the lock or if it is someone else.
- * The use of this property is entirely optional and implementations can act as they like upon it.
- * @return bool True if the lock could be acquired, false otherwise.
- */
- public function acquire_lock($key, $ownerid);
-
- /**
- * Test if there is already a lock for the given key and if there is whether it belongs to the calling code.
- *
- * @param string $key The key we are locking.
- * @param string $ownerid The identifier so we can check if we have the lock or if it is someone else.
- * @return bool True if this code has the lock, false if there is a lock but this code doesn't have it, null if there
- * is no lock.
- */
- public function check_lock_state($key, $ownerid);
-
- /**
- * Releases the lock on the given key.
- *
- * @param string $key The key we are locking.
- * @param string $ownerid The identifier so we can check if we have the lock or if it is someone else.
- * The use of this property is entirely optional and implementations can act as they like upon it.
- * @return bool True if the lock has been released, false if there was a problem releasing the lock.
- */
- public function release_lock($key, $ownerid);
-}
-
-/**
- * Cache store feature: key awareness.
- *
- * This is a feature that cache stores and cache loaders can both choose to implement.
- * If a cache store implements this then it will be made responsible for tests for items within the cache.
- * If the cache store being used doesn't implement this then it will be the responsibility of the cache loader to use the
- * equivalent get methods to mimick the functionality of these tests.
- *
- * Cache stores should only override these methods if they natively support such features or if they have a better performing
- * means of performing these tests than the handling that would otherwise take place in the cache_loader.
- *
- * Can be implemented by classes already implementing cache_store.
- */
-interface cache_is_key_aware {
-
- /**
- * Test is a cache has a key.
- *
- * The use of the has methods is strongly discouraged. In a high load environment the cache may well change between the
- * test and any subsequent action (get, set, delete etc).
- * Instead it is recommended to write your code in such a way they it performs the following steps:
- *
- * - Attempt to retrieve the information.
- * - Generate the information.
- * - Attempt to set the information
- *
- *
- * Its also worth mentioning that not all stores support key tests.
- * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
- * Just one more reason you should not use these methods unless you have a very good reason to do so.
- *
- * @param string|int $key
- * @return bool True if the cache has the requested key, false otherwise.
- */
- public function has($key);
-
- /**
- * Test if a cache has at least one of the given keys.
- *
- * It is strongly recommended to avoid the use of this function if not absolutely required.
- * In a high load environment the cache may well change between the test and any subsequent action (get, set, delete etc).
- *
- * Its also worth mentioning that not all stores support key tests.
- * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
- * Just one more reason you should not use these methods unless you have a very good reason to do so.
- *
- * @param array $keys
- * @return bool True if the cache has at least one of the given keys
- */
- public function has_any(array $keys);
-
- /**
- * Test is a cache has all of the given keys.
- *
- * It is strongly recommended to avoid the use of this function if not absolutely required.
- * In a high load environment the cache may well change between the test and any subsequent action (get, set, delete etc).
- *
- * Its also worth mentioning that not all stores support key tests.
- * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
- * Just one more reason you should not use these methods unless you have a very good reason to do so.
- *
- * @param array $keys
- * @return bool True if the cache has all of the given keys, false otherwise.
- */
- public function has_all(array $keys);
-}
-
-/**
- * Cache store feature: keys are searchable.
- *
- * Cache stores can choose to implement this interface.
- * In order for a store to be usable as a session cache it must implement this interface.
- *
- * @since Moodle 2.4.4
- */
-interface cache_is_searchable {
- /**
- * Finds all of the keys being used by the cache store.
- *
- * @return array.
- */
- public function find_all();
-
- /**
- * Finds all of the keys whose keys start with the given prefix.
- *
- * @param string $prefix
- */
- public function find_by_prefix($prefix);
-}
-
-/**
- * Cache store feature: configurable.
- *
- * This feature should be implemented by all cache stores that are configurable when adding an instance.
- * It requires the implementation of methods required to convert form data into the a configuration array for the
- * store instance, and then the reverse converting configuration data into an array that can be used to set the
- * data for the edit form.
- *
- * Can be implemented by classes already implementing cache_store.
- */
-interface cache_is_configurable {
-
- /**
- * Given the data from the add instance form this function creates a configuration array.
- *
- * @param stdClass $data
- * @return array
- */
- public static function config_get_configuration_array($data);
-
- /**
- * Allows the cache store to set its data against the edit form before it is shown to the user.
- *
- * @param moodleform $editform
- * @param array $config
- */
- public static function config_set_edit_form_data(moodleform $editform, array $config);
-}
-
-/**
- * Cache Data Source.
- *
- * The cache data source interface can be implemented by any class within Moodle.
- * If implemented then the class can be reference in a cache definition and will be used to load information that cannot be
- * retrieved from the cache. As part of its retrieval that information will also be loaded into the cache.
- *
- * This allows developers to created a complete cache solution that can be used through code ensuring consistent cache
- * interaction and loading. Allowing them in turn to centralise code and help keeps things more easily maintainable.
- *
- * Can be implemented by any class.
- *
- * @package core
- * @category cache
- * @copyright 2012 Sam Hemelryk
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-interface cache_data_source {
-
- /**
- * Returns an instance of the data source class that the cache can use for loading data using the other methods
- * specified by this interface.
- *
- * @param cache_definition $definition
- * @return object
- */
- public static function get_instance_for_cache(cache_definition $definition);
-
- /**
- * Loads the data for the key provided ready formatted for caching.
- *
- * @param string|int $key The key to load.
- * @return mixed What ever data should be returned, or false if it can't be loaded.
- */
- public function load_for_cache($key);
-
- /**
- * Loads several keys for the cache.
- *
- * @param array $keys An array of keys each of which will be string|int.
- * @return array An array of matching data items.
- */
- public function load_many_for_cache(array $keys);
-}
-
-/**
- * Versionable cache data source.
- *
- * This interface extends the main cache data source interface to add an extra required method if
- * the data source is to be used for a versioned cache.
- *
- * @package core_cache
- */
-interface cache_data_source_versionable extends cache_data_source {
- /**
- * Loads the data for the key provided ready formatted for caching.
- *
- * If there is no data for that key, or if the data for the required key has an older version
- * than the specified $requiredversion, then this returns null.
- *
- * If there is data then $actualversion should be set to the actual version number retrieved
- * (may be the same as $requiredversion or newer).
- *
- * @param string|int $key The key to load.
- * @param int $requiredversion Minimum required version
- * @param mixed $actualversion Should be set to the actual version number retrieved
- * @return mixed What ever data should be returned, or false if it can't be loaded.
- */
- public function load_for_cache_versioned($key, int $requiredversion, &$actualversion);
-}
-
-/**
- * Cacheable object.
- *
- * This interface can be implemented by any class that is going to be passed into a cache and allows it to take control of the
- * structure and the information about to be cached, as well as how to deal with it when it is retrieved from a cache.
- * Think of it like serialisation and the __sleep and __wakeup methods.
- * This is used because cache stores are responsible for how they interact with data and what they do when storing it. This
- * interface ensures there is always a guaranteed action.
- */
-interface cacheable_object {
-
- /**
- * Prepares the object for caching. Works like the __sleep method.
- *
- * @return mixed The data to cache, can be anything except a class that implements the cacheable_object... that would
- * be dumb.
- */
- public function prepare_to_cache();
-
- /**
- * Takes the data provided by prepare_to_cache and reinitialises an instance of the associated from it.
- *
- * @param mixed $data
- * @return object The instance for the given data.
- */
- public static function wake_from_cache($data);
-}
-
-/**
- * Cache lock interface
- *
- * This interface needs to be inherited by all cache lock plugins.
- */
-interface cache_lock_interface {
- /**
- * Constructs an instance of the cache lock given its name and its configuration data
- *
- * @param string $name The unique name of the lock instance
- * @param array $configuration
- */
- public function __construct($name, array $configuration = array());
-
- /**
- * Acquires a lock on a given key.
- *
- * @param string $key The key to acquire a lock for.
- * @param string $ownerid An unique identifier for the owner of this lock. It is entirely optional for the cache lock plugin
- * to use this. Each implementation can decide for themselves.
- * @param bool $block If set to true the application will wait until a lock can be acquired
- * @return bool True if the lock can be acquired false otherwise.
- */
- public function lock($key, $ownerid, $block = false);
-
- /**
- * Releases the lock held on a certain key.
- *
- * @param string $key The key to release the lock for.
- * @param string $ownerid An unique identifier for the owner of this lock. It is entirely optional for the cache lock plugin
- * to use this. Each implementation can decide for themselves.
- * @param bool $forceunlock If set to true the lock will be removed if it exists regardless of whether or not we own it.
- */
- public function unlock($key, $ownerid, $forceunlock = false);
-
- /**
- * Checks the state of the given key.
- *
- * Returns true if the key is locked and belongs to the ownerid.
- * Returns false if the key is locked but does not belong to the ownerid.
- * Returns null if there is no lock
- *
- * @param string $key The key we are checking for.
- * @param string $ownerid The identifier so we can check if we have the lock or if it is someone else.
- * @return bool True if this code has the lock, false if there is a lock but this code doesn't have it, null if there
- * is no lock.
- */
- public function check_state($key, $ownerid);
-
- /**
- * Cleans up any left over locks.
- *
- * This function MUST clean up any locks that have been acquired and not released during processing.
- * Although the situation of acquiring a lock and not releasing it should be insanely rare we need to deal with it.
- * Things such as unfortunate timeouts etc could cause this situation.
- */
- public function __destruct();
-}
diff --git a/cache/classes/key_aware_cache_interface.php b/cache/classes/key_aware_cache_interface.php
new file mode 100644
index 00000000000..e23f3439fef
--- /dev/null
+++ b/cache/classes/key_aware_cache_interface.php
@@ -0,0 +1,82 @@
+.
+
+/**
+ * Cache store feature: key awareness.
+ *
+ * This is a feature that cache stores and cache loaders can both choose to implement.
+ * If a cache store implements this then it will be made responsible for tests for items within the cache.
+ * If the cache store being used doesn't implement this then it will be the responsibility of the cache loader to use the
+ * equivalent get methods to mimick the functionality of these tests.
+ *
+ * Cache stores should only override these methods if they natively support such features or if they have a better performing
+ * means of performing these tests than the handling that would otherwise take place in the cache_loader.
+ *
+ * Can be implemented by classes already implementing cache_store.
+ */
+interface cache_is_key_aware {
+
+ /**
+ * Test is a cache has a key.
+ *
+ * The use of the has methods is strongly discouraged. In a high load environment the cache may well change between the
+ * test and any subsequent action (get, set, delete etc).
+ * Instead it is recommended to write your code in such a way they it performs the following steps:
+ *
+ * - Attempt to retrieve the information.
+ * - Generate the information.
+ * - Attempt to set the information
+ *
+ *
+ * Its also worth mentioning that not all stores support key tests.
+ * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
+ * Just one more reason you should not use these methods unless you have a very good reason to do so.
+ *
+ * @param string|int $key
+ * @return bool True if the cache has the requested key, false otherwise.
+ */
+ public function has($key);
+
+ /**
+ * Test if a cache has at least one of the given keys.
+ *
+ * It is strongly recommended to avoid the use of this function if not absolutely required.
+ * In a high load environment the cache may well change between the test and any subsequent action (get, set, delete etc).
+ *
+ * Its also worth mentioning that not all stores support key tests.
+ * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
+ * Just one more reason you should not use these methods unless you have a very good reason to do so.
+ *
+ * @param array $keys
+ * @return bool True if the cache has at least one of the given keys
+ */
+ public function has_any(array $keys);
+
+ /**
+ * Test is a cache has all of the given keys.
+ *
+ * It is strongly recommended to avoid the use of this function if not absolutely required.
+ * In a high load environment the cache may well change between the test and any subsequent action (get, set, delete etc).
+ *
+ * Its also worth mentioning that not all stores support key tests.
+ * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
+ * Just one more reason you should not use these methods unless you have a very good reason to do so.
+ *
+ * @param array $keys
+ * @return bool True if the cache has all of the given keys, false otherwise.
+ */
+ public function has_all(array $keys);
+}
diff --git a/cache/classes/loader_interface.php b/cache/classes/loader_interface.php
new file mode 100644
index 00000000000..ad250e22b9d
--- /dev/null
+++ b/cache/classes/loader_interface.php
@@ -0,0 +1,217 @@
+.
+
+/**
+ * Cache Loader.
+ *
+ * This cache loader interface provides the required structure for classes that wish to be interacted with as a
+ * means of accessing and interacting with a cache.
+ *
+ * Can be implemented by any class wishing to be a cache loader.
+ */
+interface cache_loader {
+
+ /**
+ * Retrieves the value for the given key from the cache.
+ *
+ * @param string|int $key The key for the data being requested.
+ * @param int $strictness One of IGNORE_MISSING or MUST_EXIST.
+ * @return mixed The data retrieved from the cache, or false if the key did not exist within the cache.
+ * If MUST_EXIST was used then an exception will be thrown if the key does not exist within the cache.
+ */
+ public function get($key, $strictness = IGNORE_MISSING);
+
+ /**
+ * Retrieves the value and actual version for the given key, with at least the required version.
+ *
+ * If there is no value for the key, or there is a value but it doesn't have the required
+ * version, then this function will return false (or throw an exception if you set strictness
+ * to MUST_EXIST).
+ *
+ * This function can be used to make it easier to support localisable caches (where the cache
+ * could be stored on a local server as well as a shared cache). Specifying the version means
+ * that it will automatically retrieve the correct version if available, either from the local
+ * server or [if that has an older version] from the shared server.
+ *
+ * If the cached version is newer than specified version, it will be returned regardless. For
+ * example, if you request version 4, but the locally cached version is 5, it will be returned.
+ * If you request version 6, and the locally cached version is 5, then the system will look in
+ * higher-level caches (if any); if there still isn't a version 6 or greater, it will return
+ * null.
+ *
+ * You must use this function if you use set_versioned.
+ *
+ * @param string|int $key The key for the data being requested.
+ * @param int $requiredversion Minimum required version of the data
+ * @param int $strictness One of IGNORE_MISSING or MUST_EXIST.
+ * @param mixed $actualversion If specified, will be set to the actual version number retrieved
+ * @return mixed Data from the cache, or false if the key did not exist or was too old
+ */
+ public function get_versioned($key, int $requiredversion, int $strictness = IGNORE_MISSING, &$actualversion = null);
+
+ /**
+ * Retrieves an array of values for an array of keys.
+ *
+ * Using this function comes with potential performance implications.
+ * Not all cache stores will support get_many/set_many operations and in order to replicate this functionality will call
+ * the equivalent singular method for each item provided.
+ * This should not deter you from using this function as there is a performance benefit in situations where the cache
+ * store does support it, but you should be aware of this fact.
+ *
+ * @param array $keys The keys of the data being requested.
+ * @param int $strictness One of IGNORE_MISSING or MUST_EXIST.
+ * @return array An array of key value pairs for the items that could be retrieved from the cache.
+ * If MUST_EXIST was used and not all keys existed within the cache then an exception will be thrown.
+ * Otherwise any key that did not exist will have a data value of false within the results.
+ */
+ public function get_many(array $keys, $strictness = IGNORE_MISSING);
+
+ /**
+ * Sends a key => value pair to the cache.
+ *
+ *
+ * // This code will add four entries to the cache, one for each url.
+ * $cache->set('main', 'http://moodle.org');
+ * $cache->set('docs', 'http://docs.moodle.org');
+ * $cache->set('tracker', 'http://tracker.moodle.org');
+ * $cache->set('qa', 'http://qa.moodle.net');
+ *
+ *
+ * @param string|int $key The key for the data being requested.
+ * @param mixed $data The data to set against the key.
+ * @return bool True on success, false otherwise.
+ */
+ public function set($key, $data);
+
+ /**
+ * Sets the value for the given key with the given version.
+ *
+ * The cache does not store multiple versions - any existing version will be overwritten with
+ * this one. This function should only be used if there is a known 'current version' (e.g.
+ * stored in a database table). It only ensures that the cache does not return outdated data.
+ *
+ * This function can be used to help implement localisable caches (where the cache could be
+ * stored on a local server as well as a shared cache). The version will be recorded alongside
+ * the item and get_versioned will always return the correct version.
+ *
+ * The version number must be an integer that always increases. This could be based on the
+ * current time, or a stored value that increases by 1 each time it changes, etc.
+ *
+ * If you use this function you must use get_versioned to retrieve the data.
+ *
+ * @param string|int $key The key for the data being set.
+ * @param int $version Integer for the version of the data
+ * @param mixed $data The data to set against the key.
+ * @return bool True on success, false otherwise.
+ */
+ public function set_versioned($key, int $version, $data): bool;
+
+ /**
+ * Sends several key => value pairs to the cache.
+ *
+ * Using this function comes with potential performance implications.
+ * Not all cache stores will support get_many/set_many operations and in order to replicate this functionality will call
+ * the equivalent singular method for each item provided.
+ * This should not deter you from using this function as there is a performance benefit in situations where the cache store
+ * does support it, but you should be aware of this fact.
+ *
+ *
+ * // This code will add four entries to the cache, one for each url.
+ * $cache->set_many(array(
+ * 'main' => 'http://moodle.org',
+ * 'docs' => 'http://docs.moodle.org',
+ * 'tracker' => 'http://tracker.moodle.org',
+ * 'qa' => ''http://qa.moodle.net'
+ * ));
+ *
+ *
+ * @param array $keyvaluearray An array of key => value pairs to send to the cache.
+ * @return int The number of items successfully set. It is up to the developer to check this matches the number of items.
+ * ... if they care that is.
+ */
+ public function set_many(array $keyvaluearray);
+
+ /**
+ * Test is a cache has a key.
+ *
+ * The use of the has methods is strongly discouraged. In a high load environment the cache may well change between the
+ * test and any subsequent action (get, set, delete etc).
+ * Instead it is recommended to write your code in such a way they it performs the following steps:
+ *
+ * - Attempt to retrieve the information.
+ * - Generate the information.
+ * - Attempt to set the information
+ *
+ *
+ * Its also worth mentioning that not all stores support key tests.
+ * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
+ * Just one more reason you should not use these methods unless you have a very good reason to do so.
+ *
+ * @param string|int $key
+ * @return bool True if the cache has the requested key, false otherwise.
+ */
+ public function has($key);
+
+ /**
+ * Test if a cache has at least one of the given keys.
+ *
+ * It is strongly recommended to avoid the use of this function if not absolutely required.
+ * In a high load environment the cache may well change between the test and any subsequent action (get, set, delete etc).
+ *
+ * Its also worth mentioning that not all stores support key tests.
+ * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
+ * Just one more reason you should not use these methods unless you have a very good reason to do so.
+ *
+ * @param array $keys
+ * @return bool True if the cache has at least one of the given keys
+ */
+ public function has_any(array $keys);
+
+ /**
+ * Test is a cache has all of the given keys.
+ *
+ * It is strongly recommended to avoid the use of this function if not absolutely required.
+ * In a high load environment the cache may well change between the test and any subsequent action (get, set, delete etc).
+ *
+ * Its also worth mentioning that not all stores support key tests.
+ * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
+ * Just one more reason you should not use these methods unless you have a very good reason to do so.
+ *
+ * @param array $keys
+ * @return bool True if the cache has all of the given keys, false otherwise.
+ */
+ public function has_all(array $keys);
+
+ /**
+ * Delete the given key from the cache.
+ *
+ * @param string|int $key The key to delete.
+ * @param bool $recurse When set to true the key will also be deleted from all stacked cache loaders and their stores.
+ * This happens by default and ensure that all the caches are consistent. It is NOT recommended to change this.
+ * @return bool True of success, false otherwise.
+ */
+ public function delete($key, $recurse = true);
+
+ /**
+ * Delete all of the given keys from the cache.
+ *
+ * @param array $keys The key to delete.
+ * @param bool $recurse When set to true the key will also be deleted from all stacked cache loaders and their stores.
+ * This happens by default and ensure that all the caches are consistent. It is NOT recommended to change this.
+ * @return int The number of items successfully deleted.
+ */
+ public function delete_many(array $keys, $recurse = true);
+}
diff --git a/cache/classes/loader_with_locking_interface.php b/cache/classes/loader_with_locking_interface.php
new file mode 100644
index 00000000000..f2cf04b3a28
--- /dev/null
+++ b/cache/classes/loader_with_locking_interface.php
@@ -0,0 +1,69 @@
+.
+
+/**
+ * Cache Loader supporting locking.
+ *
+ * This interface should be given to classes already implementing cache_loader that also wish to support locking.
+ * It outlines the required structure for utilising locking functionality when using a cache.
+ *
+ * Can be implemented by any class already implementing the cache_loader interface.
+ */
+interface cache_loader_with_locking {
+ /**
+ * Acquires a lock for the given key.
+ *
+ * Please note that this happens automatically if the cache definition requires locking.
+ * it is still made a public method so that adhoc caches can use it if they choose.
+ * However this doesn't guarantee consistent access. It will become the responsibility of the calling code to ensure
+ * locks are acquired, checked, and released.
+ *
+ * Prior to Moodle 4,3 this function used to return false if the lock cannot be obtained. It
+ * now always returns true, and throws an exception if the lock cannot be obtained.
+ *
+ * @param string|int $key
+ * @return bool Always returns true (for backwards compatibility)
+ * @throws moodle_exception If the lock cannot be obtained after a timeout
+ */
+ public function acquire_lock($key);
+
+ /**
+ * Checks if the cache loader owns the lock for the given key.
+ *
+ * Please note that this happens automatically if the cache definition requires locking.
+ * it is still made a public method so that adhoc caches can use it if they choose.
+ * However this doesn't guarantee consistent access. It will become the responsibility of the calling code to ensure
+ * locks are acquired, checked, and released.
+ *
+ * @param string|int $key
+ * @return bool True if this code has the lock, false if there is a lock but this code doesn't have it,
+ * null if there is no lock.
+ */
+ public function check_lock_state($key);
+
+ /**
+ * Releases the lock for the given key.
+ *
+ * Please note that this happens automatically if the cache definition requires locking.
+ * it is still made a public method so that adhoc caches can use it if they choose.
+ * However this doesn't guarantee consistent access. It will become the responsibility of the calling code to ensure
+ * locks are acquired, checked, and released.
+ *
+ * @param string|int $key
+ * @return bool True if the lock has been released, false if there was a problem releasing the lock.
+ */
+ public function release_lock($key);
+}
diff --git a/cache/classes/lockable_cache_interface.php b/cache/classes/lockable_cache_interface.php
new file mode 100644
index 00000000000..42541db9258
--- /dev/null
+++ b/cache/classes/lockable_cache_interface.php
@@ -0,0 +1,56 @@
+.
+
+/**
+ * Cache store feature: locking
+ *
+ * This is a feature that cache stores can implement if they wish to support locking themselves rather
+ * than having the cache loader handle it for them.
+ *
+ * Can be implemented by classes already implementing cache_store.
+ */
+interface cache_is_lockable {
+
+ /**
+ * Acquires a lock on the given key for the given identifier.
+ *
+ * @param string $key The key we are locking.
+ * @param string $ownerid The identifier so we can check if we have the lock or if it is someone else.
+ * The use of this property is entirely optional and implementations can act as they like upon it.
+ * @return bool True if the lock could be acquired, false otherwise.
+ */
+ public function acquire_lock($key, $ownerid);
+
+ /**
+ * Test if there is already a lock for the given key and if there is whether it belongs to the calling code.
+ *
+ * @param string $key The key we are locking.
+ * @param string $ownerid The identifier so we can check if we have the lock or if it is someone else.
+ * @return bool True if this code has the lock, false if there is a lock but this code doesn't have it, null if there
+ * is no lock.
+ */
+ public function check_lock_state($key, $ownerid);
+
+ /**
+ * Releases the lock on the given key.
+ *
+ * @param string $key The key we are locking.
+ * @param string $ownerid The identifier so we can check if we have the lock or if it is someone else.
+ * The use of this property is entirely optional and implementations can act as they like upon it.
+ * @return bool True if the lock has been released, false if there was a problem releasing the lock.
+ */
+ public function release_lock($key, $ownerid);
+}
diff --git a/cache/classes/request_cache.php b/cache/classes/request_cache.php
new file mode 100644
index 00000000000..760a2b0275f
--- /dev/null
+++ b/cache/classes/request_cache.php
@@ -0,0 +1,35 @@
+.
+
+/**
+ * An request cache.
+ *
+ * This class is used for request caches returned by the cache::make methods.
+ *
+ * This cache class should never be interacted with directly. Instead you should always use the cache::make methods.
+ * It is technically possible to call those methods through this class however there is no guarantee that you will get an
+ * instance of this class back again.
+ *
+ * @internal don't use me directly.
+ *
+ * @package core
+ * @category cache
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cache_request extends cache {
+ // This comment appeases code pre-checker ;) !
+}
diff --git a/cache/classes/searchable_cache_interface.php b/cache/classes/searchable_cache_interface.php
new file mode 100644
index 00000000000..55563798134
--- /dev/null
+++ b/cache/classes/searchable_cache_interface.php
@@ -0,0 +1,39 @@
+.
+
+/**
+ * Cache store feature: keys are searchable.
+ *
+ * Cache stores can choose to implement this interface.
+ * In order for a store to be usable as a session cache it must implement this interface.
+ *
+ * @since Moodle 2.4.4
+ */
+interface cache_is_searchable {
+ /**
+ * Finds all of the keys being used by the cache store.
+ *
+ * @return array.
+ */
+ public function find_all();
+
+ /**
+ * Finds all of the keys whose keys start with the given prefix.
+ *
+ * @param string $prefix
+ */
+ public function find_by_prefix($prefix);
+}
diff --git a/cache/classes/session_cache.php b/cache/classes/session_cache.php
new file mode 100644
index 00000000000..6a54ceb1a17
--- /dev/null
+++ b/cache/classes/session_cache.php
@@ -0,0 +1,576 @@
+.
+
+/**
+ * A session cache.
+ *
+ * This class is used for session caches returned by the cache::make methods.
+ *
+ * It differs from the application loader in a couple of noteable ways:
+ * 1. Sessions are always expected to exist.
+ * Because of this we don't ever use the static acceleration array.
+ * 2. Session data for a loader instance (store + definition) is consolidate into a
+ * single array for storage within the store.
+ * Along with this we embed a lastaccessed time with the data. This way we can
+ * check sessions for a last access time.
+ * 3. Session stores are required to support key searching and must
+ * implement cache_is_searchable. This ensures stores used for the cache can be
+ * targetted for garbage collection of session data.
+ *
+ * This cache class should never be interacted with directly. Instead you should always use the cache::make methods.
+ * It is technically possible to call those methods through this class however there is no guarantee that you will get an
+ * instance of this class back again.
+ *
+ * @todo we should support locking in the session as well. Should be pretty simple to set up.
+ *
+ * @internal don't use me directly.
+ * @method cache_store|cache_is_searchable get_store() Returns the cache store which must implement both cache_is_searchable.
+ *
+ * @package core
+ * @category cache
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cache_session extends cache {
+ /**
+ * The user the session has been established for.
+ * @var int
+ */
+ protected static $loadeduserid = null;
+
+ /**
+ * The userid this cache is currently using.
+ * @var int
+ */
+ protected $currentuserid = null;
+
+ /**
+ * The session id we are currently using.
+ * @var array
+ */
+ protected $sessionid = null;
+
+ /**
+ * The session data for the above session id.
+ * @var array
+ */
+ protected $session = null;
+
+ /**
+ * Constant used to prefix keys.
+ */
+ const KEY_PREFIX = 'sess_';
+
+ /**
+ * This is the key used to track last access.
+ */
+ const LASTACCESS = '__lastaccess__';
+
+ /**
+ * Override the cache::construct method.
+ *
+ * This function gets overriden so that we can process any invalidation events if need be.
+ * If the definition doesn't have any invalidation events then this occurs exactly as it would for the cache class.
+ * Otherwise we look at the last invalidation time and then check the invalidation data for events that have occured
+ * between then now.
+ *
+ * You should not call this method from your code, instead you should use the cache::make methods.
+ *
+ * @param cache_definition $definition
+ * @param cache_store $store
+ * @param cache_loader|cache_data_source $loader
+ */
+ public function __construct(cache_definition $definition, cache_store $store, $loader = null) {
+ // First up copy the loadeduserid to the current user id.
+ $this->currentuserid = self::$loadeduserid;
+ $this->set_session_id();
+ parent::__construct($definition, $store, $loader);
+
+ // This will trigger check tracked user. If this gets removed a call to that will need to be added here in its place.
+ $this->set(self::LASTACCESS, cache::now());
+
+ $this->handle_invalidation_events();
+ }
+
+ /**
+ * Sets the session id for the loader.
+ */
+ protected function set_session_id() {
+ $this->sessionid = preg_replace('#[^a-zA-Z0-9_]#', '_', session_id());
+ }
+
+ /**
+ * Returns the prefix used for all keys.
+ * @return string
+ */
+ protected function get_key_prefix() {
+ return 'u'.$this->currentuserid.'_'.$this->sessionid;
+ }
+
+ /**
+ * Parses the key turning it into a string (or array is required) suitable to be passed to the cache store.
+ *
+ * This function is called for every operation that uses keys. For this reason we use this function to also check
+ * that the current user is the same as the user who last used this cache.
+ *
+ * On top of that if prepends the string 'sess_' to the start of all keys. The _ ensures things are easily identifiable.
+ *
+ * @param string|int $key As passed to get|set|delete etc.
+ * @return string|array String unless the store supports multi-identifiers in which case an array if returned.
+ */
+ protected function parse_key($key) {
+ $prefix = $this->get_key_prefix();
+ if ($key === self::LASTACCESS) {
+ return $key.$prefix;
+ }
+ return $prefix.'_'.parent::parse_key($key);
+ }
+
+ /**
+ * Check that this cache instance is tracking the current user.
+ */
+ protected function check_tracked_user() {
+ if (isset($_SESSION['USER']->id) && $_SESSION['USER']->id !== null) {
+ // Get the id of the current user.
+ $new = $_SESSION['USER']->id;
+ } else {
+ // No user set up yet.
+ $new = 0;
+ }
+ if ($new !== self::$loadeduserid) {
+ // The current user doesn't match the tracked userid for this request.
+ if (!is_null(self::$loadeduserid)) {
+ // Purge the data we have for the old user.
+ // This way we don't bloat the session.
+ $this->purge();
+ }
+ self::$loadeduserid = $new;
+ $this->currentuserid = $new;
+ } else if ($new !== $this->currentuserid) {
+ // The current user matches the loaded user but not the user last used by this cache.
+ $this->purge_current_user();
+ $this->currentuserid = $new;
+ }
+ }
+
+ /**
+ * Purges the session cache of all data belonging to the current user.
+ */
+ public function purge_current_user() {
+ $keys = $this->get_store()->find_by_prefix($this->get_key_prefix());
+ $this->get_store()->delete_many($keys);
+ }
+
+ /**
+ * Retrieves the value for the given key from the cache.
+ *
+ * @param string|int $key The key for the data being requested.
+ * It can be any structure although using a scalar string or int is recommended in the interests of performance.
+ * In advanced cases an array may be useful such as in situations requiring the multi-key functionality.
+ * @param int $requiredversion Minimum required version of the data or cache::VERSION_NONE
+ * @param int $strictness One of IGNORE_MISSING | MUST_EXIST
+ * @param mixed &$actualversion If specified, will be set to the actual version number retrieved
+ * @return mixed|false The data from the cache or false if the key did not exist within the cache.
+ * @throws coding_exception
+ */
+ protected function get_implementation($key, int $requiredversion, int $strictness, &$actualversion = null) {
+ // Check the tracked user.
+ $this->check_tracked_user();
+
+ // Use parent code.
+ return parent::get_implementation($key, $requiredversion, $strictness, $actualversion);
+ }
+
+ /**
+ * Sends a key => value pair to the cache.
+ *
+ *
+ * // This code will add four entries to the cache, one for each url.
+ * $cache->set('main', 'http://moodle.org');
+ * $cache->set('docs', 'http://docs.moodle.org');
+ * $cache->set('tracker', 'http://tracker.moodle.org');
+ * $cache->set('qa', 'http://qa.moodle.net');
+ *
+ *
+ * @param string|int $key The key for the data being requested.
+ * It can be any structure although using a scalar string or int is recommended in the interests of performance.
+ * In advanced cases an array may be useful such as in situations requiring the multi-key functionality.
+ * @param mixed $data The data to set against the key.
+ * @return bool True on success, false otherwise.
+ */
+ public function set($key, $data) {
+ $this->check_tracked_user();
+ $loader = $this->get_loader();
+ if ($loader !== false) {
+ // We have a loader available set it there as well.
+ // We have to let the loader do its own parsing of data as it may be unique.
+ $loader->set($key, $data);
+ }
+ if (is_object($data) && $data instanceof cacheable_object) {
+ $data = new cache_cached_object($data);
+ } else if (!$this->get_store()->supports_dereferencing_objects() && !is_scalar($data)) {
+ // If data is an object it will be a reference.
+ // If data is an array if may contain references.
+ // We want to break references so that the cache cannot be modified outside of itself.
+ // Call the function to unreference it (in the best way possible).
+ $data = $this->unref($data);
+ }
+ // We dont' support native TTL here as we consolidate data for sessions.
+ if ($this->has_a_ttl() && !$this->store_supports_native_ttl()) {
+ $data = new cache_ttl_wrapper($data, $this->get_definition()->get_ttl());
+ }
+ $success = $this->get_store()->set($this->parse_key($key), $data);
+ if ($this->perfdebug) {
+ cache_helper::record_cache_set($this->get_store(), $this->get_definition(), 1,
+ $this->get_store()->get_last_io_bytes());
+ }
+ return $success;
+ }
+
+ /**
+ * Delete the given key from the cache.
+ *
+ * @param string|int $key The key to delete.
+ * @param bool $recurse When set to true the key will also be deleted from all stacked cache loaders and their stores.
+ * This happens by default and ensure that all the caches are consistent. It is NOT recommended to change this.
+ * @return bool True of success, false otherwise.
+ */
+ public function delete($key, $recurse = true) {
+ $parsedkey = $this->parse_key($key);
+ if ($recurse && $this->get_loader() !== false) {
+ // Delete from the bottom of the stack first.
+ $this->get_loader()->delete($key, $recurse);
+ }
+ return $this->get_store()->delete($parsedkey);
+ }
+
+ /**
+ * Retrieves an array of values for an array of keys.
+ *
+ * Using this function comes with potential performance implications.
+ * Not all cache stores will support get_many/set_many operations and in order to replicate this functionality will call
+ * the equivalent singular method for each item provided.
+ * This should not deter you from using this function as there is a performance benefit in situations where the cache store
+ * does support it, but you should be aware of this fact.
+ *
+ * @param array $keys The keys of the data being requested.
+ * Each key can be any structure although using a scalar string or int is recommended in the interests of performance.
+ * In advanced cases an array may be useful such as in situations requiring the multi-key functionality.
+ * @param int $strictness One of IGNORE_MISSING or MUST_EXIST.
+ * @return array An array of key value pairs for the items that could be retrieved from the cache.
+ * If MUST_EXIST was used and not all keys existed within the cache then an exception will be thrown.
+ * Otherwise any key that did not exist will have a data value of false within the results.
+ * @throws coding_exception
+ */
+ public function get_many(array $keys, $strictness = IGNORE_MISSING) {
+ $this->check_tracked_user();
+ $parsedkeys = array();
+ $keymap = array();
+ foreach ($keys as $key) {
+ $parsedkey = $this->parse_key($key);
+ $parsedkeys[$key] = $parsedkey;
+ $keymap[$parsedkey] = $key;
+ }
+ $result = $this->get_store()->get_many($parsedkeys);
+ if ($this->perfdebug) {
+ $readbytes = $this->get_store()->get_last_io_bytes();
+ }
+ $return = array();
+ $missingkeys = array();
+ $hasmissingkeys = false;
+ foreach ($result as $parsedkey => $value) {
+ $key = $keymap[$parsedkey];
+ if ($value instanceof cache_ttl_wrapper) {
+ /* @var cache_ttl_wrapper $value */
+ if ($value->has_expired()) {
+ $this->delete($keymap[$parsedkey]);
+ $value = false;
+ } else {
+ $value = $value->data;
+ }
+ }
+ if ($value instanceof cache_cached_object) {
+ /* @var cache_cached_object $value */
+ $value = $value->restore_object();
+ } else if (!$this->get_store()->supports_dereferencing_objects() && !is_scalar($value)) {
+ // If data is an object it will be a reference.
+ // If data is an array if may contain references.
+ // We want to break references so that the cache cannot be modified outside of itself.
+ // Call the function to unreference it (in the best way possible).
+ $value = $this->unref($value);
+ }
+ $return[$key] = $value;
+ if ($value === false) {
+ $hasmissingkeys = true;
+ $missingkeys[$parsedkey] = $key;
+ }
+ }
+ if ($hasmissingkeys) {
+ // We've got missing keys - we've got to check any loaders or data sources.
+ $loader = $this->get_loader();
+ $datasource = $this->get_datasource();
+ if ($loader !== false) {
+ foreach ($loader->get_many($missingkeys) as $key => $value) {
+ if ($value !== false) {
+ $return[$key] = $value;
+ unset($missingkeys[$parsedkeys[$key]]);
+ }
+ }
+ }
+ $hasmissingkeys = count($missingkeys) > 0;
+ if ($datasource !== false && $hasmissingkeys) {
+ // We're still missing keys but we've got a datasource.
+ foreach ($datasource->load_many_for_cache($missingkeys) as $key => $value) {
+ if ($value !== false) {
+ $return[$key] = $value;
+ unset($missingkeys[$parsedkeys[$key]]);
+ }
+ }
+ $hasmissingkeys = count($missingkeys) > 0;
+ }
+ }
+ if ($hasmissingkeys && $strictness === MUST_EXIST) {
+ throw new coding_exception('Requested key did not exist in any cache stores and could not be loaded.');
+ }
+ if ($this->perfdebug) {
+ $hits = 0;
+ $misses = 0;
+ foreach ($return as $value) {
+ if ($value === false) {
+ $misses++;
+ } else {
+ $hits++;
+ }
+ }
+ cache_helper::record_cache_hit($this->get_store(), $this->get_definition(), $hits, $readbytes);
+ cache_helper::record_cache_miss($this->get_store(), $this->get_definition(), $misses);
+ }
+ return $return;
+
+ }
+
+ /**
+ * Delete all of the given keys from the cache.
+ *
+ * @param array $keys The key to delete.
+ * @param bool $recurse When set to true the key will also be deleted from all stacked cache loaders and their stores.
+ * This happens by default and ensure that all the caches are consistent. It is NOT recommended to change this.
+ * @return int The number of items successfully deleted.
+ */
+ public function delete_many(array $keys, $recurse = true) {
+ $parsedkeys = array_map(array($this, 'parse_key'), $keys);
+ if ($recurse && $this->get_loader() !== false) {
+ // Delete from the bottom of the stack first.
+ $this->get_loader()->delete_many($keys, $recurse);
+ }
+ return $this->get_store()->delete_many($parsedkeys);
+ }
+
+ /**
+ * Sends several key => value pairs to the cache.
+ *
+ * Using this function comes with potential performance implications.
+ * Not all cache stores will support get_many/set_many operations and in order to replicate this functionality will call
+ * the equivalent singular method for each item provided.
+ * This should not deter you from using this function as there is a performance benefit in situations where the cache store
+ * does support it, but you should be aware of this fact.
+ *
+ *
+ * // This code will add four entries to the cache, one for each url.
+ * $cache->set_many(array(
+ * 'main' => 'http://moodle.org',
+ * 'docs' => 'http://docs.moodle.org',
+ * 'tracker' => 'http://tracker.moodle.org',
+ * 'qa' => ''http://qa.moodle.net'
+ * ));
+ *
+ *
+ * @param array $keyvaluearray An array of key => value pairs to send to the cache.
+ * @return int The number of items successfully set. It is up to the developer to check this matches the number of items.
+ * ... if they care that is.
+ */
+ public function set_many(array $keyvaluearray) {
+ $this->check_tracked_user();
+ $loader = $this->get_loader();
+ if ($loader !== false) {
+ // We have a loader available set it there as well.
+ // We have to let the loader do its own parsing of data as it may be unique.
+ $loader->set_many($keyvaluearray);
+ }
+ $data = array();
+ $definitionid = $this->get_definition()->get_ttl();
+ $simulatettl = $this->has_a_ttl() && !$this->store_supports_native_ttl();
+ foreach ($keyvaluearray as $key => $value) {
+ if (is_object($value) && $value instanceof cacheable_object) {
+ $value = new cache_cached_object($value);
+ } else if (!$this->get_store()->supports_dereferencing_objects() && !is_scalar($value)) {
+ // If data is an object it will be a reference.
+ // If data is an array if may contain references.
+ // We want to break references so that the cache cannot be modified outside of itself.
+ // Call the function to unreference it (in the best way possible).
+ $value = $this->unref($value);
+ }
+ if ($simulatettl) {
+ $value = new cache_ttl_wrapper($value, $definitionid);
+ }
+ $data[$key] = array(
+ 'key' => $this->parse_key($key),
+ 'value' => $value
+ );
+ }
+ $successfullyset = $this->get_store()->set_many($data);
+ if ($this->perfdebug && $successfullyset) {
+ cache_helper::record_cache_set($this->get_store(), $this->get_definition(), $successfullyset,
+ $this->get_store()->get_last_io_bytes());
+ }
+ return $successfullyset;
+ }
+
+ /**
+ * Purges the cache store, and loader if there is one.
+ *
+ * @return bool True on success, false otherwise
+ */
+ public function purge() {
+ $this->get_store()->purge();
+ if ($this->get_loader()) {
+ $this->get_loader()->purge();
+ }
+ return true;
+ }
+
+ /**
+ * Test is a cache has a key.
+ *
+ * The use of the has methods is strongly discouraged. In a high load environment the cache may well change between the
+ * test and any subsequent action (get, set, delete etc).
+ * Instead it is recommended to write your code in such a way they it performs the following steps:
+ *
+ * - Attempt to retrieve the information.
+ * - Generate the information.
+ * - Attempt to set the information
+ *
+ *
+ * Its also worth mentioning that not all stores support key tests.
+ * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
+ * Just one more reason you should not use these methods unless you have a very good reason to do so.
+ *
+ * @param string|int $key
+ * @param bool $tryloadifpossible If set to true, the cache doesn't contain the key, and there is another cache loader or
+ * data source then the code will try load the key value from the next item in the chain.
+ * @return bool True if the cache has the requested key, false otherwise.
+ */
+ public function has($key, $tryloadifpossible = false) {
+ $this->check_tracked_user();
+ $parsedkey = $this->parse_key($key);
+ $store = $this->get_store();
+ if ($this->has_a_ttl() && !$this->store_supports_native_ttl()) {
+ // The data has a TTL and the store doesn't support it natively.
+ // We must fetch the data and expect a ttl wrapper.
+ $data = $store->get($parsedkey);
+ $has = ($data instanceof cache_ttl_wrapper && !$data->has_expired());
+ } else if (!$this->store_supports_key_awareness()) {
+ // The store doesn't support key awareness, get the data and check it manually... puke.
+ // Either no TTL is set of the store supports its handling natively.
+ $data = $store->get($parsedkey);
+ $has = ($data !== false);
+ } else {
+ // The store supports key awareness, this is easy!
+ // Either no TTL is set of the store supports its handling natively.
+ /* @var cache_store|cache_is_key_aware $store */
+ $has = $store->has($parsedkey);
+ }
+ if (!$has && $tryloadifpossible) {
+ $result = null;
+ if ($this->get_loader() !== false) {
+ $result = $this->get_loader()->get($parsedkey);
+ } else if ($this->get_datasource() !== null) {
+ $result = $this->get_datasource()->load_for_cache($key);
+ }
+ $has = ($result !== null);
+ if ($has) {
+ $this->set($key, $result);
+ }
+ }
+ return $has;
+ }
+
+ /**
+ * Test is a cache has all of the given keys.
+ *
+ * It is strongly recommended to avoid the use of this function if not absolutely required.
+ * In a high load environment the cache may well change between the test and any subsequent action (get, set, delete etc).
+ *
+ * Its also worth mentioning that not all stores support key tests.
+ * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
+ * Just one more reason you should not use these methods unless you have a very good reason to do so.
+ *
+ * @param array $keys
+ * @return bool True if the cache has all of the given keys, false otherwise.
+ */
+ public function has_all(array $keys) {
+ $this->check_tracked_user();
+ if (($this->has_a_ttl() && !$this->store_supports_native_ttl()) || !$this->store_supports_key_awareness()) {
+ foreach ($keys as $key) {
+ if (!$this->has($key)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ // The cache must be key aware and if support native ttl if it a ttl is set.
+ /* @var cache_store|cache_is_key_aware $store */
+ $store = $this->get_store();
+ return $store->has_all(array_map(array($this, 'parse_key'), $keys));
+ }
+
+ /**
+ * Test if a cache has at least one of the given keys.
+ *
+ * It is strongly recommended to avoid the use of this function if not absolutely required.
+ * In a high load environment the cache may well change between the test and any subsequent action (get, set, delete etc).
+ *
+ * Its also worth mentioning that not all stores support key tests.
+ * For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
+ * Just one more reason you should not use these methods unless you have a very good reason to do so.
+ *
+ * @param array $keys
+ * @return bool True if the cache has at least one of the given keys
+ */
+ public function has_any(array $keys) {
+ if (($this->has_a_ttl() && !$this->store_supports_native_ttl()) || !$this->store_supports_key_awareness()) {
+ foreach ($keys as $key) {
+ if ($this->has($key)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ /* @var cache_store|cache_is_key_aware $store */
+ $store = $this->get_store();
+ return $store->has_any(array_map(array($this, 'parse_key'), $keys));
+ }
+
+ /**
+ * The session loader never uses static acceleration.
+ * Instead it stores things in the static $session variable. Shared between all session loaders.
+ *
+ * @return bool
+ */
+ protected function use_static_acceleration() {
+ return false;
+ }
+}
diff --git a/cache/classes/ttl_wrapper.php b/cache/classes/ttl_wrapper.php
new file mode 100644
index 00000000000..97abbe98905
--- /dev/null
+++ b/cache/classes/ttl_wrapper.php
@@ -0,0 +1,59 @@
+.
+
+/**
+ * A wrapper class used to handle ttl when the cache store doesn't natively support it.
+ *
+ * This class is exactly why you should use event driving invalidation of cache data rather than relying on ttl.
+ *
+ * @package core
+ * @category cache
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class cache_ttl_wrapper {
+
+ /**
+ * The data being stored.
+ * @var mixed
+ */
+ public $data;
+
+ /**
+ * When the cache data expires as a timestamp.
+ * @var int
+ */
+ public $expires;
+
+ /**
+ * Constructs a ttl cache wrapper.
+ *
+ * @param mixed $data
+ * @param int $ttl The time to live in seconds.
+ */
+ public function __construct($data, $ttl) {
+ $this->data = $data;
+ $this->expires = cache::now() + (int)$ttl;
+ }
+
+ /**
+ * Returns true if the data has expired.
+ * @return int
+ */
+ public function has_expired() {
+ return ($this->expires < cache::now());
+ }
+}
diff --git a/cache/classes/versionable_data_source_interface.php b/cache/classes/versionable_data_source_interface.php
new file mode 100644
index 00000000000..48fa1719883
--- /dev/null
+++ b/cache/classes/versionable_data_source_interface.php
@@ -0,0 +1,41 @@
+.
+
+/**
+ * Versionable cache data source.
+ *
+ * This interface extends the main cache data source interface to add an extra required method if
+ * the data source is to be used for a versioned cache.
+ *
+ * @package core_cache
+ */
+interface cache_data_source_versionable extends cache_data_source {
+ /**
+ * Loads the data for the key provided ready formatted for caching.
+ *
+ * If there is no data for that key, or if the data for the required key has an older version
+ * than the specified $requiredversion, then this returns null.
+ *
+ * If there is data then $actualversion should be set to the actual version number retrieved
+ * (may be the same as $requiredversion or newer).
+ *
+ * @param string|int $key The key to load.
+ * @param int $requiredversion Minimum required version
+ * @param mixed $actualversion Should be set to the actual version number retrieved
+ * @return mixed What ever data should be returned, or false if it can't be loaded.
+ */
+ public function load_for_cache_versioned($key, int $requiredversion, &$actualversion);
+}
diff --git a/cache/lib.php b/cache/lib.php
index c11b67a6193..c57ea19e0a8 100644
--- a/cache/lib.php
+++ b/cache/lib.php
@@ -31,198 +31,5 @@
defined('MOODLE_INTERNAL') || die();
// Include the required classes.
-require_once($CFG->dirroot.'/cache/classes/interfaces.php');
-require_once($CFG->dirroot.'/cache/classes/config.php');
-require_once($CFG->dirroot.'/cache/classes/helper.php');
-require_once($CFG->dirroot.'/cache/classes/factory.php');
-require_once($CFG->dirroot.'/cache/classes/loaders.php');
require_once($CFG->dirroot.'/cache/classes/store.php');
require_once($CFG->dirroot.'/cache/classes/definition.php');
-
-/**
- * A cached object wrapper.
- *
- * This class gets used when the data is an object that has implemented the cacheable_object interface.
- *
- * @package core
- * @category cache
- * @copyright 2012 Sam Hemelryk
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class cache_cached_object {
-
- /**
- * The class of the cacheable object
- * @var string
- */
- protected $class;
-
- /**
- * The data returned by the cacheable_object prepare_to_cache method.
- * @var mixed
- */
- protected $data;
-
- /**
- * Constructs a cached object wrapper.
- * @param cacheable_object $obj
- */
- public function __construct(cacheable_object $obj) {
- $this->class = get_class($obj);
- $this->data = $obj->prepare_to_cache();
- }
-
- /**
- * Restores the data as an instance of the cacheable_object class.
- * @return object
- */
- public function restore_object() {
- $class = $this->class;
- return $class::wake_from_cache($this->data);
- }
-}
-
-/**
- * A wrapper class used to handle ttl when the cache store doesn't natively support it.
- *
- * This class is exactly why you should use event driving invalidation of cache data rather than relying on ttl.
- *
- * @package core
- * @category cache
- * @copyright 2012 Sam Hemelryk
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class cache_ttl_wrapper {
-
- /**
- * The data being stored.
- * @var mixed
- */
- public $data;
-
- /**
- * When the cache data expires as a timestamp.
- * @var int
- */
- public $expires;
-
- /**
- * Constructs a ttl cache wrapper.
- *
- * @param mixed $data
- * @param int $ttl The time to live in seconds.
- */
- public function __construct($data, $ttl) {
- $this->data = $data;
- $this->expires = cache::now() + (int)$ttl;
- }
-
- /**
- * Returns true if the data has expired.
- * @return int
- */
- public function has_expired() {
- return ($this->expires < cache::now());
- }
-}
-
-/**
- * A cache exception class. Just allows people to catch cache exceptions.
- *
- * @package core
- * @category cache
- * @copyright 2012 Sam Hemelryk
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class cache_exception extends moodle_exception {
- /**
- * Constructs a new exception
- *
- * @param string $errorcode
- * @param string $module
- * @param string $link
- * @param mixed $a
- * @param mixed $debuginfo
- */
- public function __construct($errorcode, $module = 'cache', $link = '', $a = null, $debuginfo = null) {
- // This may appear like a useless override but you will notice that we have set a MUCH more useful default for $module.
- parent::__construct($errorcode, $module, $link, $a, $debuginfo);
- }
-}
-
-/**
- * An array of cacheable objects.
- *
- * This class allows a developer to create an array of cacheable objects and store that.
- * The cache API doesn't check items within an array to see whether they are cacheable. Such a check would be very costly to both
- * arrays using cacheable object and those that don't.
- * Instead the developer must explicitly use a cacheable_object_array instance.
- *
- * The following is one example of how this class can be used.
- *
- * $data = array();
- * $data[] = new cacheable_object('one');
- * $data[] = new cacheable_object('two');
- * $data[] = new cacheable_object('three');
- * $cache->set(new cacheable_object_array($data));
- *
- * Another example would be
- *
- * $data = new cacheable_object_array();
- * $data[] = new cacheable_object('one');
- * $data[] = new cacheable_object('two');
- * $data[] = new cacheable_object('three');
- * $cache->set($data);
- *
- *
- * @copyright 2012 Sam Hemelryk
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class cacheable_object_array extends ArrayObject implements cacheable_object {
-
- /**
- * Constructs a new array object instance.
- * @param array $items
- */
- final public function __construct(array $items = array()) {
- parent::__construct($items, ArrayObject::STD_PROP_LIST);
- }
-
- /**
- * Returns the data to cache for this object.
- *
- * @return array An array of cache_cached_object instances.
- * @throws coding_exception
- */
- final public function prepare_to_cache() {
- $result = array();
- foreach ($this as $key => $value) {
- if ($value instanceof cacheable_object) {
- $value = new cache_cached_object($value);
- } else {
- throw new coding_exception('Only cacheable_object instances can be added to a cacheable_array');
- }
- $result[$key] = $value;
- }
- return $result;
- }
-
- /**
- * Returns the cacheable_object_array that was originally sent to the cache.
- *
- * @param array $data
- * @return cacheable_object_array
- * @throws coding_exception
- */
- final public static function wake_from_cache($data) {
- if (!is_array($data)) {
- throw new coding_exception('Invalid data type when reviving cacheable_array data');
- }
- $result = array();
- foreach ($data as $key => $value) {
- $result[$key] = $value->restore_object();
- }
- $class = __CLASS__;
- return new $class($result);
- }
-}
diff --git a/lib/db/legacyclasses.php b/lib/db/legacyclasses.php
index 6022f731d47..6bf2dad6faf 100644
--- a/lib/db/legacyclasses.php
+++ b/lib/db/legacyclasses.php
@@ -50,6 +50,31 @@ $legacyclasses = [
\required_capability_exception::class => 'exception/required_capability_exception.php',
\webservice_parameter_exception::class => 'exception/webservice_parameter_exception.php',
+ // Cache API.
+ \cache::class => 'cache.php',
+ \cache_application::class => 'application_cache.php',
+ \cache_request::class => 'request_cache.php',
+ \cache_session::class => 'session_cache.php',
+ \cache_cached_object::class => 'cached_object.php',
+ \cache_config::class => 'config.php',
+ \cache_data_source::class => 'data_source_interface.php',
+ \cache_data_source_versionable::class => 'versionable_data_source_interface.php',
+ \cache_exception::class => 'exception/cache_exception.php',
+ \cache_factory::class => 'factory.php',
+ \cache_helper::class => 'helper.php',
+ \cache_is_key_aware::class => 'key_aware_cache_interface.php',
+ \cache_is_lockable::class => 'lockable_cache_interface.php',
+ \cache_is_searchable::class => 'searchable_cache_interface.php',
+ \cache_is_configurable::class => 'configurable_cache_interface.php',
+ \cache_loader::class => 'loader_interface.php',
+ \cache_loader_with_locking::class => 'loader_with_locking_interface.php',
+ \cache_lock_interface::class => 'cache_lock_interface.php',
+ \cache_store::class => 'store.php',
+ \cache_ttl_wrapper::class => 'ttl_wrapper.php',
+ \cache_store_interface::class => 'store_interface.php',
+ \cacheable_object::class => 'cacheable_object_interface.php',
+ \cacheable_object_array::class => 'cacheable_object_array.php',
+
// Output API.
\theme_config::class => 'output/theme_config.php',
\xhtml_container_stack::class => 'output/xhtml_container_stack.php',