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 namespace core\local\guzzle; 18 19 /** 20 * Class to handle and generates CacheItemPoolInterface objects. 21 * 22 * This class will handle save, delete, cleanup etc. for the cache item. 23 * For individual cache objects, this class will rely on {@cache_item} class. 24 * 25 * @package core 26 * @copyright 2022 Safat Shahin <safat.shahin@moodle.com> 27 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 28 */ 29 class cache_handler { 30 31 /** 32 * This array will have the kay and value for that key. 33 * Mainly individual data will be handled by {@cache_item} class for each array element. 34 * 35 * @var array $items cached items or the items currently in use by the cache pool. 36 */ 37 private array $items; 38 39 /** 40 * This array will have the cache items which might need to persisted later. 41 * It will not save the items in the cache pool using cache_item class until the commit is done for these elements. 42 * 43 * @var array $deferreditems cache items to be persisted later. 44 */ 45 private array $deferreditems; 46 47 /** @var string module name. */ 48 private string $module; 49 50 /** @var string the directory for cache. */ 51 private string $dir; 52 53 /** 54 * Constructor for class cache_handler. 55 * This class will accept the module which will determine the location of cached files. 56 * 57 * @param string $module module string for cache directory. 58 */ 59 public function __construct(string $module = 'repository') { 60 global $CFG; 61 $this->module = $module; 62 63 // Set the directory for cache. 64 $this->dir = $CFG->cachedir . '/' . $module . '/'; 65 if (!file_exists($this->dir) && !mkdir($concurrentdirectory = $this->dir, $CFG->directorypermissions, true) && 66 !is_dir($concurrentdirectory)) { 67 throw new \moodle_exception(sprintf('Directory "%s" was not created', $concurrentdirectory)); 68 } 69 70 } 71 72 /** 73 * Returns a Cache Item representing the specified key. 74 * 75 * This method must always return a CacheItemInterface object, even in case of 76 * a cache miss. It MUST NOT return null. 77 * 78 * @param string $key The key for which to return the corresponding Cache Item.. 79 * @param int|null $ttl Number of seconds for the cache item to live. 80 * @return cache_item The corresponding Cache Item. 81 */ 82 public function get_item(string$key, ?int $ttl = null): cache_item { 83 return new cache_item($key, $this->module, $ttl); 84 } 85 86 /** 87 * Returns a traversable set of cache items. 88 * 89 * @param string[] $keys An indexed array of keys of items to retrieve. 90 * @return iterable 91 * An iterable collection of Cache Items keyed by the cache keys of 92 * each item. A Cache item will be returned for each key, even if that 93 * key is not found. However, if no keys are specified then an empty 94 * traversable MUST be returned instead. 95 */ 96 public function get_items(array $keys = []): iterable { 97 $items = []; 98 99 foreach ($keys as $key) { 100 $items[$key] = $this->has_item($key) ? clone $this->items[$key] : $this->get_item($key); 101 } 102 103 return $items; 104 } 105 106 /** 107 * Confirms if the cache contains specified cache item. 108 * 109 * Note: This method MAY avoid retrieving the cached value for performance reasons. 110 * This could result in a race condition with CacheItemInterface::get(). To avoid 111 * such situation use CacheItemInterface::isHit() instead. 112 * 113 * @param string $key The key for which to check existence. 114 * @return bool True if item exists in the cache, false otherwise. 115 */ 116 public function has_item($key): bool { 117 $this->assert_key_is_valid($key); 118 119 return isset($this->items[$key]) && $this->items[$key]->isHit(); 120 } 121 122 /** 123 * Deletes all items in the pool. 124 * 125 * @return bool True if the pool was successfully cleared. False if there was an error. 126 */ 127 public function clear(): bool { 128 global $USER; 129 130 if (isset($this->items)) { 131 foreach ($this->items as $key => $item) { 132 // Delete cache file. 133 if ($dir = opendir($this->dir)) { 134 $filename = 'u' . $USER->id . '_' . md5(serialize($key)); 135 $filename = $dir . $filename; 136 if (file_exists($filename) && $this->items[$key]->isHit()) { 137 @unlink($filename); 138 } 139 closedir($dir); 140 } 141 } 142 } 143 144 $this->items = []; 145 $this->deferreditems = []; 146 147 return true; 148 } 149 150 /** 151 * Refreshes all items in the pool. 152 * 153 * @param int $ttl Seconds to live. 154 * @return void 155 */ 156 public function refresh(int $ttl): void { 157 if ($dir = opendir($this->dir)) { 158 while (false !== ($file = readdir($dir))) { 159 if (!is_dir($file) && $file !== '.' && $file !== '..') { 160 $lasttime = @filemtime($this->dir . $file); 161 if (time() - $lasttime > $ttl) { 162 mtrace($this->dir . $file); 163 @unlink($this->dir . $file); 164 } 165 } 166 } 167 closedir($dir); 168 } 169 } 170 171 /** 172 * Removes the item from the pool. 173 * 174 * @param string $key The key to delete. 175 * @return bool True if the item was successfully removed. False if there was an error. 176 */ 177 public function delete_item(string $key): bool { 178 return $this->delete_items([$key]); 179 } 180 181 /** 182 * Removes multiple items from the pool. 183 * 184 * @param string[] $keys An array of keys that should be removed from the pool. 185 * @return bool True if the items were successfully removed. False if there was an error. 186 */ 187 public function delete_items(array $keys): bool { 188 global $USER; 189 array_walk($keys, [$this, 'assert_key_is_valid']); 190 191 foreach ($keys as $key) { 192 // Delete cache file. 193 if ($dir = opendir($this->dir)) { 194 $filename = 'u' . $USER->id . '_' . md5(serialize($key)); 195 $filename = $dir . $filename; 196 if (file_exists($filename)) { 197 @unlink($filename); 198 } 199 } 200 201 unset($this->items[$key]); 202 } 203 204 return true; 205 } 206 207 /** 208 * Persists a cache item immediately. 209 * 210 * @param cache_item $item The cache item to save. 211 * @return bool True if the item was successfully persisted. False if there was an error. 212 */ 213 public function save(cache_item $item): bool { 214 global $CFG, $USER; 215 $key = $item->get_key(); 216 217 // File and directory setup. 218 $filename = 'u' . $USER->id . '_' . md5(serialize($key)); 219 $fp = fopen($this->dir . $filename, 'wb'); 220 221 // Store the item. 222 fwrite($fp, serialize($item->get())); 223 fclose($fp); 224 @chmod($this->dir . $filename, $CFG->filepermissions); 225 226 $this->items[$key] = $item; 227 228 return true; 229 } 230 231 /** 232 * Sets a cache item to be persisted later. 233 * 234 * @param cache_item $item The cache item to save. 235 * @return bool False if the item could not be queued or if a commit was attempted and failed. True otherwise. 236 */ 237 public function save_deferred(cache_item $item): bool { 238 $this->deferreditems[$item->get_key()] = $item; 239 240 return true; 241 } 242 243 /** 244 * Persists any deferred cache items. 245 * 246 * @return bool True if all not-yet-saved items were successfully saved or there were none. False otherwise. 247 */ 248 public function commit(): bool { 249 foreach ($this->deferreditems as $item) { 250 $this->save($item); 251 } 252 253 $this->deferreditems = []; 254 255 return true; 256 } 257 258 /** 259 * Asserts that the given key is valid. 260 * Some simple validation to make sure the passed key is a valid one. 261 * 262 * @param string $key The key to validate. 263 */ 264 private function assert_key_is_valid(string $key): void { 265 $invalidcharacters = '{}()/\\\\@:'; 266 267 if (!is_string($key) || preg_match("#[$invalidcharacters]#", $key)) { 268 throw new \moodle_exception('Invalid cache key'); 269 } 270 } 271 272 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body