MDL-42012 cache: Allow data sources to work when caching is disabled
[moodle.git] / cache / classes / factory.php
CommitLineData
8139ad13
SH
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 * This file contains the cache factory class.
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
29defined('MOODLE_INTERNAL') || die();
30
31/**
32 * The cache factory class.
33 *
34 * This factory class is important because it stores instances of objects used by the cache API and returns them upon requests.
35 * This allows us to both reuse objects saving on overhead, and gives us an easy place to "reset" the cache API in situations that
36 * we need such as unit testing.
37 *
38 * @copyright 2012 Sam Hemelryk
39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40 */
41class cache_factory {
42
7383a7e2
SH
43 /** The cache has not been initialised yet. */
44 const STATE_UNINITIALISED = 0;
45 /** The cache is in the process of initialising itself. */
46 const STATE_INITIALISING = 1;
47 /** The cache is in the process of saving its configuration file. */
48 const STATE_SAVING = 2;
49 /** The cache is ready to use. */
50 const STATE_READY = 3;
9890ecfc
SH
51 /** The cache is currently updating itself */
52 const STATE_UPDATING = 4;
7383a7e2
SH
53 /** The cache encountered an error while initialising. */
54 const STATE_ERROR_INITIALISING = 9;
55 /** The cache has been disabled. */
56 const STATE_DISABLED = 10;
82afd05c
SH
57 /** The cache stores have been disabled */
58 const STATE_STORES_DISABLED = 11;
7383a7e2 59
8139ad13
SH
60 /**
61 * An instance of the cache_factory class created upon the first request.
62 * @var cache_factory
63 */
64 protected static $instance;
65
66 /**
67 * An array containing caches created for definitions
68 * @var array
69 */
70 protected $cachesfromdefinitions = array();
71
72 /**
73 * Array of caches created by parameters, ad-hoc definitions will have been used.
74 * @var array
75 */
76 protected $cachesfromparams = array();
77
3d6dd54a
SH
78 /**
79 * An array of stores organised by definitions.
80 * @var array
81 */
82 protected $definitionstores = array();
83
8139ad13
SH
84 /**
85 * An array of instantiated stores.
86 * @var array
87 */
88 protected $stores = array();
89
90 /**
91 * An array of configuration instances
92 * @var array
93 */
94 protected $configs = array();
95
96 /**
97 * An array of initialised definitions
98 * @var array
99 */
100 protected $definitions = array();
101
34c84c72
SH
102 /**
103 * An array of lock plugins.
104 * @var array
105 */
7b9cb736 106 protected $lockplugins = array();
34c84c72 107
7383a7e2
SH
108 /**
109 * The current state of the cache API.
110 * @var int
111 */
112 protected $state = 0;
113
8139ad13
SH
114 /**
115 * Returns an instance of the cache_factor method.
116 *
117 * @param bool $forcereload If set to true a new cache_factory instance will be created and used.
118 * @return cache_factory
119 */
120 public static function instance($forcereload = false) {
94ef67cf 121 global $CFG;
8139ad13 122 if ($forcereload || self::$instance === null) {
94ef67cf
SH
123 // Initialise a new factory to facilitate our needs.
124 if (defined('CACHE_DISABLE_ALL') && CACHE_DISABLE_ALL !== false) {
125 // The cache has been disabled. Load disabledlib and start using the factory designed to handle this
126 // situation. It will use disabled alternatives where available.
127 require_once($CFG->dirroot.'/cache/disabledlib.php');
128 self::$instance = new cache_factory_disabled();
c0d8831a 129 } else if ((defined('PHPUNIT_TEST') && PHPUNIT_TEST) || defined('BEHAT_SITE_RUNNING')) {
45a8db7f 130 // We're using the test factory.
eefb680d
SH
131 require_once($CFG->dirroot.'/cache/tests/fixtures/lib.php');
132 self::$instance = new cache_phpunit_factory();
133 if (defined('CACHE_DISABLE_STORES') && CACHE_DISABLE_STORES !== false) {
134 // The cache stores have been disabled.
135 self::$instance->set_state(self::STATE_STORES_DISABLED);
136 }
94ef67cf
SH
137 } else {
138 // We're using the regular factory.
139 self::$instance = new cache_factory();
140 if (defined('CACHE_DISABLE_STORES') && CACHE_DISABLE_STORES !== false) {
141 // The cache stores have been disabled.
086e78da 142 self::$instance->set_state(self::STATE_STORES_DISABLED);
94ef67cf 143 }
7383a7e2 144 }
8139ad13
SH
145 }
146 return self::$instance;
147 }
148
149 /**
150 * Protected constructor, please use the static instance method.
151 */
152 protected function __construct() {
153 // Nothing to do here.
154 }
155
156 /**
157 * Resets the arrays containing instantiated caches, stores, and config instances.
158 */
159 public static function reset() {
160 $factory = self::instance();
083fa877 161 $factory->reset_cache_instances();
8139ad13
SH
162 $factory->configs = array();
163 $factory->definitions = array();
70aa00de 164 $factory->definitionstores = array();
7b9cb736 165 $factory->lockplugins = array(); // MUST be null in order to force its regeneration.
7383a7e2
SH
166 // Reset the state to uninitialised.
167 $factory->state = self::STATE_UNINITIALISED;
8139ad13
SH
168 }
169
083fa877
SH
170 /**
171 * Resets the stores, clearing the array of created stores.
172 *
173 * Cache objects still held onto by the code that initialised them will remain as is
174 * however all future requests for a cache/store will lead to a new instance being re-initialised.
175 */
176 public function reset_cache_instances() {
177 $this->cachesfromdefinitions = array();
178 $this->cachesfromparams = array();
179 $this->stores = array();
180 }
181
8139ad13
SH
182 /**
183 * Creates a cache object given the parameters for a definition.
184 *
185 * If a cache has already been created for the given definition then that cache instance will be returned.
186 *
187 * @param string $component
188 * @param string $area
189 * @param array $identifiers
83bee881 190 * @param string $unused Used to be data source aggregate however that was removed and this is now unused.
8139ad13
SH
191 * @return cache_application|cache_session|cache_request
192 */
83bee881 193 public function create_cache_from_definition($component, $area, array $identifiers = array(), $unused = null) {
f3789f2f
RS
194 $identifierstring = empty($identifiers) ? '' : '/'.http_build_query($identifiers);
195 $definitionname = $component.'/'.$area.$identifierstring;
06f6885b 196 if (isset($this->cachesfromdefinitions[$definitionname])) {
8139ad13 197 $cache = $this->cachesfromdefinitions[$definitionname];
8139ad13
SH
198 return $cache;
199 }
83bee881 200 $definition = $this->create_definition($component, $area);
f3789f2f
RS
201 // Identifiers are cached as part of the cache creation, so we store a cloned version of the cache.
202 $cacheddefinition = clone($definition);
203 $cacheddefinition->set_identifiers($identifiers);
204 $cache = $this->create_cache($cacheddefinition);
205
083fa877
SH
206 // Loaders are always held onto to speed up subsequent requests.
207 $this->cachesfromdefinitions[$definitionname] = $cache;
8139ad13
SH
208 return $cache;
209 }
210
211 /**
212 * Creates an ad-hoc cache from the given param.
213 *
214 * If a cache has already been created using the same params then that cache instance will be returned.
215 *
216 * @param int $mode
217 * @param string $component
218 * @param string $area
219 * @param array $identifiers
2566210c
SH
220 * @param array $options An array of options, available options are:
221 * - simplekeys : Set to true if the keys you will use are a-zA-Z0-9_
222 * - simpledata : Set to true if the type of the data you are going to store is scalar, or an array of scalar vars
d074fb09
SH
223 * - staticacceleration : If set to true the cache will hold onto data passing through it.
224 * - staticaccelerationsize : The maximum number of items to hold onto for acceleration purposes.
8139ad13
SH
225 * @return cache_application|cache_session|cache_request
226 */
2566210c 227 public function create_cache_from_params($mode, $component, $area, array $identifiers = array(), array $options = array()) {
f3789f2f
RS
228 $identifierstring = empty($identifiers) ? '' : '_'.http_build_query($identifiers);
229 $key = "{$mode}_{$component}_{$area}{$identifierstring}";
230 if (isset($this->cachesfromparams[$key])) {
8139ad13
SH
231 return $this->cachesfromparams[$key];
232 }
f3789f2f
RS
233 // Regular cache definitions are cached inside create_definition(). This is not the case for Adhoc definitions
234 // using load_adhoc(). They are built as a new object on each call.
235 // We do not need to clone the definition because we know it's new.
2566210c 236 $definition = cache_definition::load_adhoc($mode, $component, $area, $options);
8139ad13 237 $definition->set_identifiers($identifiers);
ca9a79da 238 $cache = $this->create_cache($definition);
083fa877 239 $this->cachesfromparams[$key] = $cache;
8139ad13
SH
240 return $cache;
241 }
242
243 /**
34c84c72 244 * Common public method to create a cache instance given a definition.
8139ad13
SH
245 *
246 * This is used by the static make methods.
247 *
248 * @param cache_definition $definition
249 * @return cache_application|cache_session|cache_store
250 * @throws coding_exception
251 */
252 public function create_cache(cache_definition $definition) {
253 $class = $definition->get_cache_class();
e28a9539 254 $stores = cache_helper::get_stores_suitable_for_definition($definition);
07658be7
SH
255 foreach ($stores as $key => $store) {
256 if (!$store::are_requirements_met()) {
257 unset($stores[$key]);
258 }
259 }
e28a9539
SH
260 if (count($stores) === 0) {
261 // Hmm still no stores, better provide a dummy store to mimic functionality. The dev will be none the wiser.
262 $stores[] = $this->create_dummy_store($definition);
8139ad13
SH
263 }
264 $loader = null;
265 if ($definition->has_data_source()) {
266 $loader = $definition->get_data_source();
267 }
268 while (($store = array_pop($stores)) !== null) {
269 $loader = new $class($definition, $store, $loader);
270 }
271 return $loader;
272 }
273
274 /**
275 * Creates a store instance given its name and configuration.
276 *
07658be7 277 * If the store has already been instantiated then the original object will be returned. (reused)
8139ad13
SH
278 *
279 * @param string $name The name of the store (must be unique remember)
280 * @param array $details
281 * @param cache_definition $definition The definition to instantiate it for.
3680c61a 282 * @return boolean|cache_store
8139ad13
SH
283 */
284 public function create_store_from_config($name, array $details, cache_definition $definition) {
285 if (!array_key_exists($name, $this->stores)) {
170f821b 286 // Properties: name, plugin, configuration, class.
8139ad13 287 $class = $details['class'];
f9599c77
RS
288 if (!$class::are_requirements_met()) {
289 return false;
290 }
8139ad13
SH
291 $store = new $class($details['name'], $details['configuration']);
292 $this->stores[$name] = $store;
293 }
07658be7 294 /* @var cache_store $store */
8139ad13 295 $store = $this->stores[$name];
c2dc13a0
SH
296 // We check are_requirements_met although we expect is_ready is going to check as well.
297 if (!$store::are_requirements_met() || !$store->is_ready() || !$store->is_supported_mode($definition->get_mode())) {
8139ad13
SH
298 return false;
299 }
79c55ca3
SH
300 // We always create a clone of the original store.
301 // If we were to clone a store that had already been initialised with a definition then
302 // we'd run into a myriad of issues.
303 // We use a method of the store to create a clone rather than just creating it ourselves
304 // so that if any store out there doesn't handle cloning they can override this method in
305 // order to address the issues.
306 $store = $this->stores[$name]->create_clone($details);
8139ad13 307 $store->initialise($definition);
3d6dd54a
SH
308 $definitionid = $definition->get_id();
309 if (!isset($this->definitionstores[$definitionid])) {
310 $this->definitionstores[$definitionid] = array();
311 }
312 $this->definitionstores[$definitionid][] = $store;
8139ad13
SH
313 return $store;
314 }
315
3d6dd54a
SH
316 /**
317 * Returns an array of cache stores that have been initialised for use in definitions.
318 * @param cache_definition $definition
319 * @return array
320 */
321 public function get_store_instances_in_use(cache_definition $definition) {
322 $id = $definition->get_id();
323 if (!isset($this->definitionstores[$id])) {
324 return array();
325 }
326 return $this->definitionstores[$id];
327 }
328
d074fb09
SH
329 /**
330 * Returns the cache instances that have been used within this request.
5bcfd504 331 * @since Moodle 2.6
d074fb09
SH
332 * @return array
333 */
334 public function get_caches_in_use() {
335 return $this->cachesfromdefinitions;
336 }
337
4998d3a5 338 /**
339 * Gets all adhoc caches that have been used within this request.
340 *
341 * @return cache_store[] Caches currently in use
342 */
343 public function get_adhoc_caches_in_use() {
344 return $this->cachesfromparams;
345 }
346
8139ad13
SH
347 /**
348 * Creates a cache config instance with the ability to write if required.
349 *
350 * @param bool $writer If set to true an instance that can update the configuration will be returned.
351 * @return cache_config|cache_config_writer
352 */
353 public function create_config_instance($writer = false) {
354 global $CFG;
355
8139ad13
SH
356 // The class to use.
357 $class = 'cache_config';
45a8db7f 358 // Are we running tests of some form?
c0d8831a 359 $testing = (defined('PHPUNIT_TEST') && PHPUNIT_TEST) || defined('BEHAT_SITE_RUNNING');
eefb680d 360
79a8ea65 361 // Check if this is a PHPUnit test and redirect to the phpunit config classes if it is.
45a8db7f 362 if ($testing) {
79a8ea65
SH
363 require_once($CFG->dirroot.'/cache/locallib.php');
364 require_once($CFG->dirroot.'/cache/tests/fixtures/lib.php');
365 // We have just a single class for PHP unit tests. We don't care enough about its
366 // performance to do otherwise and having a single method allows us to inject things into it
367 // while testing.
427c41ef 368 $class = 'cache_config_testing';
79a8ea65
SH
369 }
370
371 // Check if we need to create a config file with defaults.
372 $needtocreate = !$class::config_file_exists();
373
8139ad13
SH
374 if ($writer || $needtocreate) {
375 require_once($CFG->dirroot.'/cache/locallib.php');
45a8db7f 376 if (!$testing) {
eefb680d
SH
377 $class .= '_writer';
378 }
8139ad13
SH
379 }
380
7383a7e2 381 $error = false;
8139ad13
SH
382 if ($needtocreate) {
383 // Create the default configuration.
7383a7e2
SH
384 // Update the state, we are now initialising the cache.
385 self::set_state(self::STATE_INITIALISING);
79a8ea65 386 /** @var cache_config_writer $class */
7383a7e2
SH
387 $configuration = $class::create_default_configuration();
388 if ($configuration !== true) {
389 // Failed to create the default configuration. Disable the cache stores and update the state.
390 self::set_state(self::STATE_ERROR_INITIALISING);
391 $this->configs[$class] = new $class;
392 $this->configs[$class]->load($configuration);
393 $error = true;
394 }
8139ad13
SH
395 }
396
397 if (!array_key_exists($class, $this->configs)) {
398 // Create a new instance and call it to load it.
399 $this->configs[$class] = new $class;
400 $this->configs[$class]->load();
401 }
402
7383a7e2
SH
403 if (!$error) {
404 // The cache is now ready to use. Update the state.
405 self::set_state(self::STATE_READY);
406 }
407
8139ad13
SH
408 // Return the instance.
409 return $this->configs[$class];
410 }
411
412 /**
413 * Creates a definition instance or returns the existing one if it has already been created.
414 * @param string $component
415 * @param string $area
83bee881
SH
416 * @param string $unused This used to be data source aggregate - however that functionality has been removed and
417 * this argument is now unused.
8139ad13 418 * @return cache_definition
83bee881 419 * @throws coding_exception If the definition cannot be found.
8139ad13 420 */
83bee881 421 public function create_definition($component, $area, $unused = null) {
8139ad13 422 $id = $component.'/'.$area;
06f6885b 423 if (!isset($this->definitions[$id])) {
9890ecfc
SH
424 // This is the first time this definition has been requested.
425 if ($this->is_initialising()) {
426 // We're initialising the cache right now. Don't try to create another config instance.
427 // We'll just use an ad-hoc cache for the time being.
428 $definition = cache_definition::load_adhoc(cache_store::MODE_REQUEST, $component, $area);
429 } else {
430 // Load all the known definitions and find the desired one.
431 $instance = $this->create_config_instance();
8139ad13
SH
432 $definition = $instance->get_definition_by_id($id);
433 if (!$definition) {
9890ecfc
SH
434 // Oh-oh the definition doesn't exist.
435 // There are several things that could be going on here.
436 // We may be installing/upgrading a site and have hit a definition that hasn't been used before.
437 // Of the developer may be trying to use a newly created definition.
438 if ($this->is_updating()) {
439 // The cache is presently initialising and the requested cache definition has not been found.
440 // This means that the cache initialisation has requested something from a cache (I had recursive nightmares about this).
441 // To serve this purpose and avoid errors we are going to make use of an ad-hoc cache rather than
442 // search for the definition which would possibly cause an infitite loop trying to initialise the cache.
443 $definition = cache_definition::load_adhoc(cache_store::MODE_REQUEST, $component, $area);
9890ecfc
SH
444 } else {
445 // Either a typo of the developer has just created the definition and is using it for the first time.
446 $this->reset();
447 $instance = $this->create_config_instance(true);
448 $instance->update_definitions();
449 $definition = $instance->get_definition_by_id($id);
450 if (!$definition) {
451 throw new coding_exception('The requested cache definition does not exist.'. $id, $id);
063c9ee9
AN
452 }
453 if (!$this->is_disabled() && !($this instanceof cache_factory_disabled)) {
9890ecfc
SH
454 debugging('Cache definitions reparsed causing cache reset in order to locate definition.
455 You should bump the version number to ensure definitions are reprocessed.', DEBUG_DEVELOPER);
456 }
83bee881 457 $definition = cache_definition::load($id, $definition);
9890ecfc 458 }
8139ad13 459 } else {
83bee881 460 $definition = cache_definition::load($id, $definition);
8139ad13
SH
461 }
462 }
9890ecfc 463 $this->definitions[$id] = $definition;
8139ad13
SH
464 }
465 return $this->definitions[$id];
466 }
467
468 /**
469 * Creates a dummy store object for use when a loader has no potential stores to use.
470 *
471 * @param cache_definition $definition
6fec1820 472 * @return cachestore_dummy
8139ad13
SH
473 */
474 protected function create_dummy_store(cache_definition $definition) {
475 global $CFG;
476 require_once($CFG->dirroot.'/cache/classes/dummystore.php');
6fec1820 477 $store = new cachestore_dummy();
8139ad13
SH
478 $store->initialise($definition);
479 return $store;
480 }
34c84c72
SH
481
482 /**
483 * Returns a lock instance ready for use.
484 *
485 * @param array $config
486 * @return cache_lock_interface
487 */
488 public function create_lock_instance(array $config) {
7b9cb736 489 global $CFG;
34c84c72
SH
490 if (!array_key_exists('name', $config) || !array_key_exists('type', $config)) {
491 throw new coding_exception('Invalid cache lock instance provided');
492 }
493 $name = $config['name'];
494 $type = $config['type'];
495 unset($config['name']);
496 unset($config['type']);
497
7b9cb736
SH
498 if (!isset($this->lockplugins[$type])) {
499 $pluginname = substr($type, 10);
500 $file = $CFG->dirroot."/cache/locks/{$pluginname}/lib.php";
501 if (file_exists($file) && is_readable($file)) {
502 require_once($file);
503 }
504 if (!class_exists($type)) {
505 throw new coding_exception('Invalid lock plugin requested.');
506 }
507 $this->lockplugins[$type] = $type;
34c84c72
SH
508 }
509 if (!array_key_exists($type, $this->lockplugins)) {
510 throw new coding_exception('Invalid cache lock type.');
511 }
512 $class = $this->lockplugins[$type];
513 return new $class($name, $config);
514 }
7383a7e2
SH
515
516 /**
517 * Returns the current state of the cache API.
518 *
519 * @return int
520 */
521 public function get_state() {
522 return $this->state;
523 }
524
525 /**
526 * Updates the state fo the cache API.
527 *
528 * @param int $state
529 * @return bool
530 */
531 public function set_state($state) {
532 if ($state <= $this->state) {
533 return false;
534 }
535 $this->state = $state;
536 return true;
537 }
538
9890ecfc
SH
539 /**
540 * Informs the factory that the cache is currently updating itself.
541 *
542 * This forces the state to upgrading and can only be called once the cache is ready to use.
543 * Calling it ensure we don't try to reinstantite things when requesting cache definitions that don't exist yet.
544 */
545 public function updating_started() {
546 if ($this->state !== self::STATE_READY) {
547 return false;
548 }
549 $this->state = self::STATE_UPDATING;
550 return true;
551 }
552
553 /**
554 * Informs the factory that the upgrading has finished.
555 *
556 * This forces the state back to ready.
557 */
558 public function updating_finished() {
559 $this->state = self::STATE_READY;
560 }
561
7383a7e2
SH
562 /**
563 * Returns true if the cache API has been disabled.
564 *
565 * @return bool
566 */
567 public function is_disabled() {
568 return $this->state === self::STATE_DISABLED;
569 }
82afd05c 570
9890ecfc
SH
571 /**
572 * Returns true if the cache is currently initialising itself.
573 *
574 * This includes both initialisation and saving the cache config file as part of that initialisation.
575 *
576 * @return bool
577 */
578 public function is_initialising() {
579 return $this->state === self::STATE_INITIALISING || $this->state === self::STATE_SAVING;
580 }
581
582 /**
583 * Returns true if the cache is currently updating itself.
584 *
585 * @return bool
586 */
587 public function is_updating() {
588 return $this->state === self::STATE_UPDATING;
589 }
590
3680c61a
SH
591 /**
592 * Disables as much of the cache API as possible.
593 *
594 * All of the magic associated with the disabled cache is wrapped into this function.
595 * In switching out the factory for the disabled factory it gains full control over the initialisation of objects
596 * and can use all of the disabled alternatives.
597 * Simple!
94ef67cf
SH
598 *
599 * This function has been marked as protected so that it cannot be abused through the public API presently.
600 * Perhaps in the future we will allow this, however as per the build up to the first release containing
601 * MUC it was decided that this was just to risky and abusable.
3680c61a 602 */
94ef67cf 603 protected static function disable() {
3680c61a
SH
604 global $CFG;
605 require_once($CFG->dirroot.'/cache/disabledlib.php');
3680c61a
SH
606 self::$instance = new cache_factory_disabled();
607 }
608
82afd05c
SH
609 /**
610 * Returns true if the cache stores have been disabled.
611 *
612 * @return bool
613 */
614 public function stores_disabled() {
3680c61a 615 return $this->state === self::STATE_STORES_DISABLED || $this->is_disabled();
82afd05c
SH
616 }
617
618 /**
619 * Disables cache stores.
620 *
621 * The cache API will continue to function however none of the actual stores will be used.
622 * Instead the dummy store will be provided for all cache requests.
623 * This is useful in situations where you cannot be sure any stores are working.
624 *
625 * In order to re-enable the cache you must call the cache factories static reset method:
626 * <code>
627 * // Disable the cache factory.
628 * cache_factory::disable_stores();
629 * // Re-enable the cache factory by resetting it.
630 * cache_factory::reset();
631 * </code>
632 */
633 public static function disable_stores() {
d074fb09 634 // First reset to clear any static acceleration array.
82afd05c 635 $factory = self::instance();
083fa877 636 $factory->reset_cache_instances();
82afd05c
SH
637 $factory->set_state(self::STATE_STORES_DISABLED);
638 }
086e78da 639}