diff --git a/lang/en/cache.php b/lang/en/cache.php index 6c7dd1abbdb..754b999e8f1 100644 --- a/lang/en/cache.php +++ b/lang/en/cache.php @@ -78,6 +78,7 @@ $string['cachedef_moodlenet_usercanshare'] = 'Users can share resources to Moodl $string['cachedef_locking'] = 'Locking'; $string['cachedef_message_processors_enabled'] = "Message processors enabled status"; $string['cachedef_contextwithinsights'] = 'Context with insights'; +$string['cachedef_navigation_cache'] = 'Navigation cache'; $string['cachedef_navigation_expandcourse'] = 'Navigation expandable courses'; $string['cachedef_observers'] = 'Event observers'; $string['cachedef_plugin_functions'] = 'Plugins available callbacks'; diff --git a/lib/db/caches.php b/lib/db/caches.php index 09b36ffc4f3..2ca7f1a5a36 100644 --- a/lib/db/caches.php +++ b/lib/db/caches.php @@ -618,4 +618,11 @@ $definitions = array( 'simpledata' => true, 'staticacceleration' => true, ], + // The navigation_cache class used this cache to store the navigation nodes. + 'navigation_cache' => [ + 'mode' => cache_store::MODE_SESSION, + 'simplekeys' => true, + 'simpledata' => true, + 'ttl' => 1800, + ], ); diff --git a/lib/navigationlib.php b/lib/navigationlib.php index bb86b2d9810..3b2ec783676 100644 --- a/lib/navigationlib.php +++ b/lib/navigationlib.php @@ -5997,10 +5997,9 @@ class navigation_json { } /** - * The cache class used by global navigation and settings navigation. + * The navigation_cache class is used for global and settings navigation data. * - * It is basically an easy access point to session with a bit of smarts to make - * sure that the information that is cached is valid still. + * It provides an easy access to the session cache with TTL of 1800 seconds. * * Example use: * @@ -6019,13 +6018,14 @@ class navigation_json { class navigation_cache { /** @var int represents the time created */ protected $creation; - /** @var array An array of session keys */ - protected $session; + /** @var cache_session The session cache instance */ + protected $cache; + /** @var array The current cache area data */ + protected $session = []; + /** - * The string to use to segregate this particular cache. It can either be - * unique to start a fresh cache or if you want to share a cache then make - * it the string used in the original cache. - * @var string + * @var string A unique string to segregate this particular cache. + * It can either be unique to start a fresh cache or shared to use an existing cache. */ protected $area; /** @var int a time that the information will time out */ @@ -6038,152 +6038,121 @@ class navigation_cache { const CACHEUSERID = 1; /** @var int cache value */ const CACHEVALUE = 2; - /** @var null|array An array of navigation cache areas to expire on shutdown */ - public static $volatilecaches; + /** @var null|array An array of cache areas to expire on shutdown */ + public static $volatilecaches = null; /** - * Contructor for the cache. Requires two arguments + * Contructor for the cache. Requires a area string be passed in. * - * @param string $area The string to use to segregate this particular cache - * it can either be unique to start a fresh cache or if you want - * to share a cache then make it the string used in the original - * cache + * @param string $area The unique string to segregate this particular cache. * @param int $timeout The number of seconds to time the information out after */ public function __construct($area, $timeout=1800) { + global $USER; $this->creation = time(); - $this->area = $area; + $this->area = "user_{$USER->id}_{$area}"; $this->timeout = time() - $timeout; - if (rand(0,100) === 0) { - $this->garbage_collection(); - } + $this->cache = cache::make('core', 'navigation_cache'); } /** - * Used to set up the cache within the SESSION. + * Ensure the navigation cache is initialised * - * This is called for each access and ensure that we don't put anything into the session before - * it is required. + * This is called for each access and ensures that no data is put into the cache before it is required. */ - protected function ensure_session_cache_initialised() { - global $SESSION; + protected function ensure_navigation_cache_initialised() { if (empty($this->session)) { - if (!isset($SESSION->navcache)) { - $SESSION->navcache = new stdClass; + $this->session = $this->cache->get($this->area); + if (!is_array($this->session)) { + $this->session = []; } - if (!isset($SESSION->navcache->{$this->area})) { - $SESSION->navcache->{$this->area} = array(); - } - $this->session = &$SESSION->navcache->{$this->area}; // pointer to array, =& is correct here } } /** - * Magic Method to retrieve something by simply calling using = cache->key + * Magic Method to retrieve a cached item by simply calling using = cache->key * - * @param mixed $key The identifier for the information you want out again - * @return void|mixed Either void or what ever was put in + * @param mixed $key The identifier for the cached information + * @return mixed|void The cached information or void if not found */ public function __get($key) { if (!$this->cached($key)) { return; } - $information = $this->session[$key][self::CACHEVALUE]; - return unserialize($information); + return unserialize($this->session[$key][self::CACHEVALUE]); } /** - * Magic method that simply uses {@link set();} to store something in the cache + * Magic method that simply uses {@see navigation_cache::set()} to store an item in the cache * - * @param string|int $key - * @param mixed $information + * @param string|int $key The key to store the information against + * @param mixed $information The information to cache */ public function __set($key, $information) { $this->set($key, $information); } /** - * Sets some information against the cache (session) for later retrieval + * Sets some information in the session cache for later retrieval * * @param string|int $key * @param mixed $information */ public function set($key, $information) { global $USER; - $this->ensure_session_cache_initialised(); + $this->ensure_navigation_cache_initialised(); $information = serialize($information); - $this->session[$key]= array(self::CACHETIME=>time(), self::CACHEUSERID=>$USER->id, self::CACHEVALUE=>$information); + $this->session[$key] = [self::CACHETIME => time(), self::CACHEUSERID => $USER->id, self::CACHEVALUE => $information]; + $this->cache->set($this->area, $this->session); } /** * Check the existence of the identifier in the cache * - * @param string|int $key - * @return bool + * @param string|int $key The identifier to check + * @return bool True if the item exists in the cache, false otherwise */ public function cached($key) { - global $USER; - $this->ensure_session_cache_initialised(); - if (!array_key_exists($key, $this->session) || !is_array($this->session[$key]) || $this->session[$key][self::CACHEUSERID]!=$USER->id || $this->session[$key][self::CACHETIME] < $this->timeout) { - return false; - } - return true; + $this->ensure_navigation_cache_initialised(); + return isset($this->session[$key]) && + is_array($this->session[$key]); } /** * Compare something to it's equivilant in the cache * - * @param string $key - * @param mixed $value + * @param string $key The key to check + * @param mixed $value The value to compare * @param bool $serialise Whether to serialise the value before comparison * this should only be set to false if the value is already * serialised - * @return bool If the value is the same false if it is not set or doesn't match + * @return bool True if the value is the same as the cached one, false otherwise */ public function compare($key, $value, $serialise = true) { if ($this->cached($key)) { if ($serialise) { $value = serialize($value); } - if ($this->session[$key][self::CACHEVALUE] === $value) { - return true; - } + return $this->session[$key][self::CACHEVALUE] === $value; } return false; } /** - * Wipes the entire cache, good to force regeneration + * Deletes the entire cache area, forcing a fresh cache to be created */ public function clear() { - global $SESSION; - unset($SESSION->navcache); - $this->session = null; + $this->cache->delete($this->area); + $this->session = []; } /** - * Checks all cache entries and removes any that have expired, good ole cleanup - */ - protected function garbage_collection() { - if (empty($this->session)) { - return true; - } - foreach ($this->session as $key=>$cachedinfo) { - if (is_array($cachedinfo) && $cachedinfo[self::CACHETIME]<$this->timeout) { - unset($this->session[$key]); - } - } - } - - /** - * Marks the cache as being volatile (likely to change) + * Marks the cache as volatile (likely to change) * - * Any caches marked as volatile will be destroyed at the on shutdown by - * {@link navigation_node::destroy_volatile_caches()} which is registered - * as a shutdown function if any caches are marked as volatile. + * Any caches marked as volatile will be destroyed on shutdown by {@see navigation_node::destroy_volatile_caches()} * - * @param bool $setting True to destroy the cache false not too + * @param bool $setting True to mark the cache as volatile, false to remove the volatile flag */ public function volatile($setting = true) { - if (self::$volatilecaches===null) { - self::$volatilecaches = array(); - core_shutdown_manager::register_function(array('navigation_cache','destroy_volatile_caches')); + if (self::$volatilecaches === null) { + self::$volatilecaches = []; + core_shutdown_manager::register_function(['navigation_cache', 'destroy_volatile_caches']); } if ($setting) { @@ -6196,19 +6165,16 @@ class navigation_cache { /** * Destroys all caches marked as volatile * - * This function is static and works in conjunction with the static volatilecaches - * property of navigation cache. - * Because this function is static it manually resets the cached areas back to an - * empty array. + * This function is static and works with the static volatilecaches property of navigation cache. + * It manually resets the cached areas back to an empty array. */ public static function destroy_volatile_caches() { - global $SESSION; - if (is_array(self::$volatilecaches) && count(self::$volatilecaches)>0) { + if (is_array(self::$volatilecaches) && count(self::$volatilecaches) > 0) { + $cache = cache::make('core', 'navigation_cache'); foreach (self::$volatilecaches as $area) { - $SESSION->navcache->{$area} = array(); + $cache->delete($area); } - } else { - $SESSION->navcache = new stdClass; + self::$volatilecaches = null; } } }