[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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 memcache cache store. 19 * 20 * This file is part of the memcache cache store, it contains the API for interacting with an instance of the store. 21 * 22 * @package cachestore_memcache 23 * @copyright 2012 Sam Hemelryk 24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 */ 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 /** 30 * The memcache store class. 31 * 32 * (Not to be confused with memcached store) 33 * 34 * Configuration options: 35 * servers: string: host:port:weight , ... 36 * 37 * @copyright 2012 Sam Hemelryk 38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 39 */ 40 class cachestore_memcache extends cache_store implements cache_is_configurable { 41 42 /** 43 * The name of the store 44 * @var store 45 */ 46 protected $name; 47 48 /** 49 * The memcache connection once established. 50 * @var Memcache 51 */ 52 protected $connection; 53 54 /** 55 * Key prefix for this memcache. 56 * @var string 57 */ 58 protected $prefix; 59 60 /** 61 * An array of servers to use in the connection args. 62 * @var array 63 */ 64 protected $servers = array(); 65 66 /** 67 * An array of options used when establishing the connection. 68 * @var array 69 */ 70 protected $options = array(); 71 72 /** 73 * Set to true when things are ready to be initialised. 74 * @var bool 75 */ 76 protected $isready = false; 77 78 /** 79 * Set to true once this store instance has been initialised. 80 * @var bool 81 */ 82 protected $isinitialised = false; 83 84 /** 85 * The cache definition this store was initialised for. 86 * @var cache_definition 87 */ 88 protected $definition; 89 90 /** 91 * Set to true when this store is clustered. 92 * @var bool 93 */ 94 protected $clustered = false; 95 96 /** 97 * Array of servers to set when in clustered mode. 98 * @var array 99 */ 100 protected $setservers = array(); 101 102 /** 103 * The an array of memcache connections for the set servers, once established. 104 * @var array 105 */ 106 protected $setconnections = array(); 107 108 /** 109 * If true data going in and out will be encoded. 110 * @var bool 111 */ 112 protected $encode = true; 113 114 /** 115 * Default prefix for key names. 116 * @var string 117 */ 118 const DEFAULT_PREFIX = 'mdl_'; 119 120 /** 121 * Constructs the store instance. 122 * 123 * Noting that this function is not an initialisation. It is used to prepare the store for use. 124 * The store will be initialised when required and will be provided with a cache_definition at that time. 125 * 126 * @param string $name 127 * @param array $configuration 128 */ 129 public function __construct($name, array $configuration = array()) { 130 $this->name = $name; 131 if (!array_key_exists('servers', $configuration) || empty($configuration['servers'])) { 132 // Nothing configured. 133 return; 134 } 135 if (!is_array($configuration['servers'])) { 136 $configuration['servers'] = array($configuration['servers']); 137 } 138 foreach ($configuration['servers'] as $server) { 139 if (!is_array($server)) { 140 $server = explode(':', $server, 3); 141 } 142 if (!array_key_exists(1, $server)) { 143 $server[1] = 11211; 144 $server[2] = 100; 145 } else if (!array_key_exists(2, $server)) { 146 $server[2] = 100; 147 } 148 $this->servers[] = $server; 149 } 150 151 $this->clustered = array_key_exists('clustered', $configuration) ? (bool)$configuration['clustered'] : false; 152 153 if ($this->clustered) { 154 if (!array_key_exists('setservers', $configuration) || (count($configuration['setservers']) < 1)) { 155 // Can't setup clustering without set servers. 156 return; 157 } 158 if (count($this->servers) !== 1) { 159 // Can only setup cluster with exactly 1 get server. 160 return; 161 } 162 foreach ($configuration['setservers'] as $server) { 163 // We do not use weights (3rd part) on these servers. 164 if (!is_array($server)) { 165 $server = explode(':', $server, 3); 166 } 167 if (!array_key_exists(1, $server)) { 168 $server[1] = 11211; 169 } 170 $this->setservers[] = $server; 171 } 172 } 173 174 if (empty($configuration['prefix'])) { 175 $this->prefix = self::DEFAULT_PREFIX; 176 } else { 177 $this->prefix = $configuration['prefix']; 178 } 179 180 $this->connection = new Memcache; 181 foreach ($this->servers as $server) { 182 $this->connection->addServer($server[0], (int) $server[1], true, (int) $server[2]); 183 } 184 185 if ($this->clustered) { 186 foreach ($this->setservers as $setserver) { 187 // Since we will have a number of them with the same name, append server and port. 188 $connection = new Memcache; 189 $connection->addServer($setserver[0], $setserver[1]); 190 $this->setconnections[] = $connection; 191 } 192 } 193 194 // Test the connection to the pool of servers. 195 $this->isready = @$this->connection->set($this->parse_key('ping'), 'ping', MEMCACHE_COMPRESSED, 1); 196 } 197 198 /** 199 * Initialises the cache. 200 * 201 * Once this has been done the cache is all set to be used. 202 * 203 * @param cache_definition $definition 204 */ 205 public function initialise(cache_definition $definition) { 206 if ($this->is_initialised()) { 207 throw new coding_exception('This memcache instance has already been initialised.'); 208 } 209 $this->definition = $definition; 210 $this->isinitialised = true; 211 $this->encode = self::require_encoding(); 212 } 213 214 /** 215 * Tests if encoding is going to be required. 216 * 217 * Prior to memcache 3.0.3 scalar data types were not preserved. 218 * For earlier versions of the memcache extension we need to encode and decode scalar types 219 * to ensure that it is preserved. 220 * 221 * @param string $version The version to check, if null it is fetched from PHP. 222 * @return bool 223 */ 224 public static function require_encoding($version = null) { 225 if (!$version) { 226 $version = phpversion('memcache'); 227 } 228 return (version_compare($version, '3.0.3', '<')); 229 } 230 231 /** 232 * Returns true once this instance has been initialised. 233 * 234 * @return bool 235 */ 236 public function is_initialised() { 237 return ($this->isinitialised); 238 } 239 240 /** 241 * Returns true if this store instance is ready to be used. 242 * @return bool 243 */ 244 public function is_ready() { 245 return $this->isready; 246 } 247 248 /** 249 * Returns true if the store requirements are met. 250 * 251 * @return bool 252 */ 253 public static function are_requirements_met() { 254 return class_exists('Memcache'); 255 } 256 257 /** 258 * Returns true if the given mode is supported by this store. 259 * 260 * @param int $mode One of cache_store::MODE_* 261 * @return bool 262 */ 263 public static function is_supported_mode($mode) { 264 return ($mode === self::MODE_APPLICATION || $mode === self::MODE_SESSION); 265 } 266 267 /** 268 * Returns the supported features as a combined int. 269 * 270 * @param array $configuration 271 * @return int 272 */ 273 public static function get_supported_features(array $configuration = array()) { 274 return self::SUPPORTS_NATIVE_TTL + self::DEREFERENCES_OBJECTS; 275 } 276 277 /** 278 * Returns false as this store does not support multiple identifiers. 279 * (This optional function is a performance optimisation; it must be 280 * consistent with the value from get_supported_features.) 281 * 282 * @return bool False 283 */ 284 public function supports_multiple_identifiers() { 285 return false; 286 } 287 288 /** 289 * Returns the supported modes as a combined int. 290 * 291 * @param array $configuration 292 * @return int 293 */ 294 public static function get_supported_modes(array $configuration = array()) { 295 return self::MODE_APPLICATION; 296 } 297 298 /** 299 * Parses the given key to make it work for this memcache backend. 300 * 301 * @param string $key The raw key. 302 * @return string The resulting key. 303 */ 304 protected function parse_key($key) { 305 if (strlen($key) > 245) { 306 $key = '_sha1_'.sha1($key); 307 } 308 $key = $this->prefix . $key; 309 return $key; 310 } 311 312 /** 313 * Retrieves an item from the cache store given its key. 314 * 315 * @param string $key The key to retrieve 316 * @return mixed The data that was associated with the key, or false if the key did not exist. 317 */ 318 public function get($key) { 319 $result = $this->connection->get($this->parse_key($key)); 320 if ($this->encode && $result !== false) { 321 return @unserialize($result); 322 } 323 return $result; 324 } 325 326 /** 327 * Retrieves several items from the cache store in a single transaction. 328 * 329 * 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. 330 * 331 * @param array $keys The array of keys to retrieve 332 * @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 333 * be set to false. 334 */ 335 public function get_many($keys) { 336 $mkeys = array(); 337 foreach ($keys as $key) { 338 $mkeys[$key] = $this->parse_key($key); 339 } 340 $result = $this->connection->get($mkeys); 341 if (!is_array($result)) { 342 $result = array(); 343 } 344 $return = array(); 345 foreach ($mkeys as $key => $mkey) { 346 if (!array_key_exists($mkey, $result)) { 347 $return[$key] = false; 348 } else { 349 $return[$key] = $result[$mkey]; 350 if ($this->encode && $return[$key] !== false) { 351 $return[$key] = @unserialize($return[$key]); 352 } 353 } 354 } 355 return $return; 356 } 357 358 /** 359 * Sets an item in the cache given its key and data value. 360 * 361 * @param string $key The key to use. 362 * @param mixed $data The data to set. 363 * @return bool True if the operation was a success false otherwise. 364 */ 365 public function set($key, $data) { 366 if ($this->encode) { 367 // We must serialise this data. 368 $data = serialize($data); 369 } 370 371 if ($this->clustered) { 372 $status = true; 373 foreach ($this->setconnections as $connection) { 374 $status = $connection->set($this->parse_key($key), $data, MEMCACHE_COMPRESSED, $this->definition->get_ttl()) 375 && $status; 376 } 377 return $status; 378 } 379 380 return $this->connection->set($this->parse_key($key), $data, MEMCACHE_COMPRESSED, $this->definition->get_ttl()); 381 } 382 383 /** 384 * Sets many items in the cache in a single transaction. 385 * 386 * @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two 387 * keys, 'key' and 'value'. 388 * @return int The number of items successfully set. It is up to the developer to check this matches the number of items 389 * sent ... if they care that is. 390 */ 391 public function set_many(array $keyvaluearray) { 392 $count = 0; 393 foreach ($keyvaluearray as $pair) { 394 if ($this->set($pair['key'], $pair['value'])) { 395 $count++; 396 } 397 } 398 return $count; 399 } 400 401 /** 402 * Deletes an item from the cache store. 403 * 404 * @param string $key The key to delete. 405 * @return bool Returns true if the operation was a success, false otherwise. 406 */ 407 public function delete($key) { 408 if ($this->clustered) { 409 $status = true; 410 foreach ($this->setconnections as $connection) { 411 $status = $connection->delete($this->parse_key($key)) && $status; 412 } 413 return $status; 414 } 415 416 return $this->connection->delete($this->parse_key($key)); 417 } 418 419 /** 420 * Deletes several keys from the cache in a single action. 421 * 422 * @param array $keys The keys to delete 423 * @return int The number of items successfully deleted. 424 */ 425 public function delete_many(array $keys) { 426 $count = 0; 427 foreach ($keys as $key) { 428 if ($this->delete($key)) { 429 $count++; 430 } 431 } 432 return $count; 433 } 434 435 /** 436 * Purges the cache deleting all items within it. 437 * 438 * @return boolean True on success. False otherwise. 439 */ 440 public function purge() { 441 if ($this->isready) { 442 if ($this->clustered) { 443 foreach ($this->setconnections as $connection) { 444 $connection->flush(); 445 } 446 } else { 447 $this->connection->flush(); 448 } 449 } 450 451 return true; 452 } 453 454 /** 455 * Given the data from the add instance form this function creates a configuration array. 456 * 457 * @param stdClass $data 458 * @return array 459 */ 460 public static function config_get_configuration_array($data) { 461 $lines = explode("\n", $data->servers); 462 $servers = array(); 463 foreach ($lines as $line) { 464 // Trim surrounding colons and default whitespace. 465 $line = trim(trim($line), ":"); 466 // Skip blank lines. 467 if ($line === '') { 468 continue; 469 } 470 $servers[] = explode(':', $line, 3); 471 } 472 473 $clustered = false; 474 $setservers = array(); 475 if (isset($data->clustered)) { 476 $clustered = true; 477 478 $lines = explode("\n", $data->setservers); 479 foreach ($lines as $line) { 480 // Trim surrounding colons and default whitespace. 481 $line = trim(trim($line), ":"); 482 if ($line === '') { 483 continue; 484 } 485 $setserver = explode(':', $line, 3); 486 // We don't use weights, so display a debug message. 487 if (count($setserver) > 2) { 488 debugging('Memcache Set Server '.$setserver[0].' has too many parameters.'); 489 } 490 $setservers[] = $setserver; 491 } 492 } 493 494 return array( 495 'servers' => $servers, 496 'prefix' => $data->prefix, 497 'clustered' => $clustered, 498 'setservers' => $setservers 499 ); 500 } 501 502 /** 503 * Allows the cache store to set its data against the edit form before it is shown to the user. 504 * 505 * @param moodleform $editform 506 * @param array $config 507 */ 508 public static function config_set_edit_form_data(moodleform $editform, array $config) { 509 $data = array(); 510 if (!empty($config['servers'])) { 511 $servers = array(); 512 foreach ($config['servers'] as $server) { 513 $servers[] = join(":", $server); 514 } 515 $data['servers'] = join("\n", $servers); 516 } 517 if (!empty($config['prefix'])) { 518 $data['prefix'] = $config['prefix']; 519 } else { 520 $data['prefix'] = self::DEFAULT_PREFIX; 521 } 522 if (isset($config['clustered'])) { 523 $data['clustered'] = (bool)$config['clustered']; 524 } 525 if (!empty($config['setservers'])) { 526 $servers = array(); 527 foreach ($config['setservers'] as $server) { 528 $servers[] = join(":", $server); 529 } 530 $data['setservers'] = join("\n", $servers); 531 } 532 533 $editform->set_data($data); 534 } 535 536 /** 537 * Performs any necessary clean up when the store instance is being deleted. 538 */ 539 public function instance_deleted() { 540 if ($this->connection) { 541 $connection = $this->connection; 542 } else { 543 $connection = new Memcache; 544 foreach ($this->servers as $server) { 545 $connection->addServer($server[0], $server[1], true, $server[2]); 546 } 547 } 548 @$connection->flush(); 549 unset($connection); 550 unset($this->connection); 551 } 552 553 /** 554 * Generates an instance of the cache store that can be used for testing. 555 * 556 * @param cache_definition $definition 557 * @return cachestore_memcache|false 558 */ 559 public static function initialise_test_instance(cache_definition $definition) { 560 if (!self::are_requirements_met()) { 561 return false; 562 } 563 564 $config = get_config('cachestore_memcache'); 565 if (empty($config->testservers)) { 566 return false; 567 } 568 569 $configuration = array(); 570 $configuration['servers'] = explode("\n", $config->testservers); 571 if (!empty($config->testclustered)) { 572 $configuration['clustered'] = $config->testclustered; 573 } 574 if (!empty($config->testsetservers)) { 575 $configuration['setservers'] = explode("\n", $config->testsetservers); 576 } 577 578 $store = new cachestore_memcache('Test memcache', $configuration); 579 $store->initialise($definition); 580 581 return $store; 582 } 583 584 /** 585 * Creates a test instance for unit tests if possible. 586 * @param cache_definition $definition 587 * @return bool|cachestore_memcache 588 */ 589 public static function initialise_unit_test_instance(cache_definition $definition) { 590 if (!self::are_requirements_met()) { 591 return false; 592 } 593 if (!defined('TEST_CACHESTORE_MEMCACHE_TESTSERVERS')) { 594 return false; 595 } 596 $configuration = array(); 597 $configuration['servers'] = explode("\n", TEST_CACHESTORE_MEMCACHE_TESTSERVERS); 598 599 $store = new cachestore_memcache('Test memcache', $configuration); 600 $store->initialise($definition); 601 602 return $store; 603 } 604 605 /** 606 * Returns the name of this instance. 607 * @return string 608 */ 609 public function my_name() { 610 return $this->name; 611 } 612 613 /** 614 * Used to notify of configuration conflicts. 615 * 616 * The warnings returned here will be displayed on the cache configuration screen. 617 * 618 * @return string[] Returns an array of warnings (strings) 619 */ 620 public function get_warnings() { 621 global $CFG; 622 $warnings = array(); 623 if (isset($CFG->session_memcached_save_path) && count($this->servers)) { 624 $bits = explode(':', $CFG->session_memcached_save_path, 3); 625 $host = array_shift($bits); 626 $port = (count($bits)) ? array_shift($bits) : '11211'; 627 foreach ($this->servers as $server) { 628 if ($server[0] === $host && $server[1] == $port) { 629 $warnings[] = get_string('sessionhandlerconflict', 'cachestore_memcache', $this->my_name()); 630 break; 631 } 632 } 633 } 634 return $warnings; 635 } 636 637 /** 638 * Returns true if this cache store instance is both suitable for testing, and ready for testing. 639 * 640 * Cache stores that support being used as the default store for unit and acceptance testing should 641 * override this function and return true if there requirements have been met. 642 * 643 * @return bool 644 */ 645 public static function ready_to_be_used_for_testing() { 646 return defined('TEST_CACHESTORE_MEMCACHE_TESTSERVERS'); 647 } 648 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |