1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * APCu cache store main library. 19 * 20 * @package cachestore_apcu 21 * @copyright 2012 Sam Hemelryk 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 defined('MOODLE_INTERNAL') || die(); 26 27 /** 28 * The APCu cache store class. 29 * 30 * @copyright 2012 Sam Hemelryk 31 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 32 */ 33 class cachestore_apcu extends cache_store implements cache_is_key_aware, cache_is_configurable { 34 35 /** 36 * The required version of APCu for this extension. 37 */ 38 const REQUIRED_VERSION = '4.0.0'; 39 40 /** 41 * The name of this store instance. 42 * @var string 43 */ 44 protected $name; 45 46 /** 47 * The definition used when this instance was initialised. 48 * @var cache_definition 49 */ 50 protected $definition = null; 51 52 /** 53 * The storeprefix to use on all instances of this store. Configured as part store setup. 54 * @var string 55 */ 56 protected $storeprefix = null; 57 58 /** 59 * The prefix added specifically for this cache. 60 * @var string 61 */ 62 protected $cacheprefix = null; 63 64 /** 65 * Static method to check that the APCu stores requirements have been met. 66 * 67 * It checks that the APCu extension has been loaded and that it has been enabled. 68 * 69 * @return bool True if the stores software/hardware requirements have been met and it can be used. False otherwise. 70 */ 71 public static function are_requirements_met() { 72 $enabled = ini_get('apc.enabled') && (php_sapi_name() != "cli" || ini_get('apc.enable_cli')); 73 if (!extension_loaded('apcu') || !$enabled) { 74 return false; 75 } 76 77 $version = phpversion('apcu'); 78 return $version && version_compare($version, self::REQUIRED_VERSION, '>='); 79 } 80 81 /** 82 * Static method to check if a store is usable with the given mode. 83 * 84 * @param int $mode One of cache_store::MODE_* 85 * @return bool True if the mode is supported. 86 */ 87 public static function is_supported_mode($mode) { 88 return ($mode === self::MODE_APPLICATION || $mode === self::MODE_SESSION); 89 } 90 91 /** 92 * Returns the supported features as a binary flag. 93 * 94 * @param array $configuration The configuration of a store to consider specifically. 95 * @return int The supported features. 96 */ 97 public static function get_supported_features(array $configuration = array()) { 98 return self::SUPPORTS_NATIVE_TTL; 99 } 100 101 /** 102 * Returns the supported modes as a binary flag. 103 * 104 * @param array $configuration The configuration of a store to consider specifically. 105 * @return int The supported modes. 106 */ 107 public static function get_supported_modes(array $configuration = array()) { 108 return self::MODE_APPLICATION + self::MODE_SESSION; 109 } 110 111 /** 112 * Constructs an instance of the cache store. 113 * 114 * This method should not create connections or perform and processing, it should be used 115 * 116 * @param string $name The name of the cache store 117 * @param array $configuration The configuration for this store instance. 118 */ 119 public function __construct($name, array $configuration = array()) { 120 global $CFG; 121 $this->name = $name; 122 $this->storeprefix = $CFG->prefix; 123 if (isset($configuration['prefix'])) { 124 $this->storeprefix = $configuration['prefix']; 125 } 126 } 127 128 /** 129 * Returns the name of this store instance. 130 * @return string 131 */ 132 public function my_name() { 133 return $this->name; 134 } 135 136 /** 137 * Initialises a new instance of the cache store given the definition the instance is to be used for. 138 * 139 * This function should prepare any given connections etc. 140 * 141 * @param cache_definition $definition 142 * @return bool 143 */ 144 public function initialise(cache_definition $definition) { 145 $this->definition = $definition; 146 $this->cacheprefix = $this->storeprefix.$definition->generate_definition_hash().'__'; 147 return true; 148 } 149 150 /** 151 * Returns true if this cache store instance has been initialised. 152 * @return bool 153 */ 154 public function is_initialised() { 155 return ($this->definition !== null); 156 } 157 158 /** 159 * Prepares the given key for use. 160 * 161 * Should be called before all interaction. 162 * 163 * @param string $key The key to prepare for storing in APCu. 164 * 165 * @return string 166 */ 167 protected function prepare_key($key) { 168 return $this->cacheprefix . $key; 169 } 170 171 /** 172 * Retrieves an item from the cache store given its key. 173 * 174 * @param string $key The key to retrieve 175 * @return mixed The data that was associated with the key, or false if the key did not exist. 176 */ 177 public function get($key) { 178 $key = $this->prepare_key($key); 179 $success = false; 180 $outcome = apcu_fetch($key, $success); 181 if ($success) { 182 return $outcome; 183 } 184 return $success; 185 } 186 187 /** 188 * Retrieves several items from the cache store in a single transaction. 189 * 190 * 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. 191 * 192 * @param array $keys The array of keys to retrieve 193 * @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 194 * be set to false. 195 */ 196 public function get_many($keys) { 197 $map = array(); 198 foreach ($keys as $key) { 199 $map[$key] = $this->prepare_key($key); 200 } 201 $outcomes = array(); 202 $success = false; 203 $results = apcu_fetch($map, $success); 204 if ($success) { 205 foreach ($map as $key => $used) { 206 if (array_key_exists($used, $results)) { 207 $outcomes[$key] = $results[$used]; 208 } else { 209 $outcomes[$key] = false; 210 } 211 } 212 } else { 213 $outcomes = array_fill_keys($keys, false); 214 } 215 return $outcomes; 216 } 217 218 /** 219 * Sets an item in the cache given its key and data value. 220 * 221 * @param string $key The key to use. 222 * @param mixed $data The data to set. 223 * @return bool True if the operation was a success false otherwise. 224 */ 225 public function set($key, $data) { 226 $key = $this->prepare_key($key); 227 return apcu_store($key, $data, $this->definition->get_ttl()); 228 } 229 230 /** 231 * Sets many items in the cache in a single transaction. 232 * 233 * @param array $keyvaluearray An array of key value pairs. Each item in the array will be an associative array with two 234 * keys, 'key' and 'value'. 235 * @return int The number of items successfully set. It is up to the developer to check this matches the number of items 236 * sent ... if they care that is. 237 */ 238 public function set_many(array $keyvaluearray) { 239 $map = array(); 240 foreach ($keyvaluearray as $pair) { 241 $key = $this->prepare_key($pair['key']); 242 $map[$key] = $pair['value']; 243 } 244 $result = apcu_store($map, null, $this->definition->get_ttl()); 245 return count($map) - count($result); 246 } 247 248 /** 249 * Deletes an item from the cache store. 250 * 251 * @param string $key The key to delete. 252 * @return bool Returns true if the operation was a success, false otherwise. 253 */ 254 public function delete($key) { 255 $key = $this->prepare_key($key); 256 return apcu_delete($key); 257 } 258 259 /** 260 * Deletes several keys from the cache in a single action. 261 * 262 * @param array $keys The keys to delete 263 * @return int The number of items successfully deleted. 264 */ 265 public function delete_many(array $keys) { 266 $count = 0; 267 foreach ($keys as $key) { 268 if ($this->delete($key)) { 269 $count++; 270 } 271 } 272 return $count; 273 } 274 275 /** 276 * Purges the cache deleting all items within it. 277 * 278 * @return boolean True on success. False otherwise. 279 */ 280 public function purge() { 281 if (class_exists('APCUIterator', false)) { 282 $iterator = new APCUIterator('#^' . preg_quote($this->cacheprefix, '#') . '#'); 283 } else { 284 $iterator = new APCIterator('user', '#^' . preg_quote($this->cacheprefix, '#') . '#'); 285 } 286 return apcu_delete($iterator); 287 } 288 289 /** 290 * Performs any necessary clean up when the store instance is being deleted. 291 */ 292 public function instance_deleted() { 293 if (class_exists('APCUIterator', false)) { 294 $iterator = new APCUIterator('#^' . preg_quote($this->storeprefix, '#') . '#'); 295 } else { 296 $iterator = new APCIterator('user', '#^' . preg_quote($this->storeprefix, '#') . '#'); 297 } 298 return apcu_delete($iterator); 299 } 300 301 /** 302 * Generates an instance of the cache store that can be used for testing. 303 * 304 * Returns an instance of the cache store, or false if one cannot be created. 305 * 306 * @param cache_definition $definition 307 * @return cache_store 308 */ 309 public static function initialise_test_instance(cache_definition $definition) { 310 $testperformance = get_config('cachestore_apcu', 'testperformance'); 311 if (empty($testperformance)) { 312 return false; 313 } 314 if (!self::are_requirements_met()) { 315 return false; 316 } 317 $name = 'APCu test'; 318 $cache = new cachestore_apcu($name); 319 // No need to check if is_ready() as this has already being done by requirement check. 320 $cache->initialise($definition); 321 return $cache; 322 } 323 324 /** 325 * Test is a cache has a key. 326 * 327 * @param string|int $key 328 * @return bool True if the cache has the requested key, false otherwise. 329 */ 330 public function has($key) { 331 $key = $this->prepare_key($key); 332 return apcu_exists($key); 333 } 334 335 /** 336 * Test if a cache has at least one of the given keys. 337 * 338 * @param array $keys 339 * @return bool True if the cache has at least one of the given keys 340 */ 341 public function has_any(array $keys) { 342 foreach ($keys as $arraykey => $key) { 343 $keys[$arraykey] = $this->prepare_key($key); 344 } 345 $result = apcu_exists($keys); 346 return count($result) > 0; 347 } 348 349 /** 350 * Test is a cache has all of the given keys. 351 * 352 * @param array $keys 353 * @return bool True if the cache has all of the given keys, false otherwise. 354 */ 355 public function has_all(array $keys) { 356 foreach ($keys as $arraykey => $key) { 357 $keys[$arraykey] = $this->prepare_key($key); 358 } 359 $result = apcu_exists($keys); 360 return count($result) === count($keys); 361 } 362 363 /** 364 * Generates the appropriate configuration required for unit testing. 365 * 366 * @return array Array of unit test configuration data to be used by initialise(). 367 */ 368 public static function unit_test_configuration() { 369 return array('prefix' => 'phpunit'); 370 } 371 372 /** 373 * Given the data from the add instance form this function creates a configuration array. 374 * 375 * @param stdClass $data 376 * @return array 377 */ 378 public static function config_get_configuration_array($data) { 379 $config = array(); 380 381 if (isset($data->prefix)) { 382 $config['prefix'] = $data->prefix; 383 } 384 return $config; 385 } 386 /** 387 * Allows the cache store to set its data against the edit form before it is shown to the user. 388 * 389 * @param moodleform $editform 390 * @param array $config 391 */ 392 public static function config_set_edit_form_data(moodleform $editform, array $config) { 393 if (isset($config['prefix'])) { 394 $data['prefix'] = $config['prefix']; 395 } else { 396 $data['prefix'] = ''; 397 } 398 $editform->set_data($data); 399 } 400 401 /** 402 * Returns true if this cache store instance is both suitable for testing, and ready for testing. 403 * 404 * Cache stores that support being used as the default store for unit and acceptance testing should 405 * override this function and return true if there requirements have been met. 406 * 407 * @return bool 408 */ 409 public static function ready_to_be_used_for_testing() { 410 return true; 411 } 412 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body