Merge branch 'wip-MDL-36466-m24' of git://github.com/samhemelryk/moodle

This commit is contained in:
Dan Poltawski 2012-11-27 13:38:53 +08:00
commit 4554660c28
15 changed files with 895 additions and 18 deletions

View File

@ -127,6 +127,9 @@ if (function_exists('date_default_timezone_set') and function_exists('date_defau
/** Used by library scripts to check they are being called by Moodle */
define('MOODLE_INTERNAL', true);
// Disables caching.. just in case.
define('CACHE_DISABLE_ALL', true);
// Check that PHP is of a sufficient version
if (version_compare(phpversion(), "5.3.2") < 0) {
$phpversion = phpversion();

View File

@ -193,6 +193,7 @@ if (empty($CFG->version)) {
if ($version > $CFG->version) { // upgrade
purge_all_caches();
$PAGE->set_pagelayout('maintenance');
$PAGE->set_popup_notification_allowed(false);

21
cache/README.md vendored
View File

@ -56,6 +56,27 @@ If a data source had been specified in the definition, the following would be al
$cache = cache::make('core', 'string');
$component = $cache->get('component');
Disabling the cache stores.
There are times in code when you will want to disable the cache stores.
While the cache API must still be functional in order for calls to it to work it is possible to disable the use of the cache stores separately so that you can be sure only the cache will function in all circumstances.
// Disable the cache store at the start of your script with:
define('CACHE_DISABLE_STORES', true);
// Disable the cache within your script when you want with:
cache_factory::disable_stores();
// If you disabled it using the above means you can re-enable it with:
cache_factory::reset();
Disabling the cache entirely.
Like above there are times when you want the cache to avoid initialising anything it doesn't absolutely need. Things such as installation and upgrade require this functionality.
When the cache API is disabled it is still functional however special "disabled" classes will be used instead of the regular classes that make the Cache API tick.
These disabled classes do the least work possible and through this means we avoid all manner of intialisation and configuration.
Once disabled it cannot be re-enabled.
// To disable the cache entirely call the following:
define('CACHE_DISABLE_ALL', true);
Cache API parts
---------------

View File

@ -122,12 +122,15 @@ class cache_config {
/**
* Loads the configuration file and parses its contents into the expected structure.
*
* @param array|false $configuration Can be used to force a configuration. Should only be used when truly required.
* @return boolean
*/
public function load() {
public function load($configuration = false) {
global $CFG;
$configuration = $this->include_configuration();
if ($configuration === false) {
$configuration = $this->include_configuration();
}
$this->configstores = array();
$this->configdefinitions = array();
@ -425,7 +428,8 @@ class cache_config {
*/
public function get_stores_for_definition(cache_definition $definition) {
// Check if MUC has been disabled.
if (defined('NO_CACHE_STORES') && NO_CACHE_STORES !== false) {
$factory = cache_factory::instance();
if ($factory->stores_disabled()) {
// 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.

View File

@ -107,6 +107,11 @@ class cachestore_dummy extends cache_store {
*/
public function initialise(cache_definition $definition) {
// If the definition isn't persistent then we need to be persistent here.
// The reasoning behind this is that:
// - If the definition is persistent then the cache loader is going to
// store things in its persistent cache.
// - If the definition is not persistent then the cache loader won't try to store anything
// and we will need to store it here in order to make sure it is accessible.
$this->persist = !$definition->should_be_persistent();
}

View File

@ -40,6 +40,21 @@ defined('MOODLE_INTERNAL') || die();
*/
class cache_factory {
/** The cache has not been initialised yet. */
const STATE_UNINITIALISED = 0;
/** The cache is in the process of initialising itself. */
const STATE_INITIALISING = 1;
/** The cache is in the process of saving its configuration file. */
const STATE_SAVING = 2;
/** The cache is ready to use. */
const STATE_READY = 3;
/** The cache encountered an error while initialising. */
const STATE_ERROR_INITIALISING = 9;
/** The cache has been disabled. */
const STATE_DISABLED = 10;
/** The cache stores have been disabled */
const STATE_STORES_DISABLED = 11;
/**
* An instance of the cache_factory class created upon the first request.
* @var cache_factory
@ -82,6 +97,12 @@ class cache_factory {
*/
protected $lockplugins = null;
/**
* The current state of the cache API.
* @var int
*/
protected $state = 0;
/**
* Returns an instance of the cache_factor method.
*
@ -89,8 +110,22 @@ class cache_factory {
* @return cache_factory
*/
public static function instance($forcereload = false) {
global $CFG;
if ($forcereload || self::$instance === null) {
self::$instance = new cache_factory();
// Initialise a new factory to facilitate our needs.
if (defined('CACHE_DISABLE_ALL') && CACHE_DISABLE_ALL !== false) {
// The cache has been disabled. Load disabledlib and start using the factory designed to handle this
// situation. It will use disabled alternatives where available.
require_once($CFG->dirroot.'/cache/disabledlib.php');
self::$instance = new cache_factory_disabled();
} else {
// We're using the regular factory.
self::$instance = new cache_factory();
if (defined('CACHE_DISABLE_STORES') && CACHE_DISABLE_STORES !== false) {
// The cache stores have been disabled.
self::$instance->set_state(self::STATE_STORES_DISABLED);;
}
}
}
return self::$instance;
}
@ -113,6 +148,8 @@ class cache_factory {
$factory->configs = array();
$factory->definitions = array();
$factory->lockplugins = null; // MUST be null in order to force its regeneration.
// Reset the state to uninitialised.
$factory->state = self::STATE_UNINITIALISED;
}
/**
@ -206,7 +243,7 @@ class cache_factory {
* @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
* @return boolean|cache_store
*/
public function create_store_from_config($name, array $details, cache_definition $definition) {
if (!array_key_exists($name, $this->stores)) {
@ -253,9 +290,19 @@ class cache_factory {
$class = 'cache_config_phpunittest';
}
$error = false;
if ($needtocreate) {
// Create the default configuration.
$class::create_default_configuration();
// Update the state, we are now initialising the cache.
self::set_state(self::STATE_INITIALISING);
$configuration = $class::create_default_configuration();
if ($configuration !== true) {
// Failed to create the default configuration. Disable the cache stores and update the state.
self::set_state(self::STATE_ERROR_INITIALISING);
$this->configs[$class] = new $class;
$this->configs[$class]->load($configuration);
$error = true;
}
}
if (!array_key_exists($class, $this->configs)) {
@ -264,6 +311,11 @@ class cache_factory {
$this->configs[$class]->load();
}
if (!$error) {
// The cache is now ready to use. Update the state.
self::set_state(self::STATE_READY);
}
// Return the instance.
return $this->configs[$class];
}
@ -338,4 +390,83 @@ class cache_factory {
$class = $this->lockplugins[$type];
return new $class($name, $config);
}
/**
* Returns the current state of the cache API.
*
* @return int
*/
public function get_state() {
return $this->state;
}
/**
* Updates the state fo the cache API.
*
* @param int $state
* @return bool
*/
public function set_state($state) {
if ($state <= $this->state) {
return false;
}
$this->state = $state;
return true;
}
/**
* Returns true if the cache API has been disabled.
*
* @return bool
*/
public function is_disabled() {
return $this->state === self::STATE_DISABLED;
}
/**
* Disables as much of the cache API as possible.
*
* All of the magic associated with the disabled cache is wrapped into this function.
* In switching out the factory for the disabled factory it gains full control over the initialisation of objects
* and can use all of the disabled alternatives.
* Simple!
*
* This function has been marked as protected so that it cannot be abused through the public API presently.
* Perhaps in the future we will allow this, however as per the build up to the first release containing
* MUC it was decided that this was just to risky and abusable.
*/
protected static function disable() {
global $CFG;
require_once($CFG->dirroot.'/cache/disabledlib.php');
self::$instance = new cache_factory_disabled();
}
/**
* Returns true if the cache stores have been disabled.
*
* @return bool
*/
public function stores_disabled() {
return $this->state === self::STATE_STORES_DISABLED || $this->is_disabled();
}
/**
* Disables cache stores.
*
* The cache API will continue to function however none of the actual stores will be used.
* Instead the dummy store will be provided for all cache requests.
* This is useful in situations where you cannot be sure any stores are working.
*
* In order to re-enable the cache you must call the cache factories static reset method:
* <code>
* // Disable the cache factory.
* cache_factory::disable_stores();
* // Re-enable the cache factory by resetting it.
* cache_factory::reset();
* </code>
*/
public static function disable_stores() {
$factory = self::instance();
$factory->set_state(self::STATE_STORES_DISABLED);
}
}

462
cache/disabledlib.php vendored Normal file
View File

@ -0,0 +1,462 @@
<?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 classes that are used by the Cache API only when it is disabled.
*
* These classes are derivatives of other significant classes used by the Cache API customised specifically
* to only do what is absolutely necessary when initialising and using the Cache API when its been disabled.
*
* @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();
/**
* Required as it is needed for cache_config_disabled which extends cache_config_writer.
*/
require_once($CFG->dirroot.'/cache/locallib.php');
/**
* The cache loader class used when the Cache has been disabled.
*
* @copyright 2012 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cache_disabled extends cache {
/**
* Constructs the cache.
*
* @param cache_definition $definition
* @param cache_store $store
* @param null $loader Unused.
*/
public function __construct(cache_definition $definition, cache_store $store, $loader = null) {
$this->definition = $definition;
$this->store = $store;
}
/**
* Gets a key from the cache.
*
* @param int|string $key
* @param int $strictness Unused.
* @return bool
*/
public function get($key, $strictness = IGNORE_MISSING) {
return false;
}
/**
* Gets many keys at once from the cache.
*
* @param array $keys
* @param int $strictness Unused.
* @return array
*/
public function get_many(array $keys, $strictness = IGNORE_MISSING) {
$return = array();
foreach ($keys as $key) {
$return[$key] = false;
}
return $return;
}
/**
* Sets a key value pair in the cache.
*
* @param int|string $key Unused.
* @param mixed $data Unused.
* @return bool
*/
public function set($key, $data) {
return false;
}
/**
* Sets many key value pairs in the cache at once.
*
* @param array $keyvaluearray Unused.
* @return int
*/
public function set_many(array $keyvaluearray) {
return 0;
}
/**
* Deletes an item from the cache.
*
* @param int|string $key Unused.
* @param bool $recurse Unused.
* @return bool
*/
public function delete($key, $recurse = true) {
return false;
}
/**
* Deletes many items at once from the cache.
*
* @param array $keys Unused.
* @param bool $recurse Unused.
* @return int
*/
public function delete_many(array $keys, $recurse = true) {
return 0;
}
/**
* Checks if the cache has the requested key.
*
* @param int|string $key Unused.
* @return bool
*/
public function has($key) {
return false;
}
/**
* Checks if the cache has all of the requested keys.
* @param array $keys Unused.
* @return bool
*/
public function has_all(array $keys) {
return false;
}
/**
* Checks if the cache has any of the requested keys.
*
* @param array $keys Unused.
* @return bool
*/
public function has_any(array $keys) {
return false;
}
/**
* Purges all items from the cache.
*
* @return bool
*/
public function purge() {
return true;
}
}
/**
* The cache factory class used when the Cache has been disabled.
*
* @copyright 2012 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cache_factory_disabled extends cache_factory {
/**
* Returns an instance of the cache_factor method.
*
* @param bool $forcereload Unused.
* @return cache_factory
* @throws coding_exception
*/
public static function instance($forcereload = false) {
throw new coding_exception('You must not call to this cache factory within your code.');
}
/**
* Creates a definition instance or returns the existing one if it has already been created.
*
* @param string $component
* @param string $area
* @param string $aggregate Unused.
* @return cache_definition
*/
public function create_definition($component, $area, $aggregate = null) {
return cache_definition::load_adhoc(cache_store::MODE_REQUEST, $component, $area);
}
/**
* Common public method to create a cache instance given a definition.
*
* @param cache_definition $definition
* @return cache_application|cache_session|cache_store
* @throws coding_exception
*/
public function create_cache(cache_definition $definition) {
return new cache_disabled($definition, $this->create_dummy_store($definition));
}
/**
* Creates a cache object given the parameters for a definition.
*
* @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) {
$definition = $this->create_definition($component, $area, $aggregate);
$cache = $this->create_cache($definition, $identifiers);
return $cache;
}
/**
* Creates an ad-hoc cache from the given param.
*
* @param int $mode
* @param string $component
* @param string $area
* @param array $identifiers
* @param array $options An array of options, available options are:
* - simplekeys : Set to true if the keys you will use are a-zA-Z0-9_
* - simpledata : Set to true if the type of the data you are going to store is scalar, or an array of scalar vars
* - persistent : If set to true the cache will persist construction requests.
* @return cache_application|cache_session|cache_request
*/
public function create_cache_from_params($mode, $component, $area, array $identifiers = array(), array $options = array()) {
$definition = cache_definition::load_adhoc($mode, $component, $area);
$cache = $this->create_cache($definition, $identifiers);
return $cache;
}
/**
* Creates a store instance given its name and configuration.
*
* @param string $name Unused.
* @param array $details Unused.
* @param cache_definition $definition
* @return boolean|cache_store
*/
public function create_store_from_config($name, array $details, cache_definition $definition) {
return $this->create_dummy_store($definition);
}
/**
* Creates a cache config instance with the ability to write if required.
*
* @param bool $writer Unused.
* @return cache_config|cache_config_writer
*/
public function create_config_instance($writer = false) {
$class = 'cache_config_disabled';
if (!array_key_exists($class, $this->configs)) {
self::set_state(self::STATE_INITIALISING);
$configuration = $class::create_default_configuration();
$this->configs[$class] = new $class;
$this->configs[$class]->load($configuration);
}
self::set_state(self::STATE_READY);
// Return the instance.
return $this->configs[$class];
}
}
/**
* The cache config class used when the Cache has been disabled.
*
* @copyright 2012 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cache_config_disabled extends cache_config_writer {
/**
* Returns an instance of the configuration writer.
*
* @return cache_config_disabled
*/
public static function instance() {
$factory = cache_factory::instance();
return $factory->create_config_instance(true);
}
/**
* Saves the current configuration.
*/
protected function config_save() {
// Nothing to do here.
}
/**
* Generates a configuration array suitable to be written to the config file.
*
* @return array
*/
protected function generate_configuration_array() {
$configuration = array();
$configuration['stores'] = $this->configstores;
$configuration['modemappings'] = $this->configmodemappings;
$configuration['definitions'] = $this->configdefinitions;
$configuration['definitionmappings'] = $this->configdefinitionmappings;
$configuration['locks'] = $this->configlocks;
return $configuration;
}
/**
* Adds a plugin instance.
*
* @param string $name Unused.
* @param string $plugin Unused.
* @param array $configuration Unused.
* @return bool
* @throws cache_exception
*/
public function add_store_instance($name, $plugin, array $configuration = array()) {
return false;
}
/**
* Sets the mode mappings.
*
* @param array $modemappings Unused.
* @return bool
* @throws cache_exception
*/
public function set_mode_mappings(array $modemappings) {
return false;
}
/**
* Edits a give plugin instance.
*
* @param string $name Unused.
* @param string $plugin Unused.
* @param array $configuration Unused.
* @return bool
* @throws cache_exception
*/
public function edit_store_instance($name, $plugin, $configuration) {
return false;
}
/**
* Deletes a store instance.
*
* @param string $name Unused.
* @return bool
* @throws cache_exception
*/
public function delete_store_instance($name) {
return false;
}
/**
* Creates the default configuration and saves it.
*
* @return array
*/
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 = array();
$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
)
);
return $writer->generate_configuration_array();
}
/**
* Updates the definition in the configuration from those found in the cache files.
*
* @param bool $coreonly Unused.
*/
public static function update_definitions($coreonly = false) {
// Nothing to do here.
}
/**
* Locates all of the definition files.
*
* @param bool $coreonly Unused.
* @return array
*/
protected static function locate_definitions($coreonly = false) {
return array();
}
/**
* Sets the mappings for a given definition.
*
* @param string $definition Unused.
* @param array $mappings Unused.
* @throws coding_exception
*/
public function set_definition_mappings($definition, $mappings) {
// Nothing to do here.
}
}

49
cache/locallib.php vendored
View File

@ -41,6 +41,14 @@ defined('MOODLE_INTERNAL') || die();
*/
class cache_config_writer extends cache_config {
/**
* Switch that gets set to true when ever a cache_config_writer instance is saving the cache configuration file.
* If this is set to true when save is next called we must avoid the trying to save and instead return the
* generated config so that is may be used instead of the file.
* @var bool
*/
protected static $creatingconfig = false;
/**
* Returns an instance of the configuration writer.
*
@ -53,6 +61,10 @@ class cache_config_writer extends cache_config {
/**
* Saves the current configuration.
*
* Exceptions within this function are tolerated but must be of type cache_exception.
* They are caught during initialisation and written to the error log. This is required in order to avoid
* infinite loop situations caused by the cache throwing exceptions during its initialisation.
*/
protected function config_save() {
global $CFG;
@ -61,20 +73,15 @@ class cache_config_writer extends cache_config {
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.');
throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Cannot create config directory. Check the permissions on your moodledata directory.');
}
}
if (!file_exists($directory) || !is_writable($directory)) {
throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Config directory is not writable.');
throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Config directory is not writable. Check the permissions on the moodledata/muc directory.');
}
// 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;
$configuration = $this->generate_configuration_array();
// Prepare the file content.
$content = "<?php defined('MOODLE_INTERNAL') || die();\n \$configuration = ".var_export($configuration, true).";";
@ -106,6 +113,20 @@ class cache_config_writer extends cache_config {
}
}
/**
* Generates a configuration array suitable to be written to the config file.
* @return array
*/
protected function generate_configuration_array() {
$configuration = array();
$configuration['stores'] = $this->configstores;
$configuration['modemappings'] = $this->configmodemappings;
$configuration['definitions'] = $this->configdefinitions;
$configuration['definitionmappings'] = $this->configdefinitionmappings;
$configuration['locks'] = $this->configlocks;
return $configuration;
}
/**
* Adds a plugin instance.
*
@ -293,6 +314,9 @@ class cache_config_writer extends cache_config {
*
* 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.
*
* @return true|array Returns true if the default configuration was successfully created.
* Returns a configuration array if it could not be saved. This is a bad situation. Check your error logs.
*/
public static function create_default_configuration() {
global $CFG;
@ -357,7 +381,16 @@ class cache_config_writer extends cache_config {
'default' => true
)
);
$factory = cache_factory::instance();
// We expect the cache to be initialising presently. If its not then something has gone wrong and likely
// we are now in a loop.
if ($factory->get_state() !== cache_factory::STATE_INITIALISING) {
return $writer->generate_configuration_array();
}
$factory->set_state(cache_factory::STATE_SAVING);
$writer->config_save();
return true;
}
/**

View File

@ -50,6 +50,14 @@ class cache_phpunit_tests extends advanced_testcase {
cache_config_phpunittest::create_default_configuration();
}
/**
* Final task is to reset the cache system
*/
public static function tearDownAfterClass() {
parent::tearDownAfterClass();
cache_factory::reset();
}
/**
* Tests cache configuration
*/
@ -664,4 +672,76 @@ class cache_phpunit_tests extends advanced_testcase {
$instance = cache_config_phpunittest::instance();
$this->assertInstanceOf('cache_config', $instance);
}
/**
* Test disabling the cache stores.
*/
public function test_disable_stores() {
$instance = cache_config_phpunittest::instance();
$instance->phpunit_add_definition('phpunit/disabletest', array(
'mode' => cache_store::MODE_APPLICATION,
'component' => 'phpunit',
'area' => 'disabletest'
));
$cache = cache::make('phpunit', 'disabletest');
$this->assertInstanceOf('cache_phpunit_application', $cache);
$this->assertEquals('cachestore_file', $cache->phpunit_get_store_class());
$this->assertFalse($cache->get('test'));
$this->assertTrue($cache->set('test', 'test'));
$this->assertEquals('test', $cache->get('test'));
cache_factory::disable_stores();
$cache = cache::make('phpunit', 'disabletest');
$this->assertInstanceOf('cache_phpunit_application', $cache);
$this->assertEquals('cachestore_dummy', $cache->phpunit_get_store_class());
$this->assertFalse($cache->get('test'));
$this->assertTrue($cache->set('test', 'test'));
$this->assertEquals('test', $cache->get('test'));
}
/**
* Test disabling the cache.
*/
public function test_disable() {
global $CFG;
$configfile = $CFG->dataroot.'/muc/config.php';
// That's right, we're deleting the config file.
$this->assertTrue(@unlink($configfile));
// Disable the cache
cache_phpunit_factory::phpunit_disable();
// Check we get the expected disabled factory.
$factory = cache_factory::instance();
$this->assertInstanceOf('cache_factory_disabled', $factory);
// Check we get the expected disabled config.
$config = $factory->create_config_instance();
$this->assertInstanceOf('cache_config_disabled', $config);
// Check we get the expected disabled caches.
$cache = cache::make('phpunit', 'disable');
$this->assertInstanceOf('cache_disabled', $cache);
$cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'phpunit', 'disable');
$this->assertInstanceOf('cache_disabled', $cache);
$this->assertFalse(file_exists($configfile));
$this->assertFalse($cache->get('test'));
$this->assertFalse($cache->set('test', 'test'));
$this->assertFalse($cache->delete('test'));
$this->assertTrue($cache->purge());
cache_factory::reset();
$factory = cache_factory::instance(true);
$config = $factory->create_config_instance();
$this->assertEquals('cache_config_phpunittest', get_class($config));
}
}

View File

@ -41,6 +41,19 @@ class cache_config_phpunittest extends cache_config_writer {
* @param array $properties
*/
public function phpunit_add_definition($area, array $properties) {
if (!array_key_exists('overrideclass', $properties)) {
switch ($properties['mode']) {
case cache_store::MODE_APPLICATION:
$properties['overrideclass'] = 'cache_phpunit_application';
break;
case cache_store::MDOE_SESSION:
$properties['overrideclass'] = 'cache_phpunit_session';
break;
case cache_store::MODE_REQUEST:
$properties['overrideclass'] = 'cache_phpunit_request';
break;
}
}
$this->configdefinitions[$area] = $properties;
}
@ -137,6 +150,64 @@ class cache_phpunit_dummy_datasource implements cache_data_source {
}
}
/**
* PHPUnit application cache loader.
*
* Used to expose things we could not otherwise see within an application cache.
*
* @copyright 2012 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cache_phpunit_application extends cache_application {
/**
* Returns the class of the store immediately associated with this cache.
* @return string
*/
public function phpunit_get_store_class() {
return get_class($this->get_store());
}
}
/**
* PHPUnit session cache loader.
*
* Used to expose things we could not otherwise see within an session cache.
*
* @copyright 2012 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cache_phpunit_session extends cache_session {
/**
* Returns the class of the store immediately associated with this cache.
* @return string
*/
public function phpunit_get_store_class() {
return get_class($this->get_store());
}
}
/**
* PHPUnit request cache loader.
*
* Used to expose things we could not otherwise see within an request cache.
*
* @copyright 2012 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cache_phpunit_request extends cache_request {
/**
* Returns the class of the store immediately associated with this cache.
* @return string
*/
public function phpunit_get_store_class() {
return get_class($this->get_store());
}
}
/**
* Dummy overridden cache loader class that we can use to test overriding loader functionality.
*
@ -145,4 +216,21 @@ class cache_phpunit_dummy_datasource implements cache_data_source {
*/
class cache_phpunit_dummy_overrideclass extends cache_application {
// Satisfying the code pre-checker is just part of my day job.
}
/**
* Cache PHPUnit specific factory.
*
* @copyright 2012 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cache_phpunit_factory extends cache_factory {
/**
* Exposes the cache_factory's disable method.
*
* Perhaps one day that method will be made public, for the time being it is protected.
*/
public static function phpunit_disable() {
parent::disable();
}
}

View File

@ -50,6 +50,14 @@ class cache_config_writer_phpunit_tests extends advanced_testcase {
cache_config_phpunittest::create_default_configuration();
}
/**
* Final task is to reset the cache system
*/
public static function tearDownAfterClass() {
parent::tearDownAfterClass();
cache_factory::reset();
}
/**
* Test getting an instance. Pretty basic.
*/
@ -297,6 +305,14 @@ class cache_administration_helper_phpunit_tests extends advanced_testcase {
cache_config_phpunittest::create_default_configuration();
}
/**
* Final task is to reset the cache system
*/
public static function tearDownAfterClass() {
parent::tearDownAfterClass();
cache_factory::reset();
}
/**
* Test the numerous summaries the helper can produce.
*/

View File

@ -45,6 +45,7 @@ if (file_exists($configfile)) {
define('CLI_SCRIPT', false); // prevents some warnings later
define('AJAX_SCRIPT', false); // prevents some warnings later
define('CACHE_DISABLE_ALL', true); // Disables caching.. just in case.
// 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.

View File

@ -138,12 +138,20 @@ if (!defined('PHPUNIT_TEST')) {
define('PHPUNIT_TEST', false);
}
// When set to true MUC (Moodle caching) will be disabled as much as possible.
// A special cache factory will be used to handle this situation and will use special "disabled" equivalents objects.
// This ensure we don't attempt to read or create the config file, don't use stores, don't provide persistence or
// storage of any kind.
if (!defined('CACHE_DISABLE_ALL')) {
define('CACHE_DISABLE_ALL', 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);
if (!defined('CACHE_DISABLE_STORES')) {
define('CACHE_DISABLE_STORES', false);
}
// Servers should define a default timezone in php.ini, but if they don't then make sure something is defined.

View File

@ -1444,7 +1444,12 @@ border-color:black; background-color:#ffffee; border-style:solid; border-radius:
width: 80%; -moz-border-radius: 20px; padding: 15px">
' . $message . '
</div>';
if (!empty($CFG->debug) && $CFG->debug >= DEBUG_DEVELOPER) {
// Check whether debug is set.
$debug = (!empty($CFG->debug) && $CFG->debug >= DEBUG_DEVELOPER);
// Also check we have it set in the config file. This occurs if the method to read the config table from the
// database fails, reading from the config table is the first database interaction we have.
$debug = $debug || (!empty($CFG->config_php_settings['debug']) && $CFG->config_php_settings['debug'] >= DEBUG_DEVELOPER );
if ($debug) {
if (!empty($debuginfo)) {
$debuginfo = s($debuginfo); // removes all nasty JS
$debuginfo = str_replace("\n", '<br />', $debuginfo); // keep newlines

View File

@ -1419,6 +1419,10 @@ function install_core($version, $verbose) {
global $CFG, $DB;
try {
// Disable the use of cache stores here. We will reset the factory after we've performed the installation.
// This ensures that we don't permanently cache anything during installation.
cache_factory::disable_stores();
set_time_limit(600);
print_upgrade_part_start('moodle', true, $verbose); // does not store upgrade running flag
@ -1442,6 +1446,9 @@ function install_core($version, $verbose) {
admin_apply_default_settings(NULL, true);
print_upgrade_part_end(null, true, $verbose);
// Reset the cache, this returns it to a normal operation state.
cache_factory::reset();
} catch (exception $ex) {
upgrade_handle_exception($ex);
}
@ -1463,6 +1470,9 @@ function upgrade_core($version, $verbose) {
try {
// Reset caches before any output
purge_all_caches();
// Disable the use of cache stores here. We will reset the factory after we've performed the installation.
// This ensures that we don't permanently cache anything during installation.
cache_factory::disable_stores();
// Upgrade current language pack if we can
upgrade_language_pack();
@ -1495,7 +1505,9 @@ function upgrade_core($version, $verbose) {
// Update core definitions.
cache_helper::update_definitions(true);
// Reset caches again, just to be sure
// Reset the cache, this returns it to a normal operation state.
cache_factory::reset();
// Purge caches again, just to be sure we arn't holding onto old stuff now.
purge_all_caches();
// Clean up contexts - more and more stuff depends on existence of paths and contexts
@ -1523,11 +1535,18 @@ function upgrade_noncore($verbose) {
// upgrade all plugins types
try {
// Disable the use of cache stores here. We will reset the factory after we've performed the installation.
// This ensures that we don't permanently cache anything during installation.
cache_factory::disable_stores();
$plugintypes = get_plugin_types();
foreach ($plugintypes as $type=>$location) {
upgrade_plugins($type, 'print_upgrade_part_start', 'print_upgrade_part_end', $verbose);
}
// Update cache definitions. Involves scanning each plugin for any changes.
cache_helper::update_definitions();
// Reset the cache system to a normal state.
cache_factory::reset();
} catch (Exception $ex) {
upgrade_handle_exception($ex);
}