/** * 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); $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(); } $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->storetype, $this->get_definition()->get_id(), $hits); cache_helper::record_cache_miss($this->storetype, $this->get_definition()->get_id(), $misses); } return $return; }
/** * 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 $strictness One of IGNORE_MISSING | MUST_EXIST * @return mixed|false The data from the cache or false if the key did not exist within the cache. * @throws moodle_exception */ public function get($key, $strictness = IGNORE_MISSING) { // Check the tracked user. $this->check_tracked_user(); // 2. Parse the key. $parsedkey = $this->parse_key($key); // 3. Get it from the store. $result = false; $session = $this->get_session_data(); if (array_key_exists($parsedkey, $session)) { $result = $session[$parsedkey]; if ($result instanceof cache_ttl_wrapper) { if ($result->has_expired()) { $this->get_store()->delete($parsedkey); $result = false; } else { $result = $result->data; } } if ($result instanceof cache_cached_object) { $result = $result->restore_object(); } } // 4. Load if from the loader/datasource if we don't already have it. $setaftervalidation = false; if ($result === false) { if ($this->perfdebug) { cache_helper::record_cache_miss('**static session**', $this->get_definition()->get_id()); } if ($this->get_loader() !== false) { // We must pass the original (unparsed) key to the next loader in the chain. // The next loader will parse the key as it sees fit. It may be parsed differently // depending upon the capabilities of the store associated with the loader. $result = $this->get_loader()->get($key); } else { if ($this->get_datasource() !== false) { $result = $this->get_datasource()->load_for_cache($key); } } $setaftervalidation = $result !== false; } else { if ($this->perfdebug) { cache_helper::record_cache_hit('**static session**', $this->get_definition()->get_id()); } } // 5. Validate strictness. if ($strictness === MUST_EXIST && $result === false) { throw new moodle_exception('Requested key did not exist in any cache stores and could not be loaded.'); } // 6. Set it to the store if we got it from the loader/datasource. if ($setaftervalidation) { $this->set($key, $result); } // 7. Make sure we don't pass back anything that could be a reference. // We don't want people modifying the data in the cache. if (!is_scalar($result)) { // 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). $result = $this->unref($result); } return $result; }
/** * 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); $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->storetype, $this->get_definition(), $hits); cache_helper::record_cache_miss($this->storetype, $this->get_definition(), $misses); } return $return; }
/** * Returns the item from the persist cache if it exists there. * * @param string $key The parsed key * @return mixed|false The data from the persist cache or false if it wasn't there. */ protected function get_from_persist_cache($key) { if (is_array($key)) { $key = $key['key']; } if (!$this->persist || !array_key_exists($key, $this->persistcache)) { $result = false; } else { $data = $this->persistcache[$key]; if (!$this->has_a_ttl() || !$data instanceof cache_ttl_wrapper) { if ($data instanceof cache_cached_object) { $data = $data->restore_object(); } $result = $data; } else { if ($data->has_expired()) { $this->delete_from_persist_cache($key); $result = false; } else { if ($data instanceof cache_cached_object) { $data = $data->restore_object(); } $result = $data->data; } } } if ($result) { if ($this->perfdebug) { cache_helper::record_cache_hit('** static persist **', $this->definition->get_id()); } return $result; } else { if ($this->perfdebug) { cache_helper::record_cache_miss('** static persist **', $this->definition->get_id()); } return false; } }