mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 05:58:34 +01:00
Merge branch 'wip-MDL-25290-m24-compact' of git://github.com/samhemelryk/moodle
This commit is contained in:
commit
990853afbe
@ -492,6 +492,22 @@ foreach (get_plugin_list('tool') as $plugin => $plugindir) {
|
||||
}
|
||||
}
|
||||
|
||||
// Now add the Cache plugins
|
||||
if ($hassiteconfig) {
|
||||
$ADMIN->add('modules', new admin_category('cache', new lang_string('caching', 'cache')));
|
||||
$ADMIN->add('cache', new admin_externalpage('cacheconfig', new lang_string('cacheconfig', 'cache'), $CFG->wwwroot .'/cache/admin.php'));
|
||||
$ADMIN->add('cache', new admin_externalpage('cachetestperformance', new lang_string('testperformance', 'cache'), $CFG->wwwroot . '/cache/testperformance.php'));
|
||||
$ADMIN->add('cache', new admin_category('cachestores', new lang_string('cachestores', 'cache')));
|
||||
foreach (get_plugin_list('cachestore') as $plugin => $path) {
|
||||
$settingspath = $path.'/settings.php';
|
||||
if (file_exists($settingspath)) {
|
||||
$settings = new admin_settingpage('cachestore_'.$plugin.'_settings', new lang_string('pluginname', 'cachestore_'.$plugin), 'moodle/site:config');
|
||||
include($settingspath);
|
||||
$ADMIN->add('cachestores', $settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add all local plugins - must be always last!
|
||||
if ($hassiteconfig) {
|
||||
$ADMIN->add('modules', new admin_category('localplugins', new lang_string('localplugins')));
|
||||
|
@ -216,6 +216,7 @@ $temp->add(new admin_setting_configselect('memcachedpconn', new lang_string('mem
|
||||
array( '0' => new lang_string('no'),
|
||||
'1' => new lang_string('yes'))));
|
||||
*/
|
||||
|
||||
$ADMIN->add('server', $temp);
|
||||
|
||||
|
||||
|
207
cache/README.md
vendored
Normal file
207
cache/README.md
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
MUC development code
|
||||
====================
|
||||
|
||||
Congratulations you've found the MUC development code.
|
||||
This code is still very much in development and as such is not (and is know to not) function correctly or completely at the moment.
|
||||
Of course that will all be well and truly sorted out WELL before this gets integrated.
|
||||
|
||||
Sample code snippets
|
||||
--------------------
|
||||
|
||||
A definition:
|
||||
|
||||
$definitions = array(
|
||||
'core_string' => array( // Required, unique
|
||||
'mode' => cache_store::MODE_APPLICATION, // Required
|
||||
'component' => 'core', // Required
|
||||
'area' => 'string', // Required
|
||||
'requireidentifiers' => array( // Optional
|
||||
'lang',
|
||||
'component'
|
||||
),
|
||||
'requiredataguarantee' => false, // Optional
|
||||
'requiremultipleidentifiers' => false, // Optional
|
||||
'overrideclass' => null, // Optional
|
||||
'overrideclassfile' => null, // Optional
|
||||
'datasource' => null, // Optional
|
||||
'datasourcefile' => null, // Optional
|
||||
'persistent' => false, // Optional
|
||||
'ttl' => 0, // Optional
|
||||
'mappingsonly' => false // Optional
|
||||
'invalidationevents' => array( // Optional
|
||||
'contextmarkeddirty'
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
Getting a something from a cache using the definition:
|
||||
|
||||
$cache = cache::make('core', 'string');
|
||||
if (!$component = $cache->get('component')) {
|
||||
// get returns false if its not there and can't be loaded.
|
||||
$component = generate_data();
|
||||
$cache->set($component);
|
||||
}
|
||||
|
||||
The same thing but from using params:
|
||||
|
||||
$cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'core', 'string');
|
||||
if (!$component = $cache->get('component')) {
|
||||
// get returns false if its not there and can't be loaded.
|
||||
$component = generate_data();
|
||||
$cache->set($component);
|
||||
}
|
||||
|
||||
If a data source had been specified in the definition the following would be all that was needed.
|
||||
|
||||
$cache = cache::make('core', 'string');
|
||||
$component = $cache->get('component');
|
||||
|
||||
The bits that make up the cache API
|
||||
-----------------------------------
|
||||
|
||||
There are several parts that _**will**_ make up this solution:
|
||||
|
||||
### Loader
|
||||
The loader is central to the whole thing.
|
||||
It is used by the end developer to get an object that handles caching.
|
||||
90% of end developers will not need to know or use anything else about the cache API.
|
||||
In order to get a loader you must use one of two static methods, make, or make_with_params.
|
||||
To the end developer interacting with the loader is simple and is dictated by the cache_loader interface.
|
||||
Internally there is lots of magic going on. The important parts to know about are:
|
||||
* There are two ways to get with a loader, the first with a definition (discussed below) the second with params. When params are used they are turned into an adhoc definition with default params.
|
||||
* A loader get passed three things when being constructed, a definition, a store, and another loader or datasource if there is either.
|
||||
* If a loader is the third arg then requests will be chained to provide redundancy.
|
||||
* If a data source is provided then requests for an item that is not cached will be passed to the data source and that will be expected to load the data. If it loads data that data is stored in each store on its way back to the user.
|
||||
* There are three core loaders. One for each application, session, and request.
|
||||
* A custom loader can be used. It will be provided by the definition (thus cannot be used with adhoc definitions) and must override the appropriate core loader
|
||||
* The loader handles ttl for stores that don't natively support ttl.
|
||||
* The application loader handles locking for stores that don't natively support locking.
|
||||
|
||||
### Store
|
||||
The store is the bridge between the cache API and a cache solution.
|
||||
Cache store plugins exist within moodle/cache/store.
|
||||
The administrator of a site can configure multiple instances of each plugin, the configuration gets initialised as a store for the loader when required in code (during construction of the loader).
|
||||
The following points highlight things you should know about stores.
|
||||
* A cache_store interface is used to define the requirements of a store plugin.
|
||||
* The store plugin can inherit the cache_is_lockable interface to handle its own locking.
|
||||
* The store plugin can inherit the cache_is_key_aware interface to handle is own has checks.
|
||||
* Store plugins inform the cache API about the things they support. Features can be required by a definition.
|
||||
** Data guarantee - Data is guaranteed to exist in the cache once it is set there. It is never cleaned up to free space or because it has not been recently used.
|
||||
** Multiple identifiers - Rather than a single string key, the parts that make up the key are passed as an array.
|
||||
** Native TTL support - When required the store supports native ttl and doesn't require the cache API to manage ttl of things given to the store.
|
||||
|
||||
### Definition
|
||||
_Definitions were not a part of the previous proposal._
|
||||
Definitions are cache definitions. They will be located within a new file for each component/plugin at **db/caches.php**.
|
||||
They can be used to set all of the requirements of a cache instance and are used to ensure that a cache can only be interacted with in the same way no matter where it is being used.
|
||||
It also ensure that caches are easy to use, the config is stored in the definition and the developer using the cache does not need to know anything about it.
|
||||
When getting a loader you can either provide a definition name, or a set or params.
|
||||
* If you provide a definition name then the matching definition is found and used to construct a loader for you.
|
||||
* If you provide params then an adhoc definition is created. It will have defaults and will not have any special requirements or options set.
|
||||
|
||||
Definitions are designed to be used in situations where things are more than basic.
|
||||
|
||||
The following settings are required for a definition:
|
||||
* name - Identifies the definition and must be unique.
|
||||
* mode - Application, session, request.
|
||||
* component - The component associated the definition is associated with.
|
||||
* area - Describes the stuff being cached.
|
||||
|
||||
The following optional settings can also be defined:
|
||||
* requireidentifiers - Any identifiers the definition requires. Must be provided when creating the loader.
|
||||
* requiredataguarantee - If set to true then only stores that support data guarantee will be used.
|
||||
* requiremultipleidentifiers - If set to true then only stores that support multiple identifiers will be used.
|
||||
* overrideclass - If provided this class will be used for the loader. It must extend on of the core loader classes (based upon mode).
|
||||
* overrideclassfile - Included if required when using the overrideclass param.
|
||||
* datasource - If provided this class will be used as a data source for the definition. It must implement the cache_data_source interface.
|
||||
* datasourcefile - Included if required when using the datasource param.
|
||||
* persistent - If set to true the loader will be stored when first created and provided to subsequent requests. More on this later.
|
||||
* ttl - Can be used to set a ttl value for data being set for this cache.
|
||||
* mappingsonly - This definition can only be used if there is a store mapping for it. More on this later.
|
||||
* invalidationevents - An array of events that should trigger this cache to invalidate.
|
||||
|
||||
The persist option.
|
||||
As noted the persist option causes the loader generated for this definition to be stored when first created. Subsequent requests for this definition will be given the original loader instance.
|
||||
Data passed to or retrieved from the loader and its chained loaders gets cached by the instance.
|
||||
This option should be used when you know you will require the loader several times and perhaps in different areas of code.
|
||||
Because it caches key=>value data it avoids the need to re-fetch things from stores after the first request. Its good for performance, bad for memory.
|
||||
It should be used sparingly.
|
||||
|
||||
The mappingsonly option.
|
||||
The administrator of a site can create mappings between stores and definitions. Allowing them to designate stores for specific definitions (caches).
|
||||
Setting this option to true means that the definition can only be used if a mapping has been made for it.
|
||||
Normally if no mappings exist then the default store for the definition mode is used.
|
||||
|
||||
### Data source
|
||||
Data sources allow cache _misses_ (requests for a key that doesn't exist) to be handled and loaded internally.
|
||||
The loader gets used as the last resort if provided and means that code using the cache doesn't need to handle the situation that information isn't cached.
|
||||
They can be specified in a cache definition and must implement the cache_data_source interface.
|
||||
|
||||
### How it all chains together.
|
||||
Consider the following if you can:
|
||||
|
||||
Basic request for information (no frills):
|
||||
|
||||
=> Code calls get
|
||||
=> Loader handles get, passes the request to its store
|
||||
<= Memcache doesn't have the data. sorry.
|
||||
<= Loader returns the result.
|
||||
|= Code couldn't get the data from the cache. It must generate it and then ask the loader to cache it.
|
||||
|
||||
Advanced initial request for information not already cached (has chained stores and data source):
|
||||
|
||||
=> Code calls get
|
||||
=> Loader handles get, passes the request to its store
|
||||
=> Memcache handles request, doesn't have it passes it to the chained store
|
||||
=> File (default store) doesn't have it requests it from the loader
|
||||
=> Data source - makes required db calls, processes information
|
||||
...database calls...
|
||||
...processing and moulding...
|
||||
<= Data source returns the information
|
||||
<= File caches the information on its way back through
|
||||
<= Memcache caches the information on its way back through
|
||||
<= Loader returns the data to the user.
|
||||
|= Code the code now has the data.
|
||||
|
||||
Subsequent request for information:
|
||||
|
||||
=> Code calls get
|
||||
=> Loader handles get, passes the request to its store
|
||||
<= Store returns the data
|
||||
<= Loader returns the data
|
||||
|= Code has the data
|
||||
|
||||
Other internal magic you should be aware of
|
||||
-------------------------------------------
|
||||
The following should fill you in on a bit more of the behind the scenes stuff for the cache API.
|
||||
|
||||
### Helper class
|
||||
There is a helper class called cache_helper which is abstract with static methods.
|
||||
This class handles much of the internal generation and initialisation requirements.
|
||||
In normal use this class will not be needed outside of the API (mostly internal use only)
|
||||
|
||||
### Configuration
|
||||
There are two configuration classes cache_config and cache_config_writer.
|
||||
The reader class is used for every request, the writer is only used when modifying the configuration.
|
||||
Because the cache API is designed to cache database configuration and meta data it must be able to operate prior to database configuration being loaded.
|
||||
To get around this we store the configuration information in a file in the dataroot.
|
||||
The configuration file contains information on the configured store instances, definitions collected from definition files, and mappings.
|
||||
That information is stored and loaded in the same way we work with the lang string files.
|
||||
This means that we use the cache API as soon as it has been included.
|
||||
|
||||
### Invalidation
|
||||
Cache information can be invalidated in two ways.
|
||||
1. pass a definition name and the keys to be invalidated (or none to invalidate the whole cache).
|
||||
2. pass an event and the keys to be invalidated.
|
||||
|
||||
The first method is designed to be used when you have a single known definition you want to invalidate entries from within.
|
||||
The second method is a lot more intensive for the system. There are defined invalidation events that definitions can "subscribe" to (through the definitions invalidationevents option).
|
||||
When you invalidate by event the cache API finds all of the definitions that subscribe to the event, it then loads the stores for each of those definitions and purges the keys from each store.
|
||||
This is obviously a recursive and therefor intense process.
|
||||
|
||||
TODO's and things still to think about
|
||||
--------------------------------------
|
||||
|
||||
1. Definitions don't really need/use the component/area identifiers presently. They may be useful in the future... they may not be.
|
||||
We should consider whether we leave them, or remove them.
|
201
cache/admin.php
vendored
Normal file
201
cache/admin.php
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The administration and management interface for the cache setup and configuration.
|
||||
*
|
||||
* This file is part of Moodle's cache API, affectionately called MUC.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once('../config.php');
|
||||
require_once($CFG->dirroot.'/lib/adminlib.php');
|
||||
require_once($CFG->dirroot.'/cache/locallib.php');
|
||||
require_once($CFG->dirroot.'/cache/forms.php');
|
||||
|
||||
$action = optional_param('action', null, PARAM_ALPHA);
|
||||
|
||||
admin_externalpage_setup('cacheconfig');
|
||||
$context = context_system::instance();
|
||||
|
||||
$stores = cache_administration_helper::get_store_instance_summaries();
|
||||
$plugins = cache_administration_helper::get_store_plugin_summaries();
|
||||
$definitions = cache_administration_helper::get_definition_summaries();
|
||||
$defaultmodestores = cache_administration_helper::get_default_mode_stores();
|
||||
$locks = cache_administration_helper::get_lock_summaries();
|
||||
|
||||
$title = new lang_string('cacheadmin', 'cache');
|
||||
$mform = null;
|
||||
$notification = null;
|
||||
$notifysuccess = true;
|
||||
|
||||
if (!empty($action) && confirm_sesskey()) {
|
||||
switch ($action) {
|
||||
case 'rescandefinitions' : // Rescan definitions.
|
||||
cache_config_writer::update_definitions();
|
||||
redirect($PAGE->url);
|
||||
break;
|
||||
case 'addstore' : // Add the requested store.
|
||||
$plugin = required_param('plugin', PARAM_PLUGIN);
|
||||
$mform = cache_administration_helper::get_add_store_form($plugin);
|
||||
$title = get_string('addstore', 'cache', $plugins[$plugin]['name']);
|
||||
if ($mform->is_cancelled()) {
|
||||
rediect($PAGE->url);
|
||||
} else if ($data = $mform->get_data()) {
|
||||
$config = cache_administration_helper::get_store_configuration_from_data($data);
|
||||
$writer = cache_config_writer::instance();
|
||||
unset($config['lock']);
|
||||
foreach ($writer->get_locks() as $lock => $lockconfig) {
|
||||
if ($lock == $data->lock) {
|
||||
$config['lock'] = $data->lock;
|
||||
}
|
||||
}
|
||||
$writer->add_store_instance($data->name, $data->plugin, $config);
|
||||
redirect($PAGE->url, get_string('addstoresuccess', 'cache', $plugins[$plugin]['name']), 5);
|
||||
}
|
||||
break;
|
||||
case 'editstore' : // Edit the requested store.
|
||||
$plugin = required_param('plugin', PARAM_PLUGIN);
|
||||
$store = required_param('store', PARAM_TEXT);
|
||||
$mform = cache_administration_helper::get_edit_store_form($plugin, $store);
|
||||
$title = get_string('addstore', 'cache', $plugins[$plugin]['name']);
|
||||
if ($mform->is_cancelled()) {
|
||||
rediect($PAGE->url);
|
||||
} else if ($data = $mform->get_data()) {
|
||||
$config = cache_administration_helper::get_store_configuration_from_data($data);
|
||||
$writer = cache_config_writer::instance();
|
||||
unset($config['lock']);
|
||||
foreach ($writer->get_locks() as $lock => $lockconfig) {
|
||||
if ($lock == $data->lock) {
|
||||
$config['lock'] = $data->lock;
|
||||
}
|
||||
}
|
||||
$writer->edit_store_instance($data->name, $data->plugin, $config);
|
||||
redirect($PAGE->url, get_string('editstoresuccess', 'cache', $plugins[$plugin]['name']), 5);
|
||||
}
|
||||
break;
|
||||
case 'deletestore' : // Delete a given store.
|
||||
$store = required_param('store', PARAM_TEXT);
|
||||
$confirm = optional_param('confirm', false, PARAM_BOOL);
|
||||
|
||||
if (!array_key_exists($store, $stores)) {
|
||||
$notifysuccess = false;
|
||||
$notification = get_string('invalidstore');
|
||||
} else if ($stores[$store]['mappings'] > 0) {
|
||||
$notifysuccess = false;
|
||||
$notification = get_string('deletestorehasmappings');
|
||||
}
|
||||
|
||||
if ($notifysuccess) {
|
||||
if (!$confirm) {
|
||||
$title = get_string('confirmstoredeletion', 'cache');
|
||||
$params = array('store' => $store, 'confirm' => 1, 'action' => $action, 'sesskey' => sesskey());
|
||||
$url = new moodle_url($PAGE->url, $params);
|
||||
$button = new single_button($url, get_string('deletestore', 'cache'));
|
||||
|
||||
$PAGE->set_title($title);
|
||||
$PAGE->set_heading($SITE->fullname);
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading($title);
|
||||
$confirmation = get_string('deletestoreconfirmation', 'cache', $stores[$store]['name']);
|
||||
echo $OUTPUT->confirm($confirmation, $button, $PAGE->url);
|
||||
echo $OUTPUT->footer();
|
||||
exit;
|
||||
} else {
|
||||
$writer = cache_config_writer::instance();
|
||||
$writer->delete_store_instance($store);
|
||||
redirect($PAGE->url, get_string('deletestoresuccess', 'cache'), 5);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'editdefinitionmapping' : // Edit definition mappings.
|
||||
$definition = required_param('definition', PARAM_TEXT);
|
||||
$title = get_string('editdefinitionmappings', 'cache', $definition);
|
||||
$mform = new cache_definition_mappings_form($PAGE->url, array('definition' => $definition));
|
||||
if ($mform->is_cancelled()) {
|
||||
redirect($PAGE->url);
|
||||
} else if ($data = $mform->get_data()) {
|
||||
$writer = cache_config_writer::instance();
|
||||
$mappings = array();
|
||||
foreach ($data->mappings as $mapping) {
|
||||
if (!empty($mapping)) {
|
||||
$mappings[] = $mapping;
|
||||
}
|
||||
}
|
||||
$writer->set_definition_mappings($definition, $mappings);
|
||||
redirect($PAGE->url);
|
||||
}
|
||||
break;
|
||||
case 'editmodemappings': // Edit default mode mappings.
|
||||
$mform = new cache_mode_mappings_form(null, $stores);
|
||||
$mform->set_data(array(
|
||||
'mode_'.cache_store::MODE_APPLICATION => key($defaultmodestores[cache_store::MODE_APPLICATION]),
|
||||
'mode_'.cache_store::MODE_SESSION => key($defaultmodestores[cache_store::MODE_SESSION]),
|
||||
'mode_'.cache_store::MODE_REQUEST => key($defaultmodestores[cache_store::MODE_REQUEST]),
|
||||
));
|
||||
if ($mform->is_cancelled()) {
|
||||
redirect($PAGE->url);
|
||||
} else if ($data = $mform->get_data()) {
|
||||
$mappings = array(
|
||||
cache_store::MODE_APPLICATION => array($data->{'mode_'.cache_store::MODE_APPLICATION}),
|
||||
cache_store::MODE_SESSION => array($data->{'mode_'.cache_store::MODE_SESSION}),
|
||||
cache_store::MODE_REQUEST => array($data->{'mode_'.cache_store::MODE_REQUEST}),
|
||||
);
|
||||
$writer = cache_config_writer::instance();
|
||||
$writer->set_mode_mappings($mappings);
|
||||
redirect($PAGE->url);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'purge': // Purge a store cache.
|
||||
$store = required_param('store', PARAM_TEXT);
|
||||
cache_helper::purge_store($store);
|
||||
redirect($PAGE->url, get_string('purgestoresuccess', 'cache'), 5);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$PAGE->set_title($title);
|
||||
$PAGE->set_heading($SITE->fullname);
|
||||
$renderer = $PAGE->get_renderer('core_cache');
|
||||
|
||||
echo $renderer->header();
|
||||
echo $renderer->heading($title);
|
||||
|
||||
if (!is_null($notification)) {
|
||||
echo $renderer->notification($notification, ($notifysuccess)?'notifysuccess' : 'notifyproblem');
|
||||
}
|
||||
|
||||
if ($mform instanceof moodleform) {
|
||||
$mform->display();
|
||||
} else {
|
||||
echo $renderer->store_plugin_summaries($plugins);
|
||||
echo $renderer->store_instance_summariers($stores, $plugins);
|
||||
echo $renderer->definition_summaries($definitions, cache_administration_helper::get_definition_actions($context));
|
||||
echo $renderer->lock_summaries($locks);
|
||||
|
||||
$applicationstore = join(', ', $defaultmodestores[cache_store::MODE_APPLICATION]);
|
||||
$sessionstore = join(', ', $defaultmodestores[cache_store::MODE_SESSION]);
|
||||
$requeststore = join(', ', $defaultmodestores[cache_store::MODE_REQUEST]);
|
||||
$editurl = new moodle_url('/cache/admin.php', array('action' => 'editmodemappings', 'sesskey' => sesskey()));
|
||||
echo $renderer->mode_mappings($applicationstore, $sessionstore, $requeststore, $editurl);
|
||||
}
|
||||
|
||||
echo $renderer->footer();
|
472
cache/classes/config.php
vendored
Normal file
472
cache/classes/config.php
vendored
Normal file
@ -0,0 +1,472 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Cache configuration reader
|
||||
*
|
||||
* This file is part of Moodle's cache API, affectionately called MUC.
|
||||
* It contains the components that are requried in order to use caching.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Cache configuration reader.
|
||||
*
|
||||
* This class is used to interact with the cache's configuration.
|
||||
* The configuration is stored in the Moodle data directory.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_config {
|
||||
|
||||
/**
|
||||
* The configured stores
|
||||
* @var array
|
||||
*/
|
||||
protected $configstores = array();
|
||||
|
||||
/**
|
||||
* The configured mode mappings
|
||||
* @var array
|
||||
*/
|
||||
protected $configmodemappings = array();
|
||||
|
||||
/**
|
||||
* The configured definitions as picked up from cache.php files
|
||||
* @var array
|
||||
*/
|
||||
protected $configdefinitions = array();
|
||||
|
||||
/**
|
||||
* The definition mappings that have been configured.
|
||||
* @var array
|
||||
*/
|
||||
protected $configdefinitionmappings = array();
|
||||
|
||||
/**
|
||||
* An array of configured cache lock instances.
|
||||
* @var array
|
||||
*/
|
||||
protected $configlocks = array();
|
||||
|
||||
/**
|
||||
* Please use cache_config::instance to get an instance of the cache config that is ready to be used.
|
||||
*/
|
||||
public function __construct() {
|
||||
// Nothing to do here but look pretty.
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of the cache_configuration class.
|
||||
*
|
||||
* @return cache_config
|
||||
*/
|
||||
public static function instance() {
|
||||
$factory = cache_factory::instance();
|
||||
return $factory->create_config_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the configuration file exists.
|
||||
*
|
||||
* @return bool True if it exists
|
||||
*/
|
||||
public static function config_file_exists() {
|
||||
// Allow for late static binding.
|
||||
return file_exists(self::get_config_file_path());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expected path to the configuration file.
|
||||
*
|
||||
* @return string The absolute path
|
||||
*/
|
||||
protected static function get_config_file_path() {
|
||||
global $CFG;
|
||||
if (!empty($CFG->altcacheconfigpath)) {
|
||||
$path = $CFG->altcacheconfigpath;
|
||||
if (is_dir($path) && is_writable($path)) {
|
||||
// Its a writable directory, thats fine.
|
||||
return $path.'/cacheconfig.php';
|
||||
} else if (is_writable(dirname($path)) && (!file_exists($path) || is_writable($path))) {
|
||||
// Its a file, either it doesn't exist and the directory is writable or the file exists and is writable.
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
// Return the default location within dataroot.
|
||||
return $CFG->dataroot.'/muc/config.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the configuration file and parses its contents into the expected structure.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function load() {
|
||||
global $CFG;
|
||||
|
||||
$configuration = $this->include_configuration();
|
||||
|
||||
$this->configstores = array();
|
||||
$this->configdefinitions = array();
|
||||
$this->configlocks = array();
|
||||
$this->configmodemappings = array();
|
||||
$this->configdefinitionmappings = array();
|
||||
$this->configlockmappings = array();
|
||||
|
||||
// Filter the lock instances.
|
||||
$defaultlock = null;
|
||||
foreach ($configuration['locks'] as $conf) {
|
||||
if (!is_array($conf)) {
|
||||
// Something is very wrong here.
|
||||
continue;
|
||||
}
|
||||
if (!array_key_exists('name', $conf)) {
|
||||
// Not a valid definition configuration.
|
||||
continue;
|
||||
}
|
||||
$name = $conf['name'];
|
||||
if (array_key_exists($name, $this->configlocks)) {
|
||||
debugging('Duplicate cache lock detected. This should never happen.', DEBUG_DEVELOPER);
|
||||
continue;
|
||||
}
|
||||
$conf['default'] = (!empty($conf['default']));
|
||||
if ($defaultlock === null || $conf['default']) {
|
||||
$defaultlock = $name;
|
||||
}
|
||||
$this->configlocks[$name] = $conf;
|
||||
}
|
||||
|
||||
// Filter the stores.
|
||||
$availableplugins = cache_helper::early_get_cache_plugins();
|
||||
foreach ($configuration['stores'] as $store) {
|
||||
if (!is_array($store) || !array_key_exists('name', $store) || !array_key_exists('plugin', $store)) {
|
||||
// Not a valid instance configuration.
|
||||
debugging('Invalid cache store in config. Missing name or plugin.', DEBUG_DEVELOPER);
|
||||
continue;
|
||||
}
|
||||
$plugin = $store['plugin'];
|
||||
$class = 'cachestore_'.$plugin;
|
||||
$exists = array_key_exists($plugin, $availableplugins);
|
||||
if (!$exists && (!class_exists($class) || !is_subclass_of($class, 'cache_store'))) {
|
||||
// Not a valid plugin, or has been uninstalled, just skip it an carry on.
|
||||
debugging('Invalid cache store in config. Not an available plugin.', DEBUG_DEVELOPER);
|
||||
continue;
|
||||
}
|
||||
$file = $CFG->dirroot.'/cache/stores/'.$plugin.'/lib.php';
|
||||
if (!class_exists($class) && file_exists($file)) {
|
||||
require_once($file);
|
||||
}
|
||||
if (!class_exists($class)) {
|
||||
continue;
|
||||
}
|
||||
if (!array_key_exists('cache_store', class_implements($class))) {
|
||||
continue;
|
||||
}
|
||||
if (!array_key_exists('configuration', $store) || !is_array($store['configuration'])) {
|
||||
$store['configuration'] = array();
|
||||
}
|
||||
$store['class'] = $class;
|
||||
$store['default'] = !empty($store['default']);
|
||||
if (!array_key_exists('lock', $store) || !array_key_exists($store['lock'], $this->configlocks)) {
|
||||
$store['lock'] = $defaultlock;
|
||||
}
|
||||
|
||||
$this->configstores[$store['name']] = $store;
|
||||
}
|
||||
|
||||
// Filter the definitions.
|
||||
foreach ($configuration['definitions'] as $id => $conf) {
|
||||
if (!is_array($conf)) {
|
||||
// Something is very wrong here.
|
||||
continue;
|
||||
}
|
||||
if (!array_key_exists('mode', $conf) || !array_key_exists('component', $conf) || !array_key_exists('area', $conf)) {
|
||||
// Not a valid definition configuration.
|
||||
continue;
|
||||
}
|
||||
if (array_key_exists($id, $this->configdefinitions)) {
|
||||
debugging('Duplicate cache definition detected. This should never happen.', DEBUG_DEVELOPER);
|
||||
continue;
|
||||
}
|
||||
$conf['mode'] = (int)$conf['mode'];
|
||||
if ($conf['mode'] < cache_store::MODE_APPLICATION || $conf['mode'] > cache_store::MODE_REQUEST) {
|
||||
// Invalid cache mode used for the definition.
|
||||
continue;
|
||||
}
|
||||
$this->configdefinitions[$id] = $conf;
|
||||
}
|
||||
|
||||
// Filter the mode mappings.
|
||||
foreach ($configuration['modemappings'] as $mapping) {
|
||||
if (!is_array($mapping) || !array_key_exists('mode', $mapping) || !array_key_exists('store', $mapping)) {
|
||||
// Not a valid mapping configuration.
|
||||
debugging('A cache mode mapping entry is invalid.', DEBUG_DEVELOPER);
|
||||
continue;
|
||||
}
|
||||
if (!array_key_exists($mapping['store'], $this->configstores)) {
|
||||
// Mapped array instance doesn't exist.
|
||||
debugging('A cache mode mapping exists for a mode or store that does not exist.', DEBUG_DEVELOPER);
|
||||
continue;
|
||||
}
|
||||
$mapping['mode'] = (int)$mapping['mode'];
|
||||
if ($mapping['mode'] < 0 || $mapping['mode'] > 4) {
|
||||
// Invalid cache type used for the mapping.
|
||||
continue;
|
||||
}
|
||||
if (!array_key_exists('sort', $mapping)) {
|
||||
$mapping['sort'] = 0;
|
||||
}
|
||||
$this->configmodemappings[] = $mapping;
|
||||
}
|
||||
|
||||
// Filter the definition mappings.
|
||||
foreach ($configuration['definitionmappings'] as $mapping) {
|
||||
if (!is_array($mapping) || !array_key_exists('definition', $mapping) || !array_key_exists('store', $mapping)) {
|
||||
// Not a valid mapping configuration.
|
||||
continue;
|
||||
}
|
||||
if (!array_key_exists($mapping['store'], $this->configstores)) {
|
||||
// Mapped array instance doesn't exist.
|
||||
continue;
|
||||
}
|
||||
if (!array_key_exists($mapping['definition'], $this->configdefinitions)) {
|
||||
// Mapped array instance doesn't exist.
|
||||
continue;
|
||||
}
|
||||
if (!array_key_exists('sort', $mapping)) {
|
||||
$mapping['sort'] = 0;
|
||||
}
|
||||
$this->configdefinitionmappings[] = $mapping;
|
||||
}
|
||||
|
||||
usort($this->configmodemappings, array($this, 'sort_mappings'));
|
||||
usort($this->configdefinitionmappings, array($this, 'sort_mappings'));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes the configuration file and makes sure it contains the expected bits.
|
||||
*
|
||||
* You need to ensure that the config file exists before this is called.
|
||||
*
|
||||
* @return array
|
||||
* @throws cache_exception
|
||||
*/
|
||||
protected function include_configuration() {
|
||||
$configuration = array();
|
||||
$cachefile = self::get_config_file_path();
|
||||
|
||||
if (!file_exists($cachefile)) {
|
||||
throw new cache_exception('Default cache config could not be found. It should have already been created by now.');
|
||||
}
|
||||
include($cachefile);
|
||||
if (!is_array($configuration)) {
|
||||
throw new cache_exception('Invalid cache configuration file');
|
||||
}
|
||||
if (!array_key_exists('stores', $configuration) || !is_array($configuration['stores'])) {
|
||||
$configuration['stores'] = array();
|
||||
}
|
||||
if (!array_key_exists('modemappings', $configuration) || !is_array($configuration['modemappings'])) {
|
||||
$configuration['modemappings'] = array();
|
||||
}
|
||||
if (!array_key_exists('definitions', $configuration) || !is_array($configuration['definitions'])) {
|
||||
$configuration['definitions'] = array();
|
||||
}
|
||||
if (!array_key_exists('definitionmappings', $configuration) || !is_array($configuration['definitionmappings'])) {
|
||||
$configuration['definitionmappings'] = array();
|
||||
}
|
||||
if (!array_key_exists('locks', $configuration) || !is_array($configuration['locks'])) {
|
||||
$configuration['locks'] = array();
|
||||
}
|
||||
|
||||
return $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to sort cache config arrays based upon a sort key.
|
||||
*
|
||||
* Highest number at the top.
|
||||
*
|
||||
* @param array $a
|
||||
* @param array $b
|
||||
* @return int
|
||||
*/
|
||||
protected function sort_mappings(array $a, array $b) {
|
||||
if ($a['sort'] == $b['sort']) {
|
||||
return 0;
|
||||
}
|
||||
return ($a['sort'] < $b['sort']) ? 1 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a definition from the config given its name.
|
||||
*
|
||||
* @param string $id
|
||||
* @return bool
|
||||
*/
|
||||
public function get_definition_by_id($id) {
|
||||
if (array_key_exists($id, $this->configdefinitions)) {
|
||||
return $this->configdefinitions[$id];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the known definitions.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_definitions() {
|
||||
return $this->configdefinitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all of the stores that are suitable for the given mode and requirements.
|
||||
*
|
||||
* @param int $mode One of cache_store::MODE_*
|
||||
* @param int $requirements The requirements of the cache as a binary flag
|
||||
* @return array An array of suitable stores.
|
||||
*/
|
||||
public function get_stores($mode, $requirements = 0) {
|
||||
$stores = array();
|
||||
foreach ($this->configstores as $name => $store) {
|
||||
// If the mode is supported and all of the requirements are provided features.
|
||||
if (($store['modes'] & $mode) && ($store['features'] & $requirements) === $requirements) {
|
||||
$stores[$name] = $store;
|
||||
}
|
||||
}
|
||||
return $stores;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all of the stores that are to be used for the given definition.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return array
|
||||
*/
|
||||
public function get_stores_for_definition(cache_definition $definition) {
|
||||
// Check if MUC has been disabled.
|
||||
if (defined('NO_CACHE_STORES') && NO_CACHE_STORES !== false) {
|
||||
// Yip its been disabled.
|
||||
// To facilitate this we are going to always return an empty array of stores to use.
|
||||
// This will force all cache instances to use the cachestore_dummy.
|
||||
// MUC will still be used essentially so that code using it will still continue to function but because no cache stores
|
||||
// are being used interaction with MUC will be purely based around a static var.
|
||||
return array();
|
||||
}
|
||||
|
||||
$availablestores = $this->get_stores($definition->get_mode(), $definition->get_requirements_bin());
|
||||
$stores = array();
|
||||
$id = $definition->get_id();
|
||||
|
||||
// Now get any mappings and give them priority.
|
||||
foreach ($this->configdefinitionmappings as $mapping) {
|
||||
if ($mapping['definition'] !== $id) {
|
||||
continue;
|
||||
}
|
||||
$storename = $mapping['store'];
|
||||
if (!array_key_exists($storename, $availablestores)) {
|
||||
continue;
|
||||
}
|
||||
if (array_key_exists($storename, $stores)) {
|
||||
$store = $stores[$storename];
|
||||
unset($stores[$storename]);
|
||||
$stores[$storename] = $store;
|
||||
} else {
|
||||
$stores[$storename] = $availablestores[$storename];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($stores) && !$definition->is_for_mappings_only()) {
|
||||
$mode = $definition->get_mode();
|
||||
// Load the default stores.
|
||||
foreach ($this->configmodemappings as $mapping) {
|
||||
if ($mapping['mode'] === $mode && array_key_exists($mapping['store'], $availablestores)) {
|
||||
$store = $availablestores[$mapping['store']];
|
||||
if (empty($store['mappingsonly'])) {
|
||||
$stores[$mapping['store']] = $store;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $stores;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all of the configured stores
|
||||
* @return array
|
||||
*/
|
||||
public function get_all_stores() {
|
||||
return $this->configstores;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all of the configured mode mappings
|
||||
* @return array
|
||||
*/
|
||||
public function get_mode_mappings() {
|
||||
return $this->configmodemappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all of the known definition mappings.
|
||||
* @return array
|
||||
*/
|
||||
public function get_definition_mappings() {
|
||||
return $this->configdefinitionmappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the configured locks.
|
||||
* @return array Array of name => config
|
||||
*/
|
||||
public function get_locks() {
|
||||
return $this->configlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lock store configuration to use with a given store.
|
||||
* @param string $storename
|
||||
* @return array
|
||||
* @throws cache_exception
|
||||
*/
|
||||
public function get_lock_for_store($storename) {
|
||||
if (array_key_exists($storename, $this->configstores)) {
|
||||
if (array_key_exists($this->configstores[$storename]['lock'], $this->configlocks)) {
|
||||
$lock = $this->configstores[$storename]['lock'];
|
||||
return $this->configlocks[$lock];
|
||||
}
|
||||
}
|
||||
foreach ($this->configlocks as $lockconf) {
|
||||
if (!empty($lockconf['default'])) {
|
||||
return $lockconf;
|
||||
}
|
||||
}
|
||||
throw new cache_exception('ex_nodefaultlock');
|
||||
}
|
||||
}
|
715
cache/classes/definition.php
vendored
Normal file
715
cache/classes/definition.php
vendored
Normal file
@ -0,0 +1,715 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Cache definition class
|
||||
*
|
||||
* This file is part of Moodle's cache API, affectionately called MUC.
|
||||
* It contains the components that are requried in order to use caching.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* The cache definition class.
|
||||
*
|
||||
* Cache definitions need to be defined in db/caches.php files.
|
||||
* They can be constructed with the following options.
|
||||
*
|
||||
* Required settings:
|
||||
* + mode
|
||||
* [int] Sets the mode for the definition. Must be one of cache_store::MODE_*
|
||||
*
|
||||
* Optional settings:
|
||||
* + requireidentifiers
|
||||
* [array] An array of identifiers that must be provided to the cache when it is created.
|
||||
* + requiredataguarantee
|
||||
* [bool] If set to true then only stores that can guarantee data will remain available once set will be used.
|
||||
* + requiremultipleidentifiers
|
||||
* [bool] If set to true then only stores that support multiple identifiers will be used.
|
||||
* + requirelockingread
|
||||
* [bool] If set to true then a lock will be gained before reading from the cache store. It is recommended not to use
|
||||
* this setting unless 100% absolutely positively required. Remember 99.9% of caches will NOT need this setting.
|
||||
* This setting will only be used for application caches presently.
|
||||
* + requirelockingwrite
|
||||
* [bool] If set to true then a lock will be gained before writing to the cache store. As above this is not recommended
|
||||
* unless truly needed. Please think about the order of your code and deal with race conditions there first.
|
||||
* This setting will only be used for application caches presently.
|
||||
* + maxsize
|
||||
* [int] If set this will be used as the maximum number of entries within the cache store for this definition.
|
||||
* Its important to note that cache stores don't actually have to acknowledge this setting or maintain it as a hard limit.
|
||||
* + overrideclass
|
||||
* [string] A class to use as the loader for this cache. This is an advanced setting and will allow the developer of the
|
||||
* definition to take 100% control of the caching solution.
|
||||
* Any class used here must inherit the cache_loader interface and must extend default cache loader for the mode they are
|
||||
* using.
|
||||
* + overrideclassfile
|
||||
* [string] Suplements the above setting indicated the file containing the class to be used. This file is included when
|
||||
* required.
|
||||
* + datasource
|
||||
* [string] A class to use as the data loader for this definition.
|
||||
* Any class used here must inherit the cache_data_loader interface.
|
||||
* + datasourcefile
|
||||
* [string] Suplements the above setting indicated the file containing the class to be used. This file is included when
|
||||
* required.
|
||||
* + persistent
|
||||
* [bool] This setting does two important things. First it tells the cache API to only instantiate the cache structure for
|
||||
* this definition once, further requests will be given the original instance.
|
||||
* Second the cache loader will keep an array of the items set and retrieved to the cache during the request.
|
||||
* This has several advantages including better performance without needing to start passing the cache instance between
|
||||
* function calls, the downside is that the cache instance + the items used stay within memory.
|
||||
* Consider using this setting when you know that there are going to be many calls to the cache for the same information
|
||||
* or when you are converting existing code to the cache and need to access the cache within functions but don't want
|
||||
* to add it as an argument to the function.
|
||||
* + persistentmaxsize
|
||||
* [int] This supplements the above setting by limiting the number of items in the caches persistent array of items.
|
||||
* Tweaking this setting lower will allow you to minimise the memory implications above while hopefully still managing to
|
||||
* offset calls to the cache store.
|
||||
* + ttl
|
||||
* [int] A time to live for the data (in seconds). It is strongly recommended that you don't make use of this and
|
||||
* instead try to create an event driven invalidation system.
|
||||
* Not all cache stores will support this natively and there are undesired performance impacts if the cache store does not.
|
||||
* + mappingsonly
|
||||
* [bool] If set to true only the mapped cache store(s) will be used and the default mode store will not. This is a super
|
||||
* advanced setting and should not be used unless absolutely required. It allows you to avoid the default stores for one
|
||||
* reason or another.
|
||||
* + invalidationevents
|
||||
* [array] An array of events that should cause this cache to invalidate some or all of the items within it.
|
||||
*
|
||||
* For examples take a look at lib/db/caches.php
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_definition {
|
||||
|
||||
/**
|
||||
* The identifier for the definition
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The mode for the defintion. One of cache_store::MODE_*
|
||||
* @var int
|
||||
*/
|
||||
protected $mode;
|
||||
|
||||
/**
|
||||
* The component this definition is associated with.
|
||||
* @var string
|
||||
*/
|
||||
protected $component;
|
||||
|
||||
/**
|
||||
* The area this definition is associated with.
|
||||
* @var string
|
||||
*/
|
||||
protected $area;
|
||||
|
||||
/**
|
||||
* An array of identifiers that must be provided when the definition is used to create a cache.
|
||||
* @var array
|
||||
*/
|
||||
protected $requireidentifiers = array();
|
||||
|
||||
/**
|
||||
* If set to true then only stores that guarantee data may be used with this definition.
|
||||
* @var bool
|
||||
*/
|
||||
protected $requiredataguarantee = false;
|
||||
|
||||
/**
|
||||
* If set to true then only stores that support multple identifiers may be used with this definition.
|
||||
* @var bool
|
||||
*/
|
||||
protected $requiremultipleidentifiers = false;
|
||||
|
||||
/**
|
||||
* If set to true then we know that this definition requires the locking functionality.
|
||||
* This gets set during construction based upon the settings requirelockingread and requirelockingwrite.
|
||||
* @var bool
|
||||
*/
|
||||
protected $requirelocking = false;
|
||||
|
||||
/**
|
||||
* Set to true if this definition requires read locking.
|
||||
* @var bool
|
||||
*/
|
||||
protected $requirelockingread = false;
|
||||
|
||||
/**
|
||||
* Gets set to true if this definition requires write locking.
|
||||
* @var bool
|
||||
*/
|
||||
protected $requirelockingwrite = false;
|
||||
|
||||
/**
|
||||
* Sets the maximum number of items that can exist in the cache.
|
||||
* Please note this isn't a hard limit, and doesn't need to be enforced by the caches. They can choose to do so optionally.
|
||||
* @var int
|
||||
*/
|
||||
protected $maxsize = null;
|
||||
|
||||
/**
|
||||
* The class to use as the cache loader for this definition.
|
||||
* @var string
|
||||
*/
|
||||
protected $overrideclass = null;
|
||||
|
||||
/**
|
||||
* The file in which the override class exists. This will be included if required.
|
||||
* @var string Absolute path
|
||||
*/
|
||||
protected $overrideclassfile = null;
|
||||
|
||||
/**
|
||||
* The data source class to use with this definition.
|
||||
* @var string
|
||||
*/
|
||||
protected $datasource = null;
|
||||
|
||||
/**
|
||||
* The file in which the data source class exists. This will be included if required.
|
||||
* @var string
|
||||
*/
|
||||
protected $datasourcefile = null;
|
||||
|
||||
/**
|
||||
* The data source class aggregate to use. This is a super advanced setting.
|
||||
* @var string
|
||||
*/
|
||||
protected $datasourceaggregate = null;
|
||||
|
||||
/**
|
||||
* Set to true if the definitions cache should be persistent
|
||||
* @var bool
|
||||
*/
|
||||
protected $persistent = false;
|
||||
|
||||
/**
|
||||
* The persistent item array max size.
|
||||
* @var int
|
||||
*/
|
||||
protected $persistentmaxsize = false;
|
||||
|
||||
/**
|
||||
* The TTL for data in this cache. Please don't use this, instead use event driven invalidation.
|
||||
* @var int
|
||||
*/
|
||||
protected $ttl = 0;
|
||||
|
||||
/**
|
||||
* Set to true if this cache should only use mapped cache stores and not the default mode cache store.
|
||||
* @var bool
|
||||
*/
|
||||
protected $mappingsonly = false;
|
||||
|
||||
/**
|
||||
* An array of events that should cause this cache to invalidate.
|
||||
* @var array
|
||||
*/
|
||||
protected $invalidationevents = array();
|
||||
|
||||
/**
|
||||
* An array of identifiers provided to this cache when it was initialised.
|
||||
* @var array
|
||||
*/
|
||||
protected $identifiers = array();
|
||||
|
||||
/**
|
||||
* Key prefix for use with single key cache stores
|
||||
* @var string
|
||||
*/
|
||||
protected $keyprefixsingle = null;
|
||||
|
||||
/**
|
||||
* Key prefix to use with cache stores that support multi keys.
|
||||
* @var array
|
||||
*/
|
||||
protected $keyprefixmulti = null;
|
||||
|
||||
/**
|
||||
* A hash identifier of this definition.
|
||||
* @var string
|
||||
*/
|
||||
protected $definitionhash = null;
|
||||
|
||||
/**
|
||||
* Creates a cache definition given a definition from the cache configuration or from a caches.php file.
|
||||
*
|
||||
* @param string $id
|
||||
* @param array $definition
|
||||
* @param string $datasourceaggregate
|
||||
* @return cache_definition
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public static function load($id, array $definition, $datasourceaggregate = null) {
|
||||
if (!array_key_exists('mode', $definition)) {
|
||||
throw new coding_exception('You must provide a mode when creating a cache definition');
|
||||
}
|
||||
if (!array_key_exists('component', $definition)) {
|
||||
throw new coding_exception('You must provide a mode when creating a cache definition');
|
||||
}
|
||||
if (!array_key_exists('area', $definition)) {
|
||||
throw new coding_exception('You must provide a mode when creating a cache definition');
|
||||
}
|
||||
$mode = (int)$definition['mode'];
|
||||
$component = (string)$definition['component'];
|
||||
$area = (string)$definition['area'];
|
||||
|
||||
// Set the defaults.
|
||||
$requireidentifiers = array();
|
||||
$requiredataguarantee = false;
|
||||
$requiremultipleidentifiers = false;
|
||||
$requirelockingread = false;
|
||||
$requirelockingwrite = false;
|
||||
$maxsize = null;
|
||||
$overrideclass = null;
|
||||
$overrideclassfile = null;
|
||||
$datasource = null;
|
||||
$datasourcefile = null;
|
||||
$persistent = false;
|
||||
$persistentmaxsize = false;
|
||||
$ttl = 0;
|
||||
$mappingsonly = false;
|
||||
$invalidationevents = array();
|
||||
|
||||
if (array_key_exists('requireidentifiers', $definition)) {
|
||||
$requireidentifiers = (array)$definition['requireidentifiers'];
|
||||
}
|
||||
if (array_key_exists('requiredataguarantee', $definition)) {
|
||||
$requiredataguarantee = (bool)$definition['requiredataguarantee'];
|
||||
}
|
||||
if (array_key_exists('requiremultipleidentifiers', $definition)) {
|
||||
$requiremultipleidentifiers = (bool)$definition['requiremultipleidentifiers'];
|
||||
}
|
||||
|
||||
if (array_key_exists('requirelockingread', $definition)) {
|
||||
$requirelockingread = (bool)$definition['requirelockingread'];
|
||||
}
|
||||
if (array_key_exists('requirelockingwrite', $definition)) {
|
||||
$requirelockingwrite = (bool)$definition['requirelockingwrite'];
|
||||
}
|
||||
$requirelocking = $requirelockingwrite || $requirelockingread;
|
||||
|
||||
if (array_key_exists('maxsize', $definition)) {
|
||||
$maxsize = (int)$definition['maxsize'];
|
||||
}
|
||||
|
||||
if (array_key_exists('overrideclass', $definition)) {
|
||||
$overrideclass = $definition['overrideclass'];
|
||||
}
|
||||
if (array_key_exists('overrideclassfile', $definition)) {
|
||||
$overrideclassfile = $definition['overrideclassfile'];
|
||||
}
|
||||
|
||||
if (array_key_exists('datasource', $definition)) {
|
||||
$datasource = $definition['datasource'];
|
||||
}
|
||||
if (array_key_exists('datasourcefile', $definition)) {
|
||||
$datasourcefile = $definition['datasourcefile'];
|
||||
}
|
||||
|
||||
if (array_key_exists('persistent', $definition)) {
|
||||
$persistent = (bool)$definition['persistent'];
|
||||
}
|
||||
if (array_key_exists('persistentmaxsize', $definition)) {
|
||||
$persistentmaxsize = (int)$definition['persistentmaxsize'];
|
||||
}
|
||||
if (array_key_exists('ttl', $definition)) {
|
||||
$ttl = (int)$definition['ttl'];
|
||||
}
|
||||
if (array_key_exists('mappingsonly', $definition)) {
|
||||
$mappingsonly = (bool)$definition['mappingsonly'];
|
||||
}
|
||||
if (array_key_exists('invalidationevents', $definition)) {
|
||||
$invalidationevents = (array)$definition['invalidationevents'];
|
||||
}
|
||||
|
||||
if (!is_null($overrideclass)) {
|
||||
if (!is_null($overrideclassfile)) {
|
||||
if (!file_exists($overrideclassfile)) {
|
||||
throw new coding_exception('The override class file does not exist.');
|
||||
}
|
||||
require_once($overrideclassfile);
|
||||
}
|
||||
if (!class_exists($overrideclass)) {
|
||||
throw new coding_exception('The override class does not exist.');
|
||||
}
|
||||
|
||||
// Make sure that the provided class extends the default class for the mode.
|
||||
if (get_parent_class($overrideclass) !== cache_helper::get_class_for_mode($mode)) {
|
||||
throw new coding_exception('The override class does not immediately extend the relevant cache class.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_null($datasource)) {
|
||||
if (!is_null($datasourcefile)) {
|
||||
if (!file_exists($datasourcefile)) {
|
||||
throw new coding_exception('The override class file does not exist.');
|
||||
}
|
||||
require_once($datasourcefile);
|
||||
}
|
||||
if (!class_exists($datasource)) {
|
||||
throw new coding_exception('The override class does not exist.');
|
||||
}
|
||||
if (!array_key_exists('cache_data_source', class_implements($datasource))) {
|
||||
throw new coding_exception('Cache data source classes must implement the cache_data_source interface');
|
||||
}
|
||||
}
|
||||
|
||||
$cachedefinition = new cache_definition();
|
||||
$cachedefinition->id = $id;
|
||||
$cachedefinition->mode = $mode;
|
||||
$cachedefinition->component = $component;
|
||||
$cachedefinition->area = $area;
|
||||
$cachedefinition->requireidentifiers = $requireidentifiers;
|
||||
$cachedefinition->requiredataguarantee = $requiredataguarantee;
|
||||
$cachedefinition->requiremultipleidentifiers = $requiremultipleidentifiers;
|
||||
$cachedefinition->requirelocking = $requirelocking;
|
||||
$cachedefinition->requirelockingread = $requirelockingread;
|
||||
$cachedefinition->requirelockingwrite = $requirelockingwrite;
|
||||
$cachedefinition->maxsize = $maxsize;
|
||||
$cachedefinition->overrideclass = $overrideclass;
|
||||
$cachedefinition->overrideclassfile = $overrideclassfile;
|
||||
$cachedefinition->datasource = $datasource;
|
||||
$cachedefinition->datasourcefile = $datasourcefile;
|
||||
$cachedefinition->datasourceaggregate = $datasourceaggregate;
|
||||
$cachedefinition->persistent = $persistent;
|
||||
$cachedefinition->persistentmaxsize = $persistentmaxsize;
|
||||
$cachedefinition->ttl = $ttl;
|
||||
$cachedefinition->mappingsonly = $mappingsonly;
|
||||
$cachedefinition->invalidationevents = $invalidationevents;
|
||||
|
||||
return $cachedefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ah-hoc cache definition given the required params.
|
||||
*
|
||||
* Please note that when using an adhoc definition you cannot set any of the optional params.
|
||||
* This is because we cannot guarantee consistent access and we don't want to mislead people into thinking that.
|
||||
*
|
||||
* @param int $mode One of cache_store::MODE_*
|
||||
* @param string $component The component this definition relates to.
|
||||
* @param string $area The area this definition relates to.
|
||||
* @param string $overrideclass The class to use as the loader.
|
||||
* @param bool $persistent If this cache should be persistent.
|
||||
* @return cache_application|cache_session|cache_request
|
||||
*/
|
||||
public static function load_adhoc($mode, $component, $area, $overrideclass = null, $persistent = false) {
|
||||
$id = 'adhoc/'.$component.'_'.$area;
|
||||
$definition = array(
|
||||
'mode' => $mode,
|
||||
'component' => $component,
|
||||
'area' => $area,
|
||||
'persistent' => $persistent
|
||||
);
|
||||
if (!is_null($overrideclass)) {
|
||||
$definition['overrideclass'] = $overrideclass;
|
||||
}
|
||||
return self::load($id, $definition, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache loader class that should be used for this definition.
|
||||
* @return string
|
||||
*/
|
||||
public function get_cache_class() {
|
||||
if (!is_null($this->overrideclass)) {
|
||||
return $this->overrideclass;
|
||||
}
|
||||
return cache_helper::get_class_for_mode($this->mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of this definition.
|
||||
* @return string
|
||||
*/
|
||||
public function get_id() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name for this definition
|
||||
* @return string
|
||||
*/
|
||||
public function get_name() {
|
||||
$identifier = 'cachedef_'.clean_param($this->area, PARAM_STRINGID);
|
||||
$component = $this->component;
|
||||
if ($component === 'core') {
|
||||
$component = 'cache';
|
||||
}
|
||||
return new lang_string($identifier, $component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mode of this definition
|
||||
* @return int One more cache_store::MODE_
|
||||
*/
|
||||
public function get_mode() {
|
||||
return $this->mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the area this definition is associated with.
|
||||
* @return string
|
||||
*/
|
||||
public function get_area() {
|
||||
return $this->area;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the component this definition is associated with.
|
||||
* @return string
|
||||
*/
|
||||
public function get_component() {
|
||||
return $this->component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifiers that are being used for this definition.
|
||||
* @return array
|
||||
*/
|
||||
public function get_identifiers() {
|
||||
return $this->identifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ttl in seconds for this definition if there is one, or null if not.
|
||||
* @return int|null
|
||||
*/
|
||||
public function get_ttl() {
|
||||
return $this->ttl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of items allowed in this cache.
|
||||
* @return int
|
||||
*/
|
||||
public function get_maxsize() {
|
||||
return $this->maxsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this definition should only be used with mappings.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_for_mappings_only() {
|
||||
return $this->mappingsonly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this definition requires a data guarantee from the cache stores being used.
|
||||
* @return bool
|
||||
*/
|
||||
public function require_data_guarantee() {
|
||||
return $this->requiredataguarantee;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this definition requires that the cache stores support multiple identifiers
|
||||
* @return bool
|
||||
*/
|
||||
public function require_multiple_identifiers() {
|
||||
return $this->requiremultipleidentifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this definition requires locking functionality. Either read or write locking.
|
||||
* @return bool
|
||||
*/
|
||||
public function require_locking() {
|
||||
return $this->requirelocking;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this definition requires read locking.
|
||||
* @return bool
|
||||
*/
|
||||
public function require_locking_read() {
|
||||
return $this->requirelockingread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this definition requires write locking.
|
||||
* @return bool
|
||||
*/
|
||||
public function require_locking_write() {
|
||||
return $this->requirelockingwrite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this definition has an associated data source.
|
||||
* @return bool
|
||||
*/
|
||||
public function has_data_source() {
|
||||
return !is_null($this->datasource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the data source class used for this definition.
|
||||
*
|
||||
* @return cache_data_source
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function get_data_source() {
|
||||
if (!$this->has_data_source()) {
|
||||
throw new coding_exception('This cache does not use a datasource.');
|
||||
}
|
||||
return forward_static_call(array($this->datasource, 'get_instance_for_cache'), $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifiers for this definition, or updates them if they have already been set.
|
||||
*
|
||||
* @param array $identifiers
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function set_identifiers(array $identifiers = array()) {
|
||||
foreach ($this->requireidentifiers as $identifier) {
|
||||
if (!array_key_exists($identifier, $identifiers)) {
|
||||
throw new coding_exception('Identifier required for cache has not been provided: '.$identifier);
|
||||
}
|
||||
}
|
||||
foreach ($identifiers as $name => $value) {
|
||||
$this->identifiers[$name] = (string)$value;
|
||||
}
|
||||
// Reset the key prefix's they need updating now.
|
||||
$this->keyprefixsingle = null;
|
||||
$this->keyprefixmulti = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the requirements of this definition as a binary flag.
|
||||
* @return int
|
||||
*/
|
||||
public function get_requirements_bin() {
|
||||
$requires = 0;
|
||||
if ($this->require_data_guarantee()) {
|
||||
$requires += cache_store::SUPPORTS_DATA_GUARANTEE;
|
||||
}
|
||||
if ($this->require_multiple_identifiers()) {
|
||||
$requires += cache_store::SUPPORTS_MULTIPLE_IDENTIFIERS;
|
||||
}
|
||||
return $requires;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this definitions cache should be made persistent.
|
||||
* @return bool
|
||||
*/
|
||||
public function should_be_persistent() {
|
||||
return $this->persistent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the max size for the persistent item array in the cache.
|
||||
* @return int
|
||||
*/
|
||||
public function get_persistent_max_size() {
|
||||
return $this->persistentmaxsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a hash of this definition and returns it.
|
||||
* @return string
|
||||
*/
|
||||
public function generate_definition_hash() {
|
||||
if ($this->definitionhash === null) {
|
||||
$this->definitionhash = md5("{$this->mode} {$this->component} {$this->area}");
|
||||
}
|
||||
return $this->definitionhash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a single key prefix for this definition
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generate_single_key_prefix() {
|
||||
if ($this->keyprefixsingle === null) {
|
||||
$this->keyprefixsingle = $this->mode.'/'.$this->mode;
|
||||
$identifiers = $this->get_identifiers();
|
||||
if ($identifiers) {
|
||||
foreach ($identifiers as $key => $value) {
|
||||
$this->keyprefixsingle .= '/'.$key.'='.$value;
|
||||
}
|
||||
}
|
||||
$this->keyprefixsingle = md5($this->keyprefixsingle);
|
||||
}
|
||||
return $this->keyprefixsingle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a multi key prefix for this definition
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function generate_multi_key_parts() {
|
||||
if ($this->keyprefixmulti === null) {
|
||||
$this->keyprefixmulti = array(
|
||||
'mode' => $this->mode,
|
||||
'component' => $this->component,
|
||||
'area' => $this->area,
|
||||
);
|
||||
if (!empty($this->identifiers)) {
|
||||
$identifiers = array();
|
||||
foreach ($this->identifiers as $key => $value) {
|
||||
$identifiers[] = htmlentities($key).'='.htmlentities($value);
|
||||
}
|
||||
$this->keyprefixmulti['identifiers'] = join('&', $identifiers);
|
||||
}
|
||||
}
|
||||
return $this->keyprefixmulti;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this definition should invalidate on the given event.
|
||||
*
|
||||
* @param string $event
|
||||
* @return bool True if the definition should invalidate on the event. False otherwise.
|
||||
*/
|
||||
public function invalidates_on_event($event) {
|
||||
return (in_array($event, $this->invalidationevents));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the definition has any invalidation events.
|
||||
*
|
||||
* @return bool True if it does, false otherwise
|
||||
*/
|
||||
public function has_invalidation_events() {
|
||||
return !empty($this->invalidationevents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all of the invalidation events for this definition.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_invalidation_events() {
|
||||
return $this->invalidationevents;
|
||||
}
|
||||
}
|
277
cache/classes/dummystore.php
vendored
Normal file
277
cache/classes/dummystore.php
vendored
Normal file
@ -0,0 +1,277 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Cache dummy store.
|
||||
*
|
||||
* This dummy store is used when a load has no other stores that it can make use of.
|
||||
* This shouldn't happen in normal operation... I think.
|
||||
*
|
||||
* This file is part of Moodle's cache API, affectionately called MUC.
|
||||
* It contains the components that are requried in order to use caching.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* The cache dummy store.
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cachestore_dummy implements cache_store {
|
||||
|
||||
/**
|
||||
* The name of this store.
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* Gets set to true if this store is going to persist data.
|
||||
* This happens when the definition doesn't require it as the loader will not be persisting information and something has to.
|
||||
* @var bool
|
||||
*/
|
||||
protected $persist = false;
|
||||
|
||||
/**
|
||||
* The persistent store array
|
||||
* @var array
|
||||
*/
|
||||
protected $store = array();
|
||||
|
||||
/**
|
||||
* Constructs a dummy store instance.
|
||||
* @param string $name
|
||||
* @param array $configuration
|
||||
*/
|
||||
public function __construct($name = 'Dummy store', array $configuration = array()) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this store plugin is usable.
|
||||
* @return bool
|
||||
*/
|
||||
public static function are_requirements_met() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user can add an instance.
|
||||
* @return bool
|
||||
*/
|
||||
public static function can_add_instance() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supported features.
|
||||
* @param array $configuration
|
||||
* @return int
|
||||
*/
|
||||
public static function get_supported_features(array $configuration = array()) {
|
||||
return self::SUPPORTS_NATIVE_TTL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supported mode.
|
||||
* @param array $configuration
|
||||
* @return int
|
||||
*/
|
||||
public static function get_supported_modes(array $configuration = array()) {
|
||||
return self::MODE_APPLICATION + self::MODE_REQUEST + self::MODE_SESSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the store instance for a definition.
|
||||
* @param cache_definition $definition
|
||||
*/
|
||||
public function initialise(cache_definition $definition) {
|
||||
// If the definition isn't persistent then we need to be persistent here.
|
||||
$this->persist = !$definition->should_be_persistent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this has been initialised.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_initialised() {
|
||||
return (!empty($this->definition));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this is ready.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_ready() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true the given mode is supported.
|
||||
* @param int $mode
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_supported_mode($mode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this store supports data guarantee.
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_data_guarantee() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this store supports multiple identifiers.
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_multiple_indentifiers() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this store supports a native ttl.
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_native_ttl() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data for the given key
|
||||
* @param string $key
|
||||
* @return string|false
|
||||
*/
|
||||
public function get($key) {
|
||||
if ($this->persist && array_key_exists($key, $this->store)) {
|
||||
return $this->store[$key];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets' the values for many keys
|
||||
* @param array $keys
|
||||
* @return bool
|
||||
*/
|
||||
public function get_many($keys) {
|
||||
$return = array();
|
||||
foreach ($keys as $key) {
|
||||
if ($this->persist && array_key_exists($key, $this->store)) {
|
||||
$return[$key] = $this->store[$key];
|
||||
} else {
|
||||
$return[$key] = false;
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an item in the cache
|
||||
* @param string $key
|
||||
* @param mixed $data
|
||||
* @return bool
|
||||
*/
|
||||
public function set($key, $data) {
|
||||
if ($this->persist) {
|
||||
$this->store[$key] = $data;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets many items in the cache
|
||||
* @param array $keyvaluearray
|
||||
* @return int
|
||||
*/
|
||||
public function set_many(array $keyvaluearray) {
|
||||
if ($this->persist) {
|
||||
foreach ($keyvaluearray as $pair) {
|
||||
$this->store[$pair['key']] = $pair['value'];
|
||||
}
|
||||
return count($keyvaluearray);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an item from the cache
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($key) {
|
||||
unset($this->store[$key]);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Deletes many items from the cache
|
||||
* @param array $keys
|
||||
* @return bool
|
||||
*/
|
||||
public function delete_many(array $keys) {
|
||||
if ($this->persist) {
|
||||
foreach ($keys as $key) {
|
||||
unset($this->store[$key]);
|
||||
}
|
||||
}
|
||||
return count($keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all of the items from the cache.
|
||||
* @return bool
|
||||
*/
|
||||
public function purge() {
|
||||
$this->store = array();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs any necessary clean up when the store instance is being deleted.
|
||||
*/
|
||||
public function cleanup() {
|
||||
$this->purge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an instance of the cache store that can be used for testing.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return false
|
||||
*/
|
||||
public static function initialise_test_instance(cache_definition $definition) {
|
||||
$cache = new cachestore_dummy('Dummy store test');
|
||||
$cache->initialise($definition);
|
||||
return $cache;;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this instance.
|
||||
* @return string
|
||||
*/
|
||||
public function my_name() {
|
||||
return $this->name;
|
||||
}
|
||||
}
|
340
cache/classes/factory.php
vendored
Normal file
340
cache/classes/factory.php
vendored
Normal file
@ -0,0 +1,340 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This file contains the cache factory class.
|
||||
*
|
||||
* This file is part of Moodle's cache API, affectionately called MUC.
|
||||
* It contains the components that are requried in order to use caching.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* The cache factory class.
|
||||
*
|
||||
* This factory class is important because it stores instances of objects used by the cache API and returns them upon requests.
|
||||
* This allows us to both reuse objects saving on overhead, and gives us an easy place to "reset" the cache API in situations that
|
||||
* we need such as unit testing.
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_factory {
|
||||
|
||||
/**
|
||||
* An instance of the cache_factory class created upon the first request.
|
||||
* @var cache_factory
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
/**
|
||||
* An array containing caches created for definitions
|
||||
* @var array
|
||||
*/
|
||||
protected $cachesfromdefinitions = array();
|
||||
|
||||
/**
|
||||
* Array of caches created by parameters, ad-hoc definitions will have been used.
|
||||
* @var array
|
||||
*/
|
||||
protected $cachesfromparams = array();
|
||||
|
||||
/**
|
||||
* An array of instantiated stores.
|
||||
* @var array
|
||||
*/
|
||||
protected $stores = array();
|
||||
|
||||
/**
|
||||
* An array of configuration instances
|
||||
* @var array
|
||||
*/
|
||||
protected $configs = array();
|
||||
|
||||
/**
|
||||
* An array of initialised definitions
|
||||
* @var array
|
||||
*/
|
||||
protected $definitions = array();
|
||||
|
||||
/**
|
||||
* An array of lock plugins.
|
||||
* @var array
|
||||
*/
|
||||
protected $lockplugins = null;
|
||||
|
||||
/**
|
||||
* Returns an instance of the cache_factor method.
|
||||
*
|
||||
* @param bool $forcereload If set to true a new cache_factory instance will be created and used.
|
||||
* @return cache_factory
|
||||
*/
|
||||
public static function instance($forcereload = false) {
|
||||
if ($forcereload || self::$instance === null) {
|
||||
self::$instance = new cache_factory();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected constructor, please use the static instance method.
|
||||
*/
|
||||
protected function __construct() {
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the arrays containing instantiated caches, stores, and config instances.
|
||||
*/
|
||||
public static function reset() {
|
||||
$factory = self::instance();
|
||||
$factory->cachesfromdefinitions = array();
|
||||
$factory->cachesfromparams = array();
|
||||
$factory->stores = array();
|
||||
$factory->configs = array();
|
||||
$factory->definitions = array();
|
||||
$factory->lockplugins = null; // MUST be null in order to force its regeneration.
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cache object given the parameters for a definition.
|
||||
*
|
||||
* If a cache has already been created for the given definition then that cache instance will be returned.
|
||||
*
|
||||
* @param string $component
|
||||
* @param string $area
|
||||
* @param array $identifiers
|
||||
* @param string $aggregate
|
||||
* @return cache_application|cache_session|cache_request
|
||||
*/
|
||||
public function create_cache_from_definition($component, $area, array $identifiers = array(), $aggregate = null) {
|
||||
$definitionname = $component.'/'.$area;
|
||||
if (array_key_exists($definitionname, $this->cachesfromdefinitions)) {
|
||||
$cache = $this->cachesfromdefinitions[$definitionname];
|
||||
$cache->set_identifiers($identifiers);
|
||||
return $cache;
|
||||
}
|
||||
$definition = $this->create_definition($component, $area, $aggregate);
|
||||
$definition->set_identifiers($identifiers);
|
||||
$cache = $this->create_cache($definition, $identifiers);
|
||||
if ($definition->should_be_persistent()) {
|
||||
$this->cachesfromdefinitions[$definitionname] = $cache;
|
||||
}
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ad-hoc cache from the given param.
|
||||
*
|
||||
* If a cache has already been created using the same params then that cache instance will be returned.
|
||||
*
|
||||
* @param int $mode
|
||||
* @param string $component
|
||||
* @param string $area
|
||||
* @param array $identifiers
|
||||
* @param bool $persistent
|
||||
* @return cache_application|cache_session|cache_request
|
||||
*/
|
||||
public function create_cache_from_params($mode, $component, $area, array $identifiers = array(), $persistent = false) {
|
||||
$key = "{$mode}_{$component}_{$area}";
|
||||
if (array_key_exists($key, $this->cachesfromparams)) {
|
||||
return $this->cachesfromparams[$key];
|
||||
}
|
||||
// Get the class. Note this is a late static binding so we need to use get_called_class.
|
||||
$definition = cache_definition::load_adhoc($mode, $component, $area, null, $persistent);
|
||||
$definition->set_identifiers($identifiers);
|
||||
$cache = $this->create_cache($definition, $identifiers);
|
||||
if ($definition->should_be_persistent()) {
|
||||
$cache->persist = true;
|
||||
$cache->persistcache = array();
|
||||
$this->cachesfromparams[$key] = $cache;
|
||||
}
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common public method to create a cache instance given a definition.
|
||||
*
|
||||
* This is used by the static make methods.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return cache_application|cache_session|cache_store
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function create_cache(cache_definition $definition) {
|
||||
$class = $definition->get_cache_class();
|
||||
$stores = cache_helper::get_cache_stores($definition);
|
||||
if (count($stores) === 0) {
|
||||
// Hmm no stores, better provide a dummy store to mimick functionality. The dev will be none the wiser.
|
||||
$stores[] = $this->create_dummy_store($definition);
|
||||
}
|
||||
$loader = null;
|
||||
if ($definition->has_data_source()) {
|
||||
$loader = $definition->get_data_source();
|
||||
}
|
||||
while (($store = array_pop($stores)) !== null) {
|
||||
$loader = new $class($definition, $store, $loader);
|
||||
}
|
||||
return $loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a store instance given its name and configuration.
|
||||
*
|
||||
* If the store has already been instantiated then the original objetc will be returned. (reused)
|
||||
*
|
||||
* @param string $name The name of the store (must be unique remember)
|
||||
* @param array $details
|
||||
* @param cache_definition $definition The definition to instantiate it for.
|
||||
* @return boolean
|
||||
*/
|
||||
public function create_store_from_config($name, array $details, cache_definition $definition) {
|
||||
if (!array_key_exists($name, $this->stores)) {
|
||||
// Properties: name, plugin, configuration, class.
|
||||
$class = $details['class'];
|
||||
$store = new $class($details['name'], $details['configuration']);
|
||||
$this->stores[$name] = $store;
|
||||
}
|
||||
$store = $this->stores[$name];
|
||||
if (!$store->is_ready() || !$store->is_supported_mode($definition->get_mode())) {
|
||||
return false;
|
||||
}
|
||||
$store = clone($this->stores[$name]);
|
||||
$store->initialise($definition);
|
||||
return $store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cache config instance with the ability to write if required.
|
||||
*
|
||||
* @param bool $writer If set to true an instance that can update the configuration will be returned.
|
||||
* @return cache_config|cache_config_writer
|
||||
*/
|
||||
public function create_config_instance($writer = false) {
|
||||
global $CFG;
|
||||
|
||||
// Check if we need to create a config file with defaults.
|
||||
$needtocreate = !cache_config::config_file_exists();
|
||||
|
||||
// The class to use.
|
||||
$class = 'cache_config';
|
||||
if ($writer || $needtocreate) {
|
||||
require_once($CFG->dirroot.'/cache/locallib.php');
|
||||
$class .= '_writer';
|
||||
}
|
||||
|
||||
// Check if this is a PHPUnit test and redirect to the phpunit config classes if it is.
|
||||
if (defined('PHPUNIT_TEST') && PHPUNIT_TEST) {
|
||||
require_once($CFG->dirroot.'/cache/locallib.php');
|
||||
require_once($CFG->dirroot.'/cache/tests/fixtures/lib.php');
|
||||
// We have just a single class for PHP unit tests. We don't care enough about its
|
||||
// performance to do otherwise and having a single method allows us to inject things into it
|
||||
// while testing.
|
||||
$class = 'cache_config_phpunittest';
|
||||
}
|
||||
|
||||
if ($needtocreate) {
|
||||
// Create the default configuration.
|
||||
$class::create_default_configuration();
|
||||
}
|
||||
|
||||
if (!array_key_exists($class, $this->configs)) {
|
||||
// Create a new instance and call it to load it.
|
||||
$this->configs[$class] = new $class;
|
||||
$this->configs[$class]->load();
|
||||
}
|
||||
|
||||
// Return the instance.
|
||||
return $this->configs[$class];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a definition instance or returns the existing one if it has already been created.
|
||||
* @param string $component
|
||||
* @param string $area
|
||||
* @param string $aggregate
|
||||
* @return cache_definition
|
||||
*/
|
||||
public function create_definition($component, $area, $aggregate = null) {
|
||||
$id = $component.'/'.$area;
|
||||
if ($aggregate) {
|
||||
$id .= '::'.$aggregate;
|
||||
}
|
||||
if (!array_key_exists($id, $this->definitions)) {
|
||||
$instance = $this->create_config_instance();
|
||||
$definition = $instance->get_definition_by_id($id);
|
||||
if (!$definition) {
|
||||
$this->reset();
|
||||
$instance = $this->create_config_instance(true);
|
||||
$instance->update_definitions();
|
||||
$definition = $instance->get_definition_by_id($id);
|
||||
if (!$definition) {
|
||||
throw new coding_exception('The requested cache definition does not exist.'. $id, $id);
|
||||
} else {
|
||||
debugging('Cache definitions reparsed causing cache reset in order to locate definition.
|
||||
You should bump the version number to ensure definitions are reprocessed.', DEBUG_DEVELOPER);
|
||||
}
|
||||
}
|
||||
$this->definitions[$id] = cache_definition::load($id, $definition, $aggregate);
|
||||
}
|
||||
return $this->definitions[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a dummy store object for use when a loader has no potential stores to use.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return cachestore_dummy
|
||||
*/
|
||||
protected function create_dummy_store(cache_definition $definition) {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot.'/cache/classes/dummystore.php');
|
||||
$store = new cachestore_dummy();
|
||||
$store->initialise($definition);
|
||||
return $store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a lock instance ready for use.
|
||||
*
|
||||
* @param array $config
|
||||
* @return cache_lock_interface
|
||||
*/
|
||||
public function create_lock_instance(array $config) {
|
||||
if (!array_key_exists('name', $config) || !array_key_exists('type', $config)) {
|
||||
throw new coding_exception('Invalid cache lock instance provided');
|
||||
}
|
||||
$name = $config['name'];
|
||||
$type = $config['type'];
|
||||
unset($config['name']);
|
||||
unset($config['type']);
|
||||
|
||||
if ($this->lockplugins === null) {
|
||||
$this->lockplugins = get_plugin_list_with_class('cachelock', '', 'lib.php');
|
||||
}
|
||||
if (!array_key_exists($type, $this->lockplugins)) {
|
||||
throw new coding_exception('Invalid cache lock type.');
|
||||
}
|
||||
$class = $this->lockplugins[$type];
|
||||
return new $class($name, $config);
|
||||
}
|
||||
}
|
456
cache/classes/helper.php
vendored
Normal file
456
cache/classes/helper.php
vendored
Normal file
@ -0,0 +1,456 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Cache helper class
|
||||
*
|
||||
* This file is part of Moodle's cache API, affectionately called MUC.
|
||||
* It contains the components that are requried in order to use caching.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* The cache helper class.
|
||||
*
|
||||
* The cache helper class provides common functionality to the cache API and is useful to developers within to interact with
|
||||
* the cache API in a general way.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_helper {
|
||||
|
||||
/**
|
||||
* Statistics gathered by the cache API during its operation will be used here.
|
||||
* @static
|
||||
* @var array
|
||||
*/
|
||||
protected static $stats = array();
|
||||
|
||||
/**
|
||||
* The instance of the cache helper.
|
||||
* @var cache_helper
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
/**
|
||||
* Returns true if the cache API can be initialised before Moodle has finished initialising itself.
|
||||
*
|
||||
* This check is essential when trying to cache the likes of configuration information. It checks to make sure that the cache
|
||||
* configuration file has been created which allows use to set up caching when ever is required.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function ready_for_early_init() {
|
||||
return cache_config::config_file_exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of the cache_helper.
|
||||
*
|
||||
* This is designed for internal use only and acts as a static store.
|
||||
* @staticvar null $instance
|
||||
* @return cache_helper
|
||||
*/
|
||||
protected static function instance() {
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new cache_helper();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance of the cache_helper class. Again for internal use only.
|
||||
*/
|
||||
protected function __construct() {
|
||||
// Nothing to do here, just making sure you can't get an instance of this.
|
||||
}
|
||||
|
||||
/**
|
||||
* Used as a data store for initialised definitions.
|
||||
* @var array
|
||||
*/
|
||||
protected $definitions = array();
|
||||
|
||||
/**
|
||||
* Used as a data store for initialised cache stores
|
||||
* We use this because we want to avoid establishing multiple instances of a single store.
|
||||
* @var array
|
||||
*/
|
||||
protected $stores = array();
|
||||
|
||||
/**
|
||||
* Returns the class for use as a cache loader for the given mode.
|
||||
*
|
||||
* @param int $mode One of cache_store::MODE_
|
||||
* @return string
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public static function get_class_for_mode($mode) {
|
||||
switch ($mode) {
|
||||
case cache_store::MODE_APPLICATION :
|
||||
return 'cache_application';
|
||||
case cache_store::MODE_REQUEST :
|
||||
return 'cache_request';
|
||||
case cache_store::MODE_SESSION :
|
||||
return 'cache_session';
|
||||
}
|
||||
throw new coding_exception('Unknown cache mode passed. Must be one of cache_store::MODE_*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache stores to be used with the given definition.
|
||||
* @param cache_definition $definition
|
||||
* @return array
|
||||
*/
|
||||
public static function get_cache_stores(cache_definition $definition) {
|
||||
$instance = cache_config::instance();
|
||||
$stores = $instance->get_stores_for_definition($definition);
|
||||
$stores = self::initialise_cachestore_instances($stores, $definition);
|
||||
return $stores;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function for initialising an array of stores against a given cache definition.
|
||||
*
|
||||
* @param array $stores
|
||||
* @param cache_definition $definition
|
||||
* @return array
|
||||
*/
|
||||
protected static function initialise_cachestore_instances(array $stores, cache_definition $definition) {
|
||||
$return = array();
|
||||
$factory = cache_factory::instance();
|
||||
foreach ($stores as $name => $details) {
|
||||
$store = $factory->create_store_from_config($name, $details, $definition);
|
||||
if ($store !== false) {
|
||||
$return[] = $store;
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a cache_lock instance suitable for use with the store.
|
||||
*
|
||||
* @param cache_store $store
|
||||
* @return cache_lock_interface
|
||||
*/
|
||||
public static function get_cachelock_for_store(cache_store $store) {
|
||||
$instance = cache_config::instance();
|
||||
$lockconf = $instance->get_lock_for_store($store->my_name());
|
||||
$factory = cache_factory::instance();
|
||||
return $factory->create_lock_instance($lockconf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of plugins without using core methods.
|
||||
*
|
||||
* This function explicitly does NOT use core functions as it will in some circumstances be called before Moodle has
|
||||
* finished initialising. This happens when loading configuration for instance.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function early_get_cache_plugins() {
|
||||
global $CFG;
|
||||
$result = array();
|
||||
$ignored = array('CVS', '_vti_cnf', 'simpletest', 'db', 'yui', 'tests');
|
||||
$fulldir = $CFG->dirroot.'/cache/stores';
|
||||
$items = new DirectoryIterator($fulldir);
|
||||
foreach ($items as $item) {
|
||||
if ($item->isDot() or !$item->isDir()) {
|
||||
continue;
|
||||
}
|
||||
$pluginname = $item->getFilename();
|
||||
if (in_array($pluginname, $ignored)) {
|
||||
continue;
|
||||
}
|
||||
$pluginname = clean_param($pluginname, PARAM_PLUGIN);
|
||||
if (empty($pluginname)) {
|
||||
// Better ignore plugins with problematic names here.
|
||||
continue;
|
||||
}
|
||||
$result[$pluginname] = $fulldir.'/'.$pluginname;
|
||||
unset($item);
|
||||
}
|
||||
unset($items);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates a given set of keys from a given definition.
|
||||
*
|
||||
* @todo Invalidating by definition should also add to the event cache so that sessions can be invalidated (when required).
|
||||
*
|
||||
* @param string $component
|
||||
* @param string $area
|
||||
* @param array $identifiers
|
||||
* @param array $keys
|
||||
* @return boolean
|
||||
*/
|
||||
public static function invalidate_by_definition($component, $area, array $identifiers = array(), $keys = array()) {
|
||||
$cache = cache::make($component, $area, $identifiers);
|
||||
if (is_array($keys)) {
|
||||
$cache->delete_many($keys);
|
||||
} else if (is_scalar($keys)) {
|
||||
$cache->delete($keys);
|
||||
} else {
|
||||
throw new coding_exception('cache_helper::invalidate_by_definition only accepts $keys as array, or scalar.');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates a given set of keys by means of an event.
|
||||
*
|
||||
* @todo add support for identifiers to be supplied and utilised.
|
||||
*
|
||||
* @param string $event
|
||||
* @param array $keys
|
||||
*/
|
||||
public static function invalidate_by_event($event, array $keys) {
|
||||
$instance = cache_config::instance();
|
||||
$invalidationeventset = false;
|
||||
$factory = cache_factory::instance();
|
||||
foreach ($instance->get_definitions() as $name => $definitionarr) {
|
||||
$definition = cache_definition::load($name, $definitionarr);
|
||||
if ($definition->invalidates_on_event($event)) {
|
||||
// OK at this point we know that the definition has information to invalidate on the event.
|
||||
// There are two routes, either its an application cache in which case we can invalidate it now.
|
||||
// or it is a session cache in which case we need to set something to the "Event invalidation" definition.
|
||||
// No need to deal with request caches, we don't want to change data half way through a request.
|
||||
if ($definition->get_mode() === cache_store::MODE_APPLICATION) {
|
||||
$cache = $factory->create_cache($definition);
|
||||
$cache->delete_many($keys);
|
||||
}
|
||||
|
||||
// We need to flag the event in the "Event invalidation" cache if it hasn't already happened.
|
||||
if ($invalidationeventset === false) {
|
||||
// Get the event invalidation cache.
|
||||
$cache = cache::make('core', 'eventinvalidation');
|
||||
// Get any existing invalidated keys for this cache.
|
||||
$data = $cache->get($event);
|
||||
if ($data === false) {
|
||||
// There are none.
|
||||
$data = array();
|
||||
}
|
||||
// Add our keys to them with the current cache timestamp.
|
||||
foreach ($keys as $key) {
|
||||
$data[$key] = cache::now();
|
||||
}
|
||||
// Set that data back to the cache.
|
||||
$cache->set($event, $data);
|
||||
// This only needs to occur once.
|
||||
$invalidationeventset = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges the cache for a specific definition.
|
||||
*
|
||||
* @param string $component
|
||||
* @param string $area
|
||||
* @param array $identifiers
|
||||
* @return bool
|
||||
*/
|
||||
public static function purge_by_definition($component, $area, array $identifiers = array()) {
|
||||
// Create the cache.
|
||||
$cache = cache::make($component, $area, $identifiers);
|
||||
// Purge baby, purge.
|
||||
$cache->purge();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges a cache of all information on a given event.
|
||||
*
|
||||
* @param string $event
|
||||
*/
|
||||
public static function purge_by_event($event) {
|
||||
$instance = cache_config::instance();
|
||||
$invalidationeventset = false;
|
||||
$factory = cache_factory::instance();
|
||||
foreach ($instance->get_definitions() as $name => $definitionarr) {
|
||||
$definition = cache_definition::load($name, $definitionarr);
|
||||
if ($definition->invalidates_on_event($event)) {
|
||||
// Purge the cache.
|
||||
$cache = $factory->create_cache($definition);
|
||||
$cache->purge();
|
||||
|
||||
// We need to flag the event in the "Event invalidation" cache if it hasn't already happened.
|
||||
if ($invalidationeventset === false) {
|
||||
// Get the event invalidation cache.
|
||||
$cache = cache::make('core', 'eventinvalidation');
|
||||
// Create a key to invalidate all.
|
||||
$data = array(
|
||||
'purged' => cache::now()
|
||||
);
|
||||
// Set that data back to the cache.
|
||||
$cache->set($event, $data);
|
||||
// This only needs to occur once.
|
||||
$invalidationeventset = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the stats array is ready to collect information for the given store and definition.
|
||||
* @param string $store
|
||||
* @param string $definition
|
||||
*/
|
||||
protected static function ensure_ready_for_stats($store, $definition) {
|
||||
if (!array_key_exists($definition, self::$stats)) {
|
||||
self::$stats[$definition] = array(
|
||||
$store => array(
|
||||
'hits' => 0,
|
||||
'misses' => 0,
|
||||
'sets' => 0,
|
||||
)
|
||||
);
|
||||
} else if (!array_key_exists($store, self::$stats[$definition])) {
|
||||
self::$stats[$definition][$store] = array(
|
||||
'hits' => 0,
|
||||
'misses' => 0,
|
||||
'sets' => 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a cache hit in the stats for the given store and definition.
|
||||
*
|
||||
* @param string $store
|
||||
* @param string $definition
|
||||
*/
|
||||
public static function record_cache_hit($store, $definition) {
|
||||
self::ensure_ready_for_stats($store, $definition);
|
||||
self::$stats[$definition][$store]['hits']++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a cache miss in the stats for the given store and definition.
|
||||
*
|
||||
* @param string $store
|
||||
* @param string $definition
|
||||
*/
|
||||
public static function record_cache_miss($store, $definition) {
|
||||
self::ensure_ready_for_stats($store, $definition);
|
||||
self::$stats[$definition][$store]['misses']++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a cache set in the stats for the given store and definition.
|
||||
*
|
||||
* @param string $store
|
||||
* @param string $definition
|
||||
*/
|
||||
public static function record_cache_set($store, $definition) {
|
||||
self::ensure_ready_for_stats($store, $definition);
|
||||
self::$stats[$definition][$store]['sets']++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the stats collected so far.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_stats() {
|
||||
return self::$stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge all of the cache stores of all of their data.
|
||||
*
|
||||
* Think twice before calling this method. It will purge **ALL** caches regardless of whether they have been used recently or
|
||||
* anything. This will involve full setup of the cache + the purge operation. On a site using caching heavily this WILL be
|
||||
* painful.
|
||||
*/
|
||||
public static function purge_all() {
|
||||
$config = cache_config::instance();
|
||||
$stores = $config->get_all_stores();
|
||||
$definition = cache_definition::load_adhoc(cache_store::MODE_REQUEST, 'core', 'cache_purge');
|
||||
foreach ($stores as $store) {
|
||||
$class = $store['class'];
|
||||
$instance = new $class($store['name'], $store['configuration']);
|
||||
if (!$instance->is_ready()) {
|
||||
continue;
|
||||
}
|
||||
$instance->initialise($definition);
|
||||
$instance->purge();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges a store given its name.
|
||||
*
|
||||
* @param string $storename
|
||||
* @return bool
|
||||
*/
|
||||
public static function purge_store($storename) {
|
||||
$config = cache_config::instance();
|
||||
foreach ($config->get_all_stores() as $store) {
|
||||
if ($store['name'] !== $storename) {
|
||||
continue;
|
||||
}
|
||||
$class = $store['class'];
|
||||
$instance = new $class($store['name'], $store['configuration']);
|
||||
if (!$instance->is_ready()) {
|
||||
continue;
|
||||
}
|
||||
$definition = cache_definition::load_adhoc(cache_store::MODE_REQUEST, 'core', 'cache_purge');
|
||||
$instance->initialise($definition);
|
||||
$instance->purge();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translated name of the definition.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return lang_string
|
||||
*/
|
||||
public static function get_definition_name($definition) {
|
||||
if ($definition instanceof cache_definition) {
|
||||
return $definition->get_name();
|
||||
}
|
||||
$identifier = 'cachedef_'.clean_param($definition['area'], PARAM_STRINGID);
|
||||
$component = $definition['component'];
|
||||
if ($component === 'core') {
|
||||
$component = 'cache';
|
||||
}
|
||||
return new lang_string($identifier, $component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashes a descriptive key to make it shorter and stil unique.
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
public static function hash_key($key) {
|
||||
return crc32($key);
|
||||
}
|
||||
}
|
692
cache/classes/interfaces.php
vendored
Normal file
692
cache/classes/interfaces.php
vendored
Normal file
@ -0,0 +1,692 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Cache API interfaces
|
||||
*
|
||||
* This file is part of Moodle's cache API, affectionately called MUC.
|
||||
* It contains the components that are requried in order to use caching.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Cache Loader.
|
||||
*
|
||||
* This cache loader interface provides the required structure for classes that wish to be interacted with as a
|
||||
* means of accessing and interacting with a cache.
|
||||
*
|
||||
* Can be implemented by any class wishing to be a cache loader.
|
||||
*/
|
||||
interface cache_loader {
|
||||
|
||||
/**
|
||||
* Retrieves the value for the given key from the cache.
|
||||
*
|
||||
* @param string|int $key The key for the data being requested.
|
||||
* @param int $strictness One of IGNORE_MISSING or MUST_EXIST.
|
||||
* @return mixed The data retrieved from the cache, or false if the key did not exist within the cache.
|
||||
* If MUST_EXIST was used then an exception will be thrown if the key does not exist within the cache.
|
||||
*/
|
||||
public function get($key, $strictness = IGNORE_MISSING);
|
||||
|
||||
/**
|
||||
* Retrieves an array of values for an array of keys.
|
||||
*
|
||||
* Using this function comes with potential performance implications.
|
||||
* Not all cache stores will support get_many/set_many operations and in order to replicate this functionality will call
|
||||
* the equivalent singular method for each item provided.
|
||||
* This should not deter you from using this function as there is a performance benefit in situations where the cache
|
||||
* store does support it, but you should be aware of this fact.
|
||||
*
|
||||
* @param array $keys The keys of the data being requested.
|
||||
* @param int $strictness One of IGNORE_MISSING or MUST_EXIST.
|
||||
* @return array An array of key value pairs for the items that could be retrieved from the cache.
|
||||
* If MUST_EXIST was used and not all keys existed within the cache then an exception will be thrown.
|
||||
* Otherwise any key that did not exist will have a data value of false within the results.
|
||||
*/
|
||||
public function get_many(array $keys, $strictness = IGNORE_MISSING);
|
||||
|
||||
/**
|
||||
* Sends a key => value pair to the cache.
|
||||
*
|
||||
* <code>
|
||||
* // This code will add four entries to the cache, one for each url.
|
||||
* $cache->set('main', 'http://moodle.org');
|
||||
* $cache->set('docs', 'http://docs.moodle.org');
|
||||
* $cache->set('tracker', 'http://tracker.moodle.org');
|
||||
* $cache->set('qa', 'http://qa.moodle.net');
|
||||
* </code>
|
||||
*
|
||||
* @param string|int $key The key for the data being requested.
|
||||
* @param mixed $data The data to set against the key.
|
||||
* @return bool True on success, false otherwise.
|
||||
*/
|
||||
public function set($key, $data);
|
||||
|
||||
/**
|
||||
* Sends several key => value pairs to the cache.
|
||||
*
|
||||
* Using this function comes with potential performance implications.
|
||||
* Not all cache stores will support get_many/set_many operations and in order to replicate this functionality will call
|
||||
* the equivalent singular method for each item provided.
|
||||
* This should not deter you from using this function as there is a performance benefit in situations where the cache store
|
||||
* does support it, but you should be aware of this fact.
|
||||
*
|
||||
* <code>
|
||||
* // This code will add four entries to the cache, one for each url.
|
||||
* $cache->set_many(array(
|
||||
* 'main' => 'http://moodle.org',
|
||||
* 'docs' => 'http://docs.moodle.org',
|
||||
* 'tracker' => 'http://tracker.moodle.org',
|
||||
* 'qa' => ''http://qa.moodle.net'
|
||||
* ));
|
||||
* </code>
|
||||
*
|
||||
* @param array $keyvaluearray An array of key => value pairs to send to the cache.
|
||||
* @return int The number of items successfully set. It is up to the developer to check this matches the number of items.
|
||||
* ... if they care that is.
|
||||
*/
|
||||
public function set_many(array $keyvaluearray);
|
||||
|
||||
/**
|
||||
* Test is a cache has a key.
|
||||
*
|
||||
* The use of the has methods is strongly discouraged. In a high load environment the cache may well change between the
|
||||
* test and any subsequent action (get, set, delete etc).
|
||||
* Instead it is recommended to write your code in such a way they it performs the following steps:
|
||||
* <ol>
|
||||
* <li>Attempt to retrieve the information.</li>
|
||||
* <li>Generate the information.</li>
|
||||
* <li>Attempt to set the information</li>
|
||||
* </ol>
|
||||
*
|
||||
* Its also worth mentioning that not all stores support key tests.
|
||||
* For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
|
||||
* Just one more reason you should not use these methods unless you have a very good reason to do so.
|
||||
*
|
||||
* @param string|int $key
|
||||
* @return bool True if the cache has the requested key, false otherwise.
|
||||
*/
|
||||
public function has($key);
|
||||
|
||||
/**
|
||||
* Test if a cache has at least one of the given keys.
|
||||
*
|
||||
* It is strongly recommended to avoid the use of this function if not absolutely required.
|
||||
* In a high load environment the cache may well change between the test and any subsequent action (get, set, delete etc).
|
||||
*
|
||||
* Its also worth mentioning that not all stores support key tests.
|
||||
* For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
|
||||
* Just one more reason you should not use these methods unless you have a very good reason to do so.
|
||||
*
|
||||
* @param array $keys
|
||||
* @return bool True if the cache has at least one of the given keys
|
||||
*/
|
||||
public function has_any(array $keys);
|
||||
|
||||
/**
|
||||
* Test is a cache has all of the given keys.
|
||||
*
|
||||
* It is strongly recommended to avoid the use of this function if not absolutely required.
|
||||
* In a high load environment the cache may well change between the test and any subsequent action (get, set, delete etc).
|
||||
*
|
||||
* Its also worth mentioning that not all stores support key tests.
|
||||
* For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
|
||||
* Just one more reason you should not use these methods unless you have a very good reason to do so.
|
||||
*
|
||||
* @param array $keys
|
||||
* @return bool True if the cache has all of the given keys, false otherwise.
|
||||
*/
|
||||
public function has_all(array $keys);
|
||||
|
||||
/**
|
||||
* Delete the given key from the cache.
|
||||
*
|
||||
* @param string|int $key The key to delete.
|
||||
* @param bool $recurse When set to true the key will also be deleted from all stacked cache loaders and their stores.
|
||||
* This happens by default and ensure that all the caches are consistent. It is NOT recommended to change this.
|
||||
* @return bool True of success, false otherwise.
|
||||
*/
|
||||
public function delete($key, $recurse = true);
|
||||
|
||||
/**
|
||||
* Delete all of the given keys from the cache.
|
||||
*
|
||||
* @param array $keys The key to delete.
|
||||
* @param bool $recurse When set to true the key will also be deleted from all stacked cache loaders and their stores.
|
||||
* This happens by default and ensure that all the caches are consistent. It is NOT recommended to change this.
|
||||
* @return int The number of items successfully deleted.
|
||||
*/
|
||||
public function delete_many(array $keys, $recurse = true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache Loader supporting locking.
|
||||
*
|
||||
* This interface should be given to classes already implementing cache_loader that also wish to support locking.
|
||||
* It outlines the required structure for utilising locking functionality when using a cache.
|
||||
*
|
||||
* Can be implemented by any class already implementing the cache_loader interface.
|
||||
*/
|
||||
interface cache_loader_with_locking {
|
||||
|
||||
/**
|
||||
* Acquires a lock for the given key.
|
||||
*
|
||||
* Please note that this happens automatically if the cache definition requires locking.
|
||||
* it is still made a public method so that adhoc caches can use it if they choose.
|
||||
* However this doesn't guarantee consistent access. It will become the reponsiblity of the calling code to ensure locks
|
||||
* are acquired, checked, and released.
|
||||
*
|
||||
* @param string|int $key
|
||||
* @return bool True if the lock could be acquired, false otherwise.
|
||||
*/
|
||||
public function acquire_lock($key);
|
||||
|
||||
/**
|
||||
* Checks if the cache loader owns the lock for the given key.
|
||||
*
|
||||
* Please note that this happens automatically if the cache definition requires locking.
|
||||
* it is still made a public method so that adhoc caches can use it if they choose.
|
||||
* However this doesn't guarantee consistent access. It will become the reponsiblity of the calling code to ensure locks
|
||||
* are acquired, checked, and released.
|
||||
*
|
||||
* @param string|int $key
|
||||
* @return bool True if this code has the lock, false if there is a lock but this code doesn't have it,
|
||||
* null if there is no lock.
|
||||
*/
|
||||
public function check_lock_state($key);
|
||||
|
||||
/**
|
||||
* Releases the lock for the given key.
|
||||
*
|
||||
* Please note that this happens automatically if the cache definition requires locking.
|
||||
* it is still made a public method so that adhoc caches can use it if they choose.
|
||||
* However this doesn't guarantee consistent access. It will become the reponsiblity of the calling code to ensure locks
|
||||
* are acquired, checked, and released.
|
||||
*
|
||||
* @param string|int $key
|
||||
* @return bool True if the lock has been released, false if there was a problem releasing the lock.
|
||||
*/
|
||||
public function release_lock($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache store.
|
||||
*
|
||||
* This interface outlines the requirements for a cache store plugin.
|
||||
* It must be implemented by all such plugins and provides a reference to interacting with cache stores.
|
||||
*
|
||||
* Must be implemented by all cache store plugins.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
interface cache_store {
|
||||
|
||||
/**#@+
|
||||
* Constants for features a cache store can support
|
||||
*/
|
||||
/**
|
||||
* Supports multi-part keys
|
||||
*/
|
||||
const SUPPORTS_MULTIPLE_IDENTIFIERS = 1;
|
||||
/**
|
||||
* Ensures data remains in the cache once set.
|
||||
*/
|
||||
const SUPPORTS_DATA_GUARANTEE = 2;
|
||||
/**
|
||||
* Supports a native ttl system.
|
||||
*/
|
||||
const SUPPORTS_NATIVE_TTL = 4;
|
||||
/**#@-*/
|
||||
|
||||
/**#@+
|
||||
* Constants for the modes of a cache store
|
||||
*/
|
||||
/**
|
||||
* Application caches. These are shared caches.
|
||||
*/
|
||||
const MODE_APPLICATION = 1;
|
||||
/**
|
||||
* Session caches. Just access to the PHP session.
|
||||
*/
|
||||
const MODE_SESSION = 2;
|
||||
/**
|
||||
* Request caches. Static caches really.
|
||||
*/
|
||||
const MODE_REQUEST = 4;
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* Static method to check if the store requirements are met.
|
||||
*
|
||||
* @return bool True if the stores software/hardware requirements have been met and it can be used. False otherwise.
|
||||
*/
|
||||
public static function are_requirements_met();
|
||||
|
||||
/**
|
||||
* Static method to check if a store is usable with the given mode.
|
||||
*
|
||||
* @param int $mode One of cache_store::MODE_*
|
||||
*/
|
||||
public static function is_supported_mode($mode);
|
||||
|
||||
/**
|
||||
* Returns the supported features as a binary flag.
|
||||
*
|
||||
* @param array $configuration The configuration of a store to consider specifically.
|
||||
* @return int The supported features.
|
||||
*/
|
||||
public static function get_supported_features(array $configuration = array());
|
||||
|
||||
/**
|
||||
* Returns the supported modes as a binary flag.
|
||||
*
|
||||
* @param array $configuration The configuration of a store to consider specifically.
|
||||
* @return int The supported modes.
|
||||
*/
|
||||
public static function get_supported_modes(array $configuration = array());
|
||||
|
||||
/**
|
||||
* Returns true if this cache store instance supports multiple identifiers.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_multiple_indentifiers();
|
||||
|
||||
/**
|
||||
* Returns true if this cache store instance promotes data guarantee.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_data_guarantee();
|
||||
|
||||
/**
|
||||
* Returns true if this cache store instance supports ttl natively.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_native_ttl();
|
||||
|
||||
/**
|
||||
* Used to control the ability to add an instance of this store through the admin interfaces.
|
||||
*
|
||||
* @return bool True if the user can add an instance, false otherwise.
|
||||
*/
|
||||
public static function can_add_instance();
|
||||
|
||||
/**
|
||||
* Constructs an instance of the cache store.
|
||||
*
|
||||
* This method should not create connections or perform and processing, it should be used
|
||||
*
|
||||
* @param string $name The name of the cache store
|
||||
* @param array $configuration The configuration for this store instance.
|
||||
*/
|
||||
public function __construct($name, array $configuration = array());
|
||||
|
||||
/**
|
||||
* Returns the name of this store instance.
|
||||
* @return string
|
||||
*/
|
||||
public function my_name();
|
||||
|
||||
/**
|
||||
* Initialises a new instance of the cache store given the definition the instance is to be used for.
|
||||
*
|
||||
* This function should prepare any given connections etc.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
*/
|
||||
public function initialise(cache_definition $definition);
|
||||
|
||||
/**
|
||||
* Returns true if this cache store instance has been initialised.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_initialised();
|
||||
|
||||
/**
|
||||
* Returns true if this cache store instance is ready to use.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_ready();
|
||||
|
||||
/**
|
||||
* Retrieves an item from the cache store given its key.
|
||||
*
|
||||
* @param string $key The key to retrieve
|
||||
* @return mixed The data that was associated with the key, or false if the key did not exist.
|
||||
*/
|
||||
public function get($key);
|
||||
|
||||
/**
|
||||
* Retrieves several items from the cache store in a single transaction.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @param array $keys The array of keys to retrieve
|
||||
* @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
|
||||
* be set to false.
|
||||
*/
|
||||
public function get_many($keys);
|
||||
|
||||
/**
|
||||
* Sets an item in the cache given its key and data value.
|
||||
*
|
||||
* @param string $key The key to use.
|
||||
* @param mixed $data The data to set.
|
||||
* @return bool True if the operation was a success false otherwise.
|
||||
*/
|
||||
public function set($key, $data);
|
||||
|
||||
/**
|
||||
* Sets many items in the cache in a single transaction.
|
||||
*
|
||||
* @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two
|
||||
* keys, 'key' and 'value'.
|
||||
* @return int The number of items successfully set. It is up to the developer to check this matches the number of items
|
||||
* sent ... if they care that is.
|
||||
*/
|
||||
public function set_many(array $keyvaluearray);
|
||||
|
||||
/**
|
||||
* Deletes an item from the cache store.
|
||||
*
|
||||
* @param string $key The key to delete.
|
||||
* @return bool Returns true if the operation was a success, false otherwise.
|
||||
*/
|
||||
public function delete($key);
|
||||
|
||||
/**
|
||||
* Deletes several keys from the cache in a single action.
|
||||
*
|
||||
* @param array $keys The keys to delete
|
||||
* @return int The number of items successfully deleted.
|
||||
*/
|
||||
public function delete_many(array $keys);
|
||||
|
||||
/**
|
||||
* Purges the cache deleting all items within it.
|
||||
*
|
||||
* @return boolean True on success. False otherwise.
|
||||
*/
|
||||
public function purge();
|
||||
|
||||
/**
|
||||
* Performs any necessary clean up when the store instance is being deleted.
|
||||
*/
|
||||
public function cleanup();
|
||||
|
||||
/**
|
||||
* Generates an instance of the cache store that can be used for testing.
|
||||
*
|
||||
* Returns an instance of the cache store, or false if one cannot be created.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return cache_store|false
|
||||
*/
|
||||
public static function initialise_test_instance(cache_definition $definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache store feature: locking
|
||||
*
|
||||
* This is a feature that cache stores can implement if they wish to support locking themselves rather
|
||||
* than having the cache loader handle it for them.
|
||||
*
|
||||
* Can be implemented by classes already implementing cache_store.
|
||||
*/
|
||||
interface cache_is_lockable {
|
||||
|
||||
/**
|
||||
* Acquires a lock on the given key for the given identifier.
|
||||
*
|
||||
* @param string $key The key we are locking.
|
||||
* @param string $ownerid The identifier so we can check if we have the lock or if it is someone else.
|
||||
* The use of this property is entirely optional and implementations can act as they like upon it.
|
||||
* @return bool True if the lock could be acquired, false otherwise.
|
||||
*/
|
||||
public function acquire_lock($key, $ownerid);
|
||||
|
||||
/**
|
||||
* Test if there is already a lock for the given key and if there is whether it belongs to the calling code.
|
||||
*
|
||||
* @param string $key The key we are locking.
|
||||
* @param string $ownerid The identifier so we can check if we have the lock or if it is someone else.
|
||||
* @return bool True if this code has the lock, false if there is a lock but this code doesn't have it, null if there
|
||||
* is no lock.
|
||||
*/
|
||||
public function check_lock_state($key, $ownerid);
|
||||
|
||||
/**
|
||||
* Releases the lock on the given key.
|
||||
*
|
||||
* @param string $key The key we are locking.
|
||||
* @param string $ownerid The identifier so we can check if we have the lock or if it is someone else.
|
||||
* The use of this property is entirely optional and implementations can act as they like upon it.
|
||||
* @return bool True if the lock has been released, false if there was a problem releasing the lock.
|
||||
*/
|
||||
public function release_lock($key, $ownerid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache store feature: key awareness.
|
||||
*
|
||||
* This is a feature that cache stores and cache loaders can both choose to implement.
|
||||
* If a cache store implements this then it will be made responsible for tests for items within the cache.
|
||||
* If the cache store being used doesn't implement this then it will be the responsibility of the cache loader to use the
|
||||
* equivalent get methods to mimick the functionality of these tests.
|
||||
*
|
||||
* Cache stores should only override these methods if they natively support such features or if they have a better performing
|
||||
* means of performing these tests than the handling that would otherwise take place in the cache_loader.
|
||||
*
|
||||
* Can be implemented by classes already implementing cache_store.
|
||||
*/
|
||||
interface cache_is_key_aware {
|
||||
|
||||
/**
|
||||
* Test is a cache has a key.
|
||||
*
|
||||
* The use of the has methods is strongly discouraged. In a high load environment the cache may well change between the
|
||||
* test and any subsequent action (get, set, delete etc).
|
||||
* Instead it is recommended to write your code in such a way they it performs the following steps:
|
||||
* <ol>
|
||||
* <li>Attempt to retrieve the information.</li>
|
||||
* <li>Generate the information.</li>
|
||||
* <li>Attempt to set the information</li>
|
||||
* </ol>
|
||||
*
|
||||
* Its also worth mentioning that not all stores support key tests.
|
||||
* For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
|
||||
* Just one more reason you should not use these methods unless you have a very good reason to do so.
|
||||
*
|
||||
* @param string|int $key
|
||||
* @return bool True if the cache has the requested key, false otherwise.
|
||||
*/
|
||||
public function has($key);
|
||||
|
||||
/**
|
||||
* Test if a cache has at least one of the given keys.
|
||||
*
|
||||
* It is strongly recommended to avoid the use of this function if not absolutely required.
|
||||
* In a high load environment the cache may well change between the test and any subsequent action (get, set, delete etc).
|
||||
*
|
||||
* Its also worth mentioning that not all stores support key tests.
|
||||
* For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
|
||||
* Just one more reason you should not use these methods unless you have a very good reason to do so.
|
||||
*
|
||||
* @param array $keys
|
||||
* @return bool True if the cache has at least one of the given keys
|
||||
*/
|
||||
public function has_any(array $keys);
|
||||
|
||||
/**
|
||||
* Test is a cache has all of the given keys.
|
||||
*
|
||||
* It is strongly recommended to avoid the use of this function if not absolutely required.
|
||||
* In a high load environment the cache may well change between the test and any subsequent action (get, set, delete etc).
|
||||
*
|
||||
* Its also worth mentioning that not all stores support key tests.
|
||||
* For stores that don't support key tests this functionality is mimicked by using the equivalent get method.
|
||||
* Just one more reason you should not use these methods unless you have a very good reason to do so.
|
||||
*
|
||||
* @param array $keys
|
||||
* @return bool True if the cache has all of the given keys, false otherwise.
|
||||
*/
|
||||
public function has_all(array $keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache Data Source.
|
||||
*
|
||||
* The cache data source interface can be implemented by any class within Moodle.
|
||||
* If implemented then the class can be reference in a cache definition and will be used to load information that cannot be
|
||||
* retrieved from the cache. As part of its retrieval that information will also be loaded into the cache.
|
||||
*
|
||||
* This allows developers to created a complete cache solution that can be used through code ensuring consistent cache
|
||||
* interaction and loading. Allowing them in turn to centralise code and help keeps things more easily maintainable.
|
||||
*
|
||||
* Can be implemented by any class.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
interface cache_data_source {
|
||||
|
||||
/**
|
||||
* Returns an instance of the data source class that the cache can use for loading data using the other methods
|
||||
* specified by this interface.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return object
|
||||
*/
|
||||
public static function get_instance_for_cache(cache_definition $definition);
|
||||
|
||||
/**
|
||||
* Loads the data for the key provided ready formatted for caching.
|
||||
*
|
||||
* @param string|int $key The key to load.
|
||||
* @return mixed What ever data should be returned, or false if it can't be loaded.
|
||||
*/
|
||||
public function load_for_cache($key);
|
||||
|
||||
/**
|
||||
* Loads several keys for the cache.
|
||||
*
|
||||
* @param array $keys An array of keys each of which will be string|int.
|
||||
* @return array An array of matching data items.
|
||||
*/
|
||||
public function load_many_for_cache(array $keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cacheable object.
|
||||
*
|
||||
* This interface can be implemented by any class that is going to be passed into a cache and allows it to take control of the
|
||||
* structure and the information about to be cached, as well as how to deal with it when it is retrieved from a cache.
|
||||
* Think of it like serialisation and the __sleep and __wakeup methods.
|
||||
* This is used because cache stores are responsible for how they interact with data and what they do when storing it. This
|
||||
* interface ensures there is always a guaranteed action.
|
||||
*/
|
||||
interface cacheable_object {
|
||||
|
||||
/**
|
||||
* Prepares the object for caching. Works like the __sleep method.
|
||||
*
|
||||
* @return mixed The data to cache, can be anything except a class that implements the cacheable_object... that would
|
||||
* be dumb.
|
||||
*/
|
||||
public function prepare_to_cache();
|
||||
|
||||
/**
|
||||
* Takes the data provided by prepare_to_cache and reinitialises an instance of the associated from it.
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return object The instance for the given data.
|
||||
*/
|
||||
public static function wake_from_cache($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache lock interface
|
||||
*
|
||||
* This interface needs to be inherited by all cache lock plugins.
|
||||
*/
|
||||
interface cache_lock_interface {
|
||||
/**
|
||||
* Constructs an instance of the cache lock given its name and its configuration data
|
||||
*
|
||||
* @param string $name The unique name of the lock instance
|
||||
* @param array $configuration
|
||||
*/
|
||||
public function __construct($name, array $configuration = array());
|
||||
|
||||
/**
|
||||
* Acquires a lock on a given key.
|
||||
*
|
||||
* @param string $key The key to acquire a lock for.
|
||||
* @param string $ownerid An unique identifier for the owner of this lock. It is entirely optional for the cache lock plugin
|
||||
* to use this. Each implementation can decide for themselves.
|
||||
* @param bool $block If set to true the application will wait until a lock can be acquired
|
||||
* @return bool True if the lock can be acquired false otherwise.
|
||||
*/
|
||||
public function lock($key, $ownerid, $block = false);
|
||||
|
||||
/**
|
||||
* Releases the lock held on a certain key.
|
||||
*
|
||||
* @param string $key The key to release the lock for.
|
||||
* @param string $ownerid An unique identifier for the owner of this lock. It is entirely optional for the cache lock plugin
|
||||
* to use this. Each implementation can decide for themselves.
|
||||
* @param bool $forceunlock If set to true the lock will be removed if it exists regardless of whether or not we own it.
|
||||
*/
|
||||
public function unlock($key, $ownerid, $forceunlock = false);
|
||||
|
||||
/**
|
||||
* Checks the state of the given key.
|
||||
*
|
||||
* Returns true if the key is locked and belongs to the ownerid.
|
||||
* Returns false if the key is locked but does not belong to the ownerid.
|
||||
* Returns null if there is no lock
|
||||
*
|
||||
* @param string $key The key we are checking for.
|
||||
* @param string $ownerid The identifier so we can check if we have the lock or if it is someone else.
|
||||
* @return bool True if this code has the lock, false if there is a lock but this code doesn't have it, null if there
|
||||
* is no lock.
|
||||
*/
|
||||
public function check_state($key, $ownerid);
|
||||
|
||||
/**
|
||||
* Cleans up any left over locks.
|
||||
*
|
||||
* This function MUST clean up any locks that have been acquired and not released during processing.
|
||||
* Although the situation of acquiring a lock and not releasing it should be insanely rare we need to deal with it.
|
||||
* Things such as unfortunate timeouts etc could cause this situation.
|
||||
*/
|
||||
public function __destruct();
|
||||
}
|
1454
cache/classes/loaders.php
vendored
Normal file
1454
cache/classes/loaders.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
211
cache/forms.php
vendored
Normal file
211
cache/forms.php
vendored
Normal file
@ -0,0 +1,211 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Forms used for the administration and managemement of the cache setup.
|
||||
*
|
||||
* This file is part of Moodle's cache API, affectionately called MUC.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot.'/lib/formslib.php');
|
||||
|
||||
/**
|
||||
* Add store instance form.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cachestore_addinstance_form extends moodleform {
|
||||
|
||||
/**
|
||||
* The definition of the add instance form
|
||||
*/
|
||||
protected final function definition() {
|
||||
$form = $this->_form;
|
||||
$store = $this->_customdata['store'];
|
||||
$plugin = $this->_customdata['plugin'];
|
||||
$locks = $this->_customdata['locks'];
|
||||
|
||||
$form->addElement('hidden', 'plugin', $plugin);
|
||||
$form->addElement('hidden', 'editing', !empty($this->_customdata['store']));
|
||||
|
||||
if (!$store) {
|
||||
$form->addElement('text', 'name', get_string('storename', 'cache'));
|
||||
$form->addHelpButton('name', 'storename', 'cache');
|
||||
$form->addRule('name', get_string('required'), 'required');
|
||||
$form->setType('name', PARAM_TEXT);
|
||||
} else {
|
||||
$form->addElement('hidden', 'name', $store);
|
||||
$form->addElement('static', 'name-value', get_string('storename', 'cache'), $store);
|
||||
}
|
||||
|
||||
if (is_array($locks)) {
|
||||
$form->addElement('select', 'lock', get_string('lockmethod', 'cache'), $locks);
|
||||
$form->addHelpButton('lock', 'lockmethod', 'cache');
|
||||
$form->setType('lock', PARAM_TEXT);
|
||||
} else {
|
||||
$form->addElement('hidden', 'lock', '');
|
||||
$form->addElement('static', 'lock-value', get_string('lockmethod', 'cache'),
|
||||
'<em>'.get_string('nativelocking', 'cache').'</em>');
|
||||
}
|
||||
|
||||
if (method_exists($this, 'configuration_definition')) {
|
||||
$form->addElement('header', 'storeconfiguration', get_string('storeconfiguration', 'cache'));
|
||||
$this->configuration_definition();
|
||||
}
|
||||
|
||||
$this->add_action_buttons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the add instance form data
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $files
|
||||
* @return array
|
||||
*/
|
||||
public function validation($data, $files) {
|
||||
$errors = parent::validation($data, $files);
|
||||
|
||||
if (!array_key_exists('name', $errors)) {
|
||||
if (!preg_match('#^[a-zA-Z0-9\-_ ]+$#', $data['name'])) {
|
||||
$errors['name'] = get_string('storenameinvalid', 'cache');
|
||||
} else if (empty($this->_customdata['store'])) {
|
||||
$stores = cache_administration_helper::get_store_instance_summaries();
|
||||
if (array_key_exists($data['name'], $stores)) {
|
||||
$errors['name'] = get_string('storenamealreadyused', 'cache');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (method_exists($this, 'configuration_validation')) {
|
||||
$errors = $this->configuration_validation($data, $files);
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form to set definition mappings
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_definition_mappings_form extends moodleform {
|
||||
|
||||
/**
|
||||
* The definition of the form
|
||||
*/
|
||||
protected final function definition() {
|
||||
$definition = $this->_customdata['definition'];
|
||||
$form = $this->_form;
|
||||
|
||||
list($component, $area) = explode('/', $definition, 2);
|
||||
list($currentstores, $storeoptions, $defaults) =
|
||||
cache_administration_helper::get_definition_store_options($component, $area);
|
||||
|
||||
$form->addElement('hidden', 'definition', $definition);
|
||||
$form->addElement('hidden', 'action', 'editdefinitionmapping');
|
||||
|
||||
$requiredoptions = max(3, count($currentstores)+1);
|
||||
$requiredoptions = min($requiredoptions, count($storeoptions));
|
||||
|
||||
$options = array('' => get_string('none'));
|
||||
foreach ($storeoptions as $option => $def) {
|
||||
$options[$option] = $option;
|
||||
if ($def['default']) {
|
||||
$options[$option] .= ' '.get_string('mappingdefault', 'cache');
|
||||
}
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $requiredoptions; $i++) {
|
||||
$title = '...';
|
||||
if ($i === 0) {
|
||||
$title = get_string('mappingprimary', 'cache');
|
||||
} else if ($i === $requiredoptions-1) {
|
||||
$title = get_string('mappingfinal', 'cache');
|
||||
}
|
||||
$form->addElement('select', 'mappings['.$i.']', $title, $options);
|
||||
}
|
||||
$i = 0;
|
||||
foreach ($currentstores as $store => $def) {
|
||||
$form->setDefault('mappings['.$i.']', $store);
|
||||
$i++;
|
||||
}
|
||||
|
||||
if (!empty($defaults)) {
|
||||
$form->addElement('static', 'defaults', get_string('defaultmappings', 'cache'),
|
||||
html_writer::tag('strong', join(', ', $defaults)));
|
||||
$form->addHelpButton('defaults', 'defaultmappings', 'cache');
|
||||
}
|
||||
|
||||
$this->add_action_buttons();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form to set the mappings for a mode.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_mode_mappings_form extends moodleform {
|
||||
/**
|
||||
* The definition of the form
|
||||
*/
|
||||
protected function definition() {
|
||||
$form = $this->_form;
|
||||
$stores = $this->_customdata;
|
||||
|
||||
$options = array(
|
||||
cache_store::MODE_APPLICATION => array(),
|
||||
cache_store::MODE_SESSION => array(),
|
||||
cache_store::MODE_REQUEST => array()
|
||||
);
|
||||
foreach ($stores as $storename => $store) {
|
||||
foreach ($store['modes'] as $mode => $enabled) {
|
||||
if ($enabled) {
|
||||
if (empty($store['default'])) {
|
||||
$options[$mode][$storename] = $store['name'];
|
||||
} else {
|
||||
$options[$mode][$storename] = get_string('store_'.$store['name'], 'cache');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$form->addElement('hidden', 'action', 'editmodemappings');
|
||||
foreach ($options as $mode => $optionset) {
|
||||
$form->addElement('select', 'mode_'.$mode, get_string('mode_'.$mode, 'cache'), $optionset);
|
||||
}
|
||||
|
||||
$this->add_action_buttons();
|
||||
}
|
||||
}
|
150
cache/lib.php
vendored
Normal file
150
cache/lib.php
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The core cache API.
|
||||
*
|
||||
* Pretty much just includes the mandatory classes and contains the misc classes that arn't worth separating into individual files.
|
||||
*
|
||||
* This file is part of Moodle's cache API, affectionately called MUC.
|
||||
* It contains the components that are requried in order to use caching.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
// Include the required classes.
|
||||
require_once($CFG->dirroot.'/cache/classes/interfaces.php');
|
||||
require_once($CFG->dirroot.'/cache/classes/config.php');
|
||||
require_once($CFG->dirroot.'/cache/classes/helper.php');
|
||||
require_once($CFG->dirroot.'/cache/classes/factory.php');
|
||||
require_once($CFG->dirroot.'/cache/classes/loaders.php');
|
||||
require_once($CFG->dirroot.'/cache/classes/definition.php');
|
||||
|
||||
/**
|
||||
* A cached object wrapper.
|
||||
*
|
||||
* This class gets used when the data is an object that has implemented the cacheable_object interface.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_cached_object {
|
||||
|
||||
/**
|
||||
* The class of the cacheable object
|
||||
* @var string
|
||||
*/
|
||||
protected $class;
|
||||
|
||||
/**
|
||||
* The data returned by the cacheable_object prepare_to_cache method.
|
||||
* @var mixed
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Constructs a cached object wrapper.
|
||||
* @param cacheable_object $obj
|
||||
*/
|
||||
public function __construct(cacheable_object $obj) {
|
||||
$this->class = get_class($obj);
|
||||
$this->data = $obj->prepare_to_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the data as an instance of the cacheable_object class.
|
||||
* @return object
|
||||
*/
|
||||
public function restore_object() {
|
||||
$class = $this->class;
|
||||
return $class::wake_from_cache($this->data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper class used to handle ttl when the cache store doesn't natively support it.
|
||||
*
|
||||
* This class is exactly why you should use event driving invalidation of cache data rather than relying on ttl.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_ttl_wrapper {
|
||||
|
||||
/**
|
||||
* The data being stored.
|
||||
* @var mixed
|
||||
*/
|
||||
public $data;
|
||||
|
||||
/**
|
||||
* When the cache data expires as a timestamp.
|
||||
* @var int
|
||||
*/
|
||||
public $expires;
|
||||
|
||||
/**
|
||||
* Constructs a ttl cache wrapper.
|
||||
*
|
||||
* @param mixed $data
|
||||
* @param int $ttl The time to live in seconds.
|
||||
*/
|
||||
public function __construct($data, $ttl) {
|
||||
$this->data = $data;
|
||||
$this->expires = cache::now() + (int)$ttl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the data has expired.
|
||||
* @return int
|
||||
*/
|
||||
public function has_expired() {
|
||||
return ($this->expires < cache::now());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cache exception class. Just allows people to catch cache exceptions.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_exception extends moodle_exception {
|
||||
/**
|
||||
* Constructs a new exception
|
||||
*
|
||||
* @param string $errorcode
|
||||
* @param string $module
|
||||
* @param string $link
|
||||
* @param mixed $a
|
||||
* @param mixed $debuginfo
|
||||
*/
|
||||
public function __construct($errorcode, $module = 'cache', $link = '', $a = null, $debuginfo = null) {
|
||||
// This may appear like a useless override but you will notice that we have set a MUCH more useful default for $module.
|
||||
parent::__construct($errorcode, $module, $link, $a, $debuginfo);
|
||||
}
|
||||
}
|
942
cache/locallib.php
vendored
Normal file
942
cache/locallib.php
vendored
Normal file
@ -0,0 +1,942 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The supplementary cache API.
|
||||
*
|
||||
* This file is part of Moodle's cache API, affectionately called MUC.
|
||||
* It contains elements of the API that are not required in order to use caching.
|
||||
* Things in here are more in line with administration and management of the cache setup and configuration.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Cache configuration writer.
|
||||
*
|
||||
* This class should only be used when you need to write to the config, all read operations exist within the cache_config.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_config_writer extends cache_config {
|
||||
|
||||
/**
|
||||
* Returns an instance of the configuration writer.
|
||||
*
|
||||
* @return cache_config_writer
|
||||
*/
|
||||
public static function instance() {
|
||||
$factory = cache_factory::instance();
|
||||
return $factory->create_config_instance(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the current configuration.
|
||||
*/
|
||||
protected function config_save() {
|
||||
global $CFG;
|
||||
$cachefile = self::get_config_file_path();
|
||||
$directory = dirname($cachefile);
|
||||
if ($directory !== $CFG->dataroot && !file_exists($directory)) {
|
||||
$result = make_writable_directory($directory, false);
|
||||
if (!$result) {
|
||||
throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Cannot create config directory.');
|
||||
}
|
||||
}
|
||||
if (!file_exists($directory) || !is_writable($directory)) {
|
||||
throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Config directory is not writable.');
|
||||
}
|
||||
|
||||
// Prepare a configuration array to store.
|
||||
$configuration = array();
|
||||
$configuration['stores'] = $this->configstores;
|
||||
$configuration['modemappings'] = $this->configmodemappings;
|
||||
$configuration['definitions'] = $this->configdefinitions;
|
||||
$configuration['definitionmappings'] = $this->configdefinitionmappings;
|
||||
$configuration['locks'] = $this->configlocks;
|
||||
|
||||
// Prepare the file content.
|
||||
$content = "<?php defined('MOODLE_INTERNAL') || die();\n \$configuration = ".var_export($configuration, true).";";
|
||||
|
||||
// We need to create a temporary cache lock instance for use here. Remember we are generating the config file
|
||||
// it doesn't exist and thus we can't use the normal API for this (it'll just try to use config).
|
||||
$lockconf = reset($this->configlocks);
|
||||
if ($lockconf === false) {
|
||||
debugging('Your cache configuration file is out of date and needs to be refreshed.', DEBUG_DEVELOPER);
|
||||
// Use the default
|
||||
$lockconf = array(
|
||||
'name' => 'cachelock_file_default',
|
||||
'type' => 'cachelock_file',
|
||||
'dir' => 'filelocks',
|
||||
'default' => true
|
||||
);
|
||||
}
|
||||
$factory = cache_factory::instance();
|
||||
$locking = $factory->create_lock_instance($lockconf);
|
||||
if ($locking->lock('configwrite', 'config', true)) {
|
||||
// Its safe to use w mode here because we have already acquired the lock.
|
||||
$handle = fopen($cachefile, 'w');
|
||||
fwrite($handle, $content);
|
||||
fflush($handle);
|
||||
fclose($handle);
|
||||
$locking->unlock('configwrite', 'config');
|
||||
} else {
|
||||
throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Unable to open the cache config file.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a plugin instance.
|
||||
*
|
||||
* This function also calls save so you should redirect immediately, or at least very shortly after
|
||||
* calling this method.
|
||||
*
|
||||
* @param string $name The name for the instance (must be unique)
|
||||
* @param string $plugin The name of the plugin.
|
||||
* @param array $configuration The configuration data for the plugin instance.
|
||||
* @return bool
|
||||
* @throws cache_exception
|
||||
*/
|
||||
public function add_store_instance($name, $plugin, array $configuration = array()) {
|
||||
if (array_key_exists($name, $this->configstores)) {
|
||||
throw new cache_exception('Duplicate name specificed for cache plugin instance. You must provide a unique name.');
|
||||
}
|
||||
$class = 'cachestore_'.$plugin;
|
||||
if (!class_exists($class)) {
|
||||
$plugins = get_plugin_list_with_file('cachestore', 'lib.php');
|
||||
if (!array_key_exists($plugin, $plugins)) {
|
||||
throw new cache_exception('Invalid plugin name specified. The plugin does not exist or is not valid.');
|
||||
}
|
||||
$file = $plugins[$plugin];
|
||||
if (file_exists($file)) {
|
||||
require_once($file);
|
||||
}
|
||||
if (!class_exists($class)) {
|
||||
throw new cache_exception('Invalid cache plugin specified. The plugin does not contain the required class.');
|
||||
}
|
||||
}
|
||||
if (!is_subclass_of($class, 'cache_store')) {
|
||||
throw new cache_exception('Invalid cache plugin specified. The plugin does not extend the required class.');
|
||||
}
|
||||
if (!$class::are_requirements_met()) {
|
||||
throw new cache_exception('Unable to add new cache plugin instance. The requested plugin type is not supported.');
|
||||
}
|
||||
$this->configstores[$name] = array(
|
||||
'name' => $name,
|
||||
'plugin' => $plugin,
|
||||
'configuration' => $configuration,
|
||||
'features' => $class::get_supported_features($configuration),
|
||||
'modes' => $class::get_supported_modes($configuration),
|
||||
'mappingsonly' => !empty($configuration['mappingsonly']),
|
||||
'class' => $class,
|
||||
'default' => false
|
||||
);
|
||||
if (array_key_exists('lock', $configuration)) {
|
||||
$this->configstores[$name]['lock'] = $configuration['lock'];
|
||||
unset($this->configstores[$name]['configuration']['lock']);
|
||||
}
|
||||
$this->config_save();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mode mappings.
|
||||
*
|
||||
* These determine the default caches for the different modes.
|
||||
* This function also calls save so you should redirect immediately, or at least very shortly after
|
||||
* calling this method.
|
||||
*
|
||||
* @param array $modemappings
|
||||
* @return bool
|
||||
* @throws cache_exception
|
||||
*/
|
||||
public function set_mode_mappings(array $modemappings) {
|
||||
$mappings = array(
|
||||
cache_store::MODE_APPLICATION => array(),
|
||||
cache_store::MODE_SESSION => array(),
|
||||
cache_store::MODE_REQUEST => array(),
|
||||
);
|
||||
foreach ($modemappings as $mode => $stores) {
|
||||
if (!array_key_exists($mode, $mappings)) {
|
||||
throw new cache_exception('The cache mode for the new mapping does not exist');
|
||||
}
|
||||
$sort = 0;
|
||||
foreach ($stores as $store) {
|
||||
if (!array_key_exists($store, $this->configstores)) {
|
||||
throw new cache_exception('The instance name for the new mapping does not exist');
|
||||
}
|
||||
if (array_key_exists($store, $mappings[$mode])) {
|
||||
throw new cache_exception('This cache mapping already exists');
|
||||
}
|
||||
$mappings[$mode][] = array(
|
||||
'store' => $store,
|
||||
'mode' => $mode,
|
||||
'sort' => $sort++
|
||||
);
|
||||
}
|
||||
}
|
||||
$this->configmodemappings = array_merge(
|
||||
$mappings[cache_store::MODE_APPLICATION],
|
||||
$mappings[cache_store::MODE_SESSION],
|
||||
$mappings[cache_store::MODE_REQUEST]
|
||||
);
|
||||
|
||||
$this->config_save();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits a give plugin instance.
|
||||
*
|
||||
* The plugin instance is determined by its name, hence you cannot rename plugins.
|
||||
* This function also calls save so you should redirect immediately, or at least very shortly after
|
||||
* calling this method.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $plugin
|
||||
* @param array $configuration
|
||||
* @return bool
|
||||
* @throws cache_exception
|
||||
*/
|
||||
public function edit_store_instance($name, $plugin, $configuration) {
|
||||
if (!array_key_exists($name, $this->configstores)) {
|
||||
throw new cache_exception('The requested instance does not exist.');
|
||||
}
|
||||
$plugins = get_plugin_list_with_file('cachestore', 'lib.php');
|
||||
if (!array_key_exists($plugin, $plugins)) {
|
||||
throw new cache_exception('Invalid plugin name specified. The plugin either does not exist or is not valid.');
|
||||
}
|
||||
$class = 'cachestore_'.$plugin;
|
||||
$file = $plugins[$plugin];
|
||||
if (!class_exists($class)) {
|
||||
if (file_exists($file)) {
|
||||
require_once($file);
|
||||
}
|
||||
if (!class_exists($class)) {
|
||||
throw new cache_exception('Invalid cache plugin specified. The plugin does not contain the required class.'.$class);
|
||||
}
|
||||
}
|
||||
$this->configstores[$name] = array(
|
||||
'name' => $name,
|
||||
'plugin' => $plugin,
|
||||
'configuration' => $configuration,
|
||||
'features' => $class::get_supported_features($configuration),
|
||||
'modes' => $class::get_supported_modes($configuration),
|
||||
'mappingsonly' => !empty($configuration['mappingsonly']),
|
||||
'class' => $class,
|
||||
'default' => $this->configstores[$name]['default'] // Can't change the default.
|
||||
);
|
||||
if (array_key_exists('lock', $configuration)) {
|
||||
$this->configstores[$name]['lock'] = $configuration['lock'];
|
||||
unset($this->configstores[$name]['configuration']['lock']);
|
||||
}
|
||||
$this->config_save();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a store instance.
|
||||
*
|
||||
* This function also calls save so you should redirect immediately, or at least very shortly after
|
||||
* calling this method.
|
||||
*
|
||||
* @param string $name The name of the instance to delete.
|
||||
* @return bool
|
||||
* @throws cache_exception
|
||||
*/
|
||||
public function delete_store_instance($name) {
|
||||
if (!array_key_exists($name, $this->configstores)) {
|
||||
throw new cache_exception('The requested store does not exist.');
|
||||
}
|
||||
if ($this->configstores[$name]['default']) {
|
||||
throw new cache_exception('The can not delete the default stores.');
|
||||
}
|
||||
foreach ($this->configmodemappings as $mapping) {
|
||||
if ($mapping['store'] === $name) {
|
||||
throw new cache_exception('You cannot delete a cache store that has mode mappings.');
|
||||
}
|
||||
}
|
||||
foreach ($this->configdefinitionmappings as $mapping) {
|
||||
if ($mapping['store'] === $name) {
|
||||
throw new cache_exception('You cannot delete a cache store that has definition mappings.');
|
||||
}
|
||||
}
|
||||
unset($this->configstores[$name]);
|
||||
$this->config_save();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the default configuration and saves it.
|
||||
*
|
||||
* This function calls config_save, however it is safe to continue using it afterwards as this function should only ever
|
||||
* be called when there is no configuration file already.
|
||||
*/
|
||||
public static function create_default_configuration() {
|
||||
global $CFG;
|
||||
|
||||
// HACK ALERT.
|
||||
// We probably need to come up with a better way to create the default stores, or at least ensure 100% that the
|
||||
// default store plugins are protected from deletion.
|
||||
require_once($CFG->dirroot.'/cache/stores/file/lib.php');
|
||||
require_once($CFG->dirroot.'/cache/stores/session/lib.php');
|
||||
require_once($CFG->dirroot.'/cache/stores/static/lib.php');
|
||||
|
||||
$writer = new self;
|
||||
$writer->configstores = array(
|
||||
'default_application' => array(
|
||||
'name' => 'default_application',
|
||||
'plugin' => 'file',
|
||||
'configuration' => array(),
|
||||
'features' => cachestore_file::get_supported_features(),
|
||||
'modes' => cache_store::MODE_APPLICATION,
|
||||
'default' => true,
|
||||
),
|
||||
'default_session' => array(
|
||||
'name' => 'default_session',
|
||||
'plugin' => 'session',
|
||||
'configuration' => array(),
|
||||
'features' => cachestore_session::get_supported_features(),
|
||||
'modes' => cache_store::MODE_SESSION,
|
||||
'default' => true,
|
||||
),
|
||||
'default_request' => array(
|
||||
'name' => 'default_request',
|
||||
'plugin' => 'static',
|
||||
'configuration' => array(),
|
||||
'features' => cachestore_static::get_supported_features(),
|
||||
'modes' => cache_store::MODE_REQUEST,
|
||||
'default' => true,
|
||||
)
|
||||
);
|
||||
$writer->configdefinitions = self::locate_definitions();
|
||||
$writer->configmodemappings = array(
|
||||
array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'store' => 'default_application',
|
||||
'sort' => -1
|
||||
),
|
||||
array(
|
||||
'mode' => cache_store::MODE_SESSION,
|
||||
'store' => 'default_session',
|
||||
'sort' => -1
|
||||
),
|
||||
array(
|
||||
'mode' => cache_store::MODE_REQUEST,
|
||||
'store' => 'default_request',
|
||||
'sort' => -1
|
||||
)
|
||||
);
|
||||
$writer->configlocks = array(
|
||||
'default_file_lock' => array(
|
||||
'name' => 'cachelock_file_default',
|
||||
'type' => 'cachelock_file',
|
||||
'dir' => 'filelocks',
|
||||
'default' => true
|
||||
)
|
||||
);
|
||||
$writer->config_save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the definition in the configuration from those found in the cache files.
|
||||
*
|
||||
* Calls config_save further down, you should redirect immediately or asap after calling this method.
|
||||
*/
|
||||
public static function update_definitions() {
|
||||
$config = self::instance();
|
||||
$config->write_definitions_to_cache(self::locate_definitions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates all of the definition files.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function locate_definitions() {
|
||||
global $CFG;
|
||||
|
||||
$files = array();
|
||||
if (file_exists($CFG->dirroot.'/lib/db/caches.php')) {
|
||||
$files['core'] = $CFG->dirroot.'/lib/db/caches.php';
|
||||
}
|
||||
|
||||
$plugintypes = get_plugin_types();
|
||||
foreach ($plugintypes as $type => $location) {
|
||||
$plugins = get_plugin_list_with_file($type, 'db/caches.php');
|
||||
foreach ($plugins as $plugin => $filepath) {
|
||||
$component = clean_param($type.'_'.$plugin, PARAM_COMPONENT); // Standardised plugin name.
|
||||
$files[$component] = $filepath;
|
||||
}
|
||||
}
|
||||
|
||||
$definitions = array();
|
||||
foreach ($files as $component => $file) {
|
||||
$filedefs = self::load_caches_file($file);
|
||||
foreach ($filedefs as $area => $definition) {
|
||||
$area = clean_param($area, PARAM_AREA);
|
||||
$id = $component.'/'.$area;
|
||||
$definition['component'] = $component;
|
||||
$definition['area'] = $area;
|
||||
if (array_key_exists($id, $definitions)) {
|
||||
debugging('Error: duplicate cache definition found with name '.$name, DEBUG_DEVELOPER);
|
||||
continue;
|
||||
}
|
||||
$definitions[$id] = $definition;
|
||||
}
|
||||
}
|
||||
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the updated definitions for the config file.
|
||||
* @param array $definitions
|
||||
*/
|
||||
private function write_definitions_to_cache(array $definitions) {
|
||||
$this->configdefinitions = $definitions;
|
||||
foreach ($this->configdefinitionmappings as $key => $mapping) {
|
||||
if (!array_key_exists($mapping['definition'], $definitions)) {
|
||||
unset($this->configdefinitionmappings[$key]);
|
||||
}
|
||||
}
|
||||
$this->config_save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the caches file if it exists.
|
||||
* @param string $file Absolute path to the file.
|
||||
* @return array
|
||||
*/
|
||||
private static function load_caches_file($file) {
|
||||
if (!file_exists($file)) {
|
||||
return array();
|
||||
}
|
||||
$definitions = array();
|
||||
include($file);
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mappings for a given definition.
|
||||
*
|
||||
* @param string $definition
|
||||
* @param array $mappings
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function set_definition_mappings($definition, $mappings) {
|
||||
if (!array_key_exists($definition, $this->configdefinitions)) {
|
||||
throw new coding_exception('Invalid definition name passed when updating mappings.');
|
||||
}
|
||||
foreach ($mappings as $store) {
|
||||
if (!array_key_exists($store, $this->configstores)) {
|
||||
throw new coding_exception('Invalid store name passed when updating definition mappings.');
|
||||
}
|
||||
}
|
||||
foreach ($this->configdefinitionmappings as $key => $mapping) {
|
||||
if ($mapping['definition'] == $definition) {
|
||||
unset($this->configdefinitionmappings[$key]);
|
||||
}
|
||||
}
|
||||
$sort = count($mappings);
|
||||
foreach ($mappings as $store) {
|
||||
$this->configdefinitionmappings[] = array(
|
||||
'store' => $store,
|
||||
'definition' => $definition,
|
||||
'sort' => $sort
|
||||
);
|
||||
$sort--;
|
||||
}
|
||||
|
||||
$this->config_save();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A cache helper for administration tasks
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class cache_administration_helper extends cache_helper {
|
||||
|
||||
/**
|
||||
* Returns an array containing all of the information about stores a renderer needs.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_store_instance_summaries() {
|
||||
$return = array();
|
||||
$default = array();
|
||||
$instance = cache_config::instance();
|
||||
$stores = $instance->get_all_stores();
|
||||
foreach ($stores as $name => $details) {
|
||||
$class = $details['class'];
|
||||
$store = new $class($details['name'], $details['configuration']);
|
||||
$record = array(
|
||||
'name' => $name,
|
||||
'plugin' => $details['plugin'],
|
||||
'default' => $details['default'],
|
||||
'isready' => $store->is_ready(),
|
||||
'requirementsmet' => $store->are_requirements_met(),
|
||||
'mappings' => 0,
|
||||
'modes' => array(
|
||||
cache_store::MODE_APPLICATION =>
|
||||
($store->get_supported_modes($return) & cache_store::MODE_APPLICATION) == cache_store::MODE_APPLICATION,
|
||||
cache_store::MODE_SESSION =>
|
||||
($store->get_supported_modes($return) & cache_store::MODE_SESSION) == cache_store::MODE_SESSION,
|
||||
cache_store::MODE_REQUEST =>
|
||||
($store->get_supported_modes($return) & cache_store::MODE_REQUEST) == cache_store::MODE_REQUEST,
|
||||
),
|
||||
'supports' => array(
|
||||
'multipleidentifiers' => $store->supports_multiple_indentifiers(),
|
||||
'dataguarantee' => $store->supports_data_guarantee(),
|
||||
'nativettl' => $store->supports_native_ttl(),
|
||||
'nativelocking' => ($store instanceof cache_is_lockable),
|
||||
'keyawareness' => ($store instanceof cache_is_key_aware),
|
||||
)
|
||||
);
|
||||
if (empty($details['default'])) {
|
||||
$return[$name] = $record;
|
||||
} else {
|
||||
$default[$name] = $record;
|
||||
}
|
||||
}
|
||||
|
||||
ksort($return);
|
||||
ksort($default);
|
||||
$return = $return + $default;
|
||||
|
||||
foreach ($instance->get_definition_mappings() as $mapping) {
|
||||
if (!array_key_exists($mapping['store'], $return)) {
|
||||
continue;
|
||||
}
|
||||
$return[$mapping['store']]['mappings']++;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of information about plugins, everything a renderer needs.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_store_plugin_summaries() {
|
||||
$return = array();
|
||||
$plugins = get_plugin_list_with_file('cachestore', 'lib.php', true);
|
||||
foreach ($plugins as $plugin => $path) {
|
||||
$class = 'cachestore_'.$plugin;
|
||||
$return[$plugin] = array(
|
||||
'name' => get_string('pluginname', 'cachestore_'.$plugin),
|
||||
'requirementsmet' => $class::are_requirements_met(),
|
||||
'instances' => 0,
|
||||
'modes' => array(
|
||||
cache_store::MODE_APPLICATION => ($class::get_supported_modes() & cache_store::MODE_APPLICATION),
|
||||
cache_store::MODE_SESSION => ($class::get_supported_modes() & cache_store::MODE_SESSION),
|
||||
cache_store::MODE_REQUEST => ($class::get_supported_modes() & cache_store::MODE_REQUEST),
|
||||
),
|
||||
'supports' => array(
|
||||
'multipleidentifiers' => ($class::get_supported_features() & cache_store::SUPPORTS_MULTIPLE_IDENTIFIERS),
|
||||
'dataguarantee' => ($class::get_supported_features() & cache_store::SUPPORTS_DATA_GUARANTEE),
|
||||
'nativettl' => ($class::get_supported_features() & cache_store::SUPPORTS_NATIVE_TTL),
|
||||
'nativelocking' => (in_array('cache_is_lockable', class_implements($class))),
|
||||
'keyawareness' => (array_key_exists('cache_is_key_aware', class_implements($class))),
|
||||
),
|
||||
'canaddinstance' => ($class::can_add_instance())
|
||||
);
|
||||
}
|
||||
|
||||
$instance = cache_config::instance();
|
||||
$stores = $instance->get_all_stores();
|
||||
foreach ($stores as $store) {
|
||||
$plugin = $store['plugin'];
|
||||
if (array_key_exists($plugin, $return)) {
|
||||
$return[$plugin]['instances']++;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array about the definitions. All the information a renderer needs.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_definition_summaries() {
|
||||
$instance = cache_config::instance();
|
||||
$definitions = $instance->get_definitions();
|
||||
|
||||
$storenames = array();
|
||||
foreach ($instance->get_all_stores() as $key => $store) {
|
||||
if (!empty($store['default'])) {
|
||||
$storenames[$key] = new lang_string('store_'.$key, 'cache');
|
||||
}
|
||||
}
|
||||
|
||||
$modemappings = array();
|
||||
foreach ($instance->get_mode_mappings() as $mapping) {
|
||||
$mode = $mapping['mode'];
|
||||
if (!array_key_exists($mode, $modemappings)) {
|
||||
$modemappings[$mode] = array();
|
||||
}
|
||||
if (array_key_exists($mapping['store'], $storenames)) {
|
||||
$modemappings[$mode][] = $storenames[$mapping['store']];
|
||||
} else {
|
||||
$modemappings[$mode][] = $mapping['store'];
|
||||
}
|
||||
}
|
||||
|
||||
$definitionmappings = array();
|
||||
foreach ($instance->get_definition_mappings() as $mapping) {
|
||||
$definition = $mapping['definition'];
|
||||
if (!array_key_exists($definition, $definitionmappings)) {
|
||||
$definitionmappings[$definition] = array();
|
||||
}
|
||||
if (array_key_exists($mapping['store'], $storenames)) {
|
||||
$definitionmappings[$definition][] = $storenames[$mapping['store']];
|
||||
} else {
|
||||
$definitionmappings[$definition][] = $mapping['store'];
|
||||
}
|
||||
}
|
||||
|
||||
$return = array();
|
||||
|
||||
foreach ($definitions as $id => $definition) {
|
||||
|
||||
$mappings = array();
|
||||
if (array_key_exists($id, $definitionmappings)) {
|
||||
$mappings = $definitionmappings[$id];
|
||||
} else if (empty($definition['mappingsonly'])) {
|
||||
$mappings = $modemappings[$definition['mode']];
|
||||
}
|
||||
|
||||
$return[$id] = array(
|
||||
'id' => $id,
|
||||
'name' => cache_helper::get_definition_name($definition),
|
||||
'mode' => $definition['mode'],
|
||||
'component' => $definition['component'],
|
||||
'area' => $definition['area'],
|
||||
'mappings' => $mappings
|
||||
);
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all of the actions that can be performed on a definition.
|
||||
* @param context $context
|
||||
* @return array
|
||||
*/
|
||||
public static function get_definition_actions(context $context) {
|
||||
if (has_capability('moodle/site:config', $context)) {
|
||||
return array(
|
||||
array(
|
||||
'text' => get_string('editmappings', 'cache'),
|
||||
'url' => new moodle_url('/cache/admin.php', array('action' => 'editdefinitionmapping', 'sesskey' => sesskey()))
|
||||
)
|
||||
);
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all of the actions that can be performed on a store.
|
||||
*
|
||||
* @param string $name The name of the store
|
||||
* @param array $storedetails
|
||||
* @return array
|
||||
*/
|
||||
public static function get_store_instance_actions($name, array $storedetails) {
|
||||
$actions = array();
|
||||
if (has_capability('moodle/site:config', get_system_context())) {
|
||||
$baseurl = new moodle_url('/cache/admin.php', array('store' => $name, 'sesskey' => sesskey()));
|
||||
if (empty($storedetails['default'])) {
|
||||
$actions[] = array(
|
||||
'text' => get_string('editstore', 'cache'),
|
||||
'url' => new moodle_url($baseurl, array('action' => 'editstore', 'plugin' => $storedetails['plugin']))
|
||||
);
|
||||
$actions[] = array(
|
||||
'text' => get_string('deletestore', 'cache'),
|
||||
'url' => new moodle_url($baseurl, array('action' => 'deletestore'))
|
||||
);
|
||||
}
|
||||
$actions[] = array(
|
||||
'text' => get_string('purge', 'cache'),
|
||||
'url' => new moodle_url($baseurl, array('action' => 'purge'))
|
||||
);
|
||||
}
|
||||
return $actions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all of the actions that can be performed on a plugin.
|
||||
*
|
||||
* @param string $name The name of the plugin
|
||||
* @param array $plugindetails
|
||||
* @return array
|
||||
*/
|
||||
public static function get_store_plugin_actions($name, array $plugindetails) {
|
||||
$actions = array();
|
||||
if (has_capability('moodle/site:config', get_system_context())) {
|
||||
if (!empty($plugindetails['canaddinstance'])) {
|
||||
$url = new moodle_url('/cache/admin.php', array('action' => 'addstore', 'plugin' => $name, 'sesskey' => sesskey()));
|
||||
$actions[] = array(
|
||||
'text' => get_string('addinstance', 'cache'),
|
||||
'url' => $url
|
||||
);
|
||||
}
|
||||
}
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a form that can be used to add a store instance.
|
||||
*
|
||||
* @param string $plugin The plugin to add an instance of
|
||||
* @return cachestore_addinstance_form
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public static function get_add_store_form($plugin) {
|
||||
global $CFG; // Needed for includes.
|
||||
$plugins = get_plugin_list('cachestore');
|
||||
if (!array_key_exists($plugin, $plugins)) {
|
||||
throw new coding_exception('Invalid cache plugin used when trying to create an edit form.');
|
||||
}
|
||||
$plugindir = $plugins[$plugin];
|
||||
$class = 'cachestore_addinstance_form';
|
||||
if (file_exists($plugindir.'/addinstanceform.php')) {
|
||||
require_once($plugindir.'/addinstanceform.php');
|
||||
if (class_exists('cachestore_'.$plugin.'_addinstance_form')) {
|
||||
$class = 'cachestore_'.$plugin.'_addinstance_form';
|
||||
if (!array_key_exists('cachestore_addinstance_form', class_parents($class))) {
|
||||
throw new coding_exception('Cache plugin add instance forms must extend cachestore_addinstance_form');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$locks = self::get_possible_locks_for_stores($plugindir, $plugin);
|
||||
|
||||
$url = new moodle_url('/cache/admin.php', array('action' => 'addstore'));
|
||||
return new $class($url, array('plugin' => $plugin, 'store' => null, 'locks' => $locks));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a form that can be used to edit a store instance.
|
||||
*
|
||||
* @param string $plugin
|
||||
* @param string $store
|
||||
* @return cachestore_addinstance_form
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public static function get_edit_store_form($plugin, $store) {
|
||||
global $CFG; // Needed for includes.
|
||||
$plugins = get_plugin_list('cachestore');
|
||||
if (!array_key_exists($plugin, $plugins)) {
|
||||
throw new coding_exception('Invalid cache plugin used when trying to create an edit form.');
|
||||
}
|
||||
$factory = cache_factory::instance();
|
||||
$config = $factory->create_config_instance();
|
||||
$stores = $config->get_all_stores();
|
||||
if (!array_key_exists($store, $stores)) {
|
||||
throw new coding_exception('Invalid store name given when trying to create an edit form.');
|
||||
}
|
||||
$plugindir = $plugins[$plugin];
|
||||
$class = 'cachestore_addinstance_form';
|
||||
if (file_exists($plugindir.'/addinstanceform.php')) {
|
||||
require_once($plugindir.'/addinstanceform.php');
|
||||
if (class_exists('cachestore_'.$plugin.'_addinstance_form')) {
|
||||
$class = 'cachestore_'.$plugin.'_addinstance_form';
|
||||
if (!array_key_exists('cachestore_addinstance_form', class_parents($class))) {
|
||||
throw new coding_exception('Cache plugin add instance forms must extend cachestore_addinstance_form');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$locks = self::get_possible_locks_for_stores($plugindir, $plugin);
|
||||
|
||||
$url = new moodle_url('/cache/admin.php', array('action' => 'editstore'));
|
||||
return new $class($url, array('plugin' => $plugin, 'store' => $store, 'locks' => $locks));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of suitable lock instances for use with this plugin, or false if the plugin handles locking itself.
|
||||
*
|
||||
* @param string $plugindir
|
||||
* @param string $plugin
|
||||
* @return array|false
|
||||
*/
|
||||
protected static function get_possible_locks_for_stores($plugindir, $plugin) {
|
||||
global $CFG; // Needed for includes.
|
||||
$supportsnativelocking = false;
|
||||
if (file_exists($plugindir.'/lib.php')) {
|
||||
require_once($plugindir.'/lib.php');
|
||||
$pluginclass = 'cachestore_'.$plugin;
|
||||
if (class_exists($pluginclass)) {
|
||||
$supportsnativelocking = array_key_exists('cache_is_lockable', class_implements($pluginclass));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$supportsnativelocking) {
|
||||
$config = cache_config::instance();
|
||||
$locks = array();
|
||||
foreach ($config->get_locks() as $lock => $conf) {
|
||||
if (!empty($conf['default'])) {
|
||||
$name = get_string($lock, 'cache');
|
||||
} else {
|
||||
$name = $lock;
|
||||
}
|
||||
$locks[$lock] = $name;
|
||||
}
|
||||
} else {
|
||||
$locks = false;
|
||||
}
|
||||
|
||||
return $locks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the results of the add/edit instance form data for a plugin returning an array of config information suitable to
|
||||
* store in configuration.
|
||||
*
|
||||
* @param stdClass $data The mform data.
|
||||
* @return array
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public static function get_store_configuration_from_data(stdClass $data) {
|
||||
global $CFG;
|
||||
$file = $CFG->dirroot.'/cache/stores/'.$data->plugin.'/lib.php';
|
||||
if (!file_exists($file)) {
|
||||
throw new coding_exception('Invalid cache plugin provided. '.$file);
|
||||
}
|
||||
require_once($file);
|
||||
$class = 'cachestore_'.$data->plugin;
|
||||
$method = 'config_get_configuration_array';
|
||||
if (!class_exists($class)) {
|
||||
throw new coding_exception('Invalid cache plugin provided.');
|
||||
}
|
||||
if (method_exists($class, $method)) {
|
||||
return call_user_func(array($class, $method), $data);
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of stores that are suitable to be used for a given definition.
|
||||
*
|
||||
* @param string $component
|
||||
* @param string $area
|
||||
* @return array Array containing 3 elements
|
||||
* 1. An array of currently used stores
|
||||
* 2. An array of suitable stores
|
||||
* 3. An array of default stores
|
||||
*/
|
||||
public static function get_definition_store_options($component, $area) {
|
||||
$factory = cache_factory::instance();
|
||||
$definition = $factory->create_definition($component, $area);
|
||||
$config = cache_config::instance();
|
||||
$currentstores = $config->get_stores_for_definition($definition);
|
||||
$possiblestores = $config->get_stores($definition->get_mode(), $definition->get_requirements_bin());
|
||||
|
||||
$defaults = array();
|
||||
foreach ($currentstores as $key => $store) {
|
||||
if (!empty($store['default'])) {
|
||||
$defaults[] = $key;
|
||||
unset($currentstores[$key]);
|
||||
}
|
||||
}
|
||||
foreach ($possiblestores as $key => $store) {
|
||||
if ($store['default']) {
|
||||
unset($possiblestores[$key]);
|
||||
$possiblestores[$key] = $store;
|
||||
}
|
||||
}
|
||||
return array($currentstores, $possiblestores, $defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default stores for all modes.
|
||||
*
|
||||
* @return array An array containing sub-arrays, one for each mode.
|
||||
*/
|
||||
public static function get_default_mode_stores() {
|
||||
$instance = cache_config::instance();
|
||||
$storenames = array();
|
||||
foreach ($instance->get_all_stores() as $key => $store) {
|
||||
if (!empty($store['default'])) {
|
||||
$storenames[$key] = new lang_string('store_'.$key, 'cache');
|
||||
}
|
||||
}
|
||||
$modemappings = array(
|
||||
cache_store::MODE_APPLICATION => array(),
|
||||
cache_store::MODE_SESSION => array(),
|
||||
cache_store::MODE_REQUEST => array(),
|
||||
);
|
||||
foreach ($instance->get_mode_mappings() as $mapping) {
|
||||
$mode = $mapping['mode'];
|
||||
if (!array_key_exists($mode, $modemappings)) {
|
||||
debugging('Unknown mode in cache store mode mappings', DEBUG_DEVELOPER);
|
||||
continue;
|
||||
}
|
||||
if (array_key_exists($mapping['store'], $storenames)) {
|
||||
$modemappings[$mode][$mapping['store']] = $storenames[$mapping['store']];
|
||||
} else {
|
||||
$modemappings[$mode][$mapping['store']] = $mapping['store'];
|
||||
}
|
||||
}
|
||||
return $modemappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array summarising the locks available in the system
|
||||
*/
|
||||
public static function get_lock_summaries() {
|
||||
$locks = array();
|
||||
$instance = cache_config::instance();
|
||||
$stores = $instance->get_all_stores();
|
||||
foreach ($instance->get_locks() as $lock) {
|
||||
$default = !empty($lock['default']);
|
||||
if ($default) {
|
||||
$name = new lang_string($lock['name'], 'cache');
|
||||
} else {
|
||||
$name = $lock['name'];
|
||||
}
|
||||
$uses = 0;
|
||||
foreach ($stores as $store) {
|
||||
if (!empty($store['lock']) && $store['lock'] === $lock['name']) {
|
||||
$uses++;
|
||||
}
|
||||
}
|
||||
$lockdata = array(
|
||||
'name' => $name,
|
||||
'default' => $default,
|
||||
'uses' => $uses
|
||||
);
|
||||
$locks[] = $lockdata;
|
||||
}
|
||||
return $locks;
|
||||
}
|
||||
}
|
26
cache/locks/file/lang/en/cachelock_file.php
vendored
Normal file
26
cache/locks/file/lang/en/cachelock_file.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Strings for the cache file locking plugin
|
||||
*
|
||||
* @package cachelock_file
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$string['pluginname'] = 'File locking';
|
237
cache/locks/file/lib.php
vendored
Normal file
237
cache/locks/file/lib.php
vendored
Normal file
@ -0,0 +1,237 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* File locking for the Cache API
|
||||
*
|
||||
* @package cachelock_file
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* File locking plugin
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cachelock_file implements cache_lock_interface {
|
||||
|
||||
/**
|
||||
* The name of the cache lock instance
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The absolute directory in which lock files will be created and looked for.
|
||||
* @var string
|
||||
*/
|
||||
protected $cachedir;
|
||||
|
||||
/**
|
||||
* The maximum life in seconds for a lock file. By default null for none.
|
||||
* @var int|null
|
||||
*/
|
||||
protected $maxlife = null;
|
||||
|
||||
/**
|
||||
* The number of attempts to acquire a lock when blocking is required before throwing an exception.
|
||||
* @var int
|
||||
*/
|
||||
protected $blockattempts = 100;
|
||||
|
||||
/**
|
||||
* An array containing the locks that have been acquired but not released so far.
|
||||
* @var array Array of key => lock file path
|
||||
*/
|
||||
protected $locks = array();
|
||||
|
||||
/**
|
||||
* Initialises the cache lock instance.
|
||||
*
|
||||
* @param string $name The name of the cache lock
|
||||
* @param array $configuration
|
||||
*/
|
||||
public function __construct($name, array $configuration = array()) {
|
||||
$this->name = $name;
|
||||
if (!array_key_exists('dir', $configuration)) {
|
||||
$this->cachedir = make_cache_directory(md5($name));
|
||||
} else {
|
||||
$dir = $configuration['dir'];
|
||||
if (strpos($dir, '/') !== false && strpos($dir, '.') !== 0) {
|
||||
// This looks like an absolute path.
|
||||
if (file_exists($dir) && is_dir($dir) && is_writable($dir)) {
|
||||
$this->cachedir = $dir;
|
||||
}
|
||||
}
|
||||
if (empty($this->cachedir)) {
|
||||
$dir = preg_replace('#[^a-zA-Z0-9_]#', '_', $dir);
|
||||
$this->cachedir = make_cache_directory($dir);
|
||||
}
|
||||
}
|
||||
if (array_key_exists('maxlife', $configuration) && is_number($configuration['maxlife'])) {
|
||||
$maxlife = (int)$configuration['maxlife'];
|
||||
// Minimum lock time is 60 seconds.
|
||||
$this->maxlife = max($maxlife, 60);
|
||||
}
|
||||
if (array_key_exists('blockattempts', $configuration) && is_number($configuration['blockattempts'])) {
|
||||
$this->blockattempts = (int)$configuration['blockattempts'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire a lock.
|
||||
*
|
||||
* If the lock can be acquired:
|
||||
* This function will return true.
|
||||
*
|
||||
* If the lock cannot be acquired the result of this method is determined by the block param:
|
||||
* $block = true (default)
|
||||
* The function will block any further execution unti the lock can be acquired.
|
||||
* This involves the function attempting to acquire the lock and the sleeping for a period of time. This process
|
||||
* will be repeated until the lock is required or until a limit is hit (100 by default) in which case a cache
|
||||
* exception will be thrown.
|
||||
* $block = false
|
||||
* The function will return false immediately.
|
||||
*
|
||||
* If a max life has been specified and the lock can not be acquired then the lock file will be checked against this time.
|
||||
* In the case that the file exceeds that max time it will be forcefully deleted.
|
||||
* Because this can obviously be a dangerous thing it is not used by default. If it is used it should be set high enough that
|
||||
* we can be as sure as possible that the executing code has completed.
|
||||
*
|
||||
* @param string $key The key that we want to lock
|
||||
* @param string $ownerid A unique identifier for the owner of this lock. Not used by default.
|
||||
* @param bool $block True if we want the program block further execution until the lock has been acquired.
|
||||
* @return bool
|
||||
* @throws cache_exception If block is set to true and more than 100 attempts have been made to acquire a lock.
|
||||
*/
|
||||
public function lock($key, $ownerid, $block = false) {
|
||||
// Get the name of the lock file we want to use.
|
||||
$lockfile = $this->get_lock_file($key);
|
||||
|
||||
// Attempt to create a handle to the lock file.
|
||||
// Mode xb is the secret to this whole function.
|
||||
// x = Creates the file and opens it for writing. If the file already exists fopen returns false and a warning is thrown.
|
||||
// b = Forces binary mode.
|
||||
$result = @fopen($lockfile, 'xb');
|
||||
|
||||
// Check if we could create the file or not.
|
||||
if ($result === false) {
|
||||
// Lock exists already.
|
||||
if ($this->maxlife !== null && !array_key_exists($key, $this->locks)) {
|
||||
$mtime = filemtime($lockfile);
|
||||
if ($mtime < time() - $this->maxlife) {
|
||||
$this->unlock($key, true);
|
||||
$result = $this->lock($key, false);
|
||||
if ($result) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($block) {
|
||||
// OK we are blocking. We had better sleep and then retry to lock.
|
||||
$iterations = 0;
|
||||
$maxiterations = $this->blockattempts;
|
||||
while (($result = $this->lock($key, false)) === false) {
|
||||
// Usleep causes the application to cleep to x microseconds.
|
||||
// Before anyone asks there are 1'000'000 microseconds to a second.
|
||||
usleep(rand(1000, 50000)); // Sleep between 1 and 50 milliseconds.
|
||||
$iterations++;
|
||||
if ($iterations > $maxiterations) {
|
||||
// BOOM! We've exceeded the maximum number of iterations we want to block for.
|
||||
throw new cache_exception('ex_unabletolock');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
// We have the lock.
|
||||
fclose($result);
|
||||
$this->locks[$key] = $lockfile;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases an acquired lock.
|
||||
*
|
||||
* For more details see {@link cache_lock::unlock()}
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $ownerid A unique identifier for the owner of this lock. Not used by default.
|
||||
* @param bool $forceunlock If set to true the lock will be removed if it exists regardless of whether or not we own it.
|
||||
* @return bool
|
||||
*/
|
||||
public function unlock($key, $ownerid, $forceunlock = false) {
|
||||
if (array_key_exists($key, $this->locks)) {
|
||||
@unlink($this->locks[$key]);
|
||||
unset($this->locks[$key]);
|
||||
return true;
|
||||
} else if ($forceunlock) {
|
||||
$lockfile = $this->get_lock_file($key);
|
||||
if (file_exists($lockfile)) {
|
||||
@unlink($lockfile);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// You cannot unlock a file you didn't lock.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given key is locked.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $ownerid
|
||||
*/
|
||||
public function check_state($key, $ownerid) {
|
||||
if (key_exists($key, $this->locks)) {
|
||||
// The key is locked and we own it.
|
||||
return true;
|
||||
}
|
||||
$lockfile = $this->get_lock_file($key);
|
||||
if (file_exists($lockfile)) {
|
||||
// The key is locked and we don't own it.
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name to use for a lock file.
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function get_lock_file($key) {
|
||||
return $this->cachedir.'/'. $key .'.lock';
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the instance what it is no longer needed.
|
||||
*/
|
||||
public function __destruct() {
|
||||
foreach ($this->locks as $lockfile) {
|
||||
// Naught, naughty developers.
|
||||
@unlink($lockfile);
|
||||
}
|
||||
}
|
||||
}
|
339
cache/renderer.php
vendored
Normal file
339
cache/renderer.php
vendored
Normal file
@ -0,0 +1,339 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The Cache renderer.
|
||||
*
|
||||
* This file is part of Moodle's cache API, affectionately called MUC.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* The cache renderer (mainly admin interfaces).
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class core_cache_renderer extends plugin_renderer_base {
|
||||
|
||||
/**
|
||||
* Displays store summaries.
|
||||
*
|
||||
* @param array $stores
|
||||
* @param array $plugins
|
||||
* @return string HTML
|
||||
*/
|
||||
public function store_instance_summariers(array $stores, array $plugins) {
|
||||
$table = new html_table();
|
||||
$table->head = array(
|
||||
get_string('storename', 'cache'),
|
||||
get_string('plugin', 'cache'),
|
||||
get_string('storeready', 'cache'),
|
||||
get_string('mappings', 'cache'),
|
||||
get_string('modes', 'cache'),
|
||||
get_string('supports', 'cache'),
|
||||
get_string('actions', 'cache'),
|
||||
);
|
||||
$table->colclasses = array(
|
||||
'storename',
|
||||
'plugin',
|
||||
'storeready',
|
||||
'mappings',
|
||||
'modes',
|
||||
'supports',
|
||||
'actions'
|
||||
);
|
||||
$table->data = array();
|
||||
|
||||
$defaultstoreactions = get_string('defaultstoreactions', 'cache');
|
||||
|
||||
foreach ($stores as $name => $store) {
|
||||
$actions = cache_administration_helper::get_store_instance_actions($name, $store);
|
||||
$modes = array();
|
||||
foreach ($store['modes'] as $mode => $enabled) {
|
||||
if ($enabled) {
|
||||
$modes[] = get_string('mode_'.$mode, 'cache');
|
||||
}
|
||||
}
|
||||
|
||||
$supports = array();
|
||||
foreach ($store['supports'] as $support => $enabled) {
|
||||
if ($enabled) {
|
||||
$supports[] = get_string('supports_'.$support, 'cache');
|
||||
}
|
||||
}
|
||||
|
||||
$info = '';
|
||||
if (!empty($store['default'])) {
|
||||
$info = $this->output->pix_icon('i/info', $defaultstoreactions).' ';
|
||||
}
|
||||
$htmlactions = array();
|
||||
foreach ($actions as $action) {
|
||||
$htmlactions[] = $this->output->action_link($action['url'], $action['text']);
|
||||
}
|
||||
|
||||
$storename = $store['name'];
|
||||
if (!empty($store['default'])) {
|
||||
$storename = get_string('store_'.$store['name'], 'cache');
|
||||
}
|
||||
|
||||
$row = new html_table_row(array(
|
||||
$storename,
|
||||
get_string('pluginname', 'cachestore_'.$store['plugin']),
|
||||
($store['isready'] && $store['requirementsmet']) ? $this->output->pix_icon('i/tick_green_small', '1') : '',
|
||||
$store['mappings'],
|
||||
join(', ', $modes),
|
||||
join(', ', $supports),
|
||||
$info.join(', ', $htmlactions)
|
||||
));
|
||||
$row->attributes['class'] = 'store-'.$name;
|
||||
if ($store['default']) {
|
||||
$row->attributes['class'] .= ' default-store';
|
||||
}
|
||||
$table->data[] = $row;
|
||||
}
|
||||
|
||||
$html = html_writer::start_tag('div', array('id' => 'core-cache-store-summaries'));
|
||||
$html .= $this->output->heading(get_string('storesummaries', 'cache'), 3);
|
||||
$html .= html_writer::table($table);
|
||||
$html .= html_writer::end_tag('div');
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays plugin summaries
|
||||
*
|
||||
* @param array $plugins
|
||||
* @return string HTML
|
||||
*/
|
||||
public function store_plugin_summaries(array $plugins) {
|
||||
$table = new html_table();
|
||||
$table->head = array(
|
||||
get_string('plugin', 'cache'),
|
||||
get_string('storeready', 'cache'),
|
||||
get_string('stores', 'cache'),
|
||||
get_string('modes', 'cache'),
|
||||
get_string('supports', 'cache'),
|
||||
get_string('actions', 'cache'),
|
||||
);
|
||||
$table->colclasses = array(
|
||||
'plugin',
|
||||
'storeready',
|
||||
'stores',
|
||||
'modes',
|
||||
'supports',
|
||||
'actions'
|
||||
);
|
||||
$table->data = array();
|
||||
|
||||
foreach ($plugins as $name => $plugin) {
|
||||
$actions = cache_administration_helper::get_store_plugin_actions($name, $plugin);
|
||||
|
||||
$modes = array();
|
||||
foreach ($plugin['modes'] as $mode => $enabled) {
|
||||
if ($enabled) {
|
||||
$modes[] = get_string('mode_'.$mode, 'cache');
|
||||
}
|
||||
}
|
||||
|
||||
$supports = array();
|
||||
foreach ($plugin['supports'] as $support => $enabled) {
|
||||
if ($enabled) {
|
||||
$supports[] = get_string('supports_'.$support, 'cache');
|
||||
}
|
||||
}
|
||||
|
||||
$htmlactions = array();
|
||||
foreach ($actions as $action) {
|
||||
$htmlactions[] = $this->output->action_link($action['url'], $action['text']);
|
||||
}
|
||||
|
||||
$row = new html_table_row(array(
|
||||
$plugin['name'],
|
||||
($plugin['requirementsmet']) ? $this->output->pix_icon('i/tick_green_small', '1') : '',
|
||||
$plugin['instances'],
|
||||
join(', ', $modes),
|
||||
join(', ', $supports),
|
||||
join(', ', $htmlactions)
|
||||
));
|
||||
|
||||
$row->attributes['class'] = 'plugin-'.$name;
|
||||
$table->data[] = $row;
|
||||
}
|
||||
|
||||
$html = html_writer::start_tag('div', array('id' => 'core-cache-plugin-summaries'));
|
||||
$html .= $this->output->heading(get_string('pluginsummaries', 'cache'), 3);
|
||||
$html .= html_writer::table($table);
|
||||
$html .= html_writer::end_tag('div');
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays definition summaries
|
||||
*
|
||||
* @param array $definitions
|
||||
* @param array $actions
|
||||
* @return string HTML
|
||||
*/
|
||||
public function definition_summaries(array $definitions, array $actions) {
|
||||
$table = new html_table();
|
||||
$table->head = array(
|
||||
get_string('definition', 'cache'),
|
||||
get_string('mode', 'cache'),
|
||||
get_string('component', 'cache'),
|
||||
get_string('area', 'cache'),
|
||||
get_string('mappings', 'cache'),
|
||||
get_string('actions', 'cache'),
|
||||
);
|
||||
$table->colclasses = array(
|
||||
'definition',
|
||||
'mode',
|
||||
'component',
|
||||
'area',
|
||||
'mappings',
|
||||
'actions'
|
||||
);
|
||||
$table->data = array();
|
||||
|
||||
$none = new lang_string('none', 'cache');
|
||||
foreach ($definitions as $id => $definition) {
|
||||
$htmlactions = array();
|
||||
foreach ($actions as $action) {
|
||||
$action['url']->param('definition', $id);
|
||||
$htmlactions[] = $this->output->action_link($action['url'], $action['text']);
|
||||
}
|
||||
if (!empty($definition['mappings'])) {
|
||||
$mapping = join(', ', $definition['mappings']);
|
||||
} else {
|
||||
$mapping = '<em>'.$none.'</em>';
|
||||
}
|
||||
|
||||
$row = new html_table_row(array(
|
||||
$definition['name'],
|
||||
get_string('mode_'.$definition['mode'], 'cache'),
|
||||
$definition['component'],
|
||||
$definition['area'],
|
||||
$mapping,
|
||||
join(', ', $htmlactions)
|
||||
));
|
||||
$row->attributes['class'] = 'definition-'.$definition['component'].'-'.$definition['area'];
|
||||
$table->data[] = $row;
|
||||
}
|
||||
|
||||
$html = html_writer::start_tag('div', array('id' => 'core-cache-definition-summaries'));
|
||||
$html .= $this->output->heading(get_string('definitionsummaries', 'cache'), 3);
|
||||
$html .= html_writer::table($table);
|
||||
|
||||
$url = new moodle_url('/cache/admin.php', array('action' => 'rescandefinitions', 'sesskey' => sesskey()));
|
||||
$link = html_writer::link($url, get_string('rescandefinitions', 'cache'));
|
||||
$html .= html_writer::tag('div', $link, array('id' => 'core-cache-rescan-definitions'));
|
||||
|
||||
$html .= html_writer::end_tag('div');
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays mode mappings
|
||||
*
|
||||
* @param string $applicationstore
|
||||
* @param string $sessionstore
|
||||
* @param string $requeststore
|
||||
* @param moodle_url $editurl
|
||||
* @return string HTML
|
||||
*/
|
||||
public function mode_mappings($applicationstore, $sessionstore, $requeststore, moodle_url $editurl) {
|
||||
$table = new html_table();
|
||||
$table->colclasses = array(
|
||||
'mode',
|
||||
'mapping',
|
||||
);
|
||||
$table->rowclasses = array(
|
||||
'mode_application',
|
||||
'mode_session',
|
||||
'mode_request'
|
||||
);
|
||||
$table->head = array(
|
||||
get_string('mode', 'cache'),
|
||||
get_string('mappings', 'cache'),
|
||||
);
|
||||
$table->data = array(
|
||||
array(get_string('mode_'.cache_store::MODE_APPLICATION, 'cache'), $applicationstore),
|
||||
array(get_string('mode_'.cache_store::MODE_SESSION, 'cache'), $sessionstore),
|
||||
array(get_string('mode_'.cache_store::MODE_REQUEST, 'cache'), $requeststore)
|
||||
);
|
||||
|
||||
$html = html_writer::start_tag('div', array('id' => 'core-cache-mode-mappings'));
|
||||
$html .= $this->output->heading(get_string('defaultmappings', 'cache'), 3);
|
||||
$html .= html_writer::table($table);
|
||||
$link = html_writer::link($editurl, get_string('editmappings', 'cache'));
|
||||
$html .= html_writer::tag('div', $link, array('class' => 'edit-link'));
|
||||
$html .= html_writer::end_tag('div');
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display basic information about lock instances.
|
||||
*
|
||||
* @todo Add some actions so that people can configure lock instances.
|
||||
*
|
||||
* @param array $locks
|
||||
* @return string
|
||||
*/
|
||||
public function lock_summaries(array $locks) {
|
||||
$table = new html_table();
|
||||
$table->colclasses = array(
|
||||
'name',
|
||||
'default',
|
||||
'uses',
|
||||
// Useful later: 'actions'.
|
||||
);
|
||||
$table->rowclasses = array(
|
||||
'lock_name',
|
||||
'lock_default',
|
||||
'lock_uses',
|
||||
// Useful later: 'lock_actions',.
|
||||
);
|
||||
$table->head = array(
|
||||
get_string('lockname', 'cache'),
|
||||
get_string('lockdefault', 'cache'),
|
||||
get_string('lockuses', 'cache'),
|
||||
// Useful later: get_string('actions', 'cache').
|
||||
);
|
||||
$table->data = array();
|
||||
$tick = $this->output->pix_icon('i/tick_green_big', '');
|
||||
foreach ($locks as $lock) {
|
||||
$table->data[] = new html_table_row(array(
|
||||
new html_table_cell($lock['name']),
|
||||
new html_table_cell($lock['default'] ? $tick : ''),
|
||||
new html_table_cell($lock['uses']),
|
||||
));
|
||||
}
|
||||
|
||||
$html = html_writer::start_tag('div', array('id' => 'core-cache-lock-summary'));
|
||||
$html .= $this->output->heading(get_string('locksummary', 'cache'), 3);
|
||||
$html .= html_writer::table($table);
|
||||
$html .= html_writer::end_tag('div');
|
||||
return $html;
|
||||
}
|
||||
}
|
59
cache/stores/file/addinstanceform.php
vendored
Normal file
59
cache/stores/file/addinstanceform.php
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The library file for the file cache store.
|
||||
*
|
||||
* This file is part of the file cache store, it contains the API for interacting with an instance of the store.
|
||||
* This is used as a default cache store within the Cache API. It should never be deleted.
|
||||
*
|
||||
* @package cachestore_file
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once($CFG->dirroot.'/cache/forms.php');
|
||||
require_once($CFG->dirroot.'/cache/stores/file/lib.php');
|
||||
|
||||
/**
|
||||
* Form for adding a file instance.
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cachestore_file_addinstance_form extends cachestore_addinstance_form {
|
||||
|
||||
/**
|
||||
* Adds the desired form elements.
|
||||
*/
|
||||
protected function configuration_definition() {
|
||||
$form = $this->_form;
|
||||
|
||||
$form->addElement('text', 'path', get_string('path', 'cachestore_file'));
|
||||
$form->setType('path', PARAM_SAFEPATH);
|
||||
$form->addHelpButton('path', 'path', 'cachestore_file');
|
||||
|
||||
$form->addElement('checkbox', 'autocreate', get_string('autocreate', 'cachestore_file'));
|
||||
$form->setType('autocreate', PARAM_BOOL);
|
||||
$form->addHelpButton('autocreate', 'autocreate', 'cachestore_file');
|
||||
$form->disabledIf('autocreate', 'path', 'eq', '');
|
||||
|
||||
$form->addElement('checkbox', 'prescan', get_string('prescan', 'cachestore_file'));
|
||||
$form->setType('prescan', PARAM_BOOL);
|
||||
$form->addHelpButton('prescan', 'prescan', 'cachestore_file');
|
||||
}
|
||||
}
|
37
cache/stores/file/lang/en/cachestore_file.php
vendored
Normal file
37
cache/stores/file/lang/en/cachestore_file.php
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The library file for the file cache store.
|
||||
*
|
||||
* This file is part of the file cache store, it contains the API for interacting with an instance of the store.
|
||||
* This is used as a default cache store within the Cache API. It should never be deleted.
|
||||
*
|
||||
* @package cachestore_file
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['autocreate'] = 'Auto create directory';
|
||||
$string['autocreate_help'] = 'If enabled the directory specified in path will be automatically created if it does not already exist.';
|
||||
$string['path'] = 'Cache path';
|
||||
$string['path_help'] = 'The directory that should be used to store files for this cache store. If left blank (default) a directory will be automatically created in the moodledata directory. This can be used to point a file store towards a directory on a better performing drive (such as one in memory).';
|
||||
$string['pluginname'] = 'File cache';
|
||||
$string['prescan'] = 'Prescan directory';
|
||||
$string['prescan_help'] = 'If enabled the directory is scanned when the cache is first used and requests for files are first checked against the scan data. This can help if you have a slow file system and are finding that file operations are causing you a bottle neck.';
|
570
cache/stores/file/lib.php
vendored
Normal file
570
cache/stores/file/lib.php
vendored
Normal file
@ -0,0 +1,570 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The library file for the file cache store.
|
||||
*
|
||||
* This file is part of the file cache store, it contains the API for interacting with an instance of the store.
|
||||
* This is used as a default cache store within the Cache API. It should never be deleted.
|
||||
*
|
||||
* @package cachestore_file
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* The file store class.
|
||||
*
|
||||
* Configuration options
|
||||
* path: string: path to the cache directory, if left empty one will be created in the cache directory
|
||||
* autocreate: true, false
|
||||
* prescan: true, false
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cachestore_file implements cache_store, cache_is_key_aware {
|
||||
|
||||
/**
|
||||
* The name of the store.
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The path to use for the file storage.
|
||||
* @var string
|
||||
*/
|
||||
protected $path = null;
|
||||
|
||||
/**
|
||||
* Set to true when a prescan has been performed.
|
||||
* @var bool
|
||||
*/
|
||||
protected $prescan = false;
|
||||
|
||||
/**
|
||||
* Set to true when the path should be automatically created if it does not yet exist.
|
||||
* @var bool
|
||||
*/
|
||||
protected $autocreate = false;
|
||||
|
||||
/**
|
||||
* Set to true if a custom path is being used.
|
||||
* @var bool
|
||||
*/
|
||||
protected $custompath = false;
|
||||
|
||||
/**
|
||||
* An array of keys we are sure about presently.
|
||||
* @var array
|
||||
*/
|
||||
protected $keys = array();
|
||||
|
||||
/**
|
||||
* True when the store is ready to be initialised.
|
||||
* @var bool
|
||||
*/
|
||||
protected $isready = false;
|
||||
|
||||
/**
|
||||
* The cache definition this instance has been initialised with.
|
||||
* @var cache_definition
|
||||
*/
|
||||
protected $definition;
|
||||
|
||||
/**
|
||||
* Constructs the store instance.
|
||||
*
|
||||
* Noting that this function is not an initialisation. It is used to prepare the store for use.
|
||||
* The store will be initialised when required and will be provided with a cache_definition at that time.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $configuration
|
||||
*/
|
||||
public function __construct($name, array $configuration = array()) {
|
||||
$this->name = $name;
|
||||
if (array_key_exists('path', $configuration) && $configuration['path'] !== '') {
|
||||
$this->custompath = true;
|
||||
$this->autocreate = !empty($configuration['autocreate']);
|
||||
$path = (string)$configuration['path'];
|
||||
if (!is_dir($path)) {
|
||||
if ($this->autocreate) {
|
||||
if (!make_writable_directory($path, false)) {
|
||||
$path = false;
|
||||
debugging('Error trying to autocreate file store path. '.$path, DEBUG_DEVELOPER);
|
||||
}
|
||||
} else {
|
||||
$path = false;
|
||||
debugging('The given file cache store path does not exist. '.$path, DEBUG_DEVELOPER);
|
||||
}
|
||||
}
|
||||
if ($path !== false && !is_writable($path)) {
|
||||
$path = false;
|
||||
debugging('The given file cache store path is not writable. '.$path, DEBUG_DEVELOPER);
|
||||
}
|
||||
} else {
|
||||
$path = make_cache_directory('cachestore_file/'.preg_replace('#[^a-zA-Z0-9\.\-_]+#', '', $name));
|
||||
}
|
||||
$this->isready = $path !== false;
|
||||
$this->path = $path;
|
||||
$this->prescan = array_key_exists('prescan', $configuration) ? (bool)$configuration['prescan'] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this store instance is ready to be used.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_ready() {
|
||||
return ($this->path !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true once this instance has been initialised.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_initialised() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supported features as a combined int.
|
||||
*
|
||||
* @param array $configuration
|
||||
* @return int
|
||||
*/
|
||||
public static function get_supported_features(array $configuration = array()) {
|
||||
$supported = self::SUPPORTS_DATA_GUARANTEE +
|
||||
self::SUPPORTS_NATIVE_TTL;
|
||||
return $supported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supported modes as a combined int.
|
||||
*
|
||||
* @param array $configuration
|
||||
* @return int
|
||||
*/
|
||||
public static function get_supported_modes(array $configuration = array()) {
|
||||
return self::MODE_APPLICATION + self::MODE_SESSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store requirements are met.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function are_requirements_met() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given mode is supported by this store.
|
||||
*
|
||||
* @param int $mode One of cache_store::MODE_*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_supported_mode($mode) {
|
||||
return ($mode === self::MODE_APPLICATION || $mode === self::MODE_SESSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store instance supports multiple identifiers.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_multiple_indentifiers() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store instance guarantees data.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_data_guarantee() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store instance supports native ttl.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_native_ttl() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the cache.
|
||||
*
|
||||
* Once this has been done the cache is all set to be used.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
*/
|
||||
public function initialise(cache_definition $definition) {
|
||||
$this->definition = $definition;
|
||||
$hash = preg_replace('#[^a-zA-Z0-9]+#', '_', $this->definition->get_id());
|
||||
$this->path .= '/'.$hash;
|
||||
make_writable_directory($this->path);
|
||||
if ($this->prescan && $definition->get_mode() !== self::MODE_REQUEST) {
|
||||
$this->prescan = false;
|
||||
}
|
||||
if ($this->prescan) {
|
||||
$pattern = $this->path.'/*.cache';
|
||||
foreach (glob($pattern, GLOB_MARK | GLOB_NOSORT) as $filename) {
|
||||
$this->keys[basename($filename)] = filemtime($filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an item from the cache store given its key.
|
||||
*
|
||||
* @param string $key The key to retrieve
|
||||
* @return mixed The data that was associated with the key, or false if the key did not exist.
|
||||
*/
|
||||
public function get($key) {
|
||||
$filename = $key.'.cache';
|
||||
$file = $this->path.'/'.$filename;
|
||||
$ttl = $this->definition->get_ttl();
|
||||
if ($ttl) {
|
||||
$maxtime = cache::now() - $ttl;
|
||||
}
|
||||
$readfile = false;
|
||||
if ($this->prescan && array_key_exists($key, $this->keys)) {
|
||||
if (!$ttl || $this->keys[$filename] >= $maxtime && file_exists($file)) {
|
||||
$readfile = true;
|
||||
} else {
|
||||
$this->delete($key);
|
||||
}
|
||||
} else if (file_exists($file) && (!$ttl || filemtime($file) >= $maxtime)) {
|
||||
$readfile = true;
|
||||
}
|
||||
if (!$readfile) {
|
||||
return false;
|
||||
}
|
||||
// Check the filesize first, likely not needed but important none the less.
|
||||
$filesize = filesize($file);
|
||||
if (!$filesize) {
|
||||
return false;
|
||||
}
|
||||
// Open ensuring the file for writing, truncating it and setting the pointer to the start.
|
||||
if (!$handle = fopen($file, 'rb')) {
|
||||
return false;
|
||||
}
|
||||
// Lock it up!
|
||||
// We don't care if this succeeds or not, on some systems it will, on some it won't, meah either way.
|
||||
flock($handle, LOCK_SH);
|
||||
// HACK ALERT
|
||||
// There is a problem when reading from the file during PHPUNIT tests. For one reason or another the filesize is not correct
|
||||
// Doesn't happen during normal operation, just during unit tests.
|
||||
// Read it.
|
||||
$data = fread($handle, $filesize+128);
|
||||
// Unlock it.
|
||||
flock($handle, LOCK_UN);
|
||||
// Return it unserialised.
|
||||
return $this->prep_data_after_read($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves several items from the cache store in a single transaction.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @param array $keys The array of keys to retrieve
|
||||
* @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
|
||||
* be set to false.
|
||||
*/
|
||||
public function get_many($keys) {
|
||||
$result = array();
|
||||
foreach ($keys as $key) {
|
||||
$result[$key] = $this->get($key);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an item from the cache store.
|
||||
*
|
||||
* @param string $key The key to delete.
|
||||
* @return bool Returns true if the operation was a success, false otherwise.
|
||||
*/
|
||||
public function delete($key) {
|
||||
$filename = $key.'.cache';
|
||||
$file = $this->path.'/'.$filename;
|
||||
$result = @unlink($file);
|
||||
unset($this->keys[$filename]);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes several keys from the cache in a single action.
|
||||
*
|
||||
* @param array $keys The keys to delete
|
||||
* @return int The number of items successfully deleted.
|
||||
*/
|
||||
public function delete_many(array $keys) {
|
||||
$count = 0;
|
||||
foreach ($keys as $key) {
|
||||
if ($this->delete($key)) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an item in the cache given its key and data value.
|
||||
*
|
||||
* @param string $key The key to use.
|
||||
* @param mixed $data The data to set.
|
||||
* @return bool True if the operation was a success false otherwise.
|
||||
*/
|
||||
public function set($key, $data) {
|
||||
$this->ensure_path_exists();
|
||||
$filename = $key.'.cache';
|
||||
$file = $this->path.'/'.$filename;
|
||||
$result = $this->write_file($file, $this->prep_data_before_save($data));
|
||||
if (!$result) {
|
||||
// Couldn't write the file.
|
||||
return false;
|
||||
}
|
||||
// Record the key if required.
|
||||
if ($this->prescan) {
|
||||
$this->keys[$filename] = cache::now() + 1;
|
||||
}
|
||||
// Return true.. it all worked **miracles**.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares data to be stored in a file.
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return string
|
||||
*/
|
||||
protected function prep_data_before_save($data) {
|
||||
return serialize($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the data it has been read from the cache. Undoing what was done in prep_data_before_save.
|
||||
*
|
||||
* @param string $data
|
||||
* @return mixed
|
||||
* @throws coding_exception
|
||||
*/
|
||||
protected function prep_data_after_read($data) {
|
||||
$result = @unserialize($data);
|
||||
if ($result === false) {
|
||||
throw new coding_exception('Failed to unserialise data from file. Either failed to read, or failed to write.');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets many items in the cache in a single transaction.
|
||||
*
|
||||
* @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two
|
||||
* keys, 'key' and 'value'.
|
||||
* @return int The number of items successfully set. It is up to the developer to check this matches the number of items
|
||||
* sent ... if they care that is.
|
||||
*/
|
||||
public function set_many(array $keyvaluearray) {
|
||||
$count = 0;
|
||||
foreach ($keyvaluearray as $pair) {
|
||||
if ($this->set($pair['key'], $pair['value'])) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the store has a record for the given key and returns true if so.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key) {
|
||||
$filename = $key.'.cache';
|
||||
$file = $this->path.'/'.$key.'.cache';
|
||||
$maxtime = cache::now() - $this->definition->get_ttl();
|
||||
if ($this->prescan) {
|
||||
return array_key_exists($filename, $this->keys) && $this->keys[$filename] >= $maxtime;
|
||||
}
|
||||
return (file_exists($file) && ($this->definition->get_ttl() == 0 || filemtime($file) >= $maxtime));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store contains records for all of the given keys.
|
||||
*
|
||||
* @param array $keys
|
||||
* @return bool
|
||||
*/
|
||||
public function has_all(array $keys) {
|
||||
foreach ($keys as $key) {
|
||||
if (!$this->has($key)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store contains records for any of the given keys.
|
||||
*
|
||||
* @param array $keys
|
||||
* @return bool
|
||||
*/
|
||||
public function has_any(array $keys) {
|
||||
foreach ($keys as $key) {
|
||||
if ($this->has($key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges the cache deleting all items within it.
|
||||
*
|
||||
* @return boolean True on success. False otherwise.
|
||||
*/
|
||||
public function purge() {
|
||||
$pattern = $this->path.'/*.cache';
|
||||
foreach (glob($pattern, GLOB_MARK | GLOB_NOSORT) as $filename) {
|
||||
@unlink($filename);
|
||||
}
|
||||
$this->keys = array();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to make sure that the path for the file cache exists.
|
||||
*
|
||||
* @return bool
|
||||
* @throws coding_exception
|
||||
*/
|
||||
protected function ensure_path_exists() {
|
||||
if (!is_writable($this->path)) {
|
||||
if ($this->custompath && !$this->autocreate) {
|
||||
throw new coding_exception('File store path does not exist. It must exist and be writable by the web server.');
|
||||
}
|
||||
if (!make_writable_directory($this->path, false)) {
|
||||
throw new coding_exception('File store path does not exist and can not be created.');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user can add an instance of the store plugin.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function can_add_instance() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs any necessary clean up when the store instance is being deleted.
|
||||
*
|
||||
* 1. Purges the cache directory.
|
||||
* 2. Deletes the directory we created for this cache instances data.
|
||||
*/
|
||||
public function cleanup() {
|
||||
$this->purge();
|
||||
@rmdir($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an instance of the cache store that can be used for testing.
|
||||
*
|
||||
* Returns an instance of the cache store, or false if one cannot be created.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return cachestore_file
|
||||
*/
|
||||
public static function initialise_test_instance(cache_definition $definition) {
|
||||
$name = 'File test';
|
||||
$path = make_cache_directory('cachestore_file_test');
|
||||
$cache = new cachestore_file($name, array('path' => $path));
|
||||
$cache->initialise($definition);
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes your madness to a file.
|
||||
*
|
||||
* There are several things going on in this function to try to ensure what we don't end up with partial writes etc.
|
||||
* 1. Files for writing are opened with the mode xb, the file must be created and can not already exist.
|
||||
* 2. Renaming, data is written to a temporary file, where it can be verified using md5 and is then renamed.
|
||||
*
|
||||
* @param string $file Absolute file path
|
||||
* @param string $content The content to write.
|
||||
* @return bool
|
||||
*/
|
||||
protected function write_file($file, $content) {
|
||||
// Generate a temp file that is going to be unique. We'll rename it at the end to the desired file name.
|
||||
// in this way we avoid partial writes.
|
||||
$path = dirname($file);
|
||||
while (true) {
|
||||
$tempfile = $path.'/'.uniqid(sesskey().'.', true) . '.temp';
|
||||
if (!file_exists($tempfile)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Open the file with mode=x. This acts to create and open the file for writing only.
|
||||
// If the file already exists this will return false.
|
||||
// We also force binary.
|
||||
$handle = @fopen($tempfile, 'xb+');
|
||||
if ($handle === false) {
|
||||
// File already exists... lock already exists, return false.
|
||||
return false;
|
||||
}
|
||||
fwrite($handle, $content);
|
||||
fflush($handle);
|
||||
// Close the handle, we're done.
|
||||
fclose($handle);
|
||||
|
||||
if (md5_file($tempfile) !== md5($content)) {
|
||||
// The md5 of the content of the file must match the md5 of the content given to be written.
|
||||
@unlink($tempfile);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finally rename the temp file to the desired file, returning the true|false result.
|
||||
$result = rename($tempfile, $file);
|
||||
if (!$result) {
|
||||
// Failed to rename, don't leave files lying around.
|
||||
@unlink($tempfile);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this instance.
|
||||
* @return string
|
||||
*/
|
||||
public function my_name() {
|
||||
return $this->name;
|
||||
}
|
||||
}
|
32
cache/stores/file/version.php
vendored
Normal file
32
cache/stores/file/version.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Cache file store version information.
|
||||
*
|
||||
* This is used as a default cache store within the Cache API. It should never be deleted.
|
||||
*
|
||||
* @package cachestore_file
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$plugin->version = 2012091000; // The current module version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2012090700; // Requires this Moodle version.
|
||||
$plugin->component = 'cachestore_file'; // Full name of the plugin.
|
50
cache/stores/memcache/addinstanceform.php
vendored
Normal file
50
cache/stores/memcache/addinstanceform.php
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The library file for the memcache cache store.
|
||||
*
|
||||
* This file is part of the memcache cache store, it contains the API for interacting with an instance of the store.
|
||||
*
|
||||
* @package cachestore_memcache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot.'/cache/forms.php');
|
||||
require_once($CFG->dirroot.'/cache/stores/memcached/lib.php');
|
||||
|
||||
/**
|
||||
* Form for adding a memcache instance.
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cachestore_memcache_addinstance_form extends cachestore_addinstance_form {
|
||||
|
||||
/**
|
||||
* Add the desired form elements.
|
||||
*/
|
||||
protected function configuration_definition() {
|
||||
$form = $this->_form;
|
||||
$form->addElement('textarea', 'servers', get_string('servers', 'cachestore_memcache'), array('cols' => 75, 'rows' => 5));
|
||||
$form->addHelpButton('servers', 'servers', 'cachestore_memcache');
|
||||
$form->addRule('servers', get_string('required'), 'required');
|
||||
$form->setType('servers', PARAM_RAW);
|
||||
}
|
||||
}
|
43
cache/stores/memcache/lang/en/cachestore_memcache.php
vendored
Normal file
43
cache/stores/memcache/lang/en/cachestore_memcache.php
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The library file for the memcache cache store.
|
||||
*
|
||||
* This file is part of the memcache cache store, it contains the API for interacting with an instance of the store.
|
||||
*
|
||||
* @package cachestore_memcache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['pluginname'] = 'Memcache';
|
||||
$string['servers'] = 'Servers';
|
||||
$string['servers_help'] = 'This sets the servers that should be utilised by this memcache adapter.
|
||||
Servers should be defined one per line and consist of a server address and optionally a port and weight.
|
||||
If no port is provided then the default port (11211) is used.
|
||||
|
||||
For example:
|
||||
<pre>
|
||||
server.url.com
|
||||
ipaddress:port
|
||||
servername:port:weight
|
||||
</pre>';
|
||||
$string['testservers'] = 'Test servers';
|
||||
$string['testservers_desc'] = 'The test servers get used for unit tests and for performance tests. It is entirely optional to set up test servers. Servers should be defined one per line and consist of a server address and optionally a port and weight.
|
||||
If no port is provided then the default port (11211) is used.';
|
376
cache/stores/memcache/lib.php
vendored
Normal file
376
cache/stores/memcache/lib.php
vendored
Normal file
@ -0,0 +1,376 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The library file for the memcache cache store.
|
||||
*
|
||||
* This file is part of the memcache cache store, it contains the API for interacting with an instance of the store.
|
||||
*
|
||||
* @package cachestore_memcache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* The memcache store class.
|
||||
*
|
||||
* (Not to be confused with memcached store)
|
||||
*
|
||||
* Configuration options:
|
||||
* servers: string: host:port:weight , ...
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cachestore_memcache implements cache_store {
|
||||
|
||||
/**
|
||||
* The name of the store
|
||||
* @var store
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The memcache connection once established.
|
||||
* @var Memcache
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* An array of servers to use in the connection args.
|
||||
* @var array
|
||||
*/
|
||||
protected $servers = array();
|
||||
|
||||
/**
|
||||
* An array of options used when establishing the connection.
|
||||
* @var array
|
||||
*/
|
||||
protected $options = array();
|
||||
|
||||
/**
|
||||
* Set to true when things are ready to be initialised.
|
||||
* @var bool
|
||||
*/
|
||||
protected $isready = false;
|
||||
|
||||
/**
|
||||
* The cache definition this store was initialised for.
|
||||
* @var cache_definition
|
||||
*/
|
||||
protected $definition;
|
||||
|
||||
/**
|
||||
* Constructs the store instance.
|
||||
*
|
||||
* Noting that this function is not an initialisation. It is used to prepare the store for use.
|
||||
* The store will be initialised when required and will be provided with a cache_definition at that time.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $configuration
|
||||
*/
|
||||
public function __construct($name, array $configuration = array()) {
|
||||
$this->name = $name;
|
||||
if (!array_key_exists('servers', $configuration) || empty($configuration['servers'])) {
|
||||
// Nothing configured.
|
||||
return;
|
||||
}
|
||||
if (!is_array($configuration['servers'])) {
|
||||
$configuration['servers'] = array($configuration['servers']);
|
||||
}
|
||||
foreach ($configuration['servers'] as $server) {
|
||||
if (!is_array($server)) {
|
||||
$server = explode(':', $server, 3);
|
||||
}
|
||||
if (!array_key_exists(1, $server)) {
|
||||
$server[1] = 11211;
|
||||
$server[2] = 100;
|
||||
} else if (!array_key_exists(2, $server)) {
|
||||
$server[2] = 100;
|
||||
}
|
||||
$this->servers[] = $server;
|
||||
}
|
||||
|
||||
$this->isready = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the cache.
|
||||
*
|
||||
* Once this has been done the cache is all set to be used.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
*/
|
||||
public function initialise(cache_definition $definition) {
|
||||
if ($this->is_initialised()) {
|
||||
throw new coding_exception('This memcache instance has already been initialised.');
|
||||
}
|
||||
$this->definition = $definition;
|
||||
$this->connection = new Memcache;
|
||||
foreach ($this->servers as $server) {
|
||||
$this->connection->addServer($server[0], $server[1], true, $server[2]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true once this instance has been initialised.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_initialised() {
|
||||
return ($this->connection !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this store instance is ready to be used.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_ready() {
|
||||
return $this->isready;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store requirements are met.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function are_requirements_met() {
|
||||
return class_exists('Memcache');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given mode is supported by this store.
|
||||
*
|
||||
* @param int $mode One of cache_store::MODE_*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_supported_mode($mode) {
|
||||
return ($mode === self::MODE_APPLICATION || $mode === self::MODE_SESSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supported features as a combined int.
|
||||
*
|
||||
* @param array $configuration
|
||||
* @return int
|
||||
*/
|
||||
public static function get_supported_features(array $configuration = array()) {
|
||||
return self::SUPPORTS_NATIVE_TTL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store instance supports multiple identifiers.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_multiple_indentifiers() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store instance guarantees data.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_data_guarantee() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store instance supports native ttl.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_native_ttl() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supported modes as a combined int.
|
||||
*
|
||||
* @param array $configuration
|
||||
* @return int
|
||||
*/
|
||||
public static function get_supported_modes(array $configuration = array()) {
|
||||
return self::MODE_APPLICATION + self::MODE_SESSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an item from the cache store given its key.
|
||||
*
|
||||
* @param string $key The key to retrieve
|
||||
* @return mixed The data that was associated with the key, or false if the key did not exist.
|
||||
*/
|
||||
public function get($key) {
|
||||
return $this->connection->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves several items from the cache store in a single transaction.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @param array $keys The array of keys to retrieve
|
||||
* @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
|
||||
* be set to false.
|
||||
*/
|
||||
public function get_many($keys) {
|
||||
$result = $this->connection->get($keys);
|
||||
if (!is_array($result)) {
|
||||
$result = array();
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
if (!array_key_exists($key, $result)) {
|
||||
$result[$key] = false;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an item in the cache given its key and data value.
|
||||
*
|
||||
* @param string $key The key to use.
|
||||
* @param mixed $data The data to set.
|
||||
* @return bool True if the operation was a success false otherwise.
|
||||
*/
|
||||
public function set($key, $data) {
|
||||
return $this->connection->set($key, $data, MEMCACHE_COMPRESSED, $this->definition->get_ttl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets many items in the cache in a single transaction.
|
||||
*
|
||||
* @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two
|
||||
* keys, 'key' and 'value'.
|
||||
* @return int The number of items successfully set. It is up to the developer to check this matches the number of items
|
||||
* sent ... if they care that is.
|
||||
*/
|
||||
public function set_many(array $keyvaluearray) {
|
||||
$count = 0;
|
||||
foreach ($keyvaluearray as $pair) {
|
||||
if ($this->connection->set($pair['key'], $pair['value'], MEMCACHE_COMPRESSED, $this->definition->get_ttl())) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an item from the cache store.
|
||||
*
|
||||
* @param string $key The key to delete.
|
||||
* @return bool Returns true if the operation was a success, false otherwise.
|
||||
*/
|
||||
public function delete($key) {
|
||||
return $this->connection->delete($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes several keys from the cache in a single action.
|
||||
*
|
||||
* @param array $keys The keys to delete
|
||||
* @return int The number of items successfully deleted.
|
||||
*/
|
||||
public function delete_many(array $keys) {
|
||||
$count = 0;
|
||||
foreach ($keys as $key) {
|
||||
if ($this->delete($key)) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges the cache deleting all items within it.
|
||||
*
|
||||
* @return boolean True on success. False otherwise.
|
||||
*/
|
||||
public function purge() {
|
||||
$this->connection->flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the data from the add instance form this function creates a configuration array.
|
||||
*
|
||||
* @param stdClass $data
|
||||
* @return array
|
||||
*/
|
||||
public static function config_get_configuration_array($data) {
|
||||
$lines = explode("\n", $data->servers);
|
||||
$servers = array();
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line, ':');
|
||||
$servers[] = explode(':', $line, 3);
|
||||
}
|
||||
return array(
|
||||
'servers' => $servers,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user can add an instance of the store plugin.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function can_add_instance() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs any necessary clean up when the store instance is being deleted.
|
||||
*/
|
||||
public function cleanup() {
|
||||
$this->purge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an instance of the cache store that can be used for testing.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return false
|
||||
*/
|
||||
public static function initialise_test_instance(cache_definition $definition) {
|
||||
if (!self::are_requirements_met()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$config = get_config('cachestore_memcache');
|
||||
if (empty($config->testservers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$configuration = array();
|
||||
$configuration['servers'] = explode("\n", $config->testservers);
|
||||
|
||||
$store = new cachestore_memcache('Test memcache', $configuration);
|
||||
$store->initialise($definition);
|
||||
|
||||
return $store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this instance.
|
||||
* @return string
|
||||
*/
|
||||
public function my_name() {
|
||||
return $this->name;
|
||||
}
|
||||
}
|
33
cache/stores/memcache/settings.php
vendored
Normal file
33
cache/stores/memcache/settings.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The settings for the memcache store.
|
||||
*
|
||||
* This file is part of the memcache cache store, it contains the API for interacting with an instance of the store.
|
||||
*
|
||||
* @package cachestore_memcache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$settings->add(new admin_setting_configtextarea(
|
||||
'cachestore_memcache/testservers',
|
||||
new lang_string('testservers', 'cachestore_memcache'),
|
||||
new lang_string('testservers_desc', 'cachestore_memcache'),
|
||||
'', PARAM_RAW, 60, 3));
|
31
cache/stores/memcache/version.php
vendored
Normal file
31
cache/stores/memcache/version.php
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Cache memcache store version information.
|
||||
*
|
||||
* Not to be confused with the memcached plugin.
|
||||
*
|
||||
* @package cachestore_memcache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$plugin->version = 2012091000; // The current module version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2012090700; // Requires this Moodle version.
|
||||
$plugin->component = 'cachestore_memcache'; // Full name of the plugin.
|
77
cache/stores/memcached/addinstanceform.php
vendored
Normal file
77
cache/stores/memcached/addinstanceform.php
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The library file for the memcached cache store.
|
||||
*
|
||||
* This file is part of the memcached cache store, it contains the API for interacting with an instance of the store.
|
||||
*
|
||||
* @package cachestore_memcached
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot.'/cache/forms.php');
|
||||
require_once($CFG->dirroot.'/cache/stores/memcached/lib.php');
|
||||
|
||||
/**
|
||||
* Form for adding a memcached instance.
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cachestore_memcached_addinstance_form extends cachestore_addinstance_form {
|
||||
|
||||
/**
|
||||
* Adds the desired form elements.
|
||||
*/
|
||||
protected function configuration_definition() {
|
||||
$form = $this->_form;
|
||||
|
||||
$form->addElement('textarea', 'servers', get_string('servers', 'cachestore_memcached'), array('cols' => 75, 'rows' => 5));
|
||||
$form->addHelpButton('servers', 'servers', 'cachestore_memcached');
|
||||
$form->addRule('servers', get_string('required'), 'required');
|
||||
$form->setType('servers', PARAM_RAW);
|
||||
|
||||
$form->addElement('selectyesno', 'compression', get_string('usecompression', 'cachestore_memcached'));
|
||||
$form->addHelpButton('compression', 'usecompression', 'cachestore_memcached');
|
||||
$form->setDefault('compression', 1);
|
||||
$form->setType('compression', PARAM_BOOL);
|
||||
|
||||
$serialiseroptions = cachestore_memcached::config_get_serialiser_options();
|
||||
$form->addElement('select', 'serialiser', get_string('useserialiser', 'cachestore_memcached'), $serialiseroptions);
|
||||
$form->addHelpButton('serialiser', 'useserialiser', 'cachestore_memcached');
|
||||
$form->setDefault('serialiser', Memcached::SERIALIZER_PHP);
|
||||
$form->setType('serialiser', PARAM_NUMBER);
|
||||
|
||||
$form->addElement('text', 'prefix', get_string('prefix', 'cachestore_memcached'), array('size' => 16));
|
||||
$form->setType('prefix', PARAM_ALPHANUM);
|
||||
$form->addHelpButton('prefix', 'prefix', 'cachestore_memcached');
|
||||
|
||||
$hashoptions = cachestore_memcached::config_get_hash_options();
|
||||
$form->addElement('select', 'hash', get_string('hash', 'cachestore_memcached'), $hashoptions);
|
||||
$form->addHelpButton('hash', 'hash', 'cachestore_memcached');
|
||||
$form->setDefault('serialiser', Memcached::HASH_DEFAULT);
|
||||
$form->setType('serialiser', PARAM_INT);
|
||||
|
||||
$form->addElement('selectyesno', 'bufferwrites', get_string('bufferwrites', 'cachestore_memcached'));
|
||||
$form->addHelpButton('bufferwrites', 'bufferwrites', 'cachestore_memcached');
|
||||
$form->setDefault('bufferwrites', 0);
|
||||
$form->setType('bufferwrites', PARAM_BOOL);
|
||||
}
|
||||
}
|
67
cache/stores/memcached/lang/en/cachestore_memcached.php
vendored
Normal file
67
cache/stores/memcached/lang/en/cachestore_memcached.php
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The library file for the memcached cache store.
|
||||
*
|
||||
* This file is part of the memcached cache store, it contains the API for interacting with an instance of the store.
|
||||
*
|
||||
* @package cachestore_memcached
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['bufferwrites'] = 'Buffer writes';
|
||||
$string['bufferwrites_help'] = 'Enables or disables buffered I/O. Enabling buffered I/O causes storage commands to "buffer" instead of being sent. Any action that retrieves data causes this buffer to be sent to the remote connection. Quitting the connection or closing down the connection will also cause the buffered data to be pushed to the remote connection.';
|
||||
$string['hash'] = 'Hash method';
|
||||
$string['hash_help'] = 'Specifies the hashing algorithm used for the item keys. Each hash algorithm has its advantages and its disadvantages. Go with the default if you don\'t know or don\'t care.';
|
||||
$string['hash_default'] = 'Default (one-at-a-time)';
|
||||
$string['hash_md5'] = 'MD5';
|
||||
$string['hash_crc'] = 'CRC';
|
||||
$string['hash_fnv1_64'] = 'FNV1_64';
|
||||
$string['hash_fnv1a_64'] = 'FNV1A_64';
|
||||
$string['hash_fnv1_32'] = 'FNV1_32';
|
||||
$string['hash_fnv1a_32'] = 'FNV1A_32';
|
||||
$string['hash_hsieh'] = 'Hsieh';
|
||||
$string['hash_murmur'] = 'Murmur';
|
||||
$string['pluginname'] = 'Memcached';
|
||||
$string['prefix'] = 'Prefix key';
|
||||
$string['prefix_help'] = 'This can be used to create a "domain" for your item keys allowing you to create multiple memcached stores on a single memcached installation. It cannot be longer than 16 characters in order to ensure key length issues are not encountered.';
|
||||
$string['serialiser_igbinary'] = 'The igbinary serializer.';
|
||||
$string['serialiser_json'] = 'The JSON serializer.';
|
||||
$string['serialiser_php'] = 'The default PHP serializer.';
|
||||
$string['servers'] = 'Servers';
|
||||
$string['servers_help'] = 'This sets the servers that should be utilised by this memcached adapter.
|
||||
Servers should be defined one per line and consist of a server address and optionally a port and weight.
|
||||
If no port is provided then the default port (11211) is used.
|
||||
|
||||
For example:
|
||||
<pre>
|
||||
server.url.com
|
||||
ipaddress:port
|
||||
servername:port:weight
|
||||
</pre>';
|
||||
$string['testservers'] = 'Test servers';
|
||||
$string['testservers_desc'] = 'The test servers get used for unit tests and for performance tests. It is entirely optional to set up test servers. Servers should be defined one per line and consist of a server address and optionally a port and weight.
|
||||
If no port is provided then the default port (11211) is used.';
|
||||
$string['usecompression'] = 'Use compression';
|
||||
$string['usecompression_help'] = 'Enables or disables payload compression. When enabled, item values longer than a certain threshold (currently 100 bytes) will be compressed during storage and decompressed during retrieval transparently.';
|
||||
$string['useserialiser'] = 'Use serialiser';
|
||||
$string['useserialiser_help'] = 'Specifies the serializer to use for serializing non-scalar values.
|
||||
The valid serializers are Memcached::SERIALIZER_PHP or Memcached::SERIALIZER_IGBINARY.
|
||||
The latter is supported only when memcached is configured with --enable-memcached-igbinary option and the igbinary extension is loaded.';
|
460
cache/stores/memcached/lib.php
vendored
Normal file
460
cache/stores/memcached/lib.php
vendored
Normal file
@ -0,0 +1,460 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The library file for the memcached cache store.
|
||||
*
|
||||
* This file is part of the memcached cache store, it contains the API for interacting with an instance of the store.
|
||||
*
|
||||
* @package cachestore_memcached
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* The memcached store.
|
||||
*
|
||||
* (Not to be confused with the memcache store)
|
||||
*
|
||||
* Configuration options:
|
||||
* servers: string: host:port:weight , ...
|
||||
* compression: true, false
|
||||
* serialiser: SERIALIZER_PHP, SERIALIZER_JSON, SERIALIZER_IGBINARY
|
||||
* prefix: string: defaults to instance name
|
||||
* hashmethod: HASH_DEFAULT, HASH_MD5, HASH_CRC, HASH_FNV1_64, HASH_FNV1A_64, HASH_FNV1_32,
|
||||
* HASH_FNV1A_32, HASH_HSIEH, HASH_MURMUR
|
||||
* bufferwrites: true, false
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cachestore_memcached implements cache_store {
|
||||
|
||||
/**
|
||||
* The name of the store
|
||||
* @var store
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The memcached connection
|
||||
* @var Memcached
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* An array of servers to use during connection
|
||||
* @var array
|
||||
*/
|
||||
protected $servers = array();
|
||||
|
||||
/**
|
||||
* The options used when establishing the connection
|
||||
* @var array
|
||||
*/
|
||||
protected $options = array();
|
||||
|
||||
/**
|
||||
* True when this instance is ready to be initialised.
|
||||
* @var bool
|
||||
*/
|
||||
protected $isready = false;
|
||||
|
||||
/**
|
||||
* The cache definition this store was initialised with.
|
||||
* @var cache_definition
|
||||
*/
|
||||
protected $definition;
|
||||
|
||||
/**
|
||||
* Constructs the store instance.
|
||||
*
|
||||
* Noting that this function is not an initialisation. It is used to prepare the store for use.
|
||||
* The store will be initialised when required and will be provided with a cache_definition at that time.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $configuration
|
||||
*/
|
||||
public function __construct($name, array $configuration = array()) {
|
||||
$this->name = $name;
|
||||
if (!array_key_exists('servers', $configuration) || empty($configuration['servers'])) {
|
||||
// Nothing configured.
|
||||
return;
|
||||
}
|
||||
if (!is_array($configuration['servers'])) {
|
||||
$configuration['servers'] = array($configuration['servers']);
|
||||
}
|
||||
|
||||
$compression = array_key_exists('compression', $configuration) ? (bool)$configuration['compression'] : true;
|
||||
if (array_key_exists('serialiser', $configuration)) {
|
||||
$serialiser = (int)$configuration['serialiser'];
|
||||
} else {
|
||||
$serialiser = Memcached::SERIALIZER_PHP;
|
||||
}
|
||||
$prefix = (!empty($configuration['prefix'])) ? (string)$configuration['prefix'] : crc32($name);
|
||||
$hashmethod = (array_key_exists('hash', $configuration)) ? (int)$configuration['hash'] : Memcached::HASH_DEFAULT;
|
||||
$bufferwrites = array_key_exists('bufferwrites', $configuration) ? (bool)$configuration['bufferwrites'] : false;
|
||||
|
||||
foreach ($configuration['servers'] as $server) {
|
||||
if (!is_array($server)) {
|
||||
$server = explode(':', $server, 3);
|
||||
}
|
||||
if (!array_key_exists(1, $server)) {
|
||||
$server[1] = 11211;
|
||||
$server[2] = 100;
|
||||
} else if (!array_key_exists(2, $server)) {
|
||||
$server[2] = 100;
|
||||
}
|
||||
$this->servers[] = $server;
|
||||
}
|
||||
$this->options[Memcached::OPT_COMPRESSION] = $compression;
|
||||
$this->options[Memcached::OPT_SERIALIZER] = $serialiser;
|
||||
$this->options[Memcached::OPT_PREFIX_KEY] = $prefix;
|
||||
$this->options[Memcached::OPT_HASH] = $hashmethod;
|
||||
$this->options[Memcached::OPT_BUFFER_WRITES] = $bufferwrites;
|
||||
|
||||
$this->isready = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the cache.
|
||||
*
|
||||
* Once this has been done the cache is all set to be used.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
*/
|
||||
public function initialise(cache_definition $definition) {
|
||||
if ($this->is_initialised()) {
|
||||
throw new coding_exception('This memcached instance has already been initialised.');
|
||||
}
|
||||
$this->definition = $definition;
|
||||
$this->connection = new Memcached(crc32($this->name));
|
||||
$servers = $this->connection->getServerList();
|
||||
if (empty($servers)) {
|
||||
foreach ($this->options as $key => $value) {
|
||||
$this->connection->setOption($key, $value);
|
||||
}
|
||||
$this->connection->addServers($this->servers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true once this instance has been initialised.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_initialised() {
|
||||
return ($this->connection !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this store instance is ready to be used.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_ready() {
|
||||
return $this->isready;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store requirements are met.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function are_requirements_met() {
|
||||
return class_exists('Memcached');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given mode is supported by this store.
|
||||
*
|
||||
* @param int $mode One of cache_store::MODE_*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_supported_mode($mode) {
|
||||
return ($mode === self::MODE_APPLICATION || $mode === self::MODE_SESSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supported features as a combined int.
|
||||
*
|
||||
* @param array $configuration
|
||||
* @return int
|
||||
*/
|
||||
public static function get_supported_features(array $configuration = array()) {
|
||||
return self::SUPPORTS_NATIVE_TTL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store instance supports multiple identifiers.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_multiple_indentifiers() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store instance guarantees data.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_data_guarantee() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store instance supports native ttl.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_native_ttl() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supported modes as a combined int.
|
||||
*
|
||||
* @param array $configuration
|
||||
* @return int
|
||||
*/
|
||||
public static function get_supported_modes(array $configuration = array()) {
|
||||
return self::MODE_APPLICATION + self::MODE_SESSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an item from the cache store given its key.
|
||||
*
|
||||
* @param string $key The key to retrieve
|
||||
* @return mixed The data that was associated with the key, or false if the key did not exist.
|
||||
*/
|
||||
public function get($key) {
|
||||
return $this->connection->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves several items from the cache store in a single transaction.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @param array $keys The array of keys to retrieve
|
||||
* @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
|
||||
* be set to false.
|
||||
*/
|
||||
public function get_many($keys) {
|
||||
$result = $this->connection->getMulti($keys);
|
||||
if (!is_array($result)) {
|
||||
$result = array();
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
if (!array_key_exists($key, $result)) {
|
||||
$result[$key] = false;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an item in the cache given its key and data value.
|
||||
*
|
||||
* @param string $key The key to use.
|
||||
* @param mixed $data The data to set.
|
||||
* @return bool True if the operation was a success false otherwise.
|
||||
*/
|
||||
public function set($key, $data) {
|
||||
return $this->connection->set($key, $data, $this->definition->get_ttl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets many items in the cache in a single transaction.
|
||||
*
|
||||
* @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two
|
||||
* keys, 'key' and 'value'.
|
||||
* @return int The number of items successfully set. It is up to the developer to check this matches the number of items
|
||||
* sent ... if they care that is.
|
||||
*/
|
||||
public function set_many(array $keyvaluearray) {
|
||||
$pairs = array();
|
||||
foreach ($keyvaluearray as $pair) {
|
||||
$pairs[$pair['key']] = $pair['value'];
|
||||
}
|
||||
if ($this->connection->setMulti($pairs, $this->definition->get_ttl())) {
|
||||
return count($keyvaluearray);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an item from the cache store.
|
||||
*
|
||||
* @param string $key The key to delete.
|
||||
* @return bool Returns true if the operation was a success, false otherwise.
|
||||
*/
|
||||
public function delete($key) {
|
||||
return $this->connection->delete($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes several keys from the cache in a single action.
|
||||
*
|
||||
* @param array $keys The keys to delete
|
||||
* @return int The number of items successfully deleted.
|
||||
*/
|
||||
public function delete_many(array $keys) {
|
||||
$count = 0;
|
||||
foreach ($keys as $key) {
|
||||
if ($this->connection->delete($key)) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges the cache deleting all items within it.
|
||||
*
|
||||
* @return boolean True on success. False otherwise.
|
||||
*/
|
||||
public function purge() {
|
||||
$this->connection->flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of options to use as the serialiser.
|
||||
* @return array
|
||||
*/
|
||||
public static function config_get_serialiser_options() {
|
||||
$options = array(
|
||||
Memcached::SERIALIZER_PHP => get_string('serialiser_php', 'cachestore_memcached')
|
||||
);
|
||||
if (Memcached::HAVE_JSON) {
|
||||
$options[Memcached::SERIALIZER_JSON] = get_string('serialiser_json', 'cachestore_memcached');
|
||||
}
|
||||
if (Memcached::HAVE_IGBINARY) {
|
||||
$options[Memcached::SERIALIZER_IGBINARY] = get_string('serialiser_php', 'cachestore_memcached');
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of hash options available during configuration.
|
||||
* @return array
|
||||
*/
|
||||
public static function config_get_hash_options() {
|
||||
$options = array(
|
||||
Memcached::HASH_DEFAULT => get_string('hash_default', 'cachestore_memcached'),
|
||||
Memcached::HASH_MD5 => get_string('hash_md5', 'cachestore_memcached'),
|
||||
Memcached::HASH_CRC => get_string('hash_crc', 'cachestore_memcached'),
|
||||
Memcached::HASH_FNV1_64 => get_string('hash_fnv1_64', 'cachestore_memcached'),
|
||||
Memcached::HASH_FNV1A_64 => get_string('hash_fnv1a_64', 'cachestore_memcached'),
|
||||
Memcached::HASH_FNV1_32 => get_string('hash_fnv1_32', 'cachestore_memcached'),
|
||||
Memcached::HASH_FNV1A_32 => get_string('hash_fnv1a_32', 'cachestore_memcached'),
|
||||
Memcached::HASH_HSIEH => get_string('hash_hsieh', 'cachestore_memcached'),
|
||||
Memcached::HASH_MURMUR => get_string('hash_murmur', 'cachestore_memcached'),
|
||||
);
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the data from the add instance form this function creates a configuration array.
|
||||
*
|
||||
* @param stdClass $data
|
||||
* @return array
|
||||
*/
|
||||
public static function config_get_configuration_array($data) {
|
||||
$lines = explode("\n", $data->servers);
|
||||
$servers = array();
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line, ':');
|
||||
$servers[] = explode(':', $line, 3);
|
||||
}
|
||||
return array(
|
||||
'servers' => $servers,
|
||||
'compression' => $data->compression,
|
||||
'serialiser' => $data->serialiser,
|
||||
'prefix' => $data->prefix,
|
||||
'hash' => $data->hash,
|
||||
'bufferwrites' => $data->bufferwrites,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user can add an instance of the store plugin.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function can_add_instance() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs any necessary clean up when the store instance is being deleted.
|
||||
*/
|
||||
public function cleanup() {
|
||||
$this->purge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an instance of the cache store that can be used for testing.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return false
|
||||
*/
|
||||
public static function initialise_test_instance(cache_definition $definition) {
|
||||
|
||||
if (!self::are_requirements_met()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$config = get_config('cachestore_memcached');
|
||||
if (empty($config->testservers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$configuration = array();
|
||||
$configuration['servers'] = $config->testservers;
|
||||
if (!empty($config->testcompression)) {
|
||||
$configuration['compression'] = $config->testcompression;
|
||||
}
|
||||
if (!empty($config->testserialiser)) {
|
||||
$configuration['serialiser'] = $config->testserialiser;
|
||||
}
|
||||
if (!empty($config->testprefix)) {
|
||||
$configuration['prefix'] = $config->testprefix;
|
||||
}
|
||||
if (!empty($config->testhash)) {
|
||||
$configuration['hash'] = $config->testhash;
|
||||
}
|
||||
if (!empty($config->testbufferwrites)) {
|
||||
$configuration['bufferwrites'] = $config->testbufferwrites;
|
||||
}
|
||||
|
||||
$store = new cachestore_memcached('Test memcached', $configuration);
|
||||
$store->initialise($definition);
|
||||
|
||||
return $store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this instance.
|
||||
* @return string
|
||||
*/
|
||||
public function my_name() {
|
||||
return $this->name;
|
||||
}
|
||||
}
|
33
cache/stores/memcached/settings.php
vendored
Normal file
33
cache/stores/memcached/settings.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The settings for the memcached store.
|
||||
*
|
||||
* This file is part of the memcached cache store, it contains the API for interacting with an instance of the store.
|
||||
*
|
||||
* @package cachestore_memcached
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$settings->add(new admin_setting_configtextarea(
|
||||
'cachestore_memcached/testservers',
|
||||
new lang_string('testservers', 'cachestore_memcached'),
|
||||
new lang_string('testservers_desc', 'cachestore_memcached'),
|
||||
'', PARAM_RAW, 60, 3));
|
31
cache/stores/memcached/version.php
vendored
Normal file
31
cache/stores/memcached/version.php
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Cache memcached store version information.
|
||||
*
|
||||
* Not to be confused with the memcache plugin.
|
||||
*
|
||||
* @package cachestore_memcached
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$plugin->version = 2012091000; // The current module version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2012090700; // Requires this Moodle version.
|
||||
$plugin->component = 'cachestore_memcached'; // Full name of the plugin.
|
97
cache/stores/mongodb/addinstanceform.php
vendored
Normal file
97
cache/stores/mongodb/addinstanceform.php
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The MongoDB plugin form for adding an instance.
|
||||
*
|
||||
* The following settings are provided:
|
||||
* - server
|
||||
* - username
|
||||
* - password
|
||||
* - database
|
||||
* - replicaset
|
||||
* - usesafe
|
||||
* - extendedmode
|
||||
*
|
||||
* @package cachestore_mongodb
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
// Include the necessary evils.
|
||||
require_once($CFG->dirroot.'/cache/forms.php');
|
||||
require_once($CFG->dirroot.'/cache/stores/mongodb/lib.php');
|
||||
|
||||
/**
|
||||
* The form to add an instance of the MongoDB store to the system.
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cachestore_mongodb_addinstance_form extends cachestore_addinstance_form {
|
||||
|
||||
/**
|
||||
* The forms custom definitions.
|
||||
*/
|
||||
protected function configuration_definition() {
|
||||
$form = $this->_form;
|
||||
|
||||
$form->addElement('text', 'server', get_string('server', 'cachestore_mongodb'), array('size' => 72));
|
||||
$form->addHelpButton('server', 'server', 'cachestore_mongodb');
|
||||
$form->addRule('server', get_string('required'), 'required');
|
||||
$form->setDefault('server', 'mongodb://127.0.0.1:27017');
|
||||
$form->setType('server', PARAM_RAW);
|
||||
|
||||
$form->addElement('text', 'database', get_string('database', 'cachestore_mongodb'));
|
||||
$form->addHelpButton('database', 'database', 'cachestore_mongodb');
|
||||
$form->addRule('database', get_string('required'), 'required');
|
||||
$form->setType('database', PARAM_ALPHANUMEXT);
|
||||
$form->setDefault('database', 'mcache');
|
||||
|
||||
$form->addElement('text', 'username', get_string('username', 'cachestore_mongodb'));
|
||||
$form->addHelpButton('username', 'username', 'cachestore_mongodb');
|
||||
$form->setType('username', PARAM_ALPHANUMEXT);
|
||||
|
||||
$form->addElement('text', 'password', get_string('password', 'cachestore_mongodb'));
|
||||
$form->addHelpButton('password', 'password', 'cachestore_mongodb');
|
||||
$form->setType('password', PARAM_TEXT);
|
||||
|
||||
$form->addElement('text', 'replicaset', get_string('replicaset', 'cachestore_mongodb'));
|
||||
$form->addHelpButton('replicaset', 'replicaset', 'cachestore_mongodb');
|
||||
$form->setType('replicaset', PARAM_ALPHANUMEXT);
|
||||
$form->setAdvanced('replicaset');
|
||||
|
||||
$form->addElement('checkbox', 'usesafe', get_string('usesafe', 'cachestore_mongodb'));
|
||||
$form->addHelpButton('usesafe', 'usesafe', 'cachestore_mongodb');
|
||||
$form->setDefault('usesafe', 1);
|
||||
$form->setAdvanced('usesafe');
|
||||
$form->setType('usesafe', PARAM_BOOL);
|
||||
|
||||
$form->addElement('text', 'usesafevalue', get_string('usesafevalue', 'cachestore_mongodb'));
|
||||
$form->addHelpButton('usesafevalue', 'usesafevalue', 'cachestore_mongodb');
|
||||
$form->disabledIf('usesafevalue', 'usesafe', 'notchecked');
|
||||
$form->setType('usesafevalue', PARAM_INT);
|
||||
$form->setAdvanced('usesafevalue');
|
||||
|
||||
$form->addElement('checkbox', 'extendedmode', get_string('extendedmode', 'cachestore_mongodb'));
|
||||
$form->addHelpButton('extendedmode', 'extendedmode', 'cachestore_mongodb');
|
||||
$form->setDefault('extendedmode', 0);
|
||||
$form->setAdvanced('extendedmode');
|
||||
$form->setType('extendedmode', PARAM_BOOL);
|
||||
}
|
||||
}
|
43
cache/stores/mongodb/lang/en/cachestore_mongodb.php
vendored
Normal file
43
cache/stores/mongodb/lang/en/cachestore_mongodb.php
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The language strings for the MongoDB store plugin.
|
||||
*
|
||||
* @package cachestore_mongodb
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$string['database'] = 'Database';
|
||||
$string['database_help'] = 'The name of the database to make use of.';
|
||||
$string['extendedmode'] = 'Use extended keys';
|
||||
$string['extendedmode_help'] = 'If enabled full key sets will be used when working with the plugin. This isn\'t used internally yet but would allow you to easily search and investigate the MongoDB plugin manually if you so choose. Turning this on will add an small overhead so should only be done if you require it.';
|
||||
$string['password'] = 'Password';
|
||||
$string['password_help'] = 'The password of the user being used for the connection.';
|
||||
$string['pluginname'] = 'MongoDB';
|
||||
$string['replicaset'] = 'Replica set';
|
||||
$string['replicaset_help'] = 'The name of the replica set to connect to. If this is given the master will be determined by using the ismaster database command on the seeds, so the driver may end up connecting to a server that was not even listed.';
|
||||
$string['server'] = 'Server';
|
||||
$string['server_help'] = 'This is the connection string for the server you want to use. Multiple servers can be specified by separating them with comma\'s';
|
||||
$string['testserver'] = 'Test server';
|
||||
$string['testserver_desc'] = 'This is the connection string for the test server you want to use. Test servers are entirely optional, by specifiying a test server you can run PHPunit tests for this store and can run the performance tests.';
|
||||
$string['username'] = 'Username';
|
||||
$string['username_help'] = 'The username to use when making a connection.';
|
||||
$string['usesafe'] = 'Use safe';
|
||||
$string['usesafe_help'] = 'If enabled the usesafe option will be used during insert, get, and remove operations. If you\'ve specified a replica set this will be forced on anyway.';
|
||||
$string['usesafevalue'] = 'Use safe value';
|
||||
$string['usesafevalue_help'] = 'You can choose to provide a specific value for use safe. This will determine the number of servers that operations must be completed on before they are deemed to have been completed.';
|
484
cache/stores/mongodb/lib.php
vendored
Normal file
484
cache/stores/mongodb/lib.php
vendored
Normal file
@ -0,0 +1,484 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The library file for the MongoDB store plugin.
|
||||
*
|
||||
* This file is part of the MongoDB store plugin, it contains the API for interacting with an instance of the store.
|
||||
*
|
||||
* @package cachestore_mongodb
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* The MongoDB Cache store.
|
||||
*
|
||||
* This cache store uses the MongoDB Native Driver.
|
||||
* For installation instructions have a look at the following two links:
|
||||
* - {@link http://www.php.net/manual/en/mongo.installation.php}
|
||||
* - {@link http://www.mongodb.org/display/DOCS/PHP+Language+Center}
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cachestore_mongodb implements cache_store {
|
||||
|
||||
/**
|
||||
* The name of the store
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The server connection string. Comma separated values.
|
||||
* @var string
|
||||
*/
|
||||
protected $server = 'mongodb://127.0.0.1:27017';
|
||||
|
||||
/**
|
||||
* The database connection options
|
||||
* @var array
|
||||
*/
|
||||
protected $options = array();
|
||||
|
||||
/**
|
||||
* The name of the database to use.
|
||||
* @var string
|
||||
*/
|
||||
protected $databasename = 'mcache';
|
||||
|
||||
/**
|
||||
* The Connection object
|
||||
* @var Mongo
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The Database Object
|
||||
* @var MongoDB
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* The Collection object
|
||||
* @var MongoCollection
|
||||
*/
|
||||
protected $collection;
|
||||
|
||||
/**
|
||||
* Determines if and what safe setting is to be used.
|
||||
* @var bool|int
|
||||
*/
|
||||
protected $usesafe = false;
|
||||
|
||||
/**
|
||||
* If set to true then multiple identifiers will be requested and used.
|
||||
* @var bool
|
||||
*/
|
||||
protected $extendedmode = false;
|
||||
|
||||
/**
|
||||
* The definition has which is used in the construction of the collection.
|
||||
* @var string
|
||||
*/
|
||||
protected $definitionhash = null;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of the Mongo store but does not connect to it.
|
||||
* @param string $name
|
||||
* @param array $configuration
|
||||
*/
|
||||
public function __construct($name, array $configuration = array()) {
|
||||
$this->name = $name;
|
||||
|
||||
if (array_key_exists('server', $configuration)) {
|
||||
$this->server = $configuration['server'];
|
||||
}
|
||||
|
||||
if (array_key_exists('replicaset', $configuration)) {
|
||||
$this->options['replicaSet'] = (string)$configuration['replicaset'];
|
||||
}
|
||||
if (array_key_exists('username', $configuration) && !empty($configuration['username'])) {
|
||||
$this->options['username'] = (string)$configuration['username'];
|
||||
}
|
||||
if (array_key_exists('password', $configuration) && !empty($configuration['password'])) {
|
||||
$this->options['password'] = (string)$configuration['password'];
|
||||
}
|
||||
if (array_key_exists('database', $configuration)) {
|
||||
$this->databasename = (string)$configuration['database'];
|
||||
}
|
||||
if (array_key_exists('usesafe', $configuration)) {
|
||||
$this->usesafe = $configuration['usesafe'];
|
||||
}
|
||||
if (array_key_exists('extendedmode', $configuration)) {
|
||||
$this->extendedmode = $configuration['extendedmode'];
|
||||
}
|
||||
|
||||
$this->isready = self::are_requirements_met();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the requirements of this store have been met.
|
||||
* @return bool
|
||||
*/
|
||||
public static function are_requirements_met() {
|
||||
return class_exists('Mongo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user can add an instance of this store.
|
||||
* @return bool
|
||||
*/
|
||||
public static function can_add_instance() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supported features.
|
||||
* @param array $configuration
|
||||
* @return int
|
||||
*/
|
||||
public static function get_supported_features(array $configuration = array()) {
|
||||
$supports = self::SUPPORTS_DATA_GUARANTEE;
|
||||
if (array_key_exists('extendedmode', $configuration) && $configuration['extendedmode']) {
|
||||
$supports += self::SUPPORTS_MULTIPLE_IDENTIFIERS;
|
||||
}
|
||||
return $supports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an int describing the supported modes.
|
||||
* @param array $configuration
|
||||
* @return int
|
||||
*/
|
||||
public static function get_supported_modes(array $configuration = array()) {
|
||||
return self::MODE_APPLICATION + self::MODE_SESSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the store instance for use.
|
||||
*
|
||||
* This function is reponsible for making the connection.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @throws coding_exception
|
||||
*/
|
||||
public function initialise(cache_definition $definition) {
|
||||
if ($this->is_initialised()) {
|
||||
throw new coding_exception('This mongodb instance has already been initialised.');
|
||||
}
|
||||
$this->definitionhash = $definition->generate_definition_hash();
|
||||
$this->connection = new Mongo($this->server, $this->options);
|
||||
$this->database = $this->connection->selectDB($this->databasename);
|
||||
$this->collection = $this->database->selectCollection($this->definitionhash);
|
||||
$this->collection->ensureIndex(array('key' => 1), array(
|
||||
'safe' => $this->usesafe,
|
||||
'name' => 'idx_key'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this store instance has been initialised.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_initialised() {
|
||||
return ($this->database instanceof MongoDB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this store instance is ready to use.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_ready() {
|
||||
return $this->isready;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given mode is supported by this store.
|
||||
* @param int $mode
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_supported_mode($mode) {
|
||||
return ($mode == self::MODE_APPLICATION || $mode == self::MODE_SESSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this store guarantees its data is there once set.
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_data_guarantee() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this store is making use of multiple identifiers.
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_multiple_indentifiers() {
|
||||
return $this->extendedmode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this store supports native TTL.
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_native_ttl() {
|
||||
return false;;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an item from the cache store given its key.
|
||||
*
|
||||
* @param string $key The key to retrieve
|
||||
* @return mixed The data that was associated with the key, or false if the key did not exist.
|
||||
*/
|
||||
public function get($key) {
|
||||
if (!is_array($key)) {
|
||||
$key = array('key' => $key);
|
||||
}
|
||||
|
||||
$result = $this->collection->findOne($key);
|
||||
if ($result === null || !array_key_exists('data', $result)) {
|
||||
return false;
|
||||
}
|
||||
$data = @unserialize($result['data']);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves several items from the cache store in a single transaction.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @param array $keys The array of keys to retrieve
|
||||
* @return array An array of items from the cache.
|
||||
*/
|
||||
public function get_many($keys) {
|
||||
if ($this->extendedmode) {
|
||||
$query = $this->get_many_extendedmode_query($keys);
|
||||
$keyarray = array();
|
||||
foreach ($keys as $key) {
|
||||
$keyarray[] = $key['key'];
|
||||
}
|
||||
$keys = $keyarray;
|
||||
$query = array('key' => array('$in' => $keys));
|
||||
} else {
|
||||
$query = array('key' => array('$in' => $keys));
|
||||
}
|
||||
$cursor = $this->collection->find($query);
|
||||
$results = array();
|
||||
foreach ($cursor as $result) {
|
||||
if (array_key_exists('key', $result)) {
|
||||
$id = $result[$key];
|
||||
} else {
|
||||
$id = (string)$result['key'];
|
||||
}
|
||||
$results[$id] = unserialize($result['data']);
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
if (!array_key_exists($key, $results)) {
|
||||
$results[$key] = false;
|
||||
}
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an item in the cache given its key and data value.
|
||||
*
|
||||
* @param string $key The key to use.
|
||||
* @param mixed $data The data to set.
|
||||
* @return bool True if the operation was a success false otherwise.
|
||||
*/
|
||||
public function set($key, $data) {
|
||||
if (!is_array($key)) {
|
||||
$record = array(
|
||||
'key' => $key
|
||||
);
|
||||
} else {
|
||||
$record = $key;
|
||||
}
|
||||
$record['data'] = serialize($data);
|
||||
$options = array(
|
||||
'upsert' => true,
|
||||
'safe' => $this->usesafe
|
||||
);
|
||||
$this->delete($key);
|
||||
$result = $this->collection->insert($record, $options);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets many items in the cache in a single transaction.
|
||||
*
|
||||
* @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two
|
||||
* keys, 'key' and 'value'.
|
||||
* @return int The number of items successfully set. It is up to the developer to check this matches the number of items
|
||||
* sent ... if they care that is.
|
||||
*/
|
||||
public function set_many(array $keyvaluearray) {
|
||||
$count = 0;
|
||||
foreach ($keyvaluearray as $pair) {
|
||||
$result = $this->set($pair['key'], $pair['value']);
|
||||
if ($result === true || (is_array($result)) && !empty($result['ok'])) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an item from the cache store.
|
||||
*
|
||||
* @param string $key The key to delete.
|
||||
* @return bool Returns true if the operation was a success, false otherwise.
|
||||
*/
|
||||
public function delete($key) {
|
||||
if (!is_array($key)) {
|
||||
$criteria = array(
|
||||
'key' => $key
|
||||
);
|
||||
} else {
|
||||
$criteria = $key;
|
||||
}
|
||||
$options = array(
|
||||
'justOne' => false,
|
||||
'safe' => $this->usesafe
|
||||
);
|
||||
$result = $this->collection->remove($criteria, $options);
|
||||
if ($result === false || (is_array($result) && !array_key_exists('ok', $result)) || $result === 0) {
|
||||
return false;
|
||||
}
|
||||
return !empty($result['ok']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes several keys from the cache in a single action.
|
||||
*
|
||||
* @param array $keys The keys to delete
|
||||
* @return int The number of items successfully deleted.
|
||||
*/
|
||||
public function delete_many(array $keys) {
|
||||
$count = 0;
|
||||
foreach ($keys as $key) {
|
||||
if ($this->delete($key)) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges the cache deleting all items within it.
|
||||
*
|
||||
* @return boolean True on success. False otherwise.
|
||||
*/
|
||||
public function purge() {
|
||||
$this->collection->drop();
|
||||
$this->collection = $this->database->selectCollection($this->definitionhash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the object from the add instance store and creates a configuration array that can be used to initialise an instance.
|
||||
*
|
||||
* @param stdClass $data
|
||||
* @return array
|
||||
*/
|
||||
public static function config_get_configuration_array($data) {
|
||||
$return = array(
|
||||
'server' => $data->server,
|
||||
'database' => $data->database,
|
||||
'extendedmode' => (!empty($data->extendedmode))
|
||||
);
|
||||
if (!empty($data->username)) {
|
||||
$return['username'] = $data->username;
|
||||
}
|
||||
if (!empty($data->password)) {
|
||||
$return['password'] = $data->password;
|
||||
}
|
||||
if (!empty($data->replicaset)) {
|
||||
$return['replicaset'] = $data->replicaset;
|
||||
}
|
||||
if (!empty($data->usesafe)) {
|
||||
$return['usesafe'] = true;
|
||||
if (!empty($data->usesafevalue)) {
|
||||
$return['usesafe'] = (int)$data->usesafevalue;
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs any necessary clean up when the store instance is being deleted.
|
||||
*/
|
||||
public function cleanup() {
|
||||
$this->purge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an instance of the cache store that can be used for testing.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return false
|
||||
*/
|
||||
public static function initialise_test_instance(cache_definition $definition) {
|
||||
if (!self::are_requirements_met()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$config = get_config('cachestore_mongodb');
|
||||
if (empty($config->testserver)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$configuration = array();
|
||||
$configuration['server'] = $config->testserver;
|
||||
if (!empty($config->testreplicaset)) {
|
||||
$configuration['replicaset'] = $config->testreplicaset;
|
||||
}
|
||||
if (!empty($config->testusername)) {
|
||||
$configuration['username'] = $config->testusername;
|
||||
}
|
||||
if (!empty($config->testpassword)) {
|
||||
$configuration['password'] = $config->testpassword;
|
||||
}
|
||||
if (!empty($config->testdatabase)) {
|
||||
$configuration['database'] = $config->testdatabase;
|
||||
}
|
||||
if (!empty($config->testusesafe)) {
|
||||
$configuration['usesafe'] = $config->testusesafe;
|
||||
}
|
||||
if (!empty($config->testextendedmode)) {
|
||||
$configuration['extendedmode'] = (bool)$config->testextendedmode;
|
||||
}
|
||||
|
||||
$store = new cachestore_mongodb('Test mongodb', $configuration);
|
||||
$store->initialise($definition);
|
||||
|
||||
return $store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this instance.
|
||||
* @return string
|
||||
*/
|
||||
public function my_name() {
|
||||
return $this->name;
|
||||
}
|
||||
}
|
33
cache/stores/mongodb/settings.php
vendored
Normal file
33
cache/stores/mongodb/settings.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The settings for the mongodb store.
|
||||
*
|
||||
* This file is part of the mongodb cache store, it contains the API for interacting with an instance of the store.
|
||||
*
|
||||
* @package cachestore_mongodb
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$settings->add(new admin_setting_configtextarea(
|
||||
'cachestore_mongodb/testserver',
|
||||
new lang_string('testserver', 'cachestore_mongodb'),
|
||||
new lang_string('testserver_desc', 'cachestore_mongodb'),
|
||||
'', PARAM_RAW, 60, 3));
|
29
cache/stores/mongodb/version.php
vendored
Normal file
29
cache/stores/mongodb/version.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Cache mongodb store version information.
|
||||
*
|
||||
* @package cachestore_mongodb
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$plugin->version = 2012091000; // The current module version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2012090700; // Requires this Moodle version.
|
||||
$plugin->component = 'cachestore_mongodb'; // Full name of the plugin.
|
31
cache/stores/session/lang/en/cachestore_session.php
vendored
Normal file
31
cache/stores/session/lang/en/cachestore_session.php
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The library file for the session cache store.
|
||||
*
|
||||
* This file is part of the session cache store, it contains the API for interacting with an instance of the store.
|
||||
* This is used as a default cache store within the Cache API. It should never be deleted.
|
||||
*
|
||||
* @package cachestore_session
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['pluginname'] = 'Session cache';
|
429
cache/stores/session/lib.php
vendored
Normal file
429
cache/stores/session/lib.php
vendored
Normal file
@ -0,0 +1,429 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The library file for the session cache store.
|
||||
*
|
||||
* This file is part of the session cache store, it contains the API for interacting with an instance of the store.
|
||||
* This is used as a default cache store within the Cache API. It should never be deleted.
|
||||
*
|
||||
* @package cachestore_session
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* The Session store class.
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cachestore_session extends session_data_store implements cache_store, cache_is_key_aware {
|
||||
|
||||
/**
|
||||
* The name of the store
|
||||
* @var store
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The store id (should be unique)
|
||||
* @var string
|
||||
*/
|
||||
protected $storeid;
|
||||
|
||||
/**
|
||||
* The store we use for data.
|
||||
* @var array
|
||||
*/
|
||||
protected $store;
|
||||
|
||||
/**
|
||||
* The ttl if there is one. Hopefully not.
|
||||
* @var int
|
||||
*/
|
||||
protected $ttl = 0;
|
||||
|
||||
/**
|
||||
* Constructs the store instance.
|
||||
*
|
||||
* Noting that this function is not an initialisation. It is used to prepare the store for use.
|
||||
* The store will be initialised when required and will be provided with a cache_definition at that time.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $configuration
|
||||
*/
|
||||
public function __construct($name, array $configuration = array()) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supported features as a combined int.
|
||||
*
|
||||
* @param array $configuration
|
||||
* @return int
|
||||
*/
|
||||
public static function get_supported_features(array $configuration = array()) {
|
||||
return self::SUPPORTS_DATA_GUARANTEE +
|
||||
self::SUPPORTS_NATIVE_TTL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supported modes as a combined int.
|
||||
*
|
||||
* @param array $configuration
|
||||
* @return int
|
||||
*/
|
||||
public static function get_supported_modes(array $configuration = array()) {
|
||||
return self::MODE_SESSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store requirements are met.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function are_requirements_met() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given mode is supported by this store.
|
||||
*
|
||||
* @param int $mode One of cache_store::MODE_*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_supported_mode($mode) {
|
||||
return ($mode === self::MODE_SESSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store instance guarantees data.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_data_guarantee() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store instance supports multiple identifiers.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_multiple_indentifiers() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store instance supports native ttl.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_native_ttl() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the cache.
|
||||
*
|
||||
* Once this has been done the cache is all set to be used.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
*/
|
||||
public function initialise(cache_definition $definition) {
|
||||
$this->storeid = $definition->generate_definition_hash();
|
||||
$this->store = &self::register_store_id($definition->get_id());
|
||||
$this->ttl = $definition->get_ttl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true once this instance has been initialised.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_initialised() {
|
||||
return (is_array($this->store));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this store instance is ready to be used.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_ready() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an item from the cache store given its key.
|
||||
*
|
||||
* @param string $key The key to retrieve
|
||||
* @return mixed The data that was associated with the key, or false if the key did not exist.
|
||||
*/
|
||||
public function get($key) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
if (array_key_exists($key, $this->store)) {
|
||||
if ($this->ttl == 0) {
|
||||
return $this->store[$key];
|
||||
} else if ($this->store[$key][1] >= $maxtime) {
|
||||
return $this->store[$key][0];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves several items from the cache store in a single transaction.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @param array $keys The array of keys to retrieve
|
||||
* @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
|
||||
* be set to false.
|
||||
*/
|
||||
public function get_many($keys) {
|
||||
$return = array();
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
foreach ($keys as $key) {
|
||||
$return[$key] = false;
|
||||
if (array_key_exists($key, $this->store)) {
|
||||
if ($this->ttl == 0) {
|
||||
$return[$key] = $this->store[$key];
|
||||
} else if ($this->store[$key][1] >= $maxtime) {
|
||||
$return[$key] = $this->store[$key][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an item in the cache given its key and data value.
|
||||
*
|
||||
* @param string $key The key to use.
|
||||
* @param mixed $data The data to set.
|
||||
* @return bool True if the operation was a success false otherwise.
|
||||
*/
|
||||
public function set($key, $data) {
|
||||
if ($this->ttl == 0) {
|
||||
$this->store[$key] = $data;
|
||||
} else {
|
||||
$this->store[$key] = array($data, cache::now());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets many items in the cache in a single transaction.
|
||||
*
|
||||
* @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two
|
||||
* keys, 'key' and 'value'.
|
||||
* @return int The number of items successfully set. It is up to the developer to check this matches the number of items
|
||||
* sent ... if they care that is.
|
||||
*/
|
||||
public function set_many(array $keyvaluearray) {
|
||||
$count = 0;
|
||||
foreach ($keyvaluearray as $pair) {
|
||||
$this->set($pair['key'], $pair['value']);
|
||||
$count++;
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the store has a record for the given key and returns true if so.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
if (array_key_exists($key, $this->store)) {
|
||||
if ($this->ttl == 0) {
|
||||
return true;
|
||||
} else if ($this->store[$key][1] >= $maxtime) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store contains records for all of the given keys.
|
||||
*
|
||||
* @param array $keys
|
||||
* @return bool
|
||||
*/
|
||||
public function has_all(array $keys) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
foreach ($keys as $key) {
|
||||
if (!array_key_exists($key, $this->store)) {
|
||||
return false;
|
||||
}
|
||||
if ($this->ttl != 0 && $this->store[$key][1] < $maxtime) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store contains records for any of the given keys.
|
||||
*
|
||||
* @param array $keys
|
||||
* @return bool
|
||||
*/
|
||||
public function has_any(array $keys) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
foreach ($keys as $key) {
|
||||
if (array_key_exists($key, $this->store) && ($this->ttl == 0 || $this->store[$key][1] >= $maxtime)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an item from the cache store.
|
||||
*
|
||||
* @param string $key The key to delete.
|
||||
* @return bool Returns true if the operation was a success, false otherwise.
|
||||
*/
|
||||
public function delete($key) {
|
||||
unset($this->store[$key]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes several keys from the cache in a single action.
|
||||
*
|
||||
* @param array $keys The keys to delete
|
||||
* @return int The number of items successfully deleted.
|
||||
*/
|
||||
public function delete_many(array $keys) {
|
||||
$count = 0;
|
||||
foreach ($keys as $key) {
|
||||
unset($this->store[$key]);
|
||||
$count++;
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges the cache deleting all items within it.
|
||||
*
|
||||
* @return boolean True on success. False otherwise.
|
||||
*/
|
||||
public function purge() {
|
||||
$this->store = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user can add an instance of the store plugin.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function can_add_instance() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs any necessary clean up when the store instance is being deleted.
|
||||
*/
|
||||
public function cleanup() {
|
||||
$this->purge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an instance of the cache store that can be used for testing.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return false
|
||||
*/
|
||||
public static function initialise_test_instance(cache_definition $definition) {
|
||||
// Do something here perhaps.
|
||||
$cache = new cachestore_session('Session test');
|
||||
$cache->initialise($definition);
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this instance.
|
||||
* @return string
|
||||
*/
|
||||
public function my_name() {
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The session data store class.
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class session_data_store {
|
||||
|
||||
/**
|
||||
* Used for the actual storage.
|
||||
* @var array
|
||||
*/
|
||||
private static $sessionstore = null;
|
||||
|
||||
/**
|
||||
* Returns a static store by reference... REFERENCE SUPER IMPORTANT.
|
||||
*
|
||||
* @param string $id
|
||||
* @return array
|
||||
*/
|
||||
protected static function ®ister_store_id($id) {
|
||||
if (is_null(self::$sessionstore)) {
|
||||
global $SESSION;
|
||||
if (!isset($SESSION->cachestore_session)) {
|
||||
$SESSION->cachestore_session = array();
|
||||
}
|
||||
self::$sessionstore =& $SESSION->cachestore_session;
|
||||
}
|
||||
if (!array_key_exists($id, self::$sessionstore)) {
|
||||
self::$sessionstore[$id] = array();
|
||||
}
|
||||
return self::$sessionstore[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the data belong to the given store id.
|
||||
* @param string $id
|
||||
*/
|
||||
protected static function flush_store_by_id($id) {
|
||||
unset(self::$sessionstore[$id]);
|
||||
self::$sessionstore[$id] = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the store of all data.
|
||||
*/
|
||||
protected static function flush_store() {
|
||||
$ids = array_keys(self::$sessionstore);
|
||||
unset(self::$sessionstore);
|
||||
self::$sessionstore = array();
|
||||
foreach ($ids as $id) {
|
||||
self::$sessionstore[$id] = array();
|
||||
}
|
||||
}
|
||||
}
|
32
cache/stores/session/version.php
vendored
Normal file
32
cache/stores/session/version.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Cache session store version information.
|
||||
*
|
||||
* This is used as a default cache store within the Cache API. It should never be deleted.
|
||||
*
|
||||
* @package cachestore_session
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$plugin->version = 2012091000; // The current module version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2012090700; // Requires this Moodle version.
|
||||
$plugin->component = 'cachestore_session'; // Full name of the plugin.
|
31
cache/stores/static/lang/en/cachestore_static.php
vendored
Normal file
31
cache/stores/static/lang/en/cachestore_static.php
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The library file for the static cache store.
|
||||
*
|
||||
* This file is part of the static cache store, it contains the API for interacting with an instance of the store.
|
||||
* This is used as a default cache store within the Cache API. It should never be deleted.
|
||||
*
|
||||
* @package cachestore_static
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['pluginname'] = 'Static request cache';
|
425
cache/stores/static/lib.php
vendored
Normal file
425
cache/stores/static/lib.php
vendored
Normal file
@ -0,0 +1,425 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* The library file for the static cache store.
|
||||
*
|
||||
* This file is part of the static cache store, it contains the API for interacting with an instance of the store.
|
||||
* This is used as a default cache store within the Cache API. It should never be deleted.
|
||||
*
|
||||
* @package cachestore_static
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* The static store class.
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cachestore_static extends static_data_store implements cache_store, cache_is_key_aware {
|
||||
|
||||
/**
|
||||
* The name of the store
|
||||
* @var store
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The store id (should be unique)
|
||||
* @var string
|
||||
*/
|
||||
protected $storeid;
|
||||
|
||||
/**
|
||||
* The store we use for data.
|
||||
* @var array
|
||||
*/
|
||||
protected $store;
|
||||
|
||||
/**
|
||||
* The ttl if there is one. Hopefully not.
|
||||
* @var int
|
||||
*/
|
||||
protected $ttl = 0;
|
||||
|
||||
/**
|
||||
* Constructs the store instance.
|
||||
*
|
||||
* Noting that this function is not an initialisation. It is used to prepare the store for use.
|
||||
* The store will be initialised when required and will be provided with a cache_definition at that time.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $configuration
|
||||
*/
|
||||
public function __construct($name, array $configuration = array()) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supported features as a combined int.
|
||||
*
|
||||
* @param array $configuration
|
||||
* @return int
|
||||
*/
|
||||
public static function get_supported_features(array $configuration = array()) {
|
||||
return self::SUPPORTS_DATA_GUARANTEE +
|
||||
self::SUPPORTS_NATIVE_TTL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the supported modes as a combined int.
|
||||
*
|
||||
* @param array $configuration
|
||||
* @return int
|
||||
*/
|
||||
public static function get_supported_modes(array $configuration = array()) {
|
||||
return self::MODE_REQUEST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store requirements are met.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function are_requirements_met() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given mode is supported by this store.
|
||||
*
|
||||
* @param int $mode One of cache_store::MODE_*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_supported_mode($mode) {
|
||||
return ($mode === self::MODE_REQUEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store instance guarantees data.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_data_guarantee() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store instance supports multiple identifiers.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_multiple_indentifiers() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store instance supports native ttl.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_native_ttl() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the cache.
|
||||
*
|
||||
* Once this has been done the cache is all set to be used.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
*/
|
||||
public function initialise(cache_definition $definition) {
|
||||
$this->storeid = $definition->generate_definition_hash();
|
||||
$this->store = &self::register_store_id($this->storeid);
|
||||
$this->ttl = $definition->get_ttl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true once this instance has been initialised.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_initialised() {
|
||||
return (is_array($this->store));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this store instance is ready to be used.
|
||||
* @return bool
|
||||
*/
|
||||
public function is_ready() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an item from the cache store given its key.
|
||||
*
|
||||
* @param string $key The key to retrieve
|
||||
* @return mixed The data that was associated with the key, or false if the key did not exist.
|
||||
*/
|
||||
public function get($key) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
if (array_key_exists($key, $this->store)) {
|
||||
if ($this->ttl == 0) {
|
||||
return $this->store[$key];
|
||||
} else if ($this->store[$key][1] >= $maxtime) {
|
||||
return $this->store[$key][0];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves several items from the cache store in a single transaction.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @param array $keys The array of keys to retrieve
|
||||
* @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
|
||||
* be set to false.
|
||||
*/
|
||||
public function get_many($keys) {
|
||||
$return = array();
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
foreach ($keys as $key) {
|
||||
$return[$key] = false;
|
||||
if (array_key_exists($key, $this->store)) {
|
||||
if ($this->ttl == 0) {
|
||||
$return[$key] = $this->store[$key];
|
||||
} else if ($this->store[$key][1] >= $maxtime) {
|
||||
$return[$key] = $this->store[$key][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an item in the cache given its key and data value.
|
||||
*
|
||||
* @param string $key The key to use.
|
||||
* @param mixed $data The data to set.
|
||||
* @return bool True if the operation was a success false otherwise.
|
||||
*/
|
||||
public function set($key, $data) {
|
||||
if ($this->ttl == 0) {
|
||||
$this->store[$key] = $data;
|
||||
} else {
|
||||
$this->store[$key] = array($data, cache::now());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets many items in the cache in a single transaction.
|
||||
*
|
||||
* @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two
|
||||
* keys, 'key' and 'value'.
|
||||
* @return int The number of items successfully set. It is up to the developer to check this matches the number of items
|
||||
* sent ... if they care that is.
|
||||
*/
|
||||
public function set_many(array $keyvaluearray) {
|
||||
$count = 0;
|
||||
foreach ($keyvaluearray as $pair) {
|
||||
$this->set($pair['key'], $pair['value']);
|
||||
$count++;
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the store has a record for the given key and returns true if so.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
if (array_key_exists($key, $this->store)) {
|
||||
if ($this->ttl == 0) {
|
||||
return true;
|
||||
} else if ($this->store[$key][1] >= $maxtime) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store contains records for all of the given keys.
|
||||
*
|
||||
* @param array $keys
|
||||
* @return bool
|
||||
*/
|
||||
public function has_all(array $keys) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
foreach ($keys as $key) {
|
||||
if (!array_key_exists($key, $this->store)) {
|
||||
return false;
|
||||
}
|
||||
if ($this->ttl != 0 && $this->store[$key][1] < $maxtime) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the store contains records for any of the given keys.
|
||||
*
|
||||
* @param array $keys
|
||||
* @return bool
|
||||
*/
|
||||
public function has_any(array $keys) {
|
||||
$maxtime = cache::now() - $this->ttl;
|
||||
foreach ($keys as $key) {
|
||||
if (array_key_exists($key, $this->store) && ($this->ttl == 0 || $this->store[$key][1] >= $maxtime)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an item from the cache store.
|
||||
*
|
||||
* @param string $key The key to delete.
|
||||
* @return bool Returns true if the operation was a success, false otherwise.
|
||||
*/
|
||||
public function delete($key) {
|
||||
unset($this->store[$key]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes several keys from the cache in a single action.
|
||||
*
|
||||
* @param array $keys The keys to delete
|
||||
* @return int The number of items successfully deleted.
|
||||
*/
|
||||
public function delete_many(array $keys) {
|
||||
$count = 0;
|
||||
foreach ($keys as $key) {
|
||||
unset($this->store[$key]);
|
||||
$count++;
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges the cache deleting all items within it.
|
||||
*
|
||||
* @return boolean True on success. False otherwise.
|
||||
*/
|
||||
public function purge() {
|
||||
$this->flush_store_by_id($this->storeid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user can add an instance of the store plugin.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function can_add_instance() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs any necessary clean up when the store instance is being deleted.
|
||||
*/
|
||||
public function cleanup() {
|
||||
$this->purge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an instance of the cache store that can be used for testing.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return false
|
||||
*/
|
||||
public static function initialise_test_instance(cache_definition $definition) {
|
||||
// Do something here perhaps.
|
||||
$cache = new cachestore_static('Static store');
|
||||
$cache->initialise($definition);
|
||||
return $cache;;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this instance.
|
||||
* @return string
|
||||
*/
|
||||
public function my_name() {
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The static data store class
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class static_data_store {
|
||||
|
||||
/**
|
||||
* An array for storage.
|
||||
* @var array
|
||||
*/
|
||||
private static $staticstore = array();
|
||||
|
||||
/**
|
||||
* Returns a static store by reference... REFERENCE SUPER IMPORTANT.
|
||||
*
|
||||
* @param string $id
|
||||
* @return array
|
||||
*/
|
||||
protected static function ®ister_store_id($id) {
|
||||
if (!array_key_exists($id, self::$staticstore)) {
|
||||
self::$staticstore[$id] = array();
|
||||
}
|
||||
return self::$staticstore[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the store of all values for belonging to the store with the given id.
|
||||
* @param string $id
|
||||
*/
|
||||
protected static function flush_store_by_id($id) {
|
||||
unset(self::$staticstore[$id]);
|
||||
self::$staticstore[$id] = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes all of the values from all stores.
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
protected static function flush_store() {
|
||||
$ids = array_keys(self::$staticstore);
|
||||
unset(self::$staticstore);
|
||||
self::$staticstore = array();
|
||||
foreach ($ids as $id) {
|
||||
self::$staticstore[$id] = array();
|
||||
}
|
||||
}
|
||||
}
|
32
cache/stores/static/version.php
vendored
Normal file
32
cache/stores/static/version.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Cache static store version information.
|
||||
*
|
||||
* This is used as a default cache store within the Cache API. It should never be deleted.
|
||||
*
|
||||
* @package cachestore_static
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
$plugin->version = 2012091000; // The current module version (Date: YYYYMMDDXX)
|
||||
$plugin->requires = 2012090700; // Requires this Moodle version.
|
||||
$plugin->component = 'cachestore_static'; // Full name of the plugin.
|
202
cache/testperformance.php
vendored
Normal file
202
cache/testperformance.php
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Store performance test run + output script.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once('../config.php');
|
||||
require_once($CFG->dirroot.'/lib/adminlib.php');
|
||||
require_once($CFG->dirroot.'/cache/locallib.php');
|
||||
|
||||
$count = optional_param('count', 100, PARAM_INT);
|
||||
$count = min($count, 100000);
|
||||
$count = max($count, 0);
|
||||
|
||||
admin_externalpage_setup('cachetestperformance');
|
||||
|
||||
$applicationtable = new html_table();
|
||||
$applicationtable->head = array(
|
||||
get_string('plugin', 'cache'),
|
||||
get_string('result', 'cache'),
|
||||
get_string('set', 'cache'),
|
||||
get_string('gethit', 'cache'),
|
||||
get_string('getmiss', 'cache'),
|
||||
get_string('delete', 'cache'),
|
||||
);
|
||||
$applicationtable->data = array();
|
||||
$sessiontable = clone($applicationtable);
|
||||
$requesttable = clone($applicationtable);
|
||||
|
||||
|
||||
$application = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cache', 'applicationtest', null, false);
|
||||
$session = cache_definition::load_adhoc(cache_store::MODE_SESSION, 'cache', 'sessiontest', null, false);
|
||||
$request = cache_definition::load_adhoc(cache_store::MODE_REQUEST, 'cache', 'requesttest', null, false);
|
||||
|
||||
$strinvalidplugin = new lang_string('invalidplugin', 'cache');
|
||||
$strunsupportedmode = new lang_string('unsupportedmode', 'cache');
|
||||
$struntestable = new lang_string('untestable', 'cache');
|
||||
$strtested = new lang_string('tested', 'cache');
|
||||
|
||||
foreach (get_plugin_list_with_file('cachestore', 'lib.php', true) as $plugin => $path) {
|
||||
|
||||
$class = 'cachestore_'.$plugin;
|
||||
$plugin = get_string('pluginname', 'cachestore_'.$plugin);
|
||||
|
||||
if (!class_exists($class) || !method_exists($class, 'initialise_test_instance')) {
|
||||
$applicationtable->data[] = array($plugin, $strinvalidplugin, '-', '-', '-', '-');
|
||||
$sessiontable->data[] = array($plugin, $strinvalidplugin, '-', '-', '-', '-');
|
||||
$requesttable->data[] = array($plugin, $strinvalidplugin, '-', '-', '-', '-');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$class::is_supported_mode(cache_store::MODE_APPLICATION)) {
|
||||
$applicationtable->data[] = array($plugin, $strunsupportedmode, '-', '-', '-', '-');
|
||||
} else {
|
||||
$store = $class::initialise_test_instance($application);
|
||||
if ($store === false) {
|
||||
$applicationtable->data[] = array($plugin, $struntestable, '-', '-', '-', '-');
|
||||
} else {
|
||||
$result = array($plugin, $strtested, 0, 0, 0);
|
||||
$start = microtime(true);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$store->set('key'.$i, 'test data '.$i);
|
||||
}
|
||||
$result[2] = sprintf('%01.4f', microtime(true) - $start);
|
||||
|
||||
$start = microtime(true);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$store->get('key'.$i);
|
||||
}
|
||||
$result[3] = sprintf('%01.4f', microtime(true) - $start);
|
||||
|
||||
$start = microtime(true);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$store->get('fake'.$i);
|
||||
}
|
||||
$result[4] = sprintf('%01.4f', microtime(true) - $start);
|
||||
|
||||
$start = microtime(true);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$store->delete('key'.$i);
|
||||
}
|
||||
$result[5] = sprintf('%01.4f', microtime(true) - $start);
|
||||
$applicationtable->data[] = $result;
|
||||
$store->cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
if (!$class::is_supported_mode(cache_store::MODE_SESSION)) {
|
||||
$sessiontable->data[] = array($plugin, $strunsupportedmode, '-', '-', '-', '-');
|
||||
} else {
|
||||
$store = $class::initialise_test_instance($session);
|
||||
if ($store === false) {
|
||||
$sessiontable->data[] = array($plugin, $struntestable, '-', '-', '-', '-');
|
||||
} else {
|
||||
$result = array($plugin, $strtested, 0, 0, 0);
|
||||
$start = microtime(true);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$store->set('key'.$i, 'test data '.$i);
|
||||
}
|
||||
$result[2] = sprintf('%01.4f', microtime(true) - $start);
|
||||
|
||||
$start = microtime(true);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$store->get('key'.$i);
|
||||
}
|
||||
$result[3] = sprintf('%01.4f', microtime(true) - $start);
|
||||
|
||||
$start = microtime(true);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$store->get('fake'.$i);
|
||||
}
|
||||
$result[4] = sprintf('%01.4f', microtime(true) - $start);
|
||||
|
||||
$start = microtime(true);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$store->delete('key'.$i);
|
||||
}
|
||||
$result[5] = sprintf('%01.4f', microtime(true) - $start);
|
||||
$sessiontable->data[] = $result;
|
||||
$store->cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
if (!$class::is_supported_mode(cache_store::MODE_REQUEST)) {
|
||||
$requesttable->data[] = array($plugin, $strunsupportedmode, '-', '-', '-', '-');
|
||||
} else {
|
||||
$store = $class::initialise_test_instance($request);
|
||||
if ($store === false) {
|
||||
$requesttable->data[] = array($plugin, $struntestable, '-', '-', '-', '-');
|
||||
} else {
|
||||
$result = array($plugin, $strtested, 0, 0, 0);
|
||||
$start = microtime(true);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$store->set('key'.$i, 'test data '.$i);
|
||||
}
|
||||
$result[2] = sprintf('%01.4f', microtime(true) - $start);
|
||||
|
||||
$start = microtime(true);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$store->get('key'.$i);
|
||||
}
|
||||
$result[3] = sprintf('%01.4f', microtime(true) - $start);
|
||||
|
||||
$start = microtime(true);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$store->get('fake'.$i);
|
||||
}
|
||||
$result[4] = sprintf('%01.4f', microtime(true) - $start);
|
||||
|
||||
$start = microtime(true);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$store->delete('key'.$i);
|
||||
}
|
||||
$result[5] = sprintf('%01.4f', microtime(true) - $start);
|
||||
$requesttable->data[] = $result;
|
||||
$store->cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading(get_string('storeperformance', 'cache', $count));
|
||||
|
||||
$possiblecounts = array(1, 10, 100, 500, 1000, 5000, 10000, 50000, 100000);
|
||||
$links = array();
|
||||
foreach ($possiblecounts as $pcount) {
|
||||
$links[] = html_writer::link(new moodle_url($PAGE->url, array('count' => $pcount)), $pcount);
|
||||
}
|
||||
echo $OUTPUT->box_start('generalbox performance-test-counts');
|
||||
echo get_string('requestcount', 'cache', join(', ', $links));
|
||||
echo $OUTPUT->box_end();
|
||||
|
||||
echo $OUTPUT->heading(get_string('storeresults_application', 'cache'));
|
||||
echo html_writer::table($applicationtable);
|
||||
|
||||
echo $OUTPUT->heading(get_string('storeresults_session', 'cache'));
|
||||
echo html_writer::table($sessiontable);
|
||||
|
||||
echo $OUTPUT->heading(get_string('storeresults_request', 'cache'));
|
||||
echo html_writer::table($requesttable);
|
||||
|
||||
echo $OUTPUT->footer();
|
619
cache/tests/cache_test.php
vendored
Normal file
619
cache/tests/cache_test.php
vendored
Normal file
@ -0,0 +1,619 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* PHPunit tests for the cache API
|
||||
*
|
||||
* This file is part of Moodle's cache API, affectionately called MUC.
|
||||
* It contains the components that are requried in order to use caching.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
// Include the necessary evils.
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot.'/cache/locallib.php');
|
||||
require_once($CFG->dirroot.'/cache/tests/fixtures/lib.php');
|
||||
|
||||
/**
|
||||
* PHPunit tests for the cache API
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_phpunit_tests extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Set things back to the default before each test.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
cache_factory::reset();
|
||||
cache_config_phpunittest::create_default_configuration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests cache configuration
|
||||
*/
|
||||
public function test_cache_config() {
|
||||
$instance = cache_config::instance();
|
||||
$this->assertInstanceOf('cache_config_phpunittest', $instance);
|
||||
|
||||
$this->assertTrue(cache_config_phpunittest::config_file_exists());
|
||||
|
||||
$stores = $instance->get_all_stores();
|
||||
$this->assertCount(3, $stores);
|
||||
foreach ($stores as $name => $store) {
|
||||
// Check its an array.
|
||||
$this->assertInternalType('array', $store);
|
||||
// Check the name is the key.
|
||||
$this->assertEquals($name, $store['name']);
|
||||
// Check that it has been declared default.
|
||||
$this->assertTrue($store['default']);
|
||||
// Required attributes = name + plugin + configuration + modes + features.
|
||||
$this->assertArrayHasKey('name', $store);
|
||||
$this->assertArrayHasKey('plugin', $store);
|
||||
$this->assertArrayHasKey('configuration', $store);
|
||||
$this->assertArrayHasKey('modes', $store);
|
||||
$this->assertArrayHasKey('features', $store);
|
||||
}
|
||||
|
||||
$modemappings = $instance->get_mode_mappings();
|
||||
$this->assertCount(3, $modemappings);
|
||||
$modes = array(
|
||||
cache_store::MODE_APPLICATION => false,
|
||||
cache_store::MODE_SESSION => false,
|
||||
cache_store::MODE_REQUEST => false,
|
||||
);
|
||||
foreach ($modemappings as $mapping) {
|
||||
// We expect 3 properties.
|
||||
$this->assertCount(3, $mapping);
|
||||
// Required attributes = mode + store.
|
||||
$this->assertArrayHasKey('mode', $mapping);
|
||||
$this->assertArrayHasKey('store', $mapping);
|
||||
// Record the mode.
|
||||
$modes[$mapping['mode']] = true;
|
||||
}
|
||||
|
||||
// Must have the default 3 modes and no more.
|
||||
$this->assertCount(3, $mapping);
|
||||
foreach ($modes as $mode) {
|
||||
$this->assertTrue($mode);
|
||||
}
|
||||
|
||||
$definitions = $instance->get_definitions();
|
||||
// The event invalidation definition is required for the cache API and must be there.
|
||||
$this->assertArrayHasKey('core/eventinvalidation', $definitions);
|
||||
|
||||
$definitionmappings = $instance->get_definition_mappings();
|
||||
foreach ($definitionmappings as $mapping) {
|
||||
// Required attributes = definition + store.
|
||||
$this->assertArrayHasKey('definition', $mapping);
|
||||
$this->assertArrayHasKey('store', $mapping);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the default application cache
|
||||
*/
|
||||
public function test_default_application_cache() {
|
||||
$cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'phpunit', 'applicationtest');
|
||||
$this->assertInstanceOf('cache_application', $cache);
|
||||
$this->run_on_cache($cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the default session cache
|
||||
*/
|
||||
public function test_default_session_cache() {
|
||||
$cache = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'applicationtest');
|
||||
$this->assertInstanceOf('cache_session', $cache);
|
||||
$this->run_on_cache($cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the default request cache
|
||||
*/
|
||||
public function test_default_request_cache() {
|
||||
$cache = cache::make_from_params(cache_store::MODE_REQUEST, 'phpunit', 'applicationtest');
|
||||
$this->assertInstanceOf('cache_request', $cache);
|
||||
$this->run_on_cache($cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests using a cache system when there are no stores available (who knows what the admin did to achieve this).
|
||||
*/
|
||||
public function test_on_cache_without_store() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/nostoretest1', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'nostoretest1',
|
||||
));
|
||||
$instance->phpunit_add_definition('phpunit/nostoretest2', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'nostoretest2',
|
||||
'persistent' => true
|
||||
));
|
||||
$instance->phpunit_remove_stores();
|
||||
|
||||
$cache = cache::make('phpunit', 'nostoretest1');
|
||||
$this->run_on_cache($cache);
|
||||
|
||||
$cache = cache::make('phpunit', 'nostoretest2');
|
||||
$this->run_on_cache($cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a standard series of access and use tests on a cache instance.
|
||||
*
|
||||
* This function is great because we can use it to ensure all of the loaders perform exactly the same way.
|
||||
*
|
||||
* @param cache_loader $cache
|
||||
*/
|
||||
protected function run_on_cache(cache_loader $cache) {
|
||||
$key = 'testkey';
|
||||
$datascalar = 'test data';
|
||||
$dataarray = array('test' => 'data', 'part' => 'two');
|
||||
$dataobject = (object)$dataarray;
|
||||
|
||||
$this->assertTrue($cache->purge());
|
||||
|
||||
// Check all read methods.
|
||||
$this->assertFalse($cache->get($key));
|
||||
$this->assertFalse($cache->has($key));
|
||||
$result = $cache->get_many(array($key));
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertFalse(reset($result));
|
||||
$this->assertFalse($cache->has_any(array($key)));
|
||||
$this->assertFalse($cache->has_all(array($key)));
|
||||
|
||||
// Set the data.
|
||||
$this->assertTrue($cache->set($key, $datascalar));
|
||||
// Setting it more than once should be permitted.
|
||||
$this->assertTrue($cache->set($key, $datascalar));
|
||||
|
||||
// Recheck the read methods.
|
||||
$this->assertEquals($datascalar, $cache->get($key));
|
||||
$this->assertTrue($cache->has($key));
|
||||
$result = $cache->get_many(array($key));
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertEquals($datascalar, reset($result));
|
||||
$this->assertTrue($cache->has_any(array($key)));
|
||||
$this->assertTrue($cache->has_all(array($key)));
|
||||
|
||||
// Delete it.
|
||||
$this->assertTrue($cache->delete($key));
|
||||
|
||||
// Check its gone.
|
||||
$this->assertFalse($cache->get($key));
|
||||
$this->assertFalse($cache->has($key));
|
||||
|
||||
// Test arrays.
|
||||
$this->assertTrue($cache->set($key, $dataarray));
|
||||
$this->assertEquals($dataarray, $cache->get($key));
|
||||
|
||||
// Test objects.
|
||||
$this->assertTrue($cache->set($key, $dataobject));
|
||||
$this->assertEquals($dataobject, $cache->get($key));
|
||||
|
||||
$specobject = new cache_phpunit_dummy_object('red', 'blue');
|
||||
$this->assertTrue($cache->set($key, $specobject));
|
||||
$result = $cache->get($key);
|
||||
$this->assertInstanceOf('cache_phpunit_dummy_object', $result);
|
||||
$this->assertEquals('red_ptc_wfc', $result->property1);
|
||||
$this->assertEquals('blue_ptc_wfc', $result->property2);
|
||||
|
||||
// Test set many.
|
||||
$cache->set_many(array('key1' => 'data1', 'key2' => 'data2'));
|
||||
$this->assertEquals('data1', $cache->get('key1'));
|
||||
$this->assertEquals('data2', $cache->get('key2'));
|
||||
$this->assertTrue($cache->delete('key1'));
|
||||
$this->assertTrue($cache->delete('key2'));
|
||||
|
||||
// Test delete many.
|
||||
$this->assertTrue($cache->set('key1', 'data1'));
|
||||
$this->assertTrue($cache->set('key2', 'data2'));
|
||||
|
||||
$this->assertEquals('data1', $cache->get('key1'));
|
||||
$this->assertEquals('data2', $cache->get('key2'));
|
||||
|
||||
$this->assertEquals(2, $cache->delete_many(array('key1', 'key2')));
|
||||
|
||||
$this->assertFalse($cache->get('key1'));
|
||||
$this->assertFalse($cache->get('key2'));
|
||||
|
||||
// Quick reference test.
|
||||
$obj = new stdClass;
|
||||
$obj->key = 'value';
|
||||
$ref =& $obj;
|
||||
$this->assertTrue($cache->set('obj', $obj));
|
||||
|
||||
$obj->key = 'eulav';
|
||||
$var = $cache->get('obj');
|
||||
$this->assertInstanceOf('stdClass', $var);
|
||||
$this->assertEquals('value', $var->key);
|
||||
|
||||
$ref->key = 'eulav';
|
||||
$var = $cache->get('obj');
|
||||
$this->assertInstanceOf('stdClass', $var);
|
||||
$this->assertEquals('value', $var->key);
|
||||
|
||||
$this->assertTrue($cache->delete('obj'));
|
||||
|
||||
// Deep reference test.
|
||||
$obj1 = new stdClass;
|
||||
$obj1->key = 'value';
|
||||
$obj2 = new stdClass;
|
||||
$obj2->key = 'test';
|
||||
$obj3 = new stdClass;
|
||||
$obj3->key = 'pork';
|
||||
$obj1->subobj =& $obj2;
|
||||
$obj2->subobj =& $obj3;
|
||||
$this->assertTrue($cache->set('obj', $obj1));
|
||||
|
||||
$obj1->key = 'eulav';
|
||||
$obj2->key = 'tset';
|
||||
$obj3->key = 'krop';
|
||||
$var = $cache->get('obj');
|
||||
$this->assertInstanceOf('stdClass', $var);
|
||||
$this->assertEquals('value', $var->key);
|
||||
$this->assertInstanceOf('stdClass', $var->subobj);
|
||||
$this->assertEquals('test', $var->subobj->key);
|
||||
$this->assertInstanceOf('stdClass', $var->subobj->subobj);
|
||||
$this->assertEquals('pork', $var->subobj->subobj->key);
|
||||
$this->assertTrue($cache->delete('obj'));
|
||||
|
||||
// Death reference test... basicaly we don't want this to die.
|
||||
$obj = new stdClass;
|
||||
$obj->key = 'value';
|
||||
$obj->self =& $obj;
|
||||
$this->assertTrue($cache->set('obj', $obj));
|
||||
$var = $cache->get('obj');
|
||||
$this->assertInstanceOf('stdClass', $var);
|
||||
$this->assertEquals('value', $var->key);
|
||||
|
||||
// Reference test after retrieve.
|
||||
$obj = new stdClass;
|
||||
$obj->key = 'value';
|
||||
$this->assertTrue($cache->set('obj', $obj));
|
||||
|
||||
$var1 = $cache->get('obj');
|
||||
$this->assertInstanceOf('stdClass', $var1);
|
||||
$this->assertEquals('value', $var1->key);
|
||||
$var1->key = 'eulav';
|
||||
$this->assertEquals('eulav', $var1->key);
|
||||
|
||||
$var2 = $cache->get('obj');
|
||||
$this->assertInstanceOf('stdClass', $var2);
|
||||
$this->assertEquals('value', $var2->key);
|
||||
|
||||
$this->assertTrue($cache->delete('obj'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a definition using a data loader
|
||||
*/
|
||||
public function test_definition_data_loader() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/datasourcetest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'datasourcetest',
|
||||
'datasource' => 'cache_phpunit_dummy_datasource'
|
||||
));
|
||||
|
||||
$cache = cache::make('phpunit', 'datasourcetest');
|
||||
$this->assertInstanceOf('cache_application', $cache);
|
||||
|
||||
// Purge it to be sure.
|
||||
$this->assertTrue($cache->purge());
|
||||
// It won't be there yet.
|
||||
$this->assertFalse($cache->has('Test'));
|
||||
|
||||
// It should load it ;).
|
||||
$this->assertTrue($cache->has('Test', true));
|
||||
|
||||
// Purge it to be sure.
|
||||
$this->assertTrue($cache->purge());
|
||||
$this->assertEquals('Test has no value really.', $cache->get('Test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a definition using an overridden loader
|
||||
*/
|
||||
public function test_definition_overridden_loader() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/overridetest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'overridetest',
|
||||
'overrideclass' => 'cache_phpunit_dummy_overrideclass'
|
||||
));
|
||||
$cache = cache::make('phpunit', 'overridetest');
|
||||
$this->assertInstanceOf('cache_phpunit_dummy_overrideclass', $cache);
|
||||
$this->assertInstanceOf('cache_application', $cache);
|
||||
// Purge it to be sure.
|
||||
$this->assertTrue($cache->purge());
|
||||
// It won't be there yet.
|
||||
$this->assertFalse($cache->has('Test'));
|
||||
// Add it.
|
||||
$this->assertTrue($cache->set('Test', 'Test has no value really.'));
|
||||
// Check its there.
|
||||
$this->assertEquals('Test has no value really.', $cache->get('Test'));
|
||||
}
|
||||
|
||||
public function test_definition_ttl() {
|
||||
$instance = cache_config_phpunittest::instance(true);
|
||||
$instance->phpunit_add_definition('phpunit/ttltest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'ttltest',
|
||||
'ttl' => -10
|
||||
));
|
||||
$cache = cache::make('phpunit', 'ttltest');
|
||||
$this->assertInstanceOf('cache_application', $cache);
|
||||
|
||||
// Purge it to be sure.
|
||||
$this->assertTrue($cache->purge());
|
||||
// It won't be there yet.
|
||||
$this->assertFalse($cache->has('Test'));
|
||||
// Set it now.
|
||||
$this->assertTrue($cache->set('Test', 'Test'));
|
||||
// Check its not there.
|
||||
$this->assertFalse($cache->has('Test'));
|
||||
// Double check by trying to get it.
|
||||
$this->assertFalse($cache->get('Test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests manual locking operations on an application cache
|
||||
*/
|
||||
public function test_application_manual_locking() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance->phpunit_add_definition('phpunit/lockingtest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'lockingtest'
|
||||
));
|
||||
$cache1 = cache::make('phpunit', 'lockingtest');
|
||||
$cache2 = clone($cache1);
|
||||
|
||||
$this->assertTrue($cache1->set('testkey', 'test data'));
|
||||
$this->assertTrue($cache2->set('testkey', 'test data'));
|
||||
|
||||
$this->assertTrue($cache1->acquire_lock('testkey'));
|
||||
$this->assertFalse($cache2->acquire_lock('testkey'));
|
||||
|
||||
$this->assertTrue($cache1->check_lock_state('testkey'));
|
||||
$this->assertFalse($cache2->check_lock_state('testkey'));
|
||||
|
||||
$this->assertTrue($cache1->release_lock('testkey'));
|
||||
$this->assertFalse($cache2->release_lock('testkey'));
|
||||
|
||||
$this->assertTrue($cache1->set('testkey', 'test data'));
|
||||
$this->assertTrue($cache2->set('testkey', 'test data'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests application cache event invalidation
|
||||
*/
|
||||
public function test_application_event_invalidation() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance->phpunit_add_definition('phpunit/eventinvalidationtest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'eventinvalidationtest',
|
||||
'invalidationevents' => array(
|
||||
'crazyevent'
|
||||
)
|
||||
));
|
||||
$cache = cache::make('phpunit', 'eventinvalidationtest');
|
||||
|
||||
$this->assertTrue($cache->set('testkey1', 'test data 1'));
|
||||
$this->assertEquals('test data 1', $cache->get('testkey1'));
|
||||
$this->assertTrue($cache->set('testkey2', 'test data 2'));
|
||||
$this->assertEquals('test data 2', $cache->get('testkey2'));
|
||||
|
||||
// Test invalidating a single entry.
|
||||
cache_helper::invalidate_by_event('crazyevent', array('testkey1'));
|
||||
|
||||
$this->assertFalse($cache->get('testkey1'));
|
||||
$this->assertEquals('test data 2', $cache->get('testkey2'));
|
||||
|
||||
$this->assertTrue($cache->set('testkey1', 'test data 1'));
|
||||
|
||||
// Test invalidating both entries.
|
||||
cache_helper::invalidate_by_event('crazyevent', array('testkey1', 'testkey2'));
|
||||
|
||||
$this->assertFalse($cache->get('testkey1'));
|
||||
$this->assertFalse($cache->get('testkey2'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests application cache definition invalidation
|
||||
*/
|
||||
public function test_application_definition_invalidation() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance->phpunit_add_definition('phpunit/definitioninvalidation', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'definitioninvalidation'
|
||||
));
|
||||
$cache = cache::make('phpunit', 'definitioninvalidation');
|
||||
$this->assertTrue($cache->set('testkey1', 'test data 1'));
|
||||
$this->assertEquals('test data 1', $cache->get('testkey1'));
|
||||
$this->assertTrue($cache->set('testkey2', 'test data 2'));
|
||||
$this->assertEquals('test data 2', $cache->get('testkey2'));
|
||||
|
||||
cache_helper::invalidate_by_definition('phpunit', 'definitioninvalidation', array(), 'testkey1');
|
||||
|
||||
$this->assertFalse($cache->get('testkey1'));
|
||||
$this->assertEquals('test data 2', $cache->get('testkey2'));
|
||||
|
||||
$this->assertTrue($cache->set('testkey1', 'test data 1'));
|
||||
|
||||
cache_helper::invalidate_by_definition('phpunit', 'definitioninvalidation', array(), array('testkey1'));
|
||||
|
||||
$this->assertFalse($cache->get('testkey1'));
|
||||
$this->assertEquals('test data 2', $cache->get('testkey2'));
|
||||
|
||||
$this->assertTrue($cache->set('testkey1', 'test data 1'));
|
||||
|
||||
cache_helper::invalidate_by_definition('phpunit', 'definitioninvalidation', array(), array('testkey1', 'testkey2'));
|
||||
|
||||
$this->assertFalse($cache->get('testkey1'));
|
||||
$this->assertFalse($cache->get('testkey2'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests application cache event invalidation over a distributed setup.
|
||||
*/
|
||||
public function test_distributed_application_event_invalidation() {
|
||||
global $CFG;
|
||||
// This is going to be an intense wee test.
|
||||
// We need to add data the to cache, invalidate it by event, manually force it back without MUC knowing to simulate a
|
||||
// disconnected/distributed setup (think load balanced server using local cache), instantiate the cache again and finally
|
||||
// check that it is not picked up.
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance->phpunit_add_definition('phpunit/eventinvalidationtest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'eventinvalidationtest',
|
||||
'invalidationevents' => array(
|
||||
'crazyevent'
|
||||
)
|
||||
));
|
||||
$cache = cache::make('phpunit', 'eventinvalidationtest');
|
||||
$this->assertTrue($cache->set('testkey1', 'test data 1'));
|
||||
$this->assertEquals('test data 1', $cache->get('testkey1'));
|
||||
|
||||
cache_helper::invalidate_by_event('crazyevent', array('testkey1'));
|
||||
|
||||
$this->assertFalse($cache->get('testkey1'));
|
||||
|
||||
// OK data added, data invalidated, and invalidation time has been set.
|
||||
// Now we need to manually add back the data and adjust the invalidation time.
|
||||
$timefile = $CFG->dataroot.'/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/494515064.cache';
|
||||
$timecont = serialize(cache::now() - 60); // Back 60sec in the past to force it to re-invalidate.
|
||||
file_put_contents($timefile, $timecont);
|
||||
$this->assertTrue(file_exists($timefile));
|
||||
|
||||
$datafile = $CFG->dataroot.'/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/3140056538.cache';
|
||||
$datacont = serialize("test data 1");
|
||||
file_put_contents($datafile, $datacont);
|
||||
$this->assertTrue(file_exists($datafile));
|
||||
|
||||
// Test 1: Rebuild without the event and test its there.
|
||||
cache_factory::reset();
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance->phpunit_add_definition('phpunit/eventinvalidationtest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'eventinvalidationtest',
|
||||
));
|
||||
$cache = cache::make('phpunit', 'eventinvalidationtest');
|
||||
$this->assertEquals('test data 1', $cache->get('testkey1'));
|
||||
|
||||
// Test 2: Rebuild and test the invalidation of the event via the invalidation cache.
|
||||
cache_factory::reset();
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance->phpunit_add_definition('phpunit/eventinvalidationtest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'eventinvalidationtest',
|
||||
'invalidationevents' => array(
|
||||
'crazyevent'
|
||||
)
|
||||
));
|
||||
$cache = cache::make('phpunit', 'eventinvalidationtest');
|
||||
$this->assertFalse($cache->get('testkey1'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests application cache event purge
|
||||
*/
|
||||
public function test_application_event_purge() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance->phpunit_add_definition('phpunit/eventpurgetest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'eventpurgetest',
|
||||
'invalidationevents' => array(
|
||||
'crazyevent'
|
||||
)
|
||||
));
|
||||
$cache = cache::make('phpunit', 'eventpurgetest');
|
||||
|
||||
$this->assertTrue($cache->set('testkey1', 'test data 1'));
|
||||
$this->assertEquals('test data 1', $cache->get('testkey1'));
|
||||
$this->assertTrue($cache->set('testkey2', 'test data 2'));
|
||||
$this->assertEquals('test data 2', $cache->get('testkey2'));
|
||||
|
||||
// Purge the event.
|
||||
cache_helper::purge_by_event('crazyevent');
|
||||
|
||||
// Check things have been removed.
|
||||
$this->assertFalse($cache->get('testkey1'));
|
||||
$this->assertFalse($cache->get('testkey2'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests application cache definition purge
|
||||
*/
|
||||
public function test_application_definition_purge() {
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$instance->phpunit_add_definition('phpunit/definitionpurgetest', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'definitionpurgetest',
|
||||
'invalidationevents' => array(
|
||||
'crazyevent'
|
||||
)
|
||||
));
|
||||
$cache = cache::make('phpunit', 'definitionpurgetest');
|
||||
|
||||
$this->assertTrue($cache->set('testkey1', 'test data 1'));
|
||||
$this->assertEquals('test data 1', $cache->get('testkey1'));
|
||||
$this->assertTrue($cache->set('testkey2', 'test data 2'));
|
||||
$this->assertEquals('test data 2', $cache->get('testkey2'));
|
||||
|
||||
// Purge the event.
|
||||
cache_helper::purge_by_definition('phpunit', 'definitionpurgetest');
|
||||
|
||||
// Check things have been removed.
|
||||
$this->assertFalse($cache->get('testkey1'));
|
||||
$this->assertFalse($cache->get('testkey2'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the use of an alt path.
|
||||
* If we can generate a config instance we are done :)
|
||||
*/
|
||||
public function test_alt_cache_path() {
|
||||
global $CFG;
|
||||
$this->resetAfterTest();
|
||||
$CFG->altcacheconfigpath = $CFG->dataroot.'/cache/altcacheconfigpath';
|
||||
$instance = cache_config_phpunittest::instance();
|
||||
$this->assertInstanceOf('cache_config', $instance);
|
||||
}
|
||||
}
|
148
cache/tests/fixtures/lib.php
vendored
Normal file
148
cache/tests/fixtures/lib.php
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Support library for the cache PHPUnit tests.
|
||||
*
|
||||
* This file is part of Moodle's cache API, affectionately called MUC.
|
||||
* It contains the components that are requried in order to use caching.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Override the default cache configuration for our own maniacle purposes.
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_config_phpunittest extends cache_config_writer {
|
||||
/**
|
||||
* Adds a definition to the stack
|
||||
* @param string $area
|
||||
* @param array $properties
|
||||
*/
|
||||
public function phpunit_add_definition($area, array $properties) {
|
||||
$this->configdefinitions[$area] = $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the configured stores so that there are none available.
|
||||
*/
|
||||
public function phpunit_remove_stores() {
|
||||
$this->configstores = array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy object for testing cacheable object interface and interaction
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_phpunit_dummy_object extends stdClass implements cacheable_object {
|
||||
/**
|
||||
* Test property 1
|
||||
* @var string
|
||||
*/
|
||||
public $property1;
|
||||
/**
|
||||
* Test property 1
|
||||
* @var string
|
||||
*/
|
||||
public $property2;
|
||||
/**
|
||||
* Constructor
|
||||
* @param string $property1
|
||||
* @param string $property2
|
||||
*/
|
||||
public function __construct($property1, $property2) {
|
||||
$this->property1 = $property1;
|
||||
$this->property2 = $property2;
|
||||
}
|
||||
/**
|
||||
* Prepares this object for caching
|
||||
* @return array
|
||||
*/
|
||||
public function prepare_to_cache() {
|
||||
return array($this->property1.'_ptc', $this->property2.'_ptc');
|
||||
}
|
||||
/**
|
||||
* Returns this object from the cache
|
||||
* @param array $data
|
||||
* @return cache_phpunit_dummy_object
|
||||
*/
|
||||
public static function wake_from_cache($data) {
|
||||
return new cache_phpunit_dummy_object(array_shift($data).'_wfc', array_shift($data).'_wfc');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy data source object for testing data source interface and implementation
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_phpunit_dummy_datasource implements cache_data_source {
|
||||
/**
|
||||
* Returns an instance of this object for use with the cache.
|
||||
*
|
||||
* @param cache_definition $definition
|
||||
* @return cache_phpunit_dummy_datasource
|
||||
*/
|
||||
public static function get_instance_for_cache(cache_definition $definition) {
|
||||
return new cache_phpunit_dummy_datasource();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a key for the cache.
|
||||
*
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
public function load_for_cache($key) {
|
||||
return $key.' has no value really.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads many keys for the cache
|
||||
*
|
||||
* @param array $keys
|
||||
* @return array
|
||||
*/
|
||||
public function load_many_for_cache(array $keys) {
|
||||
$return = array();
|
||||
foreach ($keys as $key) {
|
||||
$return[$key] = $key.' has no value really.';
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy overridden cache loader class that we can use to test overriding loader functionality.
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_phpunit_dummy_overrideclass extends cache_application {
|
||||
// Satisfying the code pre-checker is just part of my day job.
|
||||
}
|
425
cache/tests/locallib_test.php
vendored
Normal file
425
cache/tests/locallib_test.php
vendored
Normal file
@ -0,0 +1,425 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* PHPunit tests for the cache API and in particular things in locallib.php
|
||||
*
|
||||
* This file is part of Moodle's cache API, affectionately called MUC.
|
||||
* It contains the components that are requried in order to use caching.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
// Include the necessary evils.
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot.'/cache/locallib.php');
|
||||
require_once($CFG->dirroot.'/cache/tests/fixtures/lib.php');
|
||||
|
||||
/**
|
||||
* PHPunit tests for the cache API and in particular the cache config writer.
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_config_writer_phpunit_tests extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Set things back to the default before each test.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
cache_factory::reset();
|
||||
cache_config_phpunittest::create_default_configuration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting an instance. Pretty basic.
|
||||
*/
|
||||
public function test_instance() {
|
||||
$config = cache_config_writer::instance();
|
||||
$this->assertInstanceOf('cache_config_writer', $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the default configuration.
|
||||
*/
|
||||
public function test_default_configuration() {
|
||||
$config = cache_config_writer::instance();
|
||||
|
||||
// First check stores.
|
||||
$stores = $config->get_all_stores();
|
||||
$hasapplication = false;
|
||||
$hassession = false;
|
||||
$hasrequest = false;
|
||||
foreach ($stores as $store) {
|
||||
// Check the required keys.
|
||||
$this->assertArrayHasKey('name', $store);
|
||||
$this->assertArrayHasKey('plugin', $store);
|
||||
$this->assertArrayHasKey('modes', $store);
|
||||
$this->assertArrayHasKey('default', $store);
|
||||
// Check the mode, we need at least one default store of each mode.
|
||||
if (!empty($store['default'])) {
|
||||
if ($store['modes'] & cache_store::MODE_APPLICATION) {
|
||||
$hasapplication = true;
|
||||
}
|
||||
if ($store['modes'] & cache_store::MODE_SESSION) {
|
||||
$hassession = true;
|
||||
}
|
||||
if ($store['modes'] & cache_store::MODE_REQUEST) {
|
||||
$hasrequest = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->assertTrue($hasapplication, 'There is no default application cache store.');
|
||||
$this->assertTrue($hassession, 'There is no default session cache store.');
|
||||
$this->assertTrue($hasrequest, 'There is no default request cache store.');
|
||||
|
||||
// Next check the definitions.
|
||||
$definitions = $config->get_definitions();
|
||||
$eventinvalidation = false;
|
||||
foreach ($definitions as $definition) {
|
||||
// Check the required keys.
|
||||
$this->assertArrayHasKey('mode', $definition);
|
||||
$this->assertArrayHasKey('component', $definition);
|
||||
$this->assertArrayHasKey('area', $definition);
|
||||
if ($definition['component'] === 'core' && $definition['area'] === 'eventinvalidation') {
|
||||
$eventinvalidation = true;
|
||||
}
|
||||
}
|
||||
$this->assertTrue($eventinvalidation, 'Missing the event invalidation definition.');
|
||||
|
||||
// Next mode mappings
|
||||
$mappings = $config->get_mode_mappings();
|
||||
$hasapplication = false;
|
||||
$hassession = false;
|
||||
$hasrequest = false;
|
||||
foreach ($mappings as $mode) {
|
||||
// Check the required keys.
|
||||
$this->assertArrayHasKey('mode', $mode);
|
||||
$this->assertArrayHasKey('store', $mode);
|
||||
|
||||
if ($mode['mode'] === cache_store::MODE_APPLICATION) {
|
||||
$hasapplication = true;
|
||||
}
|
||||
if ($mode['mode'] === cache_store::MODE_SESSION) {
|
||||
$hassession = true;
|
||||
}
|
||||
if ($mode['mode'] === cache_store::MODE_REQUEST) {
|
||||
$hasrequest = true;
|
||||
}
|
||||
}
|
||||
$this->assertTrue($hasapplication, 'There is no mapping for the application mode.');
|
||||
$this->assertTrue($hassession, 'There is no mapping for the session mode.');
|
||||
$this->assertTrue($hasrequest, 'There is no mapping for the request mode.');
|
||||
|
||||
// Finally check config locks
|
||||
$locks = $config->get_locks();
|
||||
foreach ($locks as $lock) {
|
||||
$this->assertArrayHasKey('name', $lock);
|
||||
$this->assertArrayHasKey('type', $lock);
|
||||
$this->assertArrayHasKey('default', $lock);
|
||||
}
|
||||
// There has to be at least the default lock.
|
||||
$this->assertTrue(count($locks) > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test updating the definitions.
|
||||
*/
|
||||
public function test_update_definitions() {
|
||||
$config = cache_config_writer::instance();
|
||||
$earlydefinitions = $config->get_definitions();
|
||||
unset($config);
|
||||
cache_factory::reset();
|
||||
cache_config_writer::update_definitions();
|
||||
|
||||
$config = cache_config_writer::instance();
|
||||
$latedefinitions = $config->get_definitions();
|
||||
|
||||
$this->assertSame($latedefinitions, $earlydefinitions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test adding/editing/deleting store instances.
|
||||
*/
|
||||
public function test_add_edit_delete_plugin_instance() {
|
||||
$config = cache_config_writer::instance();
|
||||
$this->assertArrayNotHasKey('addplugintest', $config->get_all_stores());
|
||||
$this->assertArrayNotHasKey('addplugintestwlock', $config->get_all_stores());
|
||||
// Add a default file instance.
|
||||
$config->add_store_instance('addplugintest', 'file');
|
||||
|
||||
cache_factory::reset();
|
||||
$config = cache_config_writer::instance();
|
||||
$this->assertArrayHasKey('addplugintest', $config->get_all_stores());
|
||||
|
||||
// Add a store with a lock described.
|
||||
$config->add_store_instance('addplugintestwlock', 'file', array('lock' => 'default_file_lock'));
|
||||
$this->assertArrayHasKey('addplugintestwlock', $config->get_all_stores());
|
||||
|
||||
$config->delete_store_instance('addplugintest');
|
||||
$this->assertArrayNotHasKey('addplugintest', $config->get_all_stores());
|
||||
$this->assertArrayHasKey('addplugintestwlock', $config->get_all_stores());
|
||||
|
||||
$config->delete_store_instance('addplugintestwlock');
|
||||
$this->assertArrayNotHasKey('addplugintest', $config->get_all_stores());
|
||||
$this->assertArrayNotHasKey('addplugintestwlock', $config->get_all_stores());
|
||||
|
||||
// Add a default file instance.
|
||||
$config->add_store_instance('storeconfigtest', 'file', array('test' => 'a', 'one' => 'two'));
|
||||
$stores = $config->get_all_stores();
|
||||
$this->assertArrayHasKey('storeconfigtest', $stores);
|
||||
$this->assertArrayHasKey('configuration', $stores['storeconfigtest']);
|
||||
$this->assertArrayHasKey('test', $stores['storeconfigtest']['configuration']);
|
||||
$this->assertArrayHasKey('one', $stores['storeconfigtest']['configuration']);
|
||||
$this->assertEquals('a', $stores['storeconfigtest']['configuration']['test']);
|
||||
$this->assertEquals('two', $stores['storeconfigtest']['configuration']['one']);
|
||||
|
||||
$config->edit_store_instance('storeconfigtest', 'file', array('test' => 'b', 'one' => 'three'));
|
||||
$stores = $config->get_all_stores();
|
||||
$this->assertArrayHasKey('storeconfigtest', $stores);
|
||||
$this->assertArrayHasKey('configuration', $stores['storeconfigtest']);
|
||||
$this->assertArrayHasKey('test', $stores['storeconfigtest']['configuration']);
|
||||
$this->assertArrayHasKey('one', $stores['storeconfigtest']['configuration']);
|
||||
$this->assertEquals('b', $stores['storeconfigtest']['configuration']['test']);
|
||||
$this->assertEquals('three', $stores['storeconfigtest']['configuration']['one']);
|
||||
|
||||
$config->delete_store_instance('storeconfigtest');
|
||||
|
||||
try {
|
||||
$config->delete_store_instance('default_application');
|
||||
$this->fail('Default store deleted. This should not be possible!');
|
||||
} catch (Exception $e) {
|
||||
$this->assertInstanceOf('cache_exception', $e);
|
||||
}
|
||||
|
||||
try {
|
||||
$config->delete_store_instance('some_crazy_store');
|
||||
$this->fail('You should not be able to delete a store that does not exist.');
|
||||
} catch (Exception $e) {
|
||||
$this->assertInstanceOf('cache_exception', $e);
|
||||
}
|
||||
|
||||
try {
|
||||
// Try with a plugin that does not exist.
|
||||
$config->add_store_instance('storeconfigtest', 'shallowfail', array('test' => 'a', 'one' => 'two'));
|
||||
$this->fail('You should not be able to add an instance of a store that does not exist.');
|
||||
} catch (Exception $e) {
|
||||
$this->assertInstanceOf('cache_exception', $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test setting some mode mappings.
|
||||
*/
|
||||
public function test_set_mode_mappings() {
|
||||
$config = cache_config_writer::instance();
|
||||
$this->assertTrue($config->add_store_instance('setmodetest', 'file'));
|
||||
$this->assertTrue($config->set_mode_mappings(array(
|
||||
cache_store::MODE_APPLICATION => array('setmodetest', 'default_application'),
|
||||
cache_store::MODE_SESSION => array('default_session'),
|
||||
cache_store::MODE_REQUEST => array('default_request'),
|
||||
)));
|
||||
$mappings = $config->get_mode_mappings();
|
||||
$setmodetestfound = false;
|
||||
foreach ($mappings as $mapping) {
|
||||
if ($mapping['store'] == 'setmodetest' && $mapping['mode'] == cache_store::MODE_APPLICATION) {
|
||||
$setmodetestfound = true;
|
||||
}
|
||||
}
|
||||
$this->assertTrue($setmodetestfound, 'Set mapping did not work as expected.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test setting some definition mappings.
|
||||
*/
|
||||
public function test_set_definition_mappings() {
|
||||
$config = cache_config_phpunittest::instance(true);
|
||||
$config->phpunit_add_definition('phpunit/testdefinition', array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'phpunit',
|
||||
'area' => 'testdefinition'
|
||||
));
|
||||
|
||||
$config = cache_config_writer::instance();
|
||||
$this->assertTrue($config->add_store_instance('setdefinitiontest', 'file'));
|
||||
$this->assertInternalType('array', $config->get_definition_by_id('phpunit/testdefinition'));
|
||||
$config->set_definition_mappings('phpunit/testdefinition', array('setdefinitiontest', 'default_application'));
|
||||
|
||||
try {
|
||||
$config->set_definition_mappings('phpunit/testdefinition', array('something that does not exist'));
|
||||
$this->fail('You should not be able to set a mapping for a store that does not exist.');
|
||||
} catch (Exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
try {
|
||||
$config->set_definition_mappings('something/crazy', array('setdefinitiontest'));
|
||||
$this->fail('You should not be able to set a mapping for a definition that does not exist.');
|
||||
} catch (Exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PHPunit tests for the cache API and in particular the cache_administration_helper
|
||||
*
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class cache_administration_helper_phpunit_tests extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Set things back to the default before each test.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
cache_factory::reset();
|
||||
cache_config_phpunittest::create_default_configuration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the numerous summaries the helper can produce.
|
||||
*/
|
||||
public function test_get_summaries() {
|
||||
// First the preparation.
|
||||
$config = cache_config_writer::instance();
|
||||
$this->assertTrue($config->add_store_instance('summariesstore', 'file'));
|
||||
$config->set_definition_mappings('core/eventinvalidation', array('summariesstore'));
|
||||
$this->assertTrue($config->set_mode_mappings(array(
|
||||
cache_store::MODE_APPLICATION => array('summariesstore'),
|
||||
cache_store::MODE_SESSION => array('default_session'),
|
||||
cache_store::MODE_REQUEST => array('default_request'),
|
||||
)));
|
||||
|
||||
$storesummaries = cache_administration_helper::get_store_instance_summaries();
|
||||
$this->assertInternalType('array', $storesummaries);
|
||||
$this->assertArrayHasKey('summariesstore', $storesummaries);
|
||||
$summary = $storesummaries['summariesstore'];
|
||||
// Check the keys
|
||||
$this->assertArrayHasKey('name', $summary);
|
||||
$this->assertArrayHasKey('plugin', $summary);
|
||||
$this->assertArrayHasKey('default', $summary);
|
||||
$this->assertArrayHasKey('isready', $summary);
|
||||
$this->assertArrayHasKey('requirementsmet', $summary);
|
||||
$this->assertArrayHasKey('mappings', $summary);
|
||||
$this->assertArrayHasKey('modes', $summary);
|
||||
$this->assertArrayHasKey('supports', $summary);
|
||||
// Check the important/known values
|
||||
$this->assertEquals('summariesstore', $summary['name']);
|
||||
$this->assertEquals('file', $summary['plugin']);
|
||||
$this->assertEquals(0, $summary['default']);
|
||||
$this->assertEquals(1, $summary['isready']);
|
||||
$this->assertEquals(1, $summary['requirementsmet']);
|
||||
$this->assertEquals(1, $summary['mappings']);
|
||||
|
||||
$definitionsummaries = cache_administration_helper::get_definition_summaries();
|
||||
$this->assertInternalType('array', $definitionsummaries);
|
||||
$this->assertArrayHasKey('core/eventinvalidation', $definitionsummaries);
|
||||
$summary = $definitionsummaries['core/eventinvalidation'];
|
||||
// Check the keys
|
||||
$this->assertArrayHasKey('id', $summary);
|
||||
$this->assertArrayHasKey('name', $summary);
|
||||
$this->assertArrayHasKey('mode', $summary);
|
||||
$this->assertArrayHasKey('component', $summary);
|
||||
$this->assertArrayHasKey('area', $summary);
|
||||
$this->assertArrayHasKey('mappings', $summary);
|
||||
// Check the important/known values
|
||||
$this->assertEquals('core/eventinvalidation', $summary['id']);
|
||||
$this->assertInstanceOf('lang_string', $summary['name']);
|
||||
$this->assertEquals(cache_store::MODE_APPLICATION, $summary['mode']);
|
||||
$this->assertEquals('core', $summary['component']);
|
||||
$this->assertEquals('eventinvalidation', $summary['area']);
|
||||
$this->assertInternalType('array', $summary['mappings']);
|
||||
$this->assertContains('summariesstore', $summary['mappings']);
|
||||
|
||||
$pluginsummaries = cache_administration_helper::get_store_plugin_summaries();
|
||||
$this->assertInternalType('array', $pluginsummaries);
|
||||
$this->assertArrayHasKey('file', $pluginsummaries);
|
||||
$summary = $pluginsummaries['file'];
|
||||
// Check the keys
|
||||
$this->assertArrayHasKey('name', $summary);
|
||||
$this->assertArrayHasKey('requirementsmet', $summary);
|
||||
$this->assertArrayHasKey('instances', $summary);
|
||||
$this->assertArrayHasKey('modes', $summary);
|
||||
$this->assertArrayHasKey('supports', $summary);
|
||||
$this->assertArrayHasKey('canaddinstance', $summary);
|
||||
|
||||
$locksummaries = cache_administration_helper::get_lock_summaries();
|
||||
$this->assertInternalType('array', $locksummaries);
|
||||
$this->assertTrue(count($locksummaries) > 0);
|
||||
|
||||
$mappings = cache_administration_helper::get_default_mode_stores();
|
||||
$this->assertInternalType('array', $mappings);
|
||||
$this->assertCount(3, $mappings);
|
||||
$this->assertArrayHasKey(cache_store::MODE_APPLICATION, $mappings);
|
||||
$this->assertInternalType('array', $mappings[cache_store::MODE_APPLICATION]);
|
||||
$this->assertContains('summariesstore', $mappings[cache_store::MODE_APPLICATION]);
|
||||
|
||||
$potentials = cache_administration_helper::get_definition_store_options('core', 'eventinvalidation');
|
||||
$this->assertInternalType('array', $potentials); // Currently used, suitable, default
|
||||
$this->assertCount(3, $potentials);
|
||||
$this->assertArrayHasKey('summariesstore', $potentials[0]);
|
||||
$this->assertArrayHasKey('summariesstore', $potentials[1]);
|
||||
$this->assertArrayHasKey('default_application', $potentials[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test instantiating an add store form.
|
||||
*/
|
||||
public function test_get_add_store_form() {
|
||||
$form = cache_administration_helper::get_add_store_form('file');
|
||||
$this->assertInstanceOf('moodleform', $form);
|
||||
|
||||
try {
|
||||
$form = cache_administration_helper::get_add_store_form('somethingstupid');
|
||||
$this->fail('You should not be able to create an add form for a store plugin that does not exist.');
|
||||
} catch (moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e, 'Needs to be: ' .get_class($e)." ::: ".$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test instantiating a form to edit a store instance.
|
||||
*/
|
||||
public function test_get_edit_store_form() {
|
||||
$config = cache_config_writer::instance();
|
||||
$this->assertTrue($config->add_store_instance('summariesstore', 'file'));
|
||||
|
||||
$form = cache_administration_helper::get_edit_store_form('file', 'summariesstore');
|
||||
$this->assertInstanceOf('moodleform', $form);
|
||||
|
||||
try {
|
||||
$form = cache_administration_helper::get_edit_store_form('somethingstupid', 'moron');
|
||||
$this->fail('You should not be able to create an edit form for a store plugin that does not exist.');
|
||||
} catch (moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
|
||||
try {
|
||||
$form = cache_administration_helper::get_edit_store_form('file', 'blisters');
|
||||
$this->fail('You should not be able to create an edit form for a store plugin that does not exist.');
|
||||
} catch (moodle_exception $e) {
|
||||
$this->assertInstanceOf('coding_exception', $e);
|
||||
}
|
||||
}
|
||||
}
|
@ -411,6 +411,17 @@ $CFG->admin = 'admin';
|
||||
//
|
||||
// $CFG->extramemorylimit = '1G';
|
||||
//
|
||||
// Moodle 2.4 introduced a new cache API.
|
||||
// The cache API stores a configuration file within the Moodle data directory and
|
||||
// uses that rather than the database in order to function in a stand-alone manner.
|
||||
// Using altcacheconfigpath you can change the location where this config file is
|
||||
// looked for.
|
||||
// It can either be a directory in which to store the file, or the full path to the
|
||||
// file if you want to take full control. Either way it must be writable by the
|
||||
// webserver.
|
||||
//
|
||||
// $CFG->altcacheconfigpath = '/var/www/shared/moodle.cache.config.php
|
||||
//
|
||||
// The CSS files the Moodle produces can be extremely large and complex, especially
|
||||
// if you are using a custom theme that builds upon several other themes.
|
||||
// In Moodle 2.3 a CSS optimiser was added as an experimental feature for advanced
|
||||
|
@ -1072,3 +1072,13 @@ $string['webproxyinfo'] = 'Fill in following options if your Moodle server can n
|
||||
$string['xmlrpcrecommended'] = 'The xmlrpc extension is needed for hub communication, and useful for web services and Moodle networking';
|
||||
$string['yuicomboloading'] = 'YUI combo loading';
|
||||
$string['ziprequired'] = 'The Zip PHP extension is now required by Moodle, info-ZIP binaries or PclZip library are not used anymore.';
|
||||
|
||||
|
||||
$string['caching'] = 'Caching';
|
||||
$string['cachesettings'] = 'Cache settings';
|
||||
$string['cacherequest'] = 'Request cache';
|
||||
$string['cacherequesthelp'] = 'User specific cache that expires when the request is complete. Designed to replace areas where we are using the static stores.';
|
||||
$string['cachesession'] = 'Session cache';
|
||||
$string['cachesessionhelp'] = 'User specific cache that expires when the users session ends. Designed to aleviate session bloat/strain.';
|
||||
$string['cacheapplication'] = 'Application cache';
|
||||
$string['cacheapplicationhelp'] = ' Cached items are shared amoung all users and expire by a determined ttl.';
|
||||
|
119
lang/en/cache.php
Normal file
119
lang/en/cache.php
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Cache language strings
|
||||
*
|
||||
* This file is part of Moodle's cache API, affectionately called MUC.
|
||||
* It contains the components that are requried in order to use caching.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$string['actions'] = 'Actions';
|
||||
$string['addinstance'] = 'Add instance';
|
||||
$string['addstore'] = 'Add {$a} store';
|
||||
$string['addstoresuccess'] = 'Successfully added a new {$a} store.';
|
||||
$string['area'] = 'Area';
|
||||
$string['caching'] = 'Caching';
|
||||
$string['cacheadmin'] = 'Cache administration';
|
||||
$string['cacheconfig'] = 'Configuration';
|
||||
$string['cachedef_config'] = 'Config settings';
|
||||
$string['cachedef_databasemeta'] = 'Database meta information';
|
||||
$string['cachedef_eventinvalidation'] = 'Event invalidation';
|
||||
$string['cachedef_locking'] = 'Locking';
|
||||
$string['cachedef_string'] = 'Language string cache';
|
||||
$string['cachelock_file_default'] = 'Default file locking';
|
||||
$string['cachestores'] = 'Cache stores';
|
||||
$string['component'] = 'Component';
|
||||
$string['confirmstoredeletion'] = 'Confirm store deletion';
|
||||
$string['defaultmappings'] = 'Stores used when no mapping is present';
|
||||
$string['defaultmappings_help'] = 'These are the default stores that will be used if you don\'t map one or more stores to the cache definition.';
|
||||
$string['defaultstoreactions'] = 'Default stores cannot be modified';
|
||||
$string['default_application'] = 'Default application store';
|
||||
$string['default_request'] = 'Default request store';
|
||||
$string['default_session'] = 'Default session store';
|
||||
$string['definition'] = 'Definition';
|
||||
$string['definitionsummaries'] = 'Known cache definitions';
|
||||
$string['delete'] = 'Delete';
|
||||
$string['deletestore'] = 'Delete store';
|
||||
$string['deletestoreconfirmation'] = 'Are you sure you want to delete the "{$a}" store?';
|
||||
$string['deletestorehasmappings'] = 'You cannot delete this store because it has mappings. Please delete all mappings before deleting the store';
|
||||
$string['deletestoresuccess'] = 'Successfully deleted the cache store';
|
||||
$string['editmappings'] = 'Edit mappings';
|
||||
$string['editstore'] = 'Edit store';
|
||||
$string['editstoresuccess'] = 'Succesfully edited the cache store.';
|
||||
$string['editdefinitionmappings'] = '{$a} definition store mappings';
|
||||
$string['ex_configcannotsave'] = 'Unable to save the cache config to file.';
|
||||
$string['ex_nodefaultlock'] = 'Unable to find a default lock instance.';
|
||||
$string['ex_unabletolock'] = 'Unable to acquire a lock for caching.';
|
||||
$string['gethit'] = 'Get - Hit';
|
||||
$string['getmiss'] = 'Get - Miss';
|
||||
$string['invalidplugin'] = 'Invalid plugin';
|
||||
$string['invalidstore'] = 'Invalid cache store provided';
|
||||
$string['lockdefault'] = 'Default';
|
||||
$string['lockmethod'] = 'Lock method';
|
||||
$string['lockmethod_help'] = 'This is the method used for locking when required of this store.';
|
||||
$string['lockname'] = 'Name';
|
||||
$string['locksummary'] = 'Summary of cache lock instances.';
|
||||
$string['lockuses'] = 'Uses';
|
||||
$string['mappings'] = 'Store mappings';
|
||||
$string['mappingdefault'] = '(default)';
|
||||
$string['mappingprimary'] = 'Primary store';
|
||||
$string['mappingfinal'] = 'Final store';
|
||||
$string['mode'] = 'Mode';
|
||||
$string['modes'] = 'Modes';
|
||||
$string['mode_1'] = 'Application';
|
||||
$string['mode_2'] = 'Session';
|
||||
$string['mode_4'] = 'Request';
|
||||
$string['nativelocking'] = 'This plugin handles its own locking.';
|
||||
$string['none'] = 'None';
|
||||
$string['plugin'] = 'Plugin';
|
||||
$string['pluginsummaries'] = 'Installed cache stores';
|
||||
$string['purge'] = 'Purge';
|
||||
$string['purgestoresuccess'] = 'Successfully purged the requested store.';
|
||||
$string['requestcount'] = 'Test with {$a} requests';
|
||||
$string['rescandefinitions'] = 'Rescan definitions';
|
||||
$string['result'] = 'Result';
|
||||
$string['set'] = 'Set';
|
||||
$string['storeconfiguration'] = 'Store configuration';
|
||||
$string['storename'] = 'Store name';
|
||||
$string['storename_help'] = 'This sets the store name. It is used to identify the store within the system and can only consist of a-z A-Z 0-9 -_ and spaces. It also must be unique. If you attempt to use a name that has already been used you will recieve an error.';
|
||||
$string['storenamealreadyused'] = 'You must choose a unique name for this store.';
|
||||
$string['storenameinvalid'] = 'Invalid store name. You can only use a-z A-Z 0-9 -_ and spaces.';
|
||||
$string['storeperformance'] = 'Cache store performance reporting - {$a} unique requests per operation.';
|
||||
$string['storeready'] = 'Ready';
|
||||
$string['storeresults_application'] = 'Store requests when used as an application cache.';
|
||||
$string['storeresults_request'] = 'Store requests when used as a request cache.';
|
||||
$string['storeresults_session'] = 'Store requests when used as a session cache.';
|
||||
$string['stores'] = 'Stores';
|
||||
$string['store_default_application'] = 'Default file store for application caches';
|
||||
$string['store_default_request'] = 'Default static store for request caches';
|
||||
$string['store_default_session'] = 'Default session store for session caches';
|
||||
$string['storesummaries'] = 'Configured store instances';
|
||||
$string['supports'] = 'Supports';
|
||||
$string['supports_multipleidentifiers'] = 'multiple identifiers';
|
||||
$string['supports_dataguarantee'] = 'data guarantee';
|
||||
$string['supports_nativettl'] = 'ttl';
|
||||
$string['supports_nativelocking'] = 'locking';
|
||||
$string['supports_keyawareness'] = 'key awareness';
|
||||
$string['tested'] = 'Tested';
|
||||
$string['testperformance'] = 'Test performance';
|
||||
$string['unsupportedmode'] = 'Unsupported mode';
|
||||
$string['untestable'] = 'Untestable';
|
@ -71,6 +71,10 @@ $string['type_auth'] = 'Authentication method';
|
||||
$string['type_auth_plural'] = 'Authentication methods';
|
||||
$string['type_block'] = 'Block';
|
||||
$string['type_block_plural'] = 'Blocks';
|
||||
$string['type_cachelock'] = 'Cache lock handler';
|
||||
$string['type_cachelock_plural'] = 'Cache lock handlers';
|
||||
$string['type_cachestore'] = 'Cache store';
|
||||
$string['type_cachestore_plural'] = 'Cache stores';
|
||||
$string['type_coursereport'] = 'Course report';
|
||||
$string['type_coursereport_plural'] = 'Course reports';
|
||||
$string['type_editor'] = 'Editor';
|
||||
|
58
lib/db/caches.php
Normal file
58
lib/db/caches.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Core cache definitions.
|
||||
*
|
||||
* This file is part of Moodle's cache API, affectionately called MUC.
|
||||
* It contains the components that are requried in order to use caching.
|
||||
*
|
||||
* @package core
|
||||
* @category cache
|
||||
* @copyright 2012 Sam Hemelryk
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$definitions = array(
|
||||
// Used to store processed lang files.
|
||||
'string' => array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'component' => 'core',
|
||||
'area' => 'string',
|
||||
'persistent' => true,
|
||||
'persistentmaxsize' => 3
|
||||
),
|
||||
// Used to store database meta information.
|
||||
'databasemeta' => array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'requireidentifiers' => array(
|
||||
'dbfamily'
|
||||
),
|
||||
'persistent' => true,
|
||||
'persistentmaxsize' => 2
|
||||
),
|
||||
// Used to store data from the config + config_plugins table in the database.
|
||||
'config' => array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'persistent' => true
|
||||
),
|
||||
// Event invalidation cache.
|
||||
'eventinvalidation' => array(
|
||||
'mode' => cache_store::MODE_APPLICATION,
|
||||
'persistent' => true,
|
||||
'requiredataguarantee' => true
|
||||
)
|
||||
);
|
@ -1487,6 +1487,10 @@ function get_users_from_config($value, $capability, $includeadmins = true) {
|
||||
|
||||
/**
|
||||
* Invalidates browser caches and cached data in temp
|
||||
*
|
||||
* IMPORTANT - If you are adding anything here to do with the cache directory you should also have a look at
|
||||
* {@see phpunit_util::reset_dataroot()}
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function purge_all_caches() {
|
||||
@ -1498,6 +1502,8 @@ function purge_all_caches() {
|
||||
get_string_manager()->reset_caches();
|
||||
textlib::reset_caches();
|
||||
|
||||
cache_helper::purge_all();
|
||||
|
||||
// purge all other caches: rss, simplepie, etc.
|
||||
remove_dir($CFG->cachedir.'', true);
|
||||
|
||||
@ -7906,6 +7912,7 @@ function get_core_subsystems() {
|
||||
'block' => 'blocks',
|
||||
'blog' => 'blog',
|
||||
'bulkusers' => NULL,
|
||||
'cache' => 'cache',
|
||||
'calendar' => 'calendar',
|
||||
'cohort' => 'cohort',
|
||||
'condition' => NULL,
|
||||
@ -8004,6 +8011,8 @@ function get_plugin_types($fullpaths=true) {
|
||||
'qformat' => 'question/format',
|
||||
'plagiarism' => 'plagiarism',
|
||||
'tool' => $CFG->admin.'/tool',
|
||||
'cachestore' => 'cache/stores',
|
||||
'cachelock' => 'cache/locks',
|
||||
'theme' => 'theme', // this is a bit hacky, themes may be in $CFG->themedir too
|
||||
);
|
||||
|
||||
@ -10425,6 +10434,37 @@ function get_performance_info() {
|
||||
$info['txt'] .= 'rcache: '.
|
||||
"{$rcache->hits}/{$rcache->misses} ";
|
||||
}*/
|
||||
|
||||
if ($stats = cache_helper::get_stats()) {
|
||||
$html = '<span class="cachesused">';
|
||||
$html .= '<span class="cache-stats-heading">Caches interaction by definition then store</span>';
|
||||
$text = 'Caches used (hits/misses/sets): ';
|
||||
$hits = 0;
|
||||
$misses = 0;
|
||||
$sets = 0;
|
||||
foreach ($stats as $definition => $stores) {
|
||||
$html .= '<span class="cache-definition-stats">'.$definition.'</span>';
|
||||
$text .= "$definition {";
|
||||
foreach ($stores as $store => $data) {
|
||||
$hits += $data['hits'];
|
||||
$misses += $data['misses'];
|
||||
$sets += $data['sets'];
|
||||
$text .= "$store($data[hits]/$data[misses]/$data[sets]) ";
|
||||
$html .= "<span class='cache-store-stats'>$store: $data[hits] / $data[misses] / $data[sets]</span>";
|
||||
}
|
||||
$text .= '} ';
|
||||
}
|
||||
$html .= "<span class='cache-total-stats'>Total Hits / Misses / Sets : $hits / $misses / $sets</span>";
|
||||
$html .= '</span> ';
|
||||
$info['cachesused'] = "$hits / $misses / $sets";
|
||||
$info['html'] .= $html;
|
||||
$info['txt'] .= $text.'. ';
|
||||
} else {
|
||||
$info['cachesused'] = '0 / 0 / 0';
|
||||
$info['html'] .= '<span class="cachesused">Caches used (hits/misses/sets): 0/0/0</span>';
|
||||
$info['txt'] .= 'Caches used (hits/misses/sets): 0/0/0 ';
|
||||
}
|
||||
|
||||
$info['html'] = '<div class="performanceinfo siteinfo">'.$info['html'].'</div>';
|
||||
return $info;
|
||||
}
|
||||
|
@ -533,6 +533,12 @@ class phpunit_util {
|
||||
make_temp_directory('');
|
||||
make_cache_directory('');
|
||||
make_cache_directory('htmlpurifier');
|
||||
// Reset the cache API so that it recreates it's required directories as well.
|
||||
cache_factory::reset();
|
||||
// Purge all data from the caches. This is required for consistency.
|
||||
// Any file caches that happened to be within the data root will have already been clearer (because we just deleted cache)
|
||||
// and now we will purge any other caches as well.
|
||||
cache_helper::purge_all();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -405,6 +405,14 @@ class plugin_manager {
|
||||
'exportimscp', 'importhtml', 'print'
|
||||
),
|
||||
|
||||
'cachelock' => array(
|
||||
'file'
|
||||
),
|
||||
|
||||
'cachestore' => array(
|
||||
'file', 'memcache', 'memcached', 'mongodb', 'session', 'static'
|
||||
),
|
||||
|
||||
'coursereport' => array(
|
||||
//deprecated!
|
||||
),
|
||||
|
@ -953,6 +953,9 @@ function session_gc() {
|
||||
function sesskey() {
|
||||
// note: do not use $USER because it may not be initialised yet
|
||||
if (empty($_SESSION['USER']->sesskey)) {
|
||||
if (!isset($_SESSION['USER'])) {
|
||||
$_SESSION['USER'] = new stdClass;
|
||||
}
|
||||
$_SESSION['USER']->sesskey = random_string(10);
|
||||
}
|
||||
|
||||
|
@ -138,6 +138,14 @@ if (!defined('PHPUNIT_TEST')) {
|
||||
define('PHPUNIT_TEST', false);
|
||||
}
|
||||
|
||||
// When set to true MUC (Moodle caching) will not use any of the defined or default stores.
|
||||
// The Cache API will continue to function however this will force the use of the cachestore_dummy so all requests
|
||||
// will be interacting with a static property and will never go to the proper cache stores.
|
||||
// Useful if you need to avoid the stores for one reason or another.
|
||||
if (!defined('NO_CACHE_STORES')) {
|
||||
define('NO_CACHE_STORES', false);
|
||||
}
|
||||
|
||||
// Servers should define a default timezone in php.ini, but if they don't then make sure something is defined.
|
||||
// This is a quick hack. Ideally we should ask the admin for a value. See MDL-22625 for more on this.
|
||||
if (function_exists('date_default_timezone_set') and function_exists('date_default_timezone_get')) {
|
||||
@ -468,6 +476,7 @@ require_once($CFG->libdir .'/sessionlib.php'); // All session and cookie re
|
||||
require_once($CFG->libdir .'/editorlib.php'); // All text editor related functions and classes
|
||||
require_once($CFG->libdir .'/messagelib.php'); // Messagelib functions
|
||||
require_once($CFG->libdir .'/modinfolib.php'); // Cached information on course-module instances
|
||||
require_once($CFG->dirroot.'/cache/lib.php'); // Cache API
|
||||
|
||||
// make sure PHP is not severly misconfigured
|
||||
setup_validate_php_configuration();
|
||||
|
@ -66,6 +66,9 @@
|
||||
<directory suffix="_test.php">question/tests</directory>
|
||||
<directory suffix="_test.php">question/type/tests</directory>
|
||||
</testsuite>
|
||||
<testsuite name="core_cache">
|
||||
<directory suffix="_test.php">cache/tests</directory>
|
||||
</testsuite>
|
||||
|
||||
<!--Plugin suites: use admin/tool/phpunit/cli/util.php to build phpunit.xml from phpunit.xml.dist with up-to-date list of plugins in current install-->
|
||||
<!--@plugin_suites_start@-->
|
||||
|
@ -256,3 +256,12 @@
|
||||
#page-admin-mnet-peers .box.deletedhosts {margin-bottom:1em;font-size:80%;}
|
||||
#page-admin-mnet-peers .mform .certdetails {background-color:white;}
|
||||
#page-admin-mnet-peers .mform .deletedhostinfo {background-color:#ffd3d9;border 2px solid #eeaaaa;padding:4px;margin-bottom:5px;}
|
||||
|
||||
#core-cache-plugin-summaries table,
|
||||
#core-cache-store-summaries table {width:100%;}
|
||||
#core-cache-lock-summary table,
|
||||
#core-cache-definition-summaries table,
|
||||
#core-cache-mode-mappings table {margin:0 auto;}
|
||||
#core-cache-store-summaries .default-store td {color:#333;font-style: italic;}
|
||||
#core-cache-rescan-definitions,
|
||||
#core-cache-mode-mappings .edit-link {margin-top:0.5em;text-align:center;}
|
@ -184,6 +184,12 @@ a.skip:active {position: static;display: block;}
|
||||
#page-footer .validators ul {margin:0px;padding:0px;list-style-type:none;}
|
||||
#page-footer .validators ul li {display:inline;margin-right:10px;margin-left:10px;}
|
||||
|
||||
.performanceinfo .cachesused {margin-top:1em;}
|
||||
.performanceinfo .cachesused .cache-stats-heading {font-weight: bold;text-decoration: underline;font-size:110%;}
|
||||
.performanceinfo .cachesused .cache-definition-stats {font-weight:bold;margin-top:0.3em;}
|
||||
.performanceinfo .cachesused .cache-store-stats {text-indent: 1em;}
|
||||
.performanceinfo .cachesused .cache-total-stats {font-weight:bold;margin-top:0.3em;}
|
||||
|
||||
/**
|
||||
* Tabs
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user