Completed
Push — master ( 1a8096...9675bb )
by Lukas
40:13 queued 27:07
created

SCSSCacher   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 192
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 8

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 192
rs 10
c 1
b 0
f 0
wmc 21
lcom 2
cbo 8

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
A process() 0 22 3
C isCached() 0 25 7
B cache() 0 47 6
A rebaseUrls() 0 10 3
A getCachedSCSS() 0 7 1
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, John Molakvoæ ([email protected])
4
 *
5
 * @license GNU AGPL version 3 or any later version
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as
9
 * published by the Free Software Foundation, either version 3 of the
10
 * License, or (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 *
20
 */
21
22
namespace OC\Template;
23
24
use Leafo\ScssPhp\Compiler;
25
use Leafo\ScssPhp\Exception\ParserException;
26
use Leafo\ScssPhp\Formatter\Crunched;
27
use Leafo\ScssPhp\Formatter\Expanded;
28
use OC\SystemConfig;
29
use OCP\Files\IAppData;
30
use OCP\Files\NotFoundException;
31
use OCP\Files\NotPermittedException;
32
use OCP\Files\SimpleFS\ISimpleFolder;
33
use OCP\ICache;
34
use OCP\IConfig;
35
use OCP\ILogger;
36
use OCP\IURLGenerator;
37
38
class SCSSCacher {
39
40
	/** @var ILogger */
41
	protected $logger;
42
43
	/** @var IAppData */
44
	protected $appData;
45
46
	/** @var IURLGenerator */
47
	protected $urlGenerator;
48
49
	/** @var IConfig */
50
	protected $config;
51
52
	/** @var string */
53
	protected $serverRoot;
54
55
	/** @var ICache */
56
	protected $depsCache;
57
58
	/**
59
	 * @param ILogger $logger
60
	 * @param IAppData $appData
61
	 * @param IURLGenerator $urlGenerator
62
	 * @param IConfig $config
63
	 * @param string $serverRoot
64
	 * @param ICache $depsCache
65
	 */
66
	public function __construct(ILogger $logger,
67
								IAppData $appData,
68
								IURLGenerator $urlGenerator,
69
								IConfig $config,
70
								$serverRoot,
71
								ICache $depsCache) {
72
		$this->logger = $logger;
73
		$this->appData = $appData;
74
		$this->urlGenerator = $urlGenerator;
75
		$this->config = $config;
76
		$this->serverRoot = $serverRoot;
77
		$this->depsCache = $depsCache;
78
	}
79
80
	/**
81
	 * Process the caching process if needed
82
	 * @param string $root Root path to the nextcloud installation
83
	 * @param string $file
84
	 * @param string $app The app name
85
	 * @return boolean
86
	 */
87
	public function process($root, $file, $app) {
88
		$path = explode('/', $root . '/' . $file);
89
90
		$fileNameSCSS = array_pop($path);
91
		$fileNameCSS = str_replace('.scss', '.css', $fileNameSCSS);
92
93
		$path = implode('/', $path);
94
95
		$webDir = substr($path, strlen($this->serverRoot)+1);
96
97
		try {
98
			$folder = $this->appData->getFolder($app);
99
		} catch(NotFoundException $e) {
100
			// creating css appdata folder
101
			$folder = $this->appData->newFolder($app);
102
		}
103
104
		if($this->isCached($fileNameCSS, $folder)) {
105
			return true;
106
		}
107
		return $this->cache($path, $fileNameCSS, $fileNameSCSS, $folder, $webDir);
108
	}
109
110
	/**
111
	 * Check if the file is cached or not
112
	 * @param string $fileNameCSS
113
	 * @param ISimpleFolder $folder
114
	 * @return boolean
115
	 */
116
	private function isCached($fileNameCSS, ISimpleFolder $folder) {
117
		try {
118
			$cachedFile = $folder->getFile($fileNameCSS);
119
			if ($cachedFile->getSize() > 0) {
120
				$depFileName = $fileNameCSS . '.deps';
121
				$deps = $this->depsCache->get($folder->getName() . '-' . $depFileName);
122
				if ($deps === null) {
123
					$depFile = $folder->getFile($depFileName);
124
					$deps = $depFile->getContent();
125
					//Set to memcache for next run
126
					$this->depsCache->set($folder->getName() . '-' . $depFileName, $deps);
127
				}
128
				$deps = json_decode($deps, true);
129
130
				foreach ($deps as $file=>$mtime) {
131
					if (!file_exists($file) || filemtime($file) > $mtime) {
132
						return false;
133
					}
134
				}
135
			}
136
			return true;
137
		} catch(NotFoundException $e) {
138
			return false;
139
		}
140
	}
141
142
	/**
143
	 * Cache the file with AppData
144
	 * @param string $path
145
	 * @param string $fileNameCSS
146
	 * @param string $fileNameSCSS
147
	 * @param ISimpleFolder $folder
148
	 * @param string $webDir
149
	 * @return boolean
150
	 */
151
	private function cache($path, $fileNameCSS, $fileNameSCSS, ISimpleFolder $folder, $webDir) {
152
		$scss = new Compiler();
153
		$scss->setImportPaths([
154
			$path,
155
			\OC::$SERVERROOT . '/core/css/',
156
		]);
157
		if($this->config->getSystemValue('debug')) {
158
			// Debug mode
159
			$scss->setFormatter(Expanded::class);
160
			$scss->setLineNumberStyle(Compiler::LINE_COMMENTS);
161
		} else {
162
			// Compression
163
			$scss->setFormatter(Crunched::class);
164
		}
165
166
		try {
167
			$cachedfile = $folder->getFile($fileNameCSS);
168
		} catch(NotFoundException $e) {
169
			$cachedfile = $folder->newFile($fileNameCSS);
170
		}
171
172
		$depFileName = $fileNameCSS . '.deps';
173
		try {
174
			$depFile = $folder->getFile($depFileName);
175
		} catch (NotFoundException $e) {
176
			$depFile = $folder->newFile($depFileName);
177
		}
178
179
		// Compile
180
		try {
181
			$compiledScss = $scss->compile(
182
				'@import "variables.scss";' .
183
				'@import "'.$fileNameSCSS.'";');
184
		} catch(ParserException $e) {
0 ignored issues
show
Bug introduced by
The class Leafo\ScssPhp\Exception\ParserException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
185
			$this->logger->error($e, ['app' => 'core']);
186
			return false;
187
		}
188
189
		try {
190
			$cachedfile->putContent($this->rebaseUrls($compiledScss, $webDir));
191
			$depFile->putContent(json_encode($scss->getParsedFiles()));
192
			$this->logger->debug($webDir.'/'.$fileNameSCSS.' compiled and successfully cached', ['app' => 'core']);
193
			return true;
194
		} catch(NotPermittedException $e) {
195
			return false;
196
		}
197
	}
198
199
	/**
200
	 * Add the correct uri prefix to make uri valid again
201
	 * @param string $css
202
	 * @param string $webDir
203
	 * @return string
204
	 */
205
	private function rebaseUrls($css, $webDir) {
206
		$re = '/url\([\'"]([\.\w?=\/-]*)[\'"]\)/x';
207
		// OC\Route\Router:75
208
		if(($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true')) {
209
			$subst = 'url(\'../../'.$webDir.'/$1\')';	
210
		} else {
211
			$subst = 'url(\'../../../'.$webDir.'/$1\')';
212
		}
213
		return preg_replace($re, $subst, $css);
214
	}
215
216
	/**
217
	 * Return the cached css file uri
218
	 * @param string $appName the app name
219
	 * @param string $fileName
220
	 * @return string
221
	 */
222
	public function getCachedSCSS($appName, $fileName) {
223
		$tmpfileLoc = explode('/', $fileName);
224
		$fileName = array_pop($tmpfileLoc);
225
		$fileName = str_replace('.scss', '.css', $fileName);
226
227
		return substr($this->urlGenerator->linkToRoute('core.Css.getCss', array('fileName' => $fileName, 'appName' => $appName)), strlen(\OC::$WEBROOT) + 1);
228
	}
229
}
230