Passed
Push — master ( c2c077...245696 )
by Morris
12:32 queued 11s
created
lib/private/Template/SCSSCacher.php 2 patches
Indentation   +474 added lines, -474 removed lines patch added patch discarded remove patch
@@ -51,478 +51,478 @@
 block discarded – undo
51 51
 
52 52
 class SCSSCacher {
53 53
 
54
-	/** @var ILogger */
55
-	protected $logger;
56
-
57
-	/** @var IAppData */
58
-	protected $appData;
59
-
60
-	/** @var IURLGenerator */
61
-	protected $urlGenerator;
62
-
63
-	/** @var IConfig */
64
-	protected $config;
65
-
66
-	/** @var \OC_Defaults */
67
-	private $defaults;
68
-
69
-	/** @var string */
70
-	protected $serverRoot;
71
-
72
-	/** @var ICache */
73
-	protected $depsCache;
74
-
75
-	/** @var null|string */
76
-	private $injectedVariables;
77
-
78
-	/** @var ICacheFactory */
79
-	private $cacheFactory;
80
-
81
-	/** @var IconsCacher */
82
-	private $iconsCacher;
83
-
84
-	/** @var ICache */
85
-	private $isCachedCache;
86
-
87
-	/** @var ITimeFactory */
88
-	private $timeFactory;
89
-
90
-	/** @var IMemcache */
91
-	private $lockingCache;
92
-
93
-	/**
94
-	 * @param ILogger $logger
95
-	 * @param Factory $appDataFactory
96
-	 * @param IURLGenerator $urlGenerator
97
-	 * @param IConfig $config
98
-	 * @param \OC_Defaults $defaults
99
-	 * @param string $serverRoot
100
-	 * @param ICacheFactory $cacheFactory
101
-	 * @param IconsCacher $iconsCacher
102
-	 * @param ITimeFactory $timeFactory
103
-	 */
104
-	public function __construct(ILogger $logger,
105
-								Factory $appDataFactory,
106
-								IURLGenerator $urlGenerator,
107
-								IConfig $config,
108
-								\OC_Defaults $defaults,
109
-								$serverRoot,
110
-								ICacheFactory $cacheFactory,
111
-								IconsCacher $iconsCacher,
112
-								ITimeFactory $timeFactory) {
113
-		$this->logger = $logger;
114
-		$this->appData = $appDataFactory->get('css');
115
-		$this->urlGenerator = $urlGenerator;
116
-		$this->config = $config;
117
-		$this->defaults = $defaults;
118
-		$this->serverRoot = $serverRoot;
119
-		$this->cacheFactory = $cacheFactory;
120
-		$this->depsCache = $cacheFactory->createDistributed('SCSS-deps-' . md5($this->urlGenerator->getBaseUrl()));
121
-		$this->isCachedCache = $cacheFactory->createDistributed('SCSS-cached-' . md5($this->urlGenerator->getBaseUrl()));
122
-		$lockingCache = $cacheFactory->createDistributed('SCSS-locks-' . md5($this->urlGenerator->getBaseUrl()));
123
-		if (!($lockingCache instanceof IMemcache)) {
124
-			$lockingCache = new NullCache();
125
-		}
126
-		$this->lockingCache = $lockingCache;
127
-		$this->iconsCacher = $iconsCacher;
128
-		$this->timeFactory = $timeFactory;
129
-	}
130
-
131
-	/**
132
-	 * Process the caching process if needed
133
-	 *
134
-	 * @param string $root Root path to the nextcloud installation
135
-	 * @param string $file
136
-	 * @param string $app The app name
137
-	 * @return boolean
138
-	 * @throws NotPermittedException
139
-	 */
140
-	public function process(string $root, string $file, string $app): bool {
141
-		$path = explode('/', $root . '/' . $file);
142
-
143
-		$fileNameSCSS = array_pop($path);
144
-		$fileNameCSS = $this->prependVersionPrefix($this->prependBaseurlPrefix(str_replace('.scss', '.css', $fileNameSCSS)), $app);
145
-
146
-		$path = implode('/', $path);
147
-		$webDir = $this->getWebDir($path, $app, $this->serverRoot, \OC::$WEBROOT);
148
-
149
-		$this->logger->debug('SCSSCacher::process ordinary check follows', ['app' => 'scss_cacher']);
150
-		if (!$this->variablesChanged() && $this->isCached($fileNameCSS, $app)) {
151
-			// Inject icons vars css if any
152
-			return $this->injectCssVariablesIfAny();
153
-		}
154
-
155
-		try {
156
-			$folder = $this->appData->getFolder($app);
157
-		} catch (NotFoundException $e) {
158
-			// creating css appdata folder
159
-			$folder = $this->appData->newFolder($app);
160
-		}
161
-
162
-		$lockKey = $webDir . '/' . $fileNameSCSS;
163
-
164
-		if (!$this->lockingCache->add($lockKey, 'locked!', 120)) {
165
-			$this->logger->debug('SCSSCacher::process could not get lock for ' . $lockKey . ' and will wait 10 seconds for cached file to be available', ['app' => 'scss_cacher']);
166
-			$retry = 0;
167
-			sleep(1);
168
-			while ($retry < 10) {
169
-				$this->logger->debug('SCSSCacher::process check in while loop follows', ['app' => 'scss_cacher']);
170
-				if (!$this->variablesChanged() && $this->isCached($fileNameCSS, $app)) {
171
-					// Inject icons vars css if any
172
-					$this->lockingCache->remove($lockKey);
173
-					$this->logger->debug("SCSSCacher::process cached file for app '$app' and file '$fileNameCSS' is now available after $retry s. Moving on...", ['app' => 'scss_cacher']);
174
-					return $this->injectCssVariablesIfAny();
175
-				}
176
-				sleep(1);
177
-				$retry++;
178
-			}
179
-			$this->logger->debug('SCSSCacher::process Giving up scss caching for ' . $lockKey, ['app' => 'scss_cacher']);
180
-			return false;
181
-		}
182
-
183
-		$this->logger->debug('SCSSCacher::process Lock acquired for ' . $lockKey, ['app' => 'scss_cacher']);
184
-		try {
185
-			$cached = $this->cache($path, $fileNameCSS, $fileNameSCSS, $folder, $webDir);
186
-		} catch (\Exception $e) {
187
-			$this->lockingCache->remove($lockKey);
188
-			throw $e;
189
-		}
190
-
191
-		// Cleaning lock
192
-		$this->lockingCache->remove($lockKey);
193
-		$this->logger->debug('SCSSCacher::process Lock removed for ' . $lockKey, ['app' => 'scss_cacher']);
194
-
195
-		// Inject icons vars css if any
196
-		if ($this->iconsCacher->getCachedCSS() && $this->iconsCacher->getCachedCSS()->getSize() > 0) {
197
-			$this->iconsCacher->injectCss();
198
-		}
199
-
200
-		return $cached;
201
-	}
202
-
203
-	/**
204
-	 * @param $appName
205
-	 * @param $fileName
206
-	 * @return ISimpleFile
207
-	 */
208
-	public function getCachedCSS(string $appName, string $fileName): ISimpleFile {
209
-		$folder = $this->appData->getFolder($appName);
210
-		$cachedFileName = $this->prependVersionPrefix($this->prependBaseurlPrefix($fileName), $appName);
211
-
212
-		return $folder->getFile($cachedFileName);
213
-	}
214
-
215
-	/**
216
-	 * Check if the file is cached or not
217
-	 * @param string $fileNameCSS
218
-	 * @param string $app
219
-	 * @return boolean
220
-	 */
221
-	private function isCached(string $fileNameCSS, string $app) {
222
-		$key = $this->config->getSystemValue('version') . '/' . $app . '/' . $fileNameCSS;
223
-
224
-		// If the file mtime is more recent than our cached one,
225
-		// let's consider the file is properly cached
226
-		if ($cacheValue = $this->isCachedCache->get($key)) {
227
-			if ($cacheValue > $this->timeFactory->getTime()) {
228
-				return true;
229
-			}
230
-		}
231
-		$this->logger->debug("SCSSCacher::isCached $fileNameCSS isCachedCache is expired or unset", ['app' => 'scss_cacher']);
232
-
233
-		// Creating file cache if none for further checks
234
-		try {
235
-			$folder = $this->appData->getFolder($app);
236
-		} catch (NotFoundException $e) {
237
-			$this->logger->debug("SCSSCacher::isCached app data folder for $app could not be fetched", ['app' => 'scss_cacher']);
238
-			return false;
239
-		}
240
-
241
-		// Checking if file size is coherent
242
-		// and if one of the css dependency changed
243
-		try {
244
-			$cachedFile = $folder->getFile($fileNameCSS);
245
-			if ($cachedFile->getSize() > 0) {
246
-				$depFileName = $fileNameCSS . '.deps';
247
-				$deps = $this->depsCache->get($folder->getName() . '-' . $depFileName);
248
-				if ($deps === null) {
249
-					$depFile = $folder->getFile($depFileName);
250
-					$deps = $depFile->getContent();
251
-					// Set to memcache for next run
252
-					$this->depsCache->set($folder->getName() . '-' . $depFileName, $deps);
253
-				}
254
-				$deps = json_decode($deps, true);
255
-
256
-				foreach ((array) $deps as $file => $mtime) {
257
-					if (!file_exists($file) || filemtime($file) > $mtime) {
258
-						$this->logger->debug("SCSSCacher::isCached $fileNameCSS is not considered as cached due to deps file $file", ['app' => 'scss_cacher']);
259
-						return false;
260
-					}
261
-				}
262
-
263
-				$this->logger->debug("SCSSCacher::isCached $fileNameCSS dependencies successfully cached for 5 minutes", ['app' => 'scss_cacher']);
264
-				// It would probably make sense to adjust this timeout to something higher and see if that has some effect then
265
-				$this->isCachedCache->set($key, $this->timeFactory->getTime() + 5 * 60);
266
-				return true;
267
-			}
268
-			$this->logger->debug("SCSSCacher::isCached $fileNameCSS is not considered as cached cacheValue: $cacheValue", ['app' => 'scss_cacher']);
269
-			return false;
270
-		} catch (NotFoundException $e) {
271
-			$this->logger->debug("SCSSCacher::isCached NotFoundException " . $e->getMessage(), ['app' => 'scss_cacher']);
272
-			return false;
273
-		}
274
-	}
275
-
276
-	/**
277
-	 * Check if the variables file has changed
278
-	 * @return bool
279
-	 */
280
-	private function variablesChanged(): bool {
281
-		$injectedVariables = $this->getInjectedVariables();
282
-		if ($this->config->getAppValue('core', 'theming.variables') !== md5($injectedVariables)) {
283
-			$this->logger->debug('SCSSCacher::variablesChanged storedVariables: ' . json_encode($this->config->getAppValue('core', 'theming.variables')) . ' currentInjectedVariables: ' . json_encode($injectedVariables), ['app' => 'scss_cacher']);
284
-			$this->config->setAppValue('core', 'theming.variables', md5($injectedVariables));
285
-			$this->resetCache();
286
-			return true;
287
-		}
288
-		return false;
289
-	}
290
-
291
-	/**
292
-	 * Cache the file with AppData
293
-	 *
294
-	 * @param string $path
295
-	 * @param string $fileNameCSS
296
-	 * @param string $fileNameSCSS
297
-	 * @param ISimpleFolder $folder
298
-	 * @param string $webDir
299
-	 * @return boolean
300
-	 * @throws NotPermittedException
301
-	 */
302
-	private function cache(string $path, string $fileNameCSS, string $fileNameSCSS, ISimpleFolder $folder, string $webDir) {
303
-		$scss = new Compiler();
304
-		$scss->setImportPaths([
305
-			$path,
306
-			$this->serverRoot . '/core/css/'
307
-		]);
308
-
309
-		// Continue after throw
310
-		$scss->setIgnoreErrors(true);
311
-		if ($this->config->getSystemValue('debug')) {
312
-			// Debug mode
313
-			$scss->setFormatter(Expanded::class);
314
-			$scss->setLineNumberStyle(Compiler::LINE_COMMENTS);
315
-		} else {
316
-			// Compression
317
-			$scss->setFormatter(Crunched::class);
318
-		}
319
-
320
-		try {
321
-			$cachedfile = $folder->getFile($fileNameCSS);
322
-		} catch (NotFoundException $e) {
323
-			$cachedfile = $folder->newFile($fileNameCSS);
324
-		}
325
-
326
-		$depFileName = $fileNameCSS . '.deps';
327
-		try {
328
-			$depFile = $folder->getFile($depFileName);
329
-		} catch (NotFoundException $e) {
330
-			$depFile = $folder->newFile($depFileName);
331
-		}
332
-
333
-		// Compile
334
-		try {
335
-			$compiledScss = $scss->compile(
336
-				'$webroot: \'' . $this->getRoutePrefix() . '\';' .
337
-				$this->getInjectedVariables() .
338
-				'@import "variables.scss";' .
339
-				'@import "functions.scss";' .
340
-				'@import "' . $fileNameSCSS . '";');
341
-		} catch (ParserException $e) {
342
-			$this->logger->logException($e, ['app' => 'scss_cacher']);
343
-
344
-			return false;
345
-		}
346
-
347
-		// Parse Icons and create related css variables
348
-		$compiledScss = $this->iconsCacher->setIconsCss($compiledScss);
349
-
350
-		// Gzip file
351
-		try {
352
-			$gzipFile = $folder->getFile($fileNameCSS . '.gzip'); # Safari doesn't like .gz
353
-		} catch (NotFoundException $e) {
354
-			$gzipFile = $folder->newFile($fileNameCSS . '.gzip'); # Safari doesn't like .gz
355
-		}
356
-
357
-		try {
358
-			$data = $this->rebaseUrls($compiledScss, $webDir);
359
-			$cachedfile->putContent($data);
360
-			$deps = json_encode($scss->getParsedFiles());
361
-			$depFile->putContent($deps);
362
-			$this->depsCache->set($folder->getName() . '-' . $depFileName, $deps);
363
-			$gzipFile->putContent(gzencode($data, 9));
364
-			$this->logger->debug('SCSSCacher::cache ' . $webDir . '/' . $fileNameSCSS . ' compiled and successfully cached', ['app' => 'scss_cacher']);
365
-
366
-			return true;
367
-		} catch (NotPermittedException $e) {
368
-			$this->logger->error('SCSSCacher::cache unable to cache: ' . $fileNameSCSS, ['app' => 'scss_cacher']);
369
-
370
-			return false;
371
-		}
372
-	}
373
-
374
-	/**
375
-	 * Reset scss cache by deleting all generated css files
376
-	 * We need to regenerate all files when variables change
377
-	 */
378
-	public function resetCache() {
379
-		$this->logger->debug('SCSSCacher::resetCache', ['app' => 'scss_cacher']);
380
-		if (!$this->lockingCache->add('resetCache', 'locked!', 120)) {
381
-			$this->logger->debug('SCSSCacher::resetCache Locked', ['app' => 'scss_cacher']);
382
-			return;
383
-		}
384
-		$this->logger->debug('SCSSCacher::resetCache Lock acquired', ['app' => 'scss_cacher']);
385
-		$this->injectedVariables = null;
386
-
387
-		// do not clear locks
388
-		$this->cacheFactory->createDistributed('SCSS-deps-')->clear();
389
-		$this->cacheFactory->createDistributed('SCSS-cached-')->clear();
390
-
391
-		$appDirectory = $this->appData->getDirectoryListing();
392
-		foreach ($appDirectory as $folder) {
393
-			foreach ($folder->getDirectoryListing() as $file) {
394
-				try {
395
-					$file->delete();
396
-				} catch (NotPermittedException $e) {
397
-					$this->logger->logException($e, ['message' => 'SCSSCacher::resetCache unable to delete file: ' . $file->getName(), 'app' => 'scss_cacher']);
398
-				}
399
-			}
400
-		}
401
-		$this->logger->debug('SCSSCacher::resetCache css cache cleared!', ['app' => 'scss_cacher']);
402
-		$this->lockingCache->remove('resetCache');
403
-		$this->logger->debug('SCSSCacher::resetCache Locking removed', ['app' => 'scss_cacher']);
404
-	}
405
-
406
-	/**
407
-	 * @return string SCSS code for variables from OC_Defaults
408
-	 */
409
-	private function getInjectedVariables(): string {
410
-		if ($this->injectedVariables !== null) {
411
-			return $this->injectedVariables;
412
-		}
413
-		$variables = '';
414
-		foreach ($this->defaults->getScssVariables() as $key => $value) {
415
-			$variables .= '$' . $key . ': ' . $value . ' !default;';
416
-		}
417
-
418
-		// check for valid variables / otherwise fall back to defaults
419
-		try {
420
-			$scss = new Compiler();
421
-			$scss->compile($variables);
422
-			$this->injectedVariables = $variables;
423
-		} catch (ParserException $e) {
424
-			$this->logger->logException($e, ['app' => 'scss_cacher']);
425
-		}
426
-
427
-		return $variables;
428
-	}
429
-
430
-	/**
431
-	 * Add the correct uri prefix to make uri valid again
432
-	 * @param string $css
433
-	 * @param string $webDir
434
-	 * @return string
435
-	 */
436
-	private function rebaseUrls(string $css, string $webDir): string {
437
-		$re = '/url\([\'"]([^\/][\.\w?=\/-]*)[\'"]\)/x';
438
-		$subst = 'url(\'' . $webDir . '/$1\')';
439
-
440
-		return preg_replace($re, $subst, $css);
441
-	}
442
-
443
-	/**
444
-	 * Return the cached css file uri
445
-	 * @param string $appName the app name
446
-	 * @param string $fileName
447
-	 * @return string
448
-	 */
449
-	public function getCachedSCSS(string $appName, string $fileName): string {
450
-		$tmpfileLoc = explode('/', $fileName);
451
-		$fileName = array_pop($tmpfileLoc);
452
-		$fileName = $this->prependVersionPrefix($this->prependBaseurlPrefix(str_replace('.scss', '.css', $fileName)), $appName);
453
-
454
-		return substr($this->urlGenerator->linkToRoute('core.Css.getCss', [
455
-			'fileName' => $fileName,
456
-			'appName' => $appName,
457
-			'v' => $this->config->getAppValue('core', 'theming.variables', '0')
458
-		]), \strlen(\OC::$WEBROOT) + 1);
459
-	}
460
-
461
-	/**
462
-	 * Prepend hashed base url to the css file
463
-	 * @param string $cssFile
464
-	 * @return string
465
-	 */
466
-	private function prependBaseurlPrefix(string $cssFile): string {
467
-		return substr(md5($this->urlGenerator->getBaseUrl() . $this->getRoutePrefix()), 0, 4) . '-' . $cssFile;
468
-	}
469
-
470
-	private function getRoutePrefix() {
471
-		$frontControllerActive = ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true');
472
-		$prefix = \OC::$WEBROOT . '/index.php';
473
-		if ($frontControllerActive) {
474
-			$prefix = \OC::$WEBROOT;
475
-		}
476
-		return $prefix;
477
-	}
478
-
479
-	/**
480
-	 * Prepend hashed app version hash
481
-	 * @param string $cssFile
482
-	 * @param string $appId
483
-	 * @return string
484
-	 */
485
-	private function prependVersionPrefix(string $cssFile, string $appId): string {
486
-		$appVersion = \OC_App::getAppVersion($appId);
487
-		if ($appVersion !== '0') {
488
-			return substr(md5($appVersion), 0, 4) . '-' . $cssFile;
489
-		}
490
-		$coreVersion = \OC_Util::getVersionString();
491
-
492
-		return substr(md5($coreVersion), 0, 4) . '-' . $cssFile;
493
-	}
494
-
495
-	/**
496
-	 * Get WebDir root
497
-	 * @param string $path the css file path
498
-	 * @param string $appName the app name
499
-	 * @param string $serverRoot the server root path
500
-	 * @param string $webRoot the nextcloud installation root path
501
-	 * @return string the webDir
502
-	 */
503
-	private function getWebDir(string $path, string $appName, string $serverRoot, string $webRoot): string {
504
-		// Detect if path is within server root AND if path is within an app path
505
-		if (strpos($path, $serverRoot) === false && $appWebPath = \OC_App::getAppWebPath($appName)) {
506
-			// Get the file path within the app directory
507
-			$appDirectoryPath = explode($appName, $path)[1];
508
-			// Remove the webroot
509
-
510
-			return str_replace($webRoot, '', $appWebPath . $appDirectoryPath);
511
-		}
512
-
513
-		return $webRoot . substr($path, strlen($serverRoot));
514
-	}
515
-
516
-	/**
517
-	 * Add the icons css cache in the header if needed
518
-	 *
519
-	 * @return boolean true
520
-	 */
521
-	private function injectCssVariablesIfAny() {
522
-		// Inject icons vars css if any
523
-		if ($this->iconsCacher->getCachedCSS() && $this->iconsCacher->getCachedCSS()->getSize() > 0) {
524
-			$this->iconsCacher->injectCss();
525
-		}
526
-		return true;
527
-	}
54
+    /** @var ILogger */
55
+    protected $logger;
56
+
57
+    /** @var IAppData */
58
+    protected $appData;
59
+
60
+    /** @var IURLGenerator */
61
+    protected $urlGenerator;
62
+
63
+    /** @var IConfig */
64
+    protected $config;
65
+
66
+    /** @var \OC_Defaults */
67
+    private $defaults;
68
+
69
+    /** @var string */
70
+    protected $serverRoot;
71
+
72
+    /** @var ICache */
73
+    protected $depsCache;
74
+
75
+    /** @var null|string */
76
+    private $injectedVariables;
77
+
78
+    /** @var ICacheFactory */
79
+    private $cacheFactory;
80
+
81
+    /** @var IconsCacher */
82
+    private $iconsCacher;
83
+
84
+    /** @var ICache */
85
+    private $isCachedCache;
86
+
87
+    /** @var ITimeFactory */
88
+    private $timeFactory;
89
+
90
+    /** @var IMemcache */
91
+    private $lockingCache;
92
+
93
+    /**
94
+     * @param ILogger $logger
95
+     * @param Factory $appDataFactory
96
+     * @param IURLGenerator $urlGenerator
97
+     * @param IConfig $config
98
+     * @param \OC_Defaults $defaults
99
+     * @param string $serverRoot
100
+     * @param ICacheFactory $cacheFactory
101
+     * @param IconsCacher $iconsCacher
102
+     * @param ITimeFactory $timeFactory
103
+     */
104
+    public function __construct(ILogger $logger,
105
+                                Factory $appDataFactory,
106
+                                IURLGenerator $urlGenerator,
107
+                                IConfig $config,
108
+                                \OC_Defaults $defaults,
109
+                                $serverRoot,
110
+                                ICacheFactory $cacheFactory,
111
+                                IconsCacher $iconsCacher,
112
+                                ITimeFactory $timeFactory) {
113
+        $this->logger = $logger;
114
+        $this->appData = $appDataFactory->get('css');
115
+        $this->urlGenerator = $urlGenerator;
116
+        $this->config = $config;
117
+        $this->defaults = $defaults;
118
+        $this->serverRoot = $serverRoot;
119
+        $this->cacheFactory = $cacheFactory;
120
+        $this->depsCache = $cacheFactory->createDistributed('SCSS-deps-' . md5($this->urlGenerator->getBaseUrl()));
121
+        $this->isCachedCache = $cacheFactory->createDistributed('SCSS-cached-' . md5($this->urlGenerator->getBaseUrl()));
122
+        $lockingCache = $cacheFactory->createDistributed('SCSS-locks-' . md5($this->urlGenerator->getBaseUrl()));
123
+        if (!($lockingCache instanceof IMemcache)) {
124
+            $lockingCache = new NullCache();
125
+        }
126
+        $this->lockingCache = $lockingCache;
127
+        $this->iconsCacher = $iconsCacher;
128
+        $this->timeFactory = $timeFactory;
129
+    }
130
+
131
+    /**
132
+     * Process the caching process if needed
133
+     *
134
+     * @param string $root Root path to the nextcloud installation
135
+     * @param string $file
136
+     * @param string $app The app name
137
+     * @return boolean
138
+     * @throws NotPermittedException
139
+     */
140
+    public function process(string $root, string $file, string $app): bool {
141
+        $path = explode('/', $root . '/' . $file);
142
+
143
+        $fileNameSCSS = array_pop($path);
144
+        $fileNameCSS = $this->prependVersionPrefix($this->prependBaseurlPrefix(str_replace('.scss', '.css', $fileNameSCSS)), $app);
145
+
146
+        $path = implode('/', $path);
147
+        $webDir = $this->getWebDir($path, $app, $this->serverRoot, \OC::$WEBROOT);
148
+
149
+        $this->logger->debug('SCSSCacher::process ordinary check follows', ['app' => 'scss_cacher']);
150
+        if (!$this->variablesChanged() && $this->isCached($fileNameCSS, $app)) {
151
+            // Inject icons vars css if any
152
+            return $this->injectCssVariablesIfAny();
153
+        }
154
+
155
+        try {
156
+            $folder = $this->appData->getFolder($app);
157
+        } catch (NotFoundException $e) {
158
+            // creating css appdata folder
159
+            $folder = $this->appData->newFolder($app);
160
+        }
161
+
162
+        $lockKey = $webDir . '/' . $fileNameSCSS;
163
+
164
+        if (!$this->lockingCache->add($lockKey, 'locked!', 120)) {
165
+            $this->logger->debug('SCSSCacher::process could not get lock for ' . $lockKey . ' and will wait 10 seconds for cached file to be available', ['app' => 'scss_cacher']);
166
+            $retry = 0;
167
+            sleep(1);
168
+            while ($retry < 10) {
169
+                $this->logger->debug('SCSSCacher::process check in while loop follows', ['app' => 'scss_cacher']);
170
+                if (!$this->variablesChanged() && $this->isCached($fileNameCSS, $app)) {
171
+                    // Inject icons vars css if any
172
+                    $this->lockingCache->remove($lockKey);
173
+                    $this->logger->debug("SCSSCacher::process cached file for app '$app' and file '$fileNameCSS' is now available after $retry s. Moving on...", ['app' => 'scss_cacher']);
174
+                    return $this->injectCssVariablesIfAny();
175
+                }
176
+                sleep(1);
177
+                $retry++;
178
+            }
179
+            $this->logger->debug('SCSSCacher::process Giving up scss caching for ' . $lockKey, ['app' => 'scss_cacher']);
180
+            return false;
181
+        }
182
+
183
+        $this->logger->debug('SCSSCacher::process Lock acquired for ' . $lockKey, ['app' => 'scss_cacher']);
184
+        try {
185
+            $cached = $this->cache($path, $fileNameCSS, $fileNameSCSS, $folder, $webDir);
186
+        } catch (\Exception $e) {
187
+            $this->lockingCache->remove($lockKey);
188
+            throw $e;
189
+        }
190
+
191
+        // Cleaning lock
192
+        $this->lockingCache->remove($lockKey);
193
+        $this->logger->debug('SCSSCacher::process Lock removed for ' . $lockKey, ['app' => 'scss_cacher']);
194
+
195
+        // Inject icons vars css if any
196
+        if ($this->iconsCacher->getCachedCSS() && $this->iconsCacher->getCachedCSS()->getSize() > 0) {
197
+            $this->iconsCacher->injectCss();
198
+        }
199
+
200
+        return $cached;
201
+    }
202
+
203
+    /**
204
+     * @param $appName
205
+     * @param $fileName
206
+     * @return ISimpleFile
207
+     */
208
+    public function getCachedCSS(string $appName, string $fileName): ISimpleFile {
209
+        $folder = $this->appData->getFolder($appName);
210
+        $cachedFileName = $this->prependVersionPrefix($this->prependBaseurlPrefix($fileName), $appName);
211
+
212
+        return $folder->getFile($cachedFileName);
213
+    }
214
+
215
+    /**
216
+     * Check if the file is cached or not
217
+     * @param string $fileNameCSS
218
+     * @param string $app
219
+     * @return boolean
220
+     */
221
+    private function isCached(string $fileNameCSS, string $app) {
222
+        $key = $this->config->getSystemValue('version') . '/' . $app . '/' . $fileNameCSS;
223
+
224
+        // If the file mtime is more recent than our cached one,
225
+        // let's consider the file is properly cached
226
+        if ($cacheValue = $this->isCachedCache->get($key)) {
227
+            if ($cacheValue > $this->timeFactory->getTime()) {
228
+                return true;
229
+            }
230
+        }
231
+        $this->logger->debug("SCSSCacher::isCached $fileNameCSS isCachedCache is expired or unset", ['app' => 'scss_cacher']);
232
+
233
+        // Creating file cache if none for further checks
234
+        try {
235
+            $folder = $this->appData->getFolder($app);
236
+        } catch (NotFoundException $e) {
237
+            $this->logger->debug("SCSSCacher::isCached app data folder for $app could not be fetched", ['app' => 'scss_cacher']);
238
+            return false;
239
+        }
240
+
241
+        // Checking if file size is coherent
242
+        // and if one of the css dependency changed
243
+        try {
244
+            $cachedFile = $folder->getFile($fileNameCSS);
245
+            if ($cachedFile->getSize() > 0) {
246
+                $depFileName = $fileNameCSS . '.deps';
247
+                $deps = $this->depsCache->get($folder->getName() . '-' . $depFileName);
248
+                if ($deps === null) {
249
+                    $depFile = $folder->getFile($depFileName);
250
+                    $deps = $depFile->getContent();
251
+                    // Set to memcache for next run
252
+                    $this->depsCache->set($folder->getName() . '-' . $depFileName, $deps);
253
+                }
254
+                $deps = json_decode($deps, true);
255
+
256
+                foreach ((array) $deps as $file => $mtime) {
257
+                    if (!file_exists($file) || filemtime($file) > $mtime) {
258
+                        $this->logger->debug("SCSSCacher::isCached $fileNameCSS is not considered as cached due to deps file $file", ['app' => 'scss_cacher']);
259
+                        return false;
260
+                    }
261
+                }
262
+
263
+                $this->logger->debug("SCSSCacher::isCached $fileNameCSS dependencies successfully cached for 5 minutes", ['app' => 'scss_cacher']);
264
+                // It would probably make sense to adjust this timeout to something higher and see if that has some effect then
265
+                $this->isCachedCache->set($key, $this->timeFactory->getTime() + 5 * 60);
266
+                return true;
267
+            }
268
+            $this->logger->debug("SCSSCacher::isCached $fileNameCSS is not considered as cached cacheValue: $cacheValue", ['app' => 'scss_cacher']);
269
+            return false;
270
+        } catch (NotFoundException $e) {
271
+            $this->logger->debug("SCSSCacher::isCached NotFoundException " . $e->getMessage(), ['app' => 'scss_cacher']);
272
+            return false;
273
+        }
274
+    }
275
+
276
+    /**
277
+     * Check if the variables file has changed
278
+     * @return bool
279
+     */
280
+    private function variablesChanged(): bool {
281
+        $injectedVariables = $this->getInjectedVariables();
282
+        if ($this->config->getAppValue('core', 'theming.variables') !== md5($injectedVariables)) {
283
+            $this->logger->debug('SCSSCacher::variablesChanged storedVariables: ' . json_encode($this->config->getAppValue('core', 'theming.variables')) . ' currentInjectedVariables: ' . json_encode($injectedVariables), ['app' => 'scss_cacher']);
284
+            $this->config->setAppValue('core', 'theming.variables', md5($injectedVariables));
285
+            $this->resetCache();
286
+            return true;
287
+        }
288
+        return false;
289
+    }
290
+
291
+    /**
292
+     * Cache the file with AppData
293
+     *
294
+     * @param string $path
295
+     * @param string $fileNameCSS
296
+     * @param string $fileNameSCSS
297
+     * @param ISimpleFolder $folder
298
+     * @param string $webDir
299
+     * @return boolean
300
+     * @throws NotPermittedException
301
+     */
302
+    private function cache(string $path, string $fileNameCSS, string $fileNameSCSS, ISimpleFolder $folder, string $webDir) {
303
+        $scss = new Compiler();
304
+        $scss->setImportPaths([
305
+            $path,
306
+            $this->serverRoot . '/core/css/'
307
+        ]);
308
+
309
+        // Continue after throw
310
+        $scss->setIgnoreErrors(true);
311
+        if ($this->config->getSystemValue('debug')) {
312
+            // Debug mode
313
+            $scss->setFormatter(Expanded::class);
314
+            $scss->setLineNumberStyle(Compiler::LINE_COMMENTS);
315
+        } else {
316
+            // Compression
317
+            $scss->setFormatter(Crunched::class);
318
+        }
319
+
320
+        try {
321
+            $cachedfile = $folder->getFile($fileNameCSS);
322
+        } catch (NotFoundException $e) {
323
+            $cachedfile = $folder->newFile($fileNameCSS);
324
+        }
325
+
326
+        $depFileName = $fileNameCSS . '.deps';
327
+        try {
328
+            $depFile = $folder->getFile($depFileName);
329
+        } catch (NotFoundException $e) {
330
+            $depFile = $folder->newFile($depFileName);
331
+        }
332
+
333
+        // Compile
334
+        try {
335
+            $compiledScss = $scss->compile(
336
+                '$webroot: \'' . $this->getRoutePrefix() . '\';' .
337
+                $this->getInjectedVariables() .
338
+                '@import "variables.scss";' .
339
+                '@import "functions.scss";' .
340
+                '@import "' . $fileNameSCSS . '";');
341
+        } catch (ParserException $e) {
342
+            $this->logger->logException($e, ['app' => 'scss_cacher']);
343
+
344
+            return false;
345
+        }
346
+
347
+        // Parse Icons and create related css variables
348
+        $compiledScss = $this->iconsCacher->setIconsCss($compiledScss);
349
+
350
+        // Gzip file
351
+        try {
352
+            $gzipFile = $folder->getFile($fileNameCSS . '.gzip'); # Safari doesn't like .gz
353
+        } catch (NotFoundException $e) {
354
+            $gzipFile = $folder->newFile($fileNameCSS . '.gzip'); # Safari doesn't like .gz
355
+        }
356
+
357
+        try {
358
+            $data = $this->rebaseUrls($compiledScss, $webDir);
359
+            $cachedfile->putContent($data);
360
+            $deps = json_encode($scss->getParsedFiles());
361
+            $depFile->putContent($deps);
362
+            $this->depsCache->set($folder->getName() . '-' . $depFileName, $deps);
363
+            $gzipFile->putContent(gzencode($data, 9));
364
+            $this->logger->debug('SCSSCacher::cache ' . $webDir . '/' . $fileNameSCSS . ' compiled and successfully cached', ['app' => 'scss_cacher']);
365
+
366
+            return true;
367
+        } catch (NotPermittedException $e) {
368
+            $this->logger->error('SCSSCacher::cache unable to cache: ' . $fileNameSCSS, ['app' => 'scss_cacher']);
369
+
370
+            return false;
371
+        }
372
+    }
373
+
374
+    /**
375
+     * Reset scss cache by deleting all generated css files
376
+     * We need to regenerate all files when variables change
377
+     */
378
+    public function resetCache() {
379
+        $this->logger->debug('SCSSCacher::resetCache', ['app' => 'scss_cacher']);
380
+        if (!$this->lockingCache->add('resetCache', 'locked!', 120)) {
381
+            $this->logger->debug('SCSSCacher::resetCache Locked', ['app' => 'scss_cacher']);
382
+            return;
383
+        }
384
+        $this->logger->debug('SCSSCacher::resetCache Lock acquired', ['app' => 'scss_cacher']);
385
+        $this->injectedVariables = null;
386
+
387
+        // do not clear locks
388
+        $this->cacheFactory->createDistributed('SCSS-deps-')->clear();
389
+        $this->cacheFactory->createDistributed('SCSS-cached-')->clear();
390
+
391
+        $appDirectory = $this->appData->getDirectoryListing();
392
+        foreach ($appDirectory as $folder) {
393
+            foreach ($folder->getDirectoryListing() as $file) {
394
+                try {
395
+                    $file->delete();
396
+                } catch (NotPermittedException $e) {
397
+                    $this->logger->logException($e, ['message' => 'SCSSCacher::resetCache unable to delete file: ' . $file->getName(), 'app' => 'scss_cacher']);
398
+                }
399
+            }
400
+        }
401
+        $this->logger->debug('SCSSCacher::resetCache css cache cleared!', ['app' => 'scss_cacher']);
402
+        $this->lockingCache->remove('resetCache');
403
+        $this->logger->debug('SCSSCacher::resetCache Locking removed', ['app' => 'scss_cacher']);
404
+    }
405
+
406
+    /**
407
+     * @return string SCSS code for variables from OC_Defaults
408
+     */
409
+    private function getInjectedVariables(): string {
410
+        if ($this->injectedVariables !== null) {
411
+            return $this->injectedVariables;
412
+        }
413
+        $variables = '';
414
+        foreach ($this->defaults->getScssVariables() as $key => $value) {
415
+            $variables .= '$' . $key . ': ' . $value . ' !default;';
416
+        }
417
+
418
+        // check for valid variables / otherwise fall back to defaults
419
+        try {
420
+            $scss = new Compiler();
421
+            $scss->compile($variables);
422
+            $this->injectedVariables = $variables;
423
+        } catch (ParserException $e) {
424
+            $this->logger->logException($e, ['app' => 'scss_cacher']);
425
+        }
426
+
427
+        return $variables;
428
+    }
429
+
430
+    /**
431
+     * Add the correct uri prefix to make uri valid again
432
+     * @param string $css
433
+     * @param string $webDir
434
+     * @return string
435
+     */
436
+    private function rebaseUrls(string $css, string $webDir): string {
437
+        $re = '/url\([\'"]([^\/][\.\w?=\/-]*)[\'"]\)/x';
438
+        $subst = 'url(\'' . $webDir . '/$1\')';
439
+
440
+        return preg_replace($re, $subst, $css);
441
+    }
442
+
443
+    /**
444
+     * Return the cached css file uri
445
+     * @param string $appName the app name
446
+     * @param string $fileName
447
+     * @return string
448
+     */
449
+    public function getCachedSCSS(string $appName, string $fileName): string {
450
+        $tmpfileLoc = explode('/', $fileName);
451
+        $fileName = array_pop($tmpfileLoc);
452
+        $fileName = $this->prependVersionPrefix($this->prependBaseurlPrefix(str_replace('.scss', '.css', $fileName)), $appName);
453
+
454
+        return substr($this->urlGenerator->linkToRoute('core.Css.getCss', [
455
+            'fileName' => $fileName,
456
+            'appName' => $appName,
457
+            'v' => $this->config->getAppValue('core', 'theming.variables', '0')
458
+        ]), \strlen(\OC::$WEBROOT) + 1);
459
+    }
460
+
461
+    /**
462
+     * Prepend hashed base url to the css file
463
+     * @param string $cssFile
464
+     * @return string
465
+     */
466
+    private function prependBaseurlPrefix(string $cssFile): string {
467
+        return substr(md5($this->urlGenerator->getBaseUrl() . $this->getRoutePrefix()), 0, 4) . '-' . $cssFile;
468
+    }
469
+
470
+    private function getRoutePrefix() {
471
+        $frontControllerActive = ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true');
472
+        $prefix = \OC::$WEBROOT . '/index.php';
473
+        if ($frontControllerActive) {
474
+            $prefix = \OC::$WEBROOT;
475
+        }
476
+        return $prefix;
477
+    }
478
+
479
+    /**
480
+     * Prepend hashed app version hash
481
+     * @param string $cssFile
482
+     * @param string $appId
483
+     * @return string
484
+     */
485
+    private function prependVersionPrefix(string $cssFile, string $appId): string {
486
+        $appVersion = \OC_App::getAppVersion($appId);
487
+        if ($appVersion !== '0') {
488
+            return substr(md5($appVersion), 0, 4) . '-' . $cssFile;
489
+        }
490
+        $coreVersion = \OC_Util::getVersionString();
491
+
492
+        return substr(md5($coreVersion), 0, 4) . '-' . $cssFile;
493
+    }
494
+
495
+    /**
496
+     * Get WebDir root
497
+     * @param string $path the css file path
498
+     * @param string $appName the app name
499
+     * @param string $serverRoot the server root path
500
+     * @param string $webRoot the nextcloud installation root path
501
+     * @return string the webDir
502
+     */
503
+    private function getWebDir(string $path, string $appName, string $serverRoot, string $webRoot): string {
504
+        // Detect if path is within server root AND if path is within an app path
505
+        if (strpos($path, $serverRoot) === false && $appWebPath = \OC_App::getAppWebPath($appName)) {
506
+            // Get the file path within the app directory
507
+            $appDirectoryPath = explode($appName, $path)[1];
508
+            // Remove the webroot
509
+
510
+            return str_replace($webRoot, '', $appWebPath . $appDirectoryPath);
511
+        }
512
+
513
+        return $webRoot . substr($path, strlen($serverRoot));
514
+    }
515
+
516
+    /**
517
+     * Add the icons css cache in the header if needed
518
+     *
519
+     * @return boolean true
520
+     */
521
+    private function injectCssVariablesIfAny() {
522
+        // Inject icons vars css if any
523
+        if ($this->iconsCacher->getCachedCSS() && $this->iconsCacher->getCachedCSS()->getSize() > 0) {
524
+            $this->iconsCacher->injectCss();
525
+        }
526
+        return true;
527
+    }
528 528
 }
Please login to merge, or discard this patch.
Spacing   +36 added lines, -36 removed lines patch added patch discarded remove patch
@@ -117,9 +117,9 @@  discard block
 block discarded – undo
117 117
 		$this->defaults = $defaults;
118 118
 		$this->serverRoot = $serverRoot;
119 119
 		$this->cacheFactory = $cacheFactory;
120
-		$this->depsCache = $cacheFactory->createDistributed('SCSS-deps-' . md5($this->urlGenerator->getBaseUrl()));
121
-		$this->isCachedCache = $cacheFactory->createDistributed('SCSS-cached-' . md5($this->urlGenerator->getBaseUrl()));
122
-		$lockingCache = $cacheFactory->createDistributed('SCSS-locks-' . md5($this->urlGenerator->getBaseUrl()));
120
+		$this->depsCache = $cacheFactory->createDistributed('SCSS-deps-'.md5($this->urlGenerator->getBaseUrl()));
121
+		$this->isCachedCache = $cacheFactory->createDistributed('SCSS-cached-'.md5($this->urlGenerator->getBaseUrl()));
122
+		$lockingCache = $cacheFactory->createDistributed('SCSS-locks-'.md5($this->urlGenerator->getBaseUrl()));
123 123
 		if (!($lockingCache instanceof IMemcache)) {
124 124
 			$lockingCache = new NullCache();
125 125
 		}
@@ -138,7 +138,7 @@  discard block
 block discarded – undo
138 138
 	 * @throws NotPermittedException
139 139
 	 */
140 140
 	public function process(string $root, string $file, string $app): bool {
141
-		$path = explode('/', $root . '/' . $file);
141
+		$path = explode('/', $root.'/'.$file);
142 142
 
143 143
 		$fileNameSCSS = array_pop($path);
144 144
 		$fileNameCSS = $this->prependVersionPrefix($this->prependBaseurlPrefix(str_replace('.scss', '.css', $fileNameSCSS)), $app);
@@ -159,10 +159,10 @@  discard block
 block discarded – undo
159 159
 			$folder = $this->appData->newFolder($app);
160 160
 		}
161 161
 
162
-		$lockKey = $webDir . '/' . $fileNameSCSS;
162
+		$lockKey = $webDir.'/'.$fileNameSCSS;
163 163
 
164 164
 		if (!$this->lockingCache->add($lockKey, 'locked!', 120)) {
165
-			$this->logger->debug('SCSSCacher::process could not get lock for ' . $lockKey . ' and will wait 10 seconds for cached file to be available', ['app' => 'scss_cacher']);
165
+			$this->logger->debug('SCSSCacher::process could not get lock for '.$lockKey.' and will wait 10 seconds for cached file to be available', ['app' => 'scss_cacher']);
166 166
 			$retry = 0;
167 167
 			sleep(1);
168 168
 			while ($retry < 10) {
@@ -176,11 +176,11 @@  discard block
 block discarded – undo
176 176
 				sleep(1);
177 177
 				$retry++;
178 178
 			}
179
-			$this->logger->debug('SCSSCacher::process Giving up scss caching for ' . $lockKey, ['app' => 'scss_cacher']);
179
+			$this->logger->debug('SCSSCacher::process Giving up scss caching for '.$lockKey, ['app' => 'scss_cacher']);
180 180
 			return false;
181 181
 		}
182 182
 
183
-		$this->logger->debug('SCSSCacher::process Lock acquired for ' . $lockKey, ['app' => 'scss_cacher']);
183
+		$this->logger->debug('SCSSCacher::process Lock acquired for '.$lockKey, ['app' => 'scss_cacher']);
184 184
 		try {
185 185
 			$cached = $this->cache($path, $fileNameCSS, $fileNameSCSS, $folder, $webDir);
186 186
 		} catch (\Exception $e) {
@@ -190,7 +190,7 @@  discard block
 block discarded – undo
190 190
 
191 191
 		// Cleaning lock
192 192
 		$this->lockingCache->remove($lockKey);
193
-		$this->logger->debug('SCSSCacher::process Lock removed for ' . $lockKey, ['app' => 'scss_cacher']);
193
+		$this->logger->debug('SCSSCacher::process Lock removed for '.$lockKey, ['app' => 'scss_cacher']);
194 194
 
195 195
 		// Inject icons vars css if any
196 196
 		if ($this->iconsCacher->getCachedCSS() && $this->iconsCacher->getCachedCSS()->getSize() > 0) {
@@ -219,7 +219,7 @@  discard block
 block discarded – undo
219 219
 	 * @return boolean
220 220
 	 */
221 221
 	private function isCached(string $fileNameCSS, string $app) {
222
-		$key = $this->config->getSystemValue('version') . '/' . $app . '/' . $fileNameCSS;
222
+		$key = $this->config->getSystemValue('version').'/'.$app.'/'.$fileNameCSS;
223 223
 
224 224
 		// If the file mtime is more recent than our cached one,
225 225
 		// let's consider the file is properly cached
@@ -243,13 +243,13 @@  discard block
 block discarded – undo
243 243
 		try {
244 244
 			$cachedFile = $folder->getFile($fileNameCSS);
245 245
 			if ($cachedFile->getSize() > 0) {
246
-				$depFileName = $fileNameCSS . '.deps';
247
-				$deps = $this->depsCache->get($folder->getName() . '-' . $depFileName);
246
+				$depFileName = $fileNameCSS.'.deps';
247
+				$deps = $this->depsCache->get($folder->getName().'-'.$depFileName);
248 248
 				if ($deps === null) {
249 249
 					$depFile = $folder->getFile($depFileName);
250 250
 					$deps = $depFile->getContent();
251 251
 					// Set to memcache for next run
252
-					$this->depsCache->set($folder->getName() . '-' . $depFileName, $deps);
252
+					$this->depsCache->set($folder->getName().'-'.$depFileName, $deps);
253 253
 				}
254 254
 				$deps = json_decode($deps, true);
255 255
 
@@ -268,7 +268,7 @@  discard block
 block discarded – undo
268 268
 			$this->logger->debug("SCSSCacher::isCached $fileNameCSS is not considered as cached cacheValue: $cacheValue", ['app' => 'scss_cacher']);
269 269
 			return false;
270 270
 		} catch (NotFoundException $e) {
271
-			$this->logger->debug("SCSSCacher::isCached NotFoundException " . $e->getMessage(), ['app' => 'scss_cacher']);
271
+			$this->logger->debug("SCSSCacher::isCached NotFoundException ".$e->getMessage(), ['app' => 'scss_cacher']);
272 272
 			return false;
273 273
 		}
274 274
 	}
@@ -280,7 +280,7 @@  discard block
 block discarded – undo
280 280
 	private function variablesChanged(): bool {
281 281
 		$injectedVariables = $this->getInjectedVariables();
282 282
 		if ($this->config->getAppValue('core', 'theming.variables') !== md5($injectedVariables)) {
283
-			$this->logger->debug('SCSSCacher::variablesChanged storedVariables: ' . json_encode($this->config->getAppValue('core', 'theming.variables')) . ' currentInjectedVariables: ' . json_encode($injectedVariables), ['app' => 'scss_cacher']);
283
+			$this->logger->debug('SCSSCacher::variablesChanged storedVariables: '.json_encode($this->config->getAppValue('core', 'theming.variables')).' currentInjectedVariables: '.json_encode($injectedVariables), ['app' => 'scss_cacher']);
284 284
 			$this->config->setAppValue('core', 'theming.variables', md5($injectedVariables));
285 285
 			$this->resetCache();
286 286
 			return true;
@@ -303,7 +303,7 @@  discard block
 block discarded – undo
303 303
 		$scss = new Compiler();
304 304
 		$scss->setImportPaths([
305 305
 			$path,
306
-			$this->serverRoot . '/core/css/'
306
+			$this->serverRoot.'/core/css/'
307 307
 		]);
308 308
 
309 309
 		// Continue after throw
@@ -323,7 +323,7 @@  discard block
 block discarded – undo
323 323
 			$cachedfile = $folder->newFile($fileNameCSS);
324 324
 		}
325 325
 
326
-		$depFileName = $fileNameCSS . '.deps';
326
+		$depFileName = $fileNameCSS.'.deps';
327 327
 		try {
328 328
 			$depFile = $folder->getFile($depFileName);
329 329
 		} catch (NotFoundException $e) {
@@ -333,11 +333,11 @@  discard block
 block discarded – undo
333 333
 		// Compile
334 334
 		try {
335 335
 			$compiledScss = $scss->compile(
336
-				'$webroot: \'' . $this->getRoutePrefix() . '\';' .
337
-				$this->getInjectedVariables() .
338
-				'@import "variables.scss";' .
339
-				'@import "functions.scss";' .
340
-				'@import "' . $fileNameSCSS . '";');
336
+				'$webroot: \''.$this->getRoutePrefix().'\';'.
337
+				$this->getInjectedVariables().
338
+				'@import "variables.scss";'.
339
+				'@import "functions.scss";'.
340
+				'@import "'.$fileNameSCSS.'";');
341 341
 		} catch (ParserException $e) {
342 342
 			$this->logger->logException($e, ['app' => 'scss_cacher']);
343 343
 
@@ -349,9 +349,9 @@  discard block
 block discarded – undo
349 349
 
350 350
 		// Gzip file
351 351
 		try {
352
-			$gzipFile = $folder->getFile($fileNameCSS . '.gzip'); # Safari doesn't like .gz
352
+			$gzipFile = $folder->getFile($fileNameCSS.'.gzip'); # Safari doesn't like .gz
353 353
 		} catch (NotFoundException $e) {
354
-			$gzipFile = $folder->newFile($fileNameCSS . '.gzip'); # Safari doesn't like .gz
354
+			$gzipFile = $folder->newFile($fileNameCSS.'.gzip'); # Safari doesn't like .gz
355 355
 		}
356 356
 
357 357
 		try {
@@ -359,13 +359,13 @@  discard block
 block discarded – undo
359 359
 			$cachedfile->putContent($data);
360 360
 			$deps = json_encode($scss->getParsedFiles());
361 361
 			$depFile->putContent($deps);
362
-			$this->depsCache->set($folder->getName() . '-' . $depFileName, $deps);
362
+			$this->depsCache->set($folder->getName().'-'.$depFileName, $deps);
363 363
 			$gzipFile->putContent(gzencode($data, 9));
364
-			$this->logger->debug('SCSSCacher::cache ' . $webDir . '/' . $fileNameSCSS . ' compiled and successfully cached', ['app' => 'scss_cacher']);
364
+			$this->logger->debug('SCSSCacher::cache '.$webDir.'/'.$fileNameSCSS.' compiled and successfully cached', ['app' => 'scss_cacher']);
365 365
 
366 366
 			return true;
367 367
 		} catch (NotPermittedException $e) {
368
-			$this->logger->error('SCSSCacher::cache unable to cache: ' . $fileNameSCSS, ['app' => 'scss_cacher']);
368
+			$this->logger->error('SCSSCacher::cache unable to cache: '.$fileNameSCSS, ['app' => 'scss_cacher']);
369 369
 
370 370
 			return false;
371 371
 		}
@@ -394,7 +394,7 @@  discard block
 block discarded – undo
394 394
 				try {
395 395
 					$file->delete();
396 396
 				} catch (NotPermittedException $e) {
397
-					$this->logger->logException($e, ['message' => 'SCSSCacher::resetCache unable to delete file: ' . $file->getName(), 'app' => 'scss_cacher']);
397
+					$this->logger->logException($e, ['message' => 'SCSSCacher::resetCache unable to delete file: '.$file->getName(), 'app' => 'scss_cacher']);
398 398
 				}
399 399
 			}
400 400
 		}
@@ -412,7 +412,7 @@  discard block
 block discarded – undo
412 412
 		}
413 413
 		$variables = '';
414 414
 		foreach ($this->defaults->getScssVariables() as $key => $value) {
415
-			$variables .= '$' . $key . ': ' . $value . ' !default;';
415
+			$variables .= '$'.$key.': '.$value.' !default;';
416 416
 		}
417 417
 
418 418
 		// check for valid variables / otherwise fall back to defaults
@@ -435,7 +435,7 @@  discard block
 block discarded – undo
435 435
 	 */
436 436
 	private function rebaseUrls(string $css, string $webDir): string {
437 437
 		$re = '/url\([\'"]([^\/][\.\w?=\/-]*)[\'"]\)/x';
438
-		$subst = 'url(\'' . $webDir . '/$1\')';
438
+		$subst = 'url(\''.$webDir.'/$1\')';
439 439
 
440 440
 		return preg_replace($re, $subst, $css);
441 441
 	}
@@ -464,12 +464,12 @@  discard block
 block discarded – undo
464 464
 	 * @return string
465 465
 	 */
466 466
 	private function prependBaseurlPrefix(string $cssFile): string {
467
-		return substr(md5($this->urlGenerator->getBaseUrl() . $this->getRoutePrefix()), 0, 4) . '-' . $cssFile;
467
+		return substr(md5($this->urlGenerator->getBaseUrl().$this->getRoutePrefix()), 0, 4).'-'.$cssFile;
468 468
 	}
469 469
 
470 470
 	private function getRoutePrefix() {
471 471
 		$frontControllerActive = ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true');
472
-		$prefix = \OC::$WEBROOT . '/index.php';
472
+		$prefix = \OC::$WEBROOT.'/index.php';
473 473
 		if ($frontControllerActive) {
474 474
 			$prefix = \OC::$WEBROOT;
475 475
 		}
@@ -485,11 +485,11 @@  discard block
 block discarded – undo
485 485
 	private function prependVersionPrefix(string $cssFile, string $appId): string {
486 486
 		$appVersion = \OC_App::getAppVersion($appId);
487 487
 		if ($appVersion !== '0') {
488
-			return substr(md5($appVersion), 0, 4) . '-' . $cssFile;
488
+			return substr(md5($appVersion), 0, 4).'-'.$cssFile;
489 489
 		}
490 490
 		$coreVersion = \OC_Util::getVersionString();
491 491
 
492
-		return substr(md5($coreVersion), 0, 4) . '-' . $cssFile;
492
+		return substr(md5($coreVersion), 0, 4).'-'.$cssFile;
493 493
 	}
494 494
 
495 495
 	/**
@@ -507,10 +507,10 @@  discard block
 block discarded – undo
507 507
 			$appDirectoryPath = explode($appName, $path)[1];
508 508
 			// Remove the webroot
509 509
 
510
-			return str_replace($webRoot, '', $appWebPath . $appDirectoryPath);
510
+			return str_replace($webRoot, '', $appWebPath.$appDirectoryPath);
511 511
 		}
512 512
 
513
-		return $webRoot . substr($path, strlen($serverRoot));
513
+		return $webRoot.substr($path, strlen($serverRoot));
514 514
 	}
515 515
 
516 516
 	/**
Please login to merge, or discard this patch.