See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]
1 <?php 2 /** 3 * SCSSPHP 4 * 5 * @copyright 2012-2019 Leaf Corcoran 6 * 7 * @license http://opensource.org/licenses/MIT MIT 8 * 9 * @link http://scssphp.github.io/scssphp 10 */ 11 12 namespace ScssPhp\ScssPhp; 13 14 use Exception; 15 16 /** 17 * The scss cache manager. 18 * 19 * In short: 20 * 21 * allow to put in cache/get from cache a generic result from a known operation on a generic dataset, 22 * taking in account options that affects the result 23 * 24 * The cache manager is agnostic about data format and only the operation is expected to be described by string 25 * 26 */ 27 28 /** 29 * SCSS cache 30 * 31 * @author Cedric Morin 32 */ 33 class Cache 34 { 35 const CACHE_VERSION = 1; 36 37 // directory used for storing data 38 public static $cacheDir = false; 39 40 // prefix for the storing data 41 public static $prefix = 'scssphp_'; 42 43 // force a refresh : 'once' for refreshing the first hit on a cache only, true to never use the cache in this hit 44 public static $forceRefresh = false; 45 46 // specifies the number of seconds after which data cached will be seen as 'garbage' and potentially cleaned up 47 public static $gcLifetime = 604800; 48 49 // array of already refreshed cache if $forceRefresh==='once' 50 protected static $refreshed = []; 51 52 /** 53 * Constructor 54 * 55 * @param array $options 56 */ 57 public function __construct($options) 58 { 59 // check $cacheDir 60 if (isset($options['cacheDir'])) { 61 self::$cacheDir = $options['cacheDir']; 62 } 63 64 if (empty(self::$cacheDir)) { 65 throw new Exception('cacheDir not set'); 66 } 67 68 if (isset($options['prefix'])) { 69 self::$prefix = $options['prefix']; 70 } 71 72 if (empty(self::$prefix)) { 73 throw new Exception('prefix not set'); 74 } 75 76 if (isset($options['forceRefresh'])) { 77 self::$forceRefresh = $options['forceRefresh']; 78 } 79 80 self::checkCacheDir(); 81 } 82 83 /** 84 * Get the cached result of $operation on $what, 85 * which is known as dependant from the content of $options 86 * 87 * @param string $operation parse, compile... 88 * @param mixed $what content key (e.g., filename to be treated) 89 * @param array $options any option that affect the operation result on the content 90 * @param integer $lastModified last modified timestamp 91 * 92 * @return mixed 93 * 94 * @throws \Exception 95 */ 96 public function getCache($operation, $what, $options = [], $lastModified = null) 97 { 98 $fileCache = self::$cacheDir . self::cacheName($operation, $what, $options); 99 100 if (((self::$forceRefresh === false) || (self::$forceRefresh === 'once' && 101 isset(self::$refreshed[$fileCache]))) && file_exists($fileCache) 102 ) { 103 $cacheTime = filemtime($fileCache); 104 105 if ((is_null($lastModified) || $cacheTime > $lastModified) && 106 $cacheTime + self::$gcLifetime > time() 107 ) { 108 $c = file_get_contents($fileCache); 109 $c = unserialize($c); 110 111 if (is_array($c) && isset($c['value'])) { 112 return $c['value']; 113 } 114 } 115 } 116 117 return null; 118 } 119 120 /** 121 * Put in cache the result of $operation on $what, 122 * which is known as dependant from the content of $options 123 * 124 * @param string $operation 125 * @param mixed $what 126 * @param mixed $value 127 * @param array $options 128 */ 129 public function setCache($operation, $what, $value, $options = []) 130 { 131 $fileCache = self::$cacheDir . self::cacheName($operation, $what, $options); 132 133 $c = ['value' => $value]; 134 $c = serialize($c); 135 file_put_contents($fileCache, $c); 136 137 if (self::$forceRefresh === 'once') { 138 self::$refreshed[$fileCache] = true; 139 } 140 } 141 142 /** 143 * Get the cache name for the caching of $operation on $what, 144 * which is known as dependant from the content of $options 145 * 146 * @param string $operation 147 * @param mixed $what 148 * @param array $options 149 * 150 * @return string 151 */ 152 private static function cacheName($operation, $what, $options = []) 153 { 154 $t = [ 155 'version' => self::CACHE_VERSION, 156 'operation' => $operation, 157 'what' => $what, 158 'options' => $options 159 ]; 160 161 $t = self::$prefix 162 . sha1(json_encode($t)) 163 . ".$operation" 164 . ".scsscache"; 165 166 return $t; 167 } 168 169 /** 170 * Check that the cache dir exists and is writeable 171 * 172 * @throws \Exception 173 */ 174 public static function checkCacheDir() 175 { 176 self::$cacheDir = str_replace('\\', '/', self::$cacheDir); 177 self::$cacheDir = rtrim(self::$cacheDir, '/') . '/'; 178 179 if (! is_dir(self::$cacheDir)) { 180 if (! mkdir(self::$cacheDir)) { 181 throw new Exception('Cache directory couldn\'t be created: ' . self::$cacheDir); 182 } 183 } 184 185 if (! is_writable(self::$cacheDir)) { 186 throw new Exception('Cache directory isn\'t writable: ' . self::$cacheDir); 187 } 188 } 189 190 /** 191 * Delete unused cached files 192 */ 193 public static function cleanCache() 194 { 195 static $clean = false; 196 197 if ($clean || empty(self::$cacheDir)) { 198 return; 199 } 200 201 $clean = true; 202 203 // only remove files with extensions created by SCSSPHP Cache 204 // css files removed based on the list files 205 $removeTypes = ['scsscache' => 1]; 206 207 $files = scandir(self::$cacheDir); 208 209 if (! $files) { 210 return; 211 } 212 213 $checkTime = time() - self::$gcLifetime; 214 215 foreach ($files as $file) { 216 // don't delete if the file wasn't created with SCSSPHP Cache 217 if (strpos($file, self::$prefix) !== 0) { 218 continue; 219 } 220 221 $parts = explode('.', $file); 222 $type = array_pop($parts); 223 224 if (! isset($removeTypes[$type])) { 225 continue; 226 } 227 228 $fullPath = self::$cacheDir . $file; 229 $mtime = filemtime($fullPath); 230 231 // don't delete if it's a relatively new file 232 if ($mtime > $checkTime) { 233 continue; 234 } 235 236 unlink($fullPath); 237 } 238 } 239 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body