/** * Performs custom validation for us. * * @param array $data An array of data sent to the form. * @param array $files An array of files sent to the form. * @return array An array of errors. */ protected function configuration_validation($data, $files) { $errors = array(); if (!array_key_exists('prefix', $data)) { $prefix = ''; } else { $prefix = clean_param($data['prefix'], PARAM_ALPHANUM); } $factory = cache_factory::instance(); $config = $factory->create_config_instance(); foreach ($config->get_all_stores() as $store) { if ($store['plugin'] !== 'xcache') { continue; } if (empty($store['configuration']['prefix'])) { $storeprefix = ''; } else { $storeprefix = $store['configuration']['prefix']; } if ($storeprefix === $prefix) { $errors['prefix'] = get_string('erroruniqueprefix'); } } return $errors; }
/** * Validates the configuration data. * * We need to check that prefix is unique. * * @param array $data * @param array $files * @param array $errors * @return array * @throws coding_exception */ public function configuration_validation($data, $files, array $errors) { if (empty($errors['prefix'])) { $factory = cache_factory::instance(); $config = $factory->create_config_instance(); foreach ($config->get_all_stores() as $store) { if ($store['plugin'] === 'apcu') { if (isset($store['configuration']['prefix'])) { if ($data['prefix'] === $store['configuration']['prefix']) { // The new store has the same prefix as an existing store, thats a problem. $errors['prefix'] = get_string('prefixnotunique', 'cachestore_apcu'); break; } } else { if (empty($data['prefix'])) { // The existing store hasn't got a prefix and neither does the new store, that's a problem. $errors['prefix'] = get_string('prefixnotunique', 'cachestore_apcu'); break; } } } } } return $errors; }
/** * 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; }
/** * 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. * * @param bool $forcesave If set to true then we will forcefully save the default configuration file. * @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($forcesave = false) { 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. $writer = new self(); $writer->configstores = self::get_default_stores(); $writer->configdefinitions = self::locate_definitions(); $defaultapplication = 'default_application'; $appdefine = defined('TEST_CACHE_USING_APPLICATION_STORE') ? TEST_CACHE_USING_APPLICATION_STORE : false; if ($appdefine !== false && preg_match('/^[a-zA-Z][a-zA-Z0-9_]+$/', $appdefine)) { $expectedstore = $appdefine; $expecteddefine = 'TEST_CACHESTORE_' . strtoupper($expectedstore) . '_TESTSERVERS'; $file = $CFG->dirroot . '/cache/stores/' . $appdefine . '/lib.php'; $class = 'cachestore_' . $appdefine; if (file_exists($file)) { require_once $file; } if (defined($expecteddefine) && class_exists($class)) { /** @var cache_store $class */ $writer->configstores['test_application'] = array('use_test_store' => true, 'name' => 'test_application', 'plugin' => $expectedstore, 'alt' => $writer->configstores[$defaultapplication], 'modes' => $class::get_supported_modes(), 'features' => $class::get_supported_features()); $defaultapplication = 'test_application'; } } $writer->configmodemappings = array(array('mode' => cache_store::MODE_APPLICATION, 'store' => $defaultapplication, '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)); $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 (!$forcesave && $factory->get_state() !== cache_factory::STATE_INITIALISING) { return $writer->generate_configuration_array(); } $factory->set_state(cache_factory::STATE_SAVING); $writer->config_save(); return true; }
/** * 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(); }
case 'purgestore': 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; case 'newlockinstance': // Adds a new lock instance. $lock = required_param('lock', PARAM_ALPHANUMEXT); $mform = cache_administration_helper::get_add_lock_form($lock); if ($mform->is_cancelled()) { redirect($PAGE->url); } else { if ($data = $mform->get_data()) { $factory = cache_factory::instance(); $config = $factory->create_config_instance(true); $name = $data->name; $data = cache_administration_helper::get_lock_configuration_from_data($lock, $data); $config->add_lock_instance($name, $lock, $data); redirect($PAGE->url, get_string('addlocksuccess', 'cache', $name), 5); } } break; case 'deletelock': // Deletes a lock instance. $lock = required_param('lock', PARAM_ALPHANUMEXT); $confirm = optional_param('confirm', false, PARAM_BOOL); if (!array_key_exists($lock, $locks)) { $notifysuccess = false; $notification = get_string('invalidlock', 'cache');
/** * 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. $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. // 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; }
/** * 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); }
/** * Returns stores suitable for use with a given definition. * * @param cache_definition $definition * @return cache_store[] */ public static function get_stores_suitable_for_definition(cache_definition $definition) { $factory = cache_factory::instance(); $stores = array(); if ($factory->is_initialising() || $factory->stores_disabled()) { // No suitable stores here. return $stores; } else { $stores = self::get_cache_stores($definition); // If mappingsonly is set, having 0 stores is ok. if (count($stores) === 0 && !$definition->is_for_mappings_only()) { // No suitable stores we found for the definition. We need to come up with a sensible default. // If this has happened we can be sure that the user has mapped custom stores to either the // mode of the definition. The first alternative to try is the system default for the mode. // e.g. the default file store instance for application definitions. $config = $factory->create_config_instance(); foreach ($config->get_stores($definition->get_mode()) as $name => $details) { if (!empty($details['default'])) { $stores[] = $factory->create_store_from_config($name, $details, $definition); break; } } } } return $stores; }
/** * Test the hash_key functionality. */ public function test_hash_key() { global $CFG; $currentdebugging = $CFG->debug; $CFG->debug = E_ALL; // First with simplekeys $instance = cache_config_phpunittest::instance(true); $instance->phpunit_add_definition('phpunit/hashtest', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'hashtest', 'simplekeys' => true)); $factory = cache_factory::instance(); $definition = $factory->create_definition('phpunit', 'hashtest'); $result = cache_helper::hash_key('test', $definition); $this->assertEquals('test-' . $definition->generate_single_key_prefix(), $result); try { cache_helper::hash_key('test/test', $definition); $this->fail('Invalid key was allowed, you should see this.'); } catch (coding_exception $e) { $this->assertEquals('test/test', $e->debuginfo); } // Second without simple keys $instance->phpunit_add_definition('phpunit/hashtest2', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'hashtest2', 'simplekeys' => false)); $definition = $factory->create_definition('phpunit', 'hashtest2'); $result = cache_helper::hash_key('test', $definition); $this->assertEquals(sha1($definition->generate_single_key_prefix() . '-test'), $result); $result = cache_helper::hash_key('test/test', $definition); $this->assertEquals(sha1($definition->generate_single_key_prefix() . '-test/test'), $result); $CFG->debug = $currentdebugging; }
/** * 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; } } } }
/** * 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)); }
/** * Test that the default stores all support searching. */ public function test_defaults_support_searching() { $instance = cache_config_testing::instance(true); $instance->phpunit_add_definition('phpunit/search1', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'search1', 'requiresearchable' => true)); $instance->phpunit_add_definition('phpunit/search2', array('mode' => cache_store::MODE_SESSION, 'component' => 'phpunit', 'area' => 'search2', 'requiresearchable' => true)); $instance->phpunit_add_definition('phpunit/search3', array('mode' => cache_store::MODE_REQUEST, 'component' => 'phpunit', 'area' => 'search3', 'requiresearchable' => true)); $factory = cache_factory::instance(); // Test application cache is searchable. $definition = $factory->create_definition('phpunit', 'search1'); $this->assertInstanceOf('cache_definition', $definition); $this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE); $cache = $factory->create_cache($definition); $this->assertInstanceOf('cache_application', $cache); $this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements()); // Test session cache is searchable. $definition = $factory->create_definition('phpunit', 'search2'); $this->assertInstanceOf('cache_definition', $definition); $this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE); $cache = $factory->create_cache($definition); $this->assertInstanceOf('cache_session', $cache); $this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements()); // Test request cache is searchable. $definition = $factory->create_definition('phpunit', 'search3'); $this->assertInstanceOf('cache_definition', $definition); $this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE); $cache = $factory->create_cache($definition); $this->assertInstanceOf('cache_request', $cache); $this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements()); }
/** * Cleans old session data from cache stores used for session based definitions. * * @param bool $output If set to true output will be given. */ public static function clean_old_session_data($output = false) { global $CFG; if ($output) { mtrace('Cleaning up stale session data from cache stores.'); } $factory = cache_factory::instance(); $config = $factory->create_config_instance(); $definitions = $config->get_definitions(); $purgetime = time() - $CFG->sessiontimeout; foreach ($definitions as $definitionarray) { // We are only interested in session caches. if (!($definitionarray['mode'] & cache_store::MODE_SESSION)) { continue; } $definition = $factory->create_definition($definitionarray['component'], $definitionarray['area']); $stores = $config->get_stores_for_definition($definition); // Turn them into store instances. $stores = self::initialise_cachestore_instances($stores, $definition); // Initialise all of the stores used for that definition. foreach ($stores as $store) { // If the store doesn't support searching we can skip it. if (!$store instanceof cache_is_searchable) { debugging('Cache stores used for session definitions should ideally be searchable.', DEBUG_DEVELOPER); continue; } // Get all of the keys. $keys = $store->find_by_prefix(cache_session::KEY_PREFIX); $todelete = array(); foreach ($store->get_many($keys) as $key => $value) { if (strpos($key, cache_session::KEY_PREFIX) !== 0 || !is_array($value) || !isset($value['lastaccess'])) { continue; } if ((int) $value['lastaccess'] < $purgetime || true) { $todelete[] = $key; } } if (count($todelete)) { $outcome = (int) $store->delete_many($todelete); if ($output) { $strdef = s($definition->get_id()); $strstore = s($store->my_name()); mtrace("- Removed {$outcome} old {$strdef} sessions from the '{$strstore}' cache store."); } } } } }
/** * Test purge routines. */ public function test_purge_routines() { $instance = cache_config_phpunittest::instance(true); $instance->phpunit_add_definition('phpunit/purge1', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'purge1')); $instance->phpunit_add_definition('phpunit/purge2', array('mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'purge2', 'requireidentifiers' => array('id'))); $factory = cache_factory::instance(); $definition = $factory->create_definition('phpunit', 'purge1'); $this->assertFalse($definition->has_required_identifiers()); $cache = $factory->create_cache($definition); $this->assertInstanceOf('cache_application', $cache); $this->assertTrue($cache->set('test', 'test')); $this->assertTrue($cache->has('test')); cache_helper::purge_by_definition('phpunit', 'purge1'); $this->assertFalse($cache->has('test')); $factory = cache_factory::instance(); $definition = $factory->create_definition('phpunit', 'purge2'); $this->assertTrue($definition->has_required_identifiers()); $cache = $factory->create_cache($definition); $this->assertInstanceOf('cache_application', $cache); $this->assertTrue($cache->set('test', 'test')); $this->assertTrue($cache->has('test')); cache_helper::purge_stores_used_by_definition('phpunit', 'purge2'); $this->assertFalse($cache->has('test')); try { cache_helper::purge_by_definition('phpunit', 'purge2'); $this->fail('Should not be able to purge a definition required identifiers without providing them.'); } catch (coding_exception $ex) { $this->assertContains('Identifier required for cache has not been provided', $ex->getMessage()); } }
/** * Returns the site identifier. * * @return string */ public static function get_site_identifier() { if (is_null(self::$siteidentifier)) { $factory = cache_factory::instance(); $config = $factory->create_config_instance(); self::$siteidentifier = $config->get_site_identifier(); } return self::$siteidentifier; }
/** * Creates a new cache instance based upon the given params. * * @param int $mode One of cache_store::MODE_* * @param string $component The component this cache relates to. * @param string $area The area this cache relates to. * @param array $identifiers Any additional identifiers that should be provided to the definition. * @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 * - staticacceleration : If set to true the cache will hold onto data passing through it. * - staticaccelerationsize : The max size for the static acceleration array. * @return cache_application|cache_session|cache_store */ public static function make_from_params($mode, $component, $area, array $identifiers = array(), array $options = array()) { $factory = cache_factory::instance(); return $factory->create_cache_from_params($mode, $component, $area, $identifiers, $options); }
/** * Gets an instance of the cache_configuration class. * * @return cache_config */ public static function instance() { $factory = cache_factory::instance(); return $factory->create_config_instance(); }