MDL-40903 cache: converted persistent into persistentdata
[moodle.git] / cache / classes / factory.php
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/>.
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  */
29 defined('MOODLE_INTERNAL') || die();
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  */
41 class cache_factory {
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;
51     /** The cache is currently updating itself */
52     const STATE_UPDATING = 4;
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;
57     /** The cache stores have been disabled */
58     const STATE_STORES_DISABLED = 11;
60     /**
61      * An instance of the cache_factory class created upon the first request.
62      * @var cache_factory
63      */
64     protected static $instance;
66     /**
67      * An array containing caches created for definitions
68      * @var array
69      */
70     protected $cachesfromdefinitions = array();
72     /**
73      * Array of caches created by parameters, ad-hoc definitions will have been used.
74      * @var array
75      */
76     protected $cachesfromparams = array();
78     /**
79      * An array of stores organised by definitions.
80      * @var array
81      */
82     protected $definitionstores = array();
84     /**
85      * An array of instantiated stores.
86      * @var array
87      */
88     protected $stores = array();
90     /**
91      * An array of configuration instances
92      * @var array
93      */
94     protected $configs = array();
96     /**
97      * An array of initialised definitions
98      * @var array
99      */
100     protected $definitions = array();
102     /**
103      * An array of lock plugins.
104      * @var array
105      */
106     protected $lockplugins = array();
108     /**
109      * The current state of the cache API.
110      * @var int
111      */
112     protected $state = 0;
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) {
121         global $CFG;
122         if ($forcereload || self::$instance === null) {
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();
129             } else {
130                 // We're using the regular factory.
131                 self::$instance = new cache_factory();
132                 if (defined('CACHE_DISABLE_STORES') && CACHE_DISABLE_STORES !== false) {
133                     // The cache stores have been disabled.
134                     self::$instance->set_state(self::STATE_STORES_DISABLED);
135                 }
136             }
137         }
138         return self::$instance;
139     }
141     /**
142      * Protected constructor, please use the static instance method.
143      */
144     protected function __construct() {
145         // Nothing to do here.
146     }
148     /**
149      * Resets the arrays containing instantiated caches, stores, and config instances.
150      */
151     public static function reset() {
152         $factory = self::instance();
153         $factory->reset_cache_instances();
154         $factory->configs = array();
155         $factory->definitions = array();
156         $factory->lockplugins = array(); // MUST be null in order to force its regeneration.
157         // Reset the state to uninitialised.
158         $factory->state = self::STATE_UNINITIALISED;
159     }
161     /**
162      * Resets the stores, clearing the array of created stores.
163      *
164      * Cache objects still held onto by the code that initialised them will remain as is
165      * however all future requests for a cache/store will lead to a new instance being re-initialised.
166      */
167     public function reset_cache_instances() {
168         $this->cachesfromdefinitions = array();
169         $this->cachesfromparams = array();
170         $this->stores = array();
171     }
173     /**
174      * Creates a cache object given the parameters for a definition.
175      *
176      * If a cache has already been created for the given definition then that cache instance will be returned.
177      *
178      * @param string $component
179      * @param string $area
180      * @param array $identifiers
181      * @param string $aggregate
182      * @return cache_application|cache_session|cache_request
183      */
184     public function create_cache_from_definition($component, $area, array $identifiers = array(), $aggregate = null) {
185         $definitionname = $component.'/'.$area;
186         if (isset($this->cachesfromdefinitions[$definitionname])) {
187             $cache = $this->cachesfromdefinitions[$definitionname];
188             $cache->set_identifiers($identifiers);
189             return $cache;
190         }
191         $definition = $this->create_definition($component, $area, $aggregate);
192         $definition->set_identifiers($identifiers);
193         $cache = $this->create_cache($definition, $identifiers);
194         // Loaders are always held onto to speed up subsequent requests.
195         $this->cachesfromdefinitions[$definitionname] = $cache;
196         return $cache;
197     }
199     /**
200      * Creates an ad-hoc cache from the given param.
201      *
202      * If a cache has already been created using the same params then that cache instance will be returned.
203      *
204      * @param int $mode
205      * @param string $component
206      * @param string $area
207      * @param array $identifiers
208      * @param array $options An array of options, available options are:
209      *   - simplekeys : Set to true if the keys you will use are a-zA-Z0-9_
210      *   - simpledata : Set to true if the type of the data you are going to store is scalar, or an array of scalar vars
211      *   - persistent : If set to true the cache will persist construction requests.
212      * @return cache_application|cache_session|cache_request
213      */
214     public function create_cache_from_params($mode, $component, $area, array $identifiers = array(), array $options = array()) {
215         $key = "{$mode}_{$component}_{$area}";
216         if (array_key_exists($key, $this->cachesfromparams)) {
217             return $this->cachesfromparams[$key];
218         }
219         $definition = cache_definition::load_adhoc($mode, $component, $area, $options);
220         $definition->set_identifiers($identifiers);
221         $cache = $this->create_cache($definition, $identifiers);
222         $this->cachesfromparams[$key] = $cache;
223         return $cache;
224     }
226     /**
227      * Common public method to create a cache instance given a definition.
228      *
229      * This is used by the static make methods.
230      *
231      * @param cache_definition $definition
232      * @return cache_application|cache_session|cache_store
233      * @throws coding_exception
234      */
235     public function create_cache(cache_definition $definition) {
236         $class = $definition->get_cache_class();
237         if ($this->is_initialising()) {
238             // Do nothing we just want the dummy store.
239             $stores = array();
240         } else {
241             $stores = cache_helper::get_cache_stores($definition);
242         }
243         if (count($stores) === 0) {
244             // Hmm no stores, better provide a dummy store to mimick functionality. The dev will be none the wiser.
245             $stores[] = $this->create_dummy_store($definition);
246         }
247         $loader = null;
248         if ($definition->has_data_source()) {
249             $loader = $definition->get_data_source();
250         }
251         while (($store = array_pop($stores)) !== null) {
252             $loader = new $class($definition, $store, $loader);
253         }
254         return $loader;
255     }
257     /**
258      * Creates a store instance given its name and configuration.
259      *
260      * If the store has already been instantiated then the original objetc will be returned. (reused)
261      *
262      * @param string $name The name of the store (must be unique remember)
263      * @param array $details
264      * @param cache_definition $definition The definition to instantiate it for.
265      * @return boolean|cache_store
266      */
267     public function create_store_from_config($name, array $details, cache_definition $definition) {
268         if (!array_key_exists($name, $this->stores)) {
269             // Properties: name, plugin, configuration, class.
270             $class = $details['class'];
271             $store = new $class($details['name'], $details['configuration']);
272             $this->stores[$name] = $store;
273         }
274         $store = $this->stores[$name];
275         if (!$store->is_ready() || !$store->is_supported_mode($definition->get_mode())) {
276             return false;
277         }
278         // We always create a clone of the original store.
279         // If we were to clone a store that had already been initialised with a definition then
280         // we'd run into a myriad of issues.
281         // We use a method of the store to create a clone rather than just creating it ourselves
282         // so that if any store out there doesn't handle cloning they can override this method in
283         // order to address the issues.
284         $store = $this->stores[$name]->create_clone($details);
285         $store->initialise($definition);
286         $definitionid = $definition->get_id();
287         if (!isset($this->definitionstores[$definitionid])) {
288             $this->definitionstores[$definitionid] = array();
289         }
290         $this->definitionstores[$definitionid][] = $store;
291         return $store;
292     }
294     /**
295      * Returns an array of cache stores that have been initialised for use in definitions.
296      * @param cache_definition $definition
297      * @return array
298      */
299     public function get_store_instances_in_use(cache_definition $definition) {
300         $id = $definition->get_id();
301         if (!isset($this->definitionstores[$id])) {
302             return array();
303         }
304         return $this->definitionstores[$id];
305     }
307     /**
308      * Creates a cache config instance with the ability to write if required.
309      *
310      * @param bool $writer If set to true an instance that can update the configuration will be returned.
311      * @return cache_config|cache_config_writer
312      */
313     public function create_config_instance($writer = false) {
314         global $CFG;
316         // Check if we need to create a config file with defaults.
317         $needtocreate = !cache_config::config_file_exists();
319         // The class to use.
320         $class = 'cache_config';
321         if ($writer || $needtocreate) {
322             require_once($CFG->dirroot.'/cache/locallib.php');
323             $class .= '_writer';
324         }
326         // Check if this is a PHPUnit test and redirect to the phpunit config classes if it is.
327         if (defined('PHPUNIT_TEST') && PHPUNIT_TEST) {
328             require_once($CFG->dirroot.'/cache/locallib.php');
329             require_once($CFG->dirroot.'/cache/tests/fixtures/lib.php');
330             // We have just a single class for PHP unit tests. We don't care enough about its
331             // performance to do otherwise and having a single method allows us to inject things into it
332             // while testing.
333             $class = 'cache_config_phpunittest';
334         }
336         $error = false;
337         if ($needtocreate) {
338             // Create the default configuration.
339             // Update the state, we are now initialising the cache.
340             self::set_state(self::STATE_INITIALISING);
341             $configuration = $class::create_default_configuration();
342             if ($configuration !== true) {
343                 // Failed to create the default configuration. Disable the cache stores and update the state.
344                 self::set_state(self::STATE_ERROR_INITIALISING);
345                 $this->configs[$class] = new $class;
346                 $this->configs[$class]->load($configuration);
347                 $error = true;
348             }
349         }
351         if (!array_key_exists($class, $this->configs)) {
352             // Create a new instance and call it to load it.
353             $this->configs[$class] = new $class;
354             $this->configs[$class]->load();
355         }
357         if (!$error) {
358             // The cache is now ready to use. Update the state.
359             self::set_state(self::STATE_READY);
360         }
362         // Return the instance.
363         return $this->configs[$class];
364     }
366     /**
367      * Creates a definition instance or returns the existing one if it has already been created.
368      * @param string $component
369      * @param string $area
370      * @param string $aggregate
371      * @return cache_definition
372      */
373     public function create_definition($component, $area, $aggregate = null) {
374         $id = $component.'/'.$area;
375         if ($aggregate) {
376             $id .= '::'.$aggregate;
377         }
378         if (!isset($this->definitions[$id])) {
379             // This is the first time this definition has been requested.
380             if ($this->is_initialising()) {
381                 // We're initialising the cache right now. Don't try to create another config instance.
382                 // We'll just use an ad-hoc cache for the time being.
383                 $definition = cache_definition::load_adhoc(cache_store::MODE_REQUEST, $component, $area);
384             } else {
385                 // Load all the known definitions and find the desired one.
386                 $instance = $this->create_config_instance();
387                 $definition = $instance->get_definition_by_id($id);
388                 if (!$definition) {
389                     // Oh-oh the definition doesn't exist.
390                     // There are several things that could be going on here.
391                     // We may be installing/upgrading a site and have hit a definition that hasn't been used before.
392                     // Of the developer may be trying to use a newly created definition.
393                     if ($this->is_updating()) {
394                         // The cache is presently initialising and the requested cache definition has not been found.
395                         // This means that the cache initialisation has requested something from a cache (I had recursive nightmares about this).
396                         // To serve this purpose and avoid errors we are going to make use of an ad-hoc cache rather than
397                         // search for the definition which would possibly cause an infitite loop trying to initialise the cache.
398                         $definition = cache_definition::load_adhoc(cache_store::MODE_REQUEST, $component, $area);
399                         if ($aggregate !== null) {
400                             // If you get here you deserve a warning. We have to use an ad-hoc cache here, so we can't find the definition and therefor
401                             // can't find any information about the datasource or any of its aggregated.
402                             // Best of luck.
403                             debugging('An unknown cache was requested during development with an aggregate that could not be loaded. Ad-hoc cache used instead.', DEBUG_DEVELOPER);
404                             $aggregate = null;
405                         }
406                     } else {
407                         // Either a typo of the developer has just created the definition and is using it for the first time.
408                         $this->reset();
409                         $instance = $this->create_config_instance(true);
410                         $instance->update_definitions();
411                         $definition = $instance->get_definition_by_id($id);
412                         if (!$definition) {
413                             throw new coding_exception('The requested cache definition does not exist.'. $id, $id);
414                         } else if (!$this->is_disabled()) {
415                             debugging('Cache definitions reparsed causing cache reset in order to locate definition.
416                                 You should bump the version number to ensure definitions are reprocessed.', DEBUG_DEVELOPER);
417                         }
418                         $definition = cache_definition::load($id, $definition, $aggregate);
419                     }
420                 } else {
421                     $definition = cache_definition::load($id, $definition, $aggregate);
422                 }
423             }
424             $this->definitions[$id] = $definition;
425         }
426         return $this->definitions[$id];
427     }
429     /**
430      * Creates a dummy store object for use when a loader has no potential stores to use.
431      *
432      * @param cache_definition $definition
433      * @return cachestore_dummy
434      */
435     protected function create_dummy_store(cache_definition $definition) {
436         global $CFG;
437         require_once($CFG->dirroot.'/cache/classes/dummystore.php');
438         $store = new cachestore_dummy();
439         $store->initialise($definition);
440         return $store;
441     }
443     /**
444      * Returns a lock instance ready for use.
445      *
446      * @param array $config
447      * @return cache_lock_interface
448      */
449     public function create_lock_instance(array $config) {
450         global $CFG;
451         if (!array_key_exists('name', $config) || !array_key_exists('type', $config)) {
452             throw new coding_exception('Invalid cache lock instance provided');
453         }
454         $name = $config['name'];
455         $type = $config['type'];
456         unset($config['name']);
457         unset($config['type']);
459         if (!isset($this->lockplugins[$type])) {
460             $pluginname = substr($type, 10);
461             $file = $CFG->dirroot."/cache/locks/{$pluginname}/lib.php";
462             if (file_exists($file) && is_readable($file)) {
463                 require_once($file);
464             }
465             if (!class_exists($type)) {
466                 throw new coding_exception('Invalid lock plugin requested.');
467             }
468             $this->lockplugins[$type] = $type;
469         }
470         if (!array_key_exists($type, $this->lockplugins)) {
471             throw new coding_exception('Invalid cache lock type.');
472         }
473         $class = $this->lockplugins[$type];
474         return new $class($name, $config);
475     }
477     /**
478      * Returns the current state of the cache API.
479      *
480      * @return int
481      */
482     public function get_state() {
483         return $this->state;
484     }
486     /**
487      * Updates the state fo the cache API.
488      *
489      * @param int $state
490      * @return bool
491      */
492     public function set_state($state) {
493         if ($state <= $this->state) {
494             return false;
495         }
496         $this->state = $state;
497         return true;
498     }
500     /**
501      * Informs the factory that the cache is currently updating itself.
502      *
503      * This forces the state to upgrading and can only be called once the cache is ready to use.
504      * Calling it ensure we don't try to reinstantite things when requesting cache definitions that don't exist yet.
505      */
506     public function updating_started() {
507         if ($this->state !== self::STATE_READY) {
508             return false;
509         }
510         $this->state = self::STATE_UPDATING;
511         return true;
512     }
514     /**
515      * Informs the factory that the upgrading has finished.
516      *
517      * This forces the state back to ready.
518      */
519     public function updating_finished() {
520         $this->state = self::STATE_READY;
521     }
523     /**
524      * Returns true if the cache API has been disabled.
525      *
526      * @return bool
527      */
528     public function is_disabled() {
529         return $this->state === self::STATE_DISABLED;
530     }
532     /**
533      * Returns true if the cache is currently initialising itself.
534      *
535      * This includes both initialisation and saving the cache config file as part of that initialisation.
536      *
537      * @return bool
538      */
539     public function is_initialising() {
540         return $this->state === self::STATE_INITIALISING || $this->state === self::STATE_SAVING;
541     }
543     /**
544      * Returns true if the cache is currently updating itself.
545      *
546      * @return bool
547      */
548     public function is_updating() {
549         return $this->state === self::STATE_UPDATING;
550     }
552     /**
553      * Disables as much of the cache API as possible.
554      *
555      * All of the magic associated with the disabled cache is wrapped into this function.
556      * In switching out the factory for the disabled factory it gains full control over the initialisation of objects
557      * and can use all of the disabled alternatives.
558      * Simple!
559      *
560      * This function has been marked as protected so that it cannot be abused through the public API presently.
561      * Perhaps in the future we will allow this, however as per the build up to the first release containing
562      * MUC it was decided that this was just to risky and abusable.
563      */
564     protected static function disable() {
565         global $CFG;
566         require_once($CFG->dirroot.'/cache/disabledlib.php');
567         self::$instance = new cache_factory_disabled();
568     }
570     /**
571      * Returns true if the cache stores have been disabled.
572      *
573      * @return bool
574      */
575     public function stores_disabled() {
576         return $this->state === self::STATE_STORES_DISABLED || $this->is_disabled();
577     }
579     /**
580      * Disables cache stores.
581      *
582      * The cache API will continue to function however none of the actual stores will be used.
583      * Instead the dummy store will be provided for all cache requests.
584      * This is useful in situations where you cannot be sure any stores are working.
585      *
586      * In order to re-enable the cache you must call the cache factories static reset method:
587      * <code>
588      * // Disable the cache factory.
589      * cache_factory::disable_stores();
590      * // Re-enable the cache factory by resetting it.
591      * cache_factory::reset();
592      * </code>
593      */
594     public static function disable_stores() {
595         // First reset to clear any persistent caches.
596         $factory = self::instance();
597         $factory->reset_cache_instances();
598         $factory->set_state(self::STATE_STORES_DISABLED);
599     }