5 * @copyright 2012-2019 Leaf Corcoran
7 * @license http://opensource.org/licenses/MIT MIT
9 * @link http://scssphp.github.io/scssphp
12 namespace ScssPhp\ScssPhp
;
17 * The scss cache manager.
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
24 * The cache manager is agnostic about data format and only the operation is expected to be described by string
31 * @author Cedric Morin
35 const CACHE_VERSION
= 1;
37 // directory used for storing data
38 public static $cacheDir = false;
40 // prefix for the storing data
41 public static $prefix = 'scssphp_';
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;
46 // specifies the number of seconds after which data cached will be seen as 'garbage' and potentially cleaned up
47 public static $gcLifetime = 604800;
49 // array of already refreshed cache if $forceRefresh==='once'
50 protected static $refreshed = [];
55 * @param array $options
57 public function __construct($options)
60 if (isset($options['cache_dir'])) {
61 self
::$cacheDir = $options['cache_dir'];
64 if (empty(self
::$cacheDir)) {
65 throw new Exception('cache_dir not set');
68 if (isset($options['prefix'])) {
69 self
::$prefix = $options['prefix'];
72 if (empty(self
::$prefix)) {
73 throw new Exception('prefix not set');
76 if (isset($options['forceRefresh'])) {
77 self
::$forceRefresh = $options['force_refresh'];
80 self
::checkCacheDir();
84 * Get the cached result of $operation on $what,
85 * which is known as dependant from the content of $options
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
96 public function getCache($operation, $what, $options = [], $lastModified = null)
98 $fileCache = self
::$cacheDir . self
::cacheName($operation, $what, $options);
100 if ((! self
::$forceRefresh ||
(self
::$forceRefresh === 'once' &&
101 isset(self
::$refreshed[$fileCache]))) && file_exists($fileCache)
103 $cacheTime = filemtime($fileCache);
105 if ((is_null($lastModified) ||
$cacheTime > $lastModified) &&
106 $cacheTime + self
::$gcLifetime > time()
108 $c = file_get_contents($fileCache);
109 $c = unserialize($c);
111 if (is_array($c) && isset($c['value'])) {
121 * Put in cache the result of $operation on $what,
122 * which is known as dependant from the content of $options
124 * @param string $operation
126 * @param mixed $value
127 * @param array $options
129 public function setCache($operation, $what, $value, $options = [])
131 $fileCache = self
::$cacheDir . self
::cacheName($operation, $what, $options);
133 $c = ['value' => $value];
135 file_put_contents($fileCache, $c);
137 if (self
::$forceRefresh === 'once') {
138 self
::$refreshed[$fileCache] = true;
143 * Get the cache name for the caching of $operation on $what,
144 * which is known as dependant from the content of $options
146 * @param string $operation
148 * @param array $options
152 private static function cacheName($operation, $what, $options = [])
155 'version' => self
::CACHE_VERSION
,
156 'operation' => $operation,
158 'options' => $options
162 . sha1(json_encode($t))
170 * Check that the cache dir exists and is writeable
174 public static function checkCacheDir()
176 self
::$cacheDir = str_replace('\\', '/', self
::$cacheDir);
177 self
::$cacheDir = rtrim(self
::$cacheDir, '/') . '/';
179 if (! file_exists(self
::$cacheDir)) {
180 if (! mkdir(self
::$cacheDir)) {
181 throw new Exception('Cache directory couldn\'t be created: ' . self
::$cacheDir);
183 } elseif (! is_dir(self
::$cacheDir)) {
184 throw new Exception('Cache directory doesn\'t exist: ' . self
::$cacheDir);
185 } elseif (! is_writable(self
::$cacheDir)) {
186 throw new Exception('Cache directory isn\'t writable: ' . self
::$cacheDir);
191 * Delete unused cached files
193 public static function cleanCache()
195 static $clean = false;
197 if ($clean ||
empty(self
::$cacheDir)) {
203 // only remove files with extensions created by SCSSPHP Cache
204 // css files removed based on the list files
205 $removeTypes = ['scsscache' => 1];
207 $files = scandir(self
::$cacheDir);
213 $checkTime = time() - self
::$gcLifetime;
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) {
221 $parts = explode('.', $file);
222 $type = array_pop($parts);
224 if (! isset($removeTypes[$type])) {
228 $fullPath = self
::$cacheDir . $file;
229 $mtime = filemtime($fullPath);
231 // don't delete if it's a relatively new file
232 if ($mtime > $checkTime) {