See Release Notes
Long Term Support Release
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * The library file for the static cache store. 19 * 20 * This file is part of the static cache store, it contains the API for interacting with an instance of the store. 21 * This is used as a default cache store within the Cache API. It should never be deleted. 22 * 23 * @package cachestore_static 24 * @category cache 25 * @copyright 2012 Sam Hemelryk 26 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 27 */ 28 29 defined('MOODLE_INTERNAL') || die(); 30 31 /** 32 * The static data store class 33 * 34 * @copyright 2012 Sam Hemelryk 35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 */ 37 abstract class static_data_store extends cache_store { 38 39 /** 40 * An array for storage. 41 * @var array 42 */ 43 private static $staticstore = array(); 44 45 /** 46 * Returns a static store by reference... REFERENCE SUPER IMPORTANT. 47 * 48 * @param string $id 49 * @return array 50 */ 51 protected static function ®ister_store_id($id) { 52 if (!array_key_exists($id, self::$staticstore)) { 53 self::$staticstore[$id] = array(); 54 } 55 return self::$staticstore[$id]; 56 } 57 58 /** 59 * Flushes the store of all values for belonging to the store with the given id. 60 * @param string $id 61 */ 62 protected static function flush_store_by_id($id) { 63 unset(self::$staticstore[$id]); 64 self::$staticstore[$id] = array(); 65 } 66 67 /** 68 * Flushes all of the values from all stores. 69 * 70 * @copyright 2012 Sam Hemelryk 71 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 72 */ 73 protected static function flush_store() { 74 $ids = array_keys(self::$staticstore); 75 unset(self::$staticstore); 76 self::$staticstore = array(); 77 foreach ($ids as $id) { 78 self::$staticstore[$id] = array(); 79 } 80 } 81 } 82 83 /** 84 * The static store class. 85 * 86 * @copyright 2012 Sam Hemelryk 87 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 88 */ 89 class cachestore_static extends static_data_store implements cache_is_key_aware, cache_is_searchable { 90 91 /** 92 * The name of the store 93 * @var store 94 */ 95 protected $name; 96 97 /** 98 * The store id (should be unique) 99 * @var string 100 */ 101 protected $storeid; 102 103 /** 104 * The store we use for data. 105 * @var array 106 */ 107 protected $store; 108 109 /** 110 * The maximum size for the store, or false if there isn't one. 111 * @var bool 112 */ 113 protected $maxsize = false; 114 115 /** 116 * Where this cache uses simpledata and we don't need to serialize it. 117 * @var bool 118 */ 119 protected $simpledata = false; 120 121 /** 122 * The number of items currently being stored. 123 * @var int 124 */ 125 protected $storecount = 0; 126 127 /** 128 * igbinary extension available. 129 * @var bool 130 */ 131 protected $igbinaryfound = false; 132 133 /** 134 * Constructs the store instance. 135 * 136 * Noting that this function is not an initialisation. It is used to prepare the store for use. 137 * The store will be initialised when required and will be provided with a cache_definition at that time. 138 * 139 * @param string $name 140 * @param array $configuration 141 */ 142 public function __construct($name, array $configuration = array()) { 143 $this->name = $name; 144 } 145 146 /** 147 * Returns the supported features as a combined int. 148 * 149 * @param array $configuration 150 * @return int 151 */ 152 public static function get_supported_features(array $configuration = array()) { 153 return self::SUPPORTS_DATA_GUARANTEE + 154 self::SUPPORTS_NATIVE_TTL + 155 self::IS_SEARCHABLE + 156 self::SUPPORTS_MULTIPLE_IDENTIFIERS + 157 self::DEREFERENCES_OBJECTS; 158 } 159 160 /** 161 * Returns true as this store does support multiple identifiers. 162 * (This optional function is a performance optimisation; it must be 163 * consistent with the value from get_supported_features.) 164 * 165 * @return bool true 166 */ 167 public function supports_multiple_identifiers() { 168 return true; 169 } 170 171 /** 172 * Returns the supported modes as a combined int. 173 * 174 * @param array $configuration 175 * @return int 176 */ 177 public static function get_supported_modes(array $configuration = array()) { 178 return self::MODE_REQUEST; 179 } 180 181 /** 182 * Returns true if the store requirements are met. 183 * 184 * @return bool 185 */ 186 public static function are_requirements_met() { 187 return true; 188 } 189 190 /** 191 * Returns true if the given mode is supported by this store. 192 * 193 * @param int $mode One of cache_store::MODE_* 194 * @return bool 195 */ 196 public static function is_supported_mode($mode) { 197 return ($mode === self::MODE_REQUEST); 198 } 199 200 /** 201 * Initialises the cache. 202 * 203 * Once this has been done the cache is all set to be used. 204 * 205 * @param cache_definition $definition 206 */ 207 public function initialise(cache_definition $definition) { 208 $keyarray = $definition->generate_multi_key_parts(); 209 $this->storeid = $keyarray['mode'].'/'.$keyarray['component'].'/'.$keyarray['area'].'/'.$keyarray['siteidentifier']; 210 $this->store = &self::register_store_id($this->storeid); 211 $maxsize = $definition->get_maxsize(); 212 $this->simpledata = $definition->uses_simple_data(); 213 $this->igbinaryfound = extension_loaded('igbinary'); 214 if ($maxsize !== null) { 215 // Must be a positive int. 216 $this->maxsize = abs((int)$maxsize); 217 $this->storecount = count($this->store); 218 } 219 } 220 221 /** 222 * Returns true once this instance has been initialised. 223 * 224 * @return bool 225 */ 226 public function is_initialised() { 227 return (is_array($this->store)); 228 } 229 230 /** 231 * Uses igbinary serializer if igbinary extension is loaded. 232 * Fallback to PHP serializer. 233 * 234 * @param mixed $data 235 * The value to be serialized. 236 * @return string a string containing a byte-stream representation of 237 * value that can be stored anywhere. 238 */ 239 protected function serialize($data) { 240 if ($this->igbinaryfound) { 241 return igbinary_serialize($data); 242 } else { 243 return serialize($data); 244 } 245 } 246 247 /** 248 * Uses igbinary unserializer if igbinary extension is loaded. 249 * Fallback to PHP unserializer. 250 * 251 * @param string $str 252 * The serialized string. 253 * @return mixed The converted value is returned, and can be a boolean, 254 * integer, float, string, 255 * array or object. 256 */ 257 protected function unserialize($str) { 258 if ($this->igbinaryfound) { 259 return igbinary_unserialize($str); 260 } else { 261 return unserialize($str); 262 } 263 } 264 265 /** 266 * Retrieves an item from the cache store given its key. 267 * 268 * @param string $key The key to retrieve 269 * @return mixed The data that was associated with the key, or false if the key did not exist. 270 */ 271 public function get($key) { 272 if (!is_array($key)) { 273 $key = array('key' => $key); 274 } 275 276 $key = $key['key']; 277 if (isset($this->store[$key])) { 278 if ($this->store[$key]['serialized']) { 279 return $this->unserialize($this->store[$key]['data']); 280 } else { 281 return $this->store[$key]['data']; 282 } 283 } 284 return false; 285 } 286 287 /** 288 * Retrieves several items from the cache store in a single transaction. 289 * 290 * If not all of the items are available in the cache then the data value for those that are missing will be set to false. 291 * 292 * @param array $keys The array of keys to retrieve 293 * @return array An array of items from the cache. There will be an item for each key, those that were not in the store will 294 * be set to false. 295 */ 296 public function get_many($keys) { 297 $return = array(); 298 299 foreach ($keys as $key) { 300 if (!is_array($key)) { 301 $key = array('key' => $key); 302 } 303 $key = $key['key']; 304 $return[$key] = false; 305 if (isset($this->store[$key])) { 306 if ($this->store[$key]['serialized']) { 307 $return[$key] = $this->unserialize($this->store[$key]['data']); 308 } else { 309 $return[$key] = $this->store[$key]['data']; 310 } 311 } 312 } 313 return $return; 314 } 315 316 /** 317 * Sets an item in the cache given its key and data value. 318 * 319 * @param string $key The key to use. 320 * @param mixed $data The data to set. 321 * @param bool $testmaxsize If set to true then we test the maxsize arg and reduce if required. 322 * @return bool True if the operation was a success false otherwise. 323 */ 324 public function set($key, $data, $testmaxsize = true) { 325 if (!is_array($key)) { 326 $key = array('key' => $key); 327 } 328 $key = $key['key']; 329 $testmaxsize = ($testmaxsize && $this->maxsize !== false); 330 if ($testmaxsize) { 331 $increment = (!isset($this->store[$key])); 332 } 333 334 if ($this->simpledata || is_scalar($data)) { 335 $this->store[$key]['data'] = $data; 336 $this->store[$key]['serialized'] = false; 337 } else { 338 $this->store[$key]['data'] = $this->serialize($data); 339 $this->store[$key]['serialized'] = true; 340 } 341 342 if ($testmaxsize && $increment) { 343 $this->storecount++; 344 if ($this->storecount > $this->maxsize) { 345 $this->reduce_for_maxsize(); 346 } 347 } 348 return true; 349 } 350 351 /** 352 * Sets many items in the cache in a single transaction. 353 * 354 * @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two 355 * keys, 'key' and 'value'. 356 * @return int The number of items successfully set. It is up to the developer to check this matches the number of items 357 * sent ... if they care that is. 358 */ 359 public function set_many(array $keyvaluearray) { 360 $count = 0; 361 foreach ($keyvaluearray as $pair) { 362 if (!is_array($pair['key'])) { 363 $pair['key'] = array('key' => $pair['key']); 364 } 365 // Don't test the maxsize here. We'll do it once when we are done. 366 $this->set($pair['key']['key'], $pair['value'], false); 367 $count++; 368 } 369 if ($this->maxsize !== false) { 370 $this->storecount += $count; 371 if ($this->storecount > $this->maxsize) { 372 $this->reduce_for_maxsize(); 373 } 374 } 375 return $count; 376 } 377 378 /** 379 * Checks if the store has a record for the given key and returns true if so. 380 * 381 * @param string $key 382 * @return bool 383 */ 384 public function has($key) { 385 if (is_array($key)) { 386 $key = $key['key']; 387 } 388 return isset($this->store[$key]); 389 } 390 391 /** 392 * Returns true if the store contains records for all of the given keys. 393 * 394 * @param array $keys 395 * @return bool 396 */ 397 public function has_all(array $keys) { 398 foreach ($keys as $key) { 399 if (!is_array($key)) { 400 $key = array('key' => $key); 401 } 402 $key = $key['key']; 403 if (!isset($this->store[$key])) { 404 return false; 405 } 406 } 407 return true; 408 } 409 410 /** 411 * Returns true if the store contains records for any of the given keys. 412 * 413 * @param array $keys 414 * @return bool 415 */ 416 public function has_any(array $keys) { 417 foreach ($keys as $key) { 418 if (!is_array($key)) { 419 $key = array('key' => $key); 420 } 421 $key = $key['key']; 422 423 if (isset($this->store[$key])) { 424 return true; 425 } 426 } 427 return false; 428 } 429 430 /** 431 * Deletes an item from the cache store. 432 * 433 * @param string $key The key to delete. 434 * @return bool Returns true if the operation was a success, false otherwise. 435 */ 436 public function delete($key) { 437 if (!is_array($key)) { 438 $key = array('key' => $key); 439 } 440 $key = $key['key']; 441 $result = isset($this->store[$key]); 442 unset($this->store[$key]); 443 if ($this->maxsize !== false) { 444 $this->storecount--; 445 } 446 return $result; 447 } 448 449 /** 450 * Deletes several keys from the cache in a single action. 451 * 452 * @param array $keys The keys to delete 453 * @return int The number of items successfully deleted. 454 */ 455 public function delete_many(array $keys) { 456 $count = 0; 457 foreach ($keys as $key) { 458 if (!is_array($key)) { 459 $key = array('key' => $key); 460 } 461 $key = $key['key']; 462 if (isset($this->store[$key])) { 463 $count++; 464 } 465 unset($this->store[$key]); 466 } 467 if ($this->maxsize !== false) { 468 $this->storecount -= $count; 469 } 470 return $count; 471 } 472 473 /** 474 * Purges the cache deleting all items within it. 475 * 476 * @return boolean True on success. False otherwise. 477 */ 478 public function purge() { 479 $this->flush_store_by_id($this->storeid); 480 $this->store = &self::register_store_id($this->storeid); 481 // Don't worry about checking if we're using max size just set it as thats as fast as the check. 482 $this->storecount = 0; 483 return true; 484 } 485 486 /** 487 * Reduces the size of the array if maxsize has been hit. 488 * 489 * This function reduces the size of the store reducing it by 10% of its maxsize. 490 * It removes the oldest items in the store when doing this. 491 * The reason it does this an doesn't use a least recently used system is purely the overhead such a system 492 * requires. The current approach is focused on speed, MUC already adds enough overhead to static/session caches 493 * and avoiding more is of benefit. 494 * 495 * @return int 496 */ 497 protected function reduce_for_maxsize() { 498 $diff = $this->storecount - $this->maxsize; 499 if ($diff < 1) { 500 return 0; 501 } 502 // Reduce it by an extra 10% to avoid calling this repetitively if we are in a loop. 503 $diff += floor($this->maxsize / 10); 504 $this->store = array_slice($this->store, $diff, null, true); 505 $this->storecount -= $diff; 506 return $diff; 507 } 508 509 /** 510 * Returns true if the user can add an instance of the store plugin. 511 * 512 * @return bool 513 */ 514 public static function can_add_instance() { 515 return false; 516 } 517 518 /** 519 * Performs any necessary clean up when the store instance is being deleted. 520 */ 521 public function instance_deleted() { 522 $this->purge(); 523 } 524 525 /** 526 * Generates an instance of the cache store that can be used for testing. 527 * 528 * @param cache_definition $definition 529 * @return cachestore_static 530 */ 531 public static function initialise_test_instance(cache_definition $definition) { 532 // Do something here perhaps. 533 $cache = new cachestore_static('Static store'); 534 $cache->initialise($definition); 535 return $cache; 536 } 537 538 /** 539 * Generates the appropriate configuration required for unit testing. 540 * 541 * @return array Array of unit test configuration data to be used by initialise(). 542 */ 543 public static function unit_test_configuration() { 544 return array(); 545 } 546 547 /** 548 * Returns the name of this instance. 549 * @return string 550 */ 551 public function my_name() { 552 return $this->name; 553 } 554 555 /** 556 * Finds all of the keys being stored in the cache store instance. 557 * 558 * @return array 559 */ 560 public function find_all() { 561 return array_keys($this->store); 562 } 563 564 /** 565 * Finds all of the keys whose keys start with the given prefix. 566 * 567 * @param string $prefix 568 */ 569 public function find_by_prefix($prefix) { 570 $return = array(); 571 foreach ($this->find_all() as $key) { 572 if (strpos($key, $prefix) === 0) { 573 $return[] = $key; 574 } 575 } 576 return $return; 577 } 578 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body