[ Index ] |
PHP Cross Reference of moodle-2.8 |
[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 * Cache configuration reader 19 * 20 * This file is part of Moodle's cache API, affectionately called MUC. 21 * It contains the components that are requried in order to use caching. 22 * 23 * @package core 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 * Cache configuration reader. 33 * 34 * This class is used to interact with the cache's configuration. 35 * The configuration is stored in the Moodle data directory. 36 * 37 * @package core 38 * @category cache 39 * @copyright 2012 Sam Hemelryk 40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 41 */ 42 class cache_config { 43 44 /** 45 * The configured stores 46 * @var array 47 */ 48 protected $configstores = array(); 49 50 /** 51 * The configured mode mappings 52 * @var array 53 */ 54 protected $configmodemappings = array(); 55 56 /** 57 * The configured definitions as picked up from cache.php files 58 * @var array 59 */ 60 protected $configdefinitions = array(); 61 62 /** 63 * The definition mappings that have been configured. 64 * @var array 65 */ 66 protected $configdefinitionmappings = array(); 67 68 /** 69 * An array of configured cache lock instances. 70 * @var array 71 */ 72 protected $configlocks = array(); 73 74 /** 75 * The site identifier used when the cache config was last saved. 76 * @var string 77 */ 78 protected $siteidentifier = null; 79 80 /** 81 * Please use cache_config::instance to get an instance of the cache config that is ready to be used. 82 */ 83 public function __construct() { 84 // Nothing to do here but look pretty. 85 } 86 87 /** 88 * Gets an instance of the cache_configuration class. 89 * 90 * @return cache_config 91 */ 92 public static function instance() { 93 $factory = cache_factory::instance(); 94 return $factory->create_config_instance(); 95 } 96 97 /** 98 * Checks if the configuration file exists. 99 * 100 * @return bool True if it exists 101 */ 102 public static function config_file_exists() { 103 // Allow for late static binding by using static. 104 return file_exists(static::get_config_file_path()); 105 } 106 107 /** 108 * Returns the expected path to the configuration file. 109 * 110 * @return string The absolute path 111 */ 112 protected static function get_config_file_path() { 113 global $CFG; 114 if (!empty($CFG->altcacheconfigpath)) { 115 $path = $CFG->altcacheconfigpath; 116 if (is_dir($path) && is_writable($path)) { 117 // Its a writable directory, thats fine. 118 return $path.'/cacheconfig.php'; 119 } else if (is_writable(dirname($path)) && (!file_exists($path) || is_writable($path))) { 120 // Its a file, either it doesn't exist and the directory is writable or the file exists and is writable. 121 return $path; 122 } 123 } 124 // Return the default location within dataroot. 125 return $CFG->dataroot.'/muc/config.php'; 126 } 127 128 /** 129 * Loads the configuration file and parses its contents into the expected structure. 130 * 131 * @param array|false $configuration Can be used to force a configuration. Should only be used when truly required. 132 * @return boolean 133 */ 134 public function load($configuration = false) { 135 global $CFG; 136 137 if ($configuration === false) { 138 $configuration = $this->include_configuration(); 139 } 140 141 $this->configstores = array(); 142 $this->configdefinitions = array(); 143 $this->configlocks = array(); 144 $this->configmodemappings = array(); 145 $this->configdefinitionmappings = array(); 146 $this->configlockmappings = array(); 147 148 $siteidentifier = 'unknown'; 149 if (array_key_exists('siteidentifier', $configuration)) { 150 $siteidentifier = $configuration['siteidentifier']; 151 } 152 $this->siteidentifier = $siteidentifier; 153 154 // Filter the lock instances. 155 $defaultlock = null; 156 foreach ($configuration['locks'] as $conf) { 157 if (!is_array($conf)) { 158 // Something is very wrong here. 159 continue; 160 } 161 if (!array_key_exists('name', $conf)) { 162 // Not a valid definition configuration. 163 continue; 164 } 165 $name = $conf['name']; 166 if (array_key_exists($name, $this->configlocks)) { 167 debugging('Duplicate cache lock detected. This should never happen.', DEBUG_DEVELOPER); 168 continue; 169 } 170 $conf['default'] = (!empty($conf['default'])); 171 if ($defaultlock === null || $conf['default']) { 172 $defaultlock = $name; 173 } 174 $this->configlocks[$name] = $conf; 175 } 176 177 // Filter the stores. 178 $availableplugins = cache_helper::early_get_cache_plugins(); 179 foreach ($configuration['stores'] as $store) { 180 if (!is_array($store) || !array_key_exists('name', $store) || !array_key_exists('plugin', $store)) { 181 // Not a valid instance configuration. 182 debugging('Invalid cache store in config. Missing name or plugin.', DEBUG_DEVELOPER); 183 continue; 184 } 185 $plugin = $store['plugin']; 186 $class = 'cachestore_'.$plugin; 187 $exists = array_key_exists($plugin, $availableplugins); 188 if (!$exists) { 189 // Not a valid plugin, or has been uninstalled, just skip it an carry on. 190 debugging('Invalid cache store in config. Not an available plugin.', DEBUG_DEVELOPER); 191 continue; 192 } 193 $file = $CFG->dirroot.'/cache/stores/'.$plugin.'/lib.php'; 194 if (!class_exists($class) && file_exists($file)) { 195 require_once($file); 196 } 197 if (!class_exists($class)) { 198 continue; 199 } 200 if (!array_key_exists('cache_store', class_parents($class))) { 201 continue; 202 } 203 if (!array_key_exists('configuration', $store) || !is_array($store['configuration'])) { 204 $store['configuration'] = array(); 205 } 206 $store['class'] = $class; 207 $store['default'] = !empty($store['default']); 208 if (!array_key_exists('lock', $store) || !array_key_exists($store['lock'], $this->configlocks)) { 209 $store['lock'] = $defaultlock; 210 } 211 212 $this->configstores[$store['name']] = $store; 213 } 214 215 // Filter the definitions. 216 foreach ($configuration['definitions'] as $id => $conf) { 217 if (!is_array($conf)) { 218 // Something is very wrong here. 219 continue; 220 } 221 if (!array_key_exists('mode', $conf) || !array_key_exists('component', $conf) || !array_key_exists('area', $conf)) { 222 // Not a valid definition configuration. 223 continue; 224 } 225 if (array_key_exists($id, $this->configdefinitions)) { 226 debugging('Duplicate cache definition detected. This should never happen.', DEBUG_DEVELOPER); 227 continue; 228 } 229 $conf['mode'] = (int)$conf['mode']; 230 if ($conf['mode'] < cache_store::MODE_APPLICATION || $conf['mode'] > cache_store::MODE_REQUEST) { 231 // Invalid cache mode used for the definition. 232 continue; 233 } 234 if ($conf['mode'] === cache_store::MODE_SESSION || $conf['mode'] === cache_store::MODE_REQUEST) { 235 // We force this for session and request caches. 236 // They are only allowed to use the default as we don't want people changing them. 237 $conf['sharingoptions'] = cache_definition::SHARING_DEFAULT; 238 $conf['selectedsharingoption'] = cache_definition::SHARING_DEFAULT; 239 $conf['userinputsharingkey'] = ''; 240 } else { 241 // Default the sharing option as it was added for 2.5. 242 // This can be removed sometime after 2.5 is the minimum version someone can upgrade from. 243 if (!isset($conf['sharingoptions'])) { 244 $conf['sharingoptions'] = cache_definition::SHARING_DEFAULTOPTIONS; 245 } 246 // Default the selected sharing option as it was added for 2.5. 247 // This can be removed sometime after 2.5 is the minimum version someone can upgrade from. 248 if (!isset($conf['selectedsharingoption'])) { 249 $conf['selectedsharingoption'] = cache_definition::SHARING_DEFAULT; 250 } 251 // Default the user input sharing key as it was added for 2.5. 252 // This can be removed sometime after 2.5 is the minimum version someone can upgrade from. 253 if (!isset($conf['userinputsharingkey'])) { 254 $conf['userinputsharingkey'] = ''; 255 } 256 } 257 $this->configdefinitions[$id] = $conf; 258 } 259 260 // Filter the mode mappings. 261 foreach ($configuration['modemappings'] as $mapping) { 262 if (!is_array($mapping) || !array_key_exists('mode', $mapping) || !array_key_exists('store', $mapping)) { 263 // Not a valid mapping configuration. 264 debugging('A cache mode mapping entry is invalid.', DEBUG_DEVELOPER); 265 continue; 266 } 267 if (!array_key_exists($mapping['store'], $this->configstores)) { 268 // Mapped array instance doesn't exist. 269 debugging('A cache mode mapping exists for a mode or store that does not exist.', DEBUG_DEVELOPER); 270 continue; 271 } 272 $mapping['mode'] = (int)$mapping['mode']; 273 if ($mapping['mode'] < 0 || $mapping['mode'] > 4) { 274 // Invalid cache type used for the mapping. 275 continue; 276 } 277 if (!array_key_exists('sort', $mapping)) { 278 $mapping['sort'] = 0; 279 } 280 $this->configmodemappings[] = $mapping; 281 } 282 283 // Filter the definition mappings. 284 foreach ($configuration['definitionmappings'] as $mapping) { 285 if (!is_array($mapping) || !array_key_exists('definition', $mapping) || !array_key_exists('store', $mapping)) { 286 // Not a valid mapping configuration. 287 continue; 288 } 289 if (!array_key_exists($mapping['store'], $this->configstores)) { 290 // Mapped array instance doesn't exist. 291 continue; 292 } 293 if (!array_key_exists($mapping['definition'], $this->configdefinitions)) { 294 // Mapped array instance doesn't exist. 295 continue; 296 } 297 if (!array_key_exists('sort', $mapping)) { 298 $mapping['sort'] = 0; 299 } 300 $this->configdefinitionmappings[] = $mapping; 301 } 302 303 usort($this->configmodemappings, array($this, 'sort_mappings')); 304 usort($this->configdefinitionmappings, array($this, 'sort_mappings')); 305 306 return true; 307 } 308 309 /** 310 * Returns the site identifier used by the cache API. 311 * @return string 312 */ 313 public function get_site_identifier() { 314 return $this->siteidentifier; 315 } 316 317 /** 318 * Includes the configuration file and makes sure it contains the expected bits. 319 * 320 * You need to ensure that the config file exists before this is called. 321 * 322 * @return array 323 * @throws cache_exception 324 */ 325 protected function include_configuration() { 326 $configuration = array(); 327 // We need to allow for late static bindings to allow for class path mudling happending for unit tests. 328 $cachefile = static::get_config_file_path(); 329 330 if (!file_exists($cachefile)) { 331 throw new cache_exception('Default cache config could not be found. It should have already been created by now.'); 332 } 333 include($cachefile); 334 if (!is_array($configuration)) { 335 throw new cache_exception('Invalid cache configuration file'); 336 } 337 if (!array_key_exists('stores', $configuration) || !is_array($configuration['stores'])) { 338 $configuration['stores'] = array(); 339 } 340 if (!array_key_exists('modemappings', $configuration) || !is_array($configuration['modemappings'])) { 341 $configuration['modemappings'] = array(); 342 } 343 if (!array_key_exists('definitions', $configuration) || !is_array($configuration['definitions'])) { 344 $configuration['definitions'] = array(); 345 } 346 if (!array_key_exists('definitionmappings', $configuration) || !is_array($configuration['definitionmappings'])) { 347 $configuration['definitionmappings'] = array(); 348 } 349 if (!array_key_exists('locks', $configuration) || !is_array($configuration['locks'])) { 350 $configuration['locks'] = array(); 351 } 352 353 return $configuration; 354 } 355 356 /** 357 * Used to sort cache config arrays based upon a sort key. 358 * 359 * Highest number at the top. 360 * 361 * @param array $a 362 * @param array $b 363 * @return int 364 */ 365 protected function sort_mappings(array $a, array $b) { 366 if ($a['sort'] == $b['sort']) { 367 return 0; 368 } 369 return ($a['sort'] < $b['sort']) ? 1 : -1; 370 } 371 372 /** 373 * Gets a definition from the config given its name. 374 * 375 * @param string $id 376 * @return bool 377 */ 378 public function get_definition_by_id($id) { 379 if (array_key_exists($id, $this->configdefinitions)) { 380 return $this->configdefinitions[$id]; 381 } 382 return false; 383 } 384 385 /** 386 * Returns all the known definitions. 387 * 388 * @return array 389 */ 390 public function get_definitions() { 391 return $this->configdefinitions; 392 } 393 394 /** 395 * Returns the definitions mapped into the given store name. 396 * 397 * @param string $storename 398 * @return array Associative array of definitions, id=>definition 399 */ 400 public function get_definitions_by_store($storename) { 401 $definitions = array(); 402 403 // This function was accidentally made static at some stage in the past. 404 // It was converted to an instance method but to be backwards compatible 405 // we must step around this in code. 406 if (!isset($this)) { 407 $config = cache_config::instance(); 408 } else { 409 $config = $this; 410 } 411 412 $stores = $config->get_all_stores(); 413 if (!array_key_exists($storename, $stores)) { 414 // The store does not exist. 415 return false; 416 } 417 418 $defmappings = $config->get_definition_mappings(); 419 // Create an associative array for the definition mappings. 420 $thedefmappings = array(); 421 foreach ($defmappings as $defmapping) { 422 $thedefmappings[$defmapping['definition']] = $defmapping; 423 } 424 425 // Search for matches in default mappings. 426 $defs = $config->get_definitions(); 427 foreach($config->get_mode_mappings() as $modemapping) { 428 if ($modemapping['store'] !== $storename) { 429 continue; 430 } 431 foreach($defs as $id => $definition) { 432 if ($definition['mode'] !== $modemapping['mode']) { 433 continue; 434 } 435 // Exclude custom definitions mapping: they will be managed few lines below. 436 if (array_key_exists($id, $thedefmappings)) { 437 continue; 438 } 439 $definitions[$id] = $definition; 440 } 441 } 442 443 // Search for matches in the custom definitions mapping 444 foreach ($defmappings as $defmapping) { 445 if ($defmapping['store'] !== $storename) { 446 continue; 447 } 448 $definition = $config->get_definition_by_id($defmapping['definition']); 449 if ($definition) { 450 $definitions[$defmapping['definition']] = $definition; 451 } 452 } 453 454 return $definitions; 455 } 456 457 /** 458 * Returns all of the stores that are suitable for the given mode and requirements. 459 * 460 * @param int $mode One of cache_store::MODE_* 461 * @param int $requirements The requirements of the cache as a binary flag 462 * @return array An array of suitable stores. 463 */ 464 public function get_stores($mode, $requirements = 0) { 465 $stores = array(); 466 foreach ($this->configstores as $name => $store) { 467 // If the mode is supported and all of the requirements are provided features. 468 if (($store['modes'] & $mode) && ($store['features'] & $requirements) === $requirements) { 469 $stores[$name] = $store; 470 } 471 } 472 return $stores; 473 } 474 475 /** 476 * Gets all of the stores that are to be used for the given definition. 477 * 478 * @param cache_definition $definition 479 * @return array 480 */ 481 public function get_stores_for_definition(cache_definition $definition) { 482 // Check if MUC has been disabled. 483 $factory = cache_factory::instance(); 484 if ($factory->stores_disabled()) { 485 // Yip its been disabled. 486 // To facilitate this we are going to always return an empty array of stores to use. 487 // This will force all cache instances to use the cachestore_dummy. 488 // MUC will still be used essentially so that code using it will still continue to function but because no cache stores 489 // are being used interaction with MUC will be purely based around a static var. 490 return array(); 491 } 492 493 $availablestores = $this->get_stores($definition->get_mode(), $definition->get_requirements_bin()); 494 $stores = array(); 495 $id = $definition->get_id(); 496 497 // Now get any mappings and give them priority. 498 foreach ($this->configdefinitionmappings as $mapping) { 499 if ($mapping['definition'] !== $id) { 500 continue; 501 } 502 $storename = $mapping['store']; 503 if (!array_key_exists($storename, $availablestores)) { 504 continue; 505 } 506 if (array_key_exists($storename, $stores)) { 507 $store = $stores[$storename]; 508 unset($stores[$storename]); 509 $stores[$storename] = $store; 510 } else { 511 $stores[$storename] = $availablestores[$storename]; 512 } 513 } 514 515 if (empty($stores) && !$definition->is_for_mappings_only()) { 516 $mode = $definition->get_mode(); 517 // Load the default stores. 518 foreach ($this->configmodemappings as $mapping) { 519 if ($mapping['mode'] === $mode && array_key_exists($mapping['store'], $availablestores)) { 520 $store = $availablestores[$mapping['store']]; 521 if (empty($store['mappingsonly'])) { 522 $stores[$mapping['store']] = $store; 523 } 524 } 525 } 526 } 527 528 return $stores; 529 } 530 531 /** 532 * Returns all of the configured stores 533 * @return array 534 */ 535 public function get_all_stores() { 536 return $this->configstores; 537 } 538 539 /** 540 * Returns all of the configured mode mappings 541 * @return array 542 */ 543 public function get_mode_mappings() { 544 return $this->configmodemappings; 545 } 546 547 /** 548 * Returns all of the known definition mappings. 549 * @return array 550 */ 551 public function get_definition_mappings() { 552 return $this->configdefinitionmappings; 553 } 554 555 /** 556 * Returns an array of the configured locks. 557 * @return array Array of name => config 558 */ 559 public function get_locks() { 560 return $this->configlocks; 561 } 562 563 /** 564 * Returns the lock store configuration to use with a given store. 565 * @param string $storename 566 * @return array 567 * @throws cache_exception 568 */ 569 public function get_lock_for_store($storename) { 570 if (array_key_exists($storename, $this->configstores)) { 571 if (array_key_exists($this->configstores[$storename]['lock'], $this->configlocks)) { 572 $lock = $this->configstores[$storename]['lock']; 573 return $this->configlocks[$lock]; 574 } 575 } 576 return $this->get_default_lock(); 577 } 578 579 /** 580 * Gets the default lock instance. 581 * 582 * @return array 583 * @throws cache_exception 584 */ 585 public function get_default_lock() { 586 foreach ($this->configlocks as $lockconf) { 587 if (!empty($lockconf['default'])) { 588 return $lockconf; 589 } 590 } 591 throw new cache_exception('ex_nodefaultlock'); 592 } 593 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |