Completed
Push — master ( db5fc4...6fd0e7 )
by Lukas
23s
created

SCSSCacher::isCached()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 13
nc 10
nop 4
dl 0
loc 19
rs 8.8571
c 1
b 0
f 0
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\SimpleFS\ISimpleFolder;
32
use OCP\ILogger;
33
use OCP\IURLGenerator;
34
35
class SCSSCacher {
36
37
	/** @var ILogger */
38
	protected $logger;
39
40
	/** @var IAppData */
41
	protected $appData;
42
43
	/** @var IURLGenerator */
44
	protected $urlGenerator;
45
46
	/** @var SystemConfig */
47
	protected $systemConfig;
48
49
	/**
50
	 * @param ILogger $logger
51
	 * @param IAppData $appData
52
	 * @param IURLGenerator $urlGenerator
53
	 * @param SystemConfig $systemConfig
54
	 */
55
	public function __construct(ILogger $logger, IAppData $appData, IURLGenerator $urlGenerator, SystemConfig $systemConfig) {
56
		$this->logger = $logger;
57
		$this->appData = $appData;
58
		$this->urlGenerator = $urlGenerator;
59
		$this->systemConfig = $systemConfig;
60
	}
61
62
	/**
63
	 * Process the caching process if needed
64
	 * @param string $root Root path to the nextcloud installation
65
	 * @param string $file
66
	 * @param string $app The app name
67
	 * @return boolean
68
	 */
69
	public function process($root, $file, $app) {
70
		$path = explode('/', $root . '/' . $file);
71
72
		$fileNameSCSS = array_pop($path);
73
		$fileNameCSS = str_replace('.scss', '.css', $fileNameSCSS);
74
75
		$path = implode('/', $path);
76
77
		$webDir = explode('/', $file);
78
		array_pop($webDir);
79
		$webDir = implode('/', $webDir);
80
81
		try {
82
			$folder = $this->appData->getFolder($app);
83
		} catch(NotFoundException $e) {
84
			// creating css appdata folder
85
			$folder = $this->appData->newFolder($app);
86
		}
87
88
		if($this->isCached($fileNameCSS, $fileNameSCSS, $folder, $path)) {
89
			return true;
90
		} else {
91
			return $this->cache($path, $fileNameCSS, $fileNameSCSS, $folder, $webDir);
92
		}
93
	}
94
95
	/**
96
	 * Check if the file is cached or not
97
	 * @param string $fileNameCSS
98
	 * @param string $fileNameSCSS
99
	 * @param ISimpleFolder $folder
100
	 * @param string $path
101
	 * @return boolean
102
	 */
103
	private function isCached($fileNameCSS, $fileNameSCSS, ISimpleFolder $folder, $path) {
0 ignored issues
show
Unused Code introduced by
The parameter $fileNameSCSS is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $path is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
104
		try{
105
			$cachedFile = $folder->getFile($fileNameCSS);
106
			if ($cachedFile->getSize() > 0) {
107
				$depFile = $folder->getFile($fileNameCSS . '.deps');
108
				$deps = json_decode($depFile->getContent(), true);
109
110
				foreach ($deps as $file=>$mtime) {
111
					if (!file_exists($file) || filemtime($file) > $mtime) {
112
						return false;
113
					}
114
				}
115
			}
116
			return true;
117
		} catch(NotFoundException $e) {
118
			return false;
119
		}
120
		return false;
0 ignored issues
show
Unused Code introduced by
return false; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
121
	}
122
123
	/**
124
	 * Cache the file with AppData
125
	 * @param string $path
126
	 * @param string $fileNameCSS
127
	 * @param string $fileNameSCSS
128
	 * @param ISimpleFolder $folder
129
	 * @param string $webDir
130
	 * @return boolean
131
	 */
132
	private function cache($path, $fileNameCSS, $fileNameSCSS, ISimpleFolder $folder, $webDir) {
133
		$scss = new Compiler();
134
		$scss->setImportPaths($path);
135
		if($this->systemConfig->getValue('debug')) {
136
			// Debug mode
137
			$scss->setFormatter(Expanded::class);
138
			$scss->setLineNumberStyle(Compiler::LINE_COMMENTS);
139
		} else {
140
			// Compression
141
			$scss->setFormatter(Crunched::class);
142
		}
143
144
		try {
145
			$cachedfile = $folder->getFile($fileNameCSS);
146
		} catch(NotFoundException $e) {
147
			$cachedfile = $folder->newFile($fileNameCSS);
148
		}
149
150
		$depFileName = $fileNameCSS . '.deps';
151
		try {
152
			$depFile = $folder->getFile($depFileName);
153
		} catch (NotFoundException $e) {
154
			$depFile = $folder->newFile($depFileName);
155
		}
156
157
		// Compile
158
		try {
159
			$compiledScss = $scss->compile('@import "'.$fileNameSCSS.'";');
160
		} 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...
161
			$this->logger->error($e, ['app' => 'core']);
162
			return false;
163
		}
164
165
		try {
166
			$cachedfile->putContent($this->rebaseUrls($compiledScss, $webDir));
167
			$depFile->putContent(json_encode($scss->getParsedFiles()));
168
			$this->logger->debug($webDir.'/'.$fileNameSCSS.' compiled and successfully cached', ['app' => 'core']);
169
			return true;
170
		} catch(NotFoundException $e) {
171
			return false;
172
		}
173
	}
174
175
	/**
176
	 * Add the correct uri prefix to make uri valid again
177
	 * @param string $css
178
	 * @param string $webDir
179
	 * @return string
180
	 */
181
	private function rebaseUrls($css, $webDir) {
182
		$re = '/url\([\'"]([\.\w?=\/-]*)[\'"]\)/x';
183
		// OC\Route\Router:75
184
		if(($this->systemConfig->getValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true')) {
185
			$subst = 'url(\'../../'.$webDir.'/$1\')';	
186
		} else {
187
			$subst = 'url(\'../../../'.$webDir.'/$1\')';
188
		}
189
		return preg_replace($re, $subst, $css);
190
	}
191
192
	/**
193
	 * Return the cached css file uri
194
	 * @param string $appName the app name
195
	 * @param string $fileName
196
	 * @return string
197
	 */
198
	public function getCachedSCSS($appName, $fileName) {
199
		$tmpfileLoc = explode('/', $fileName);
200
		$fileName = array_pop($tmpfileLoc);
201
		$fileName = str_replace('.scss', '.css', $fileName);
202
203
		return substr($this->urlGenerator->linkToRoute('core.Css.getCss', array('fileName' => $fileName, 'appName' => $appName)), strlen(\OC::$WEBROOT) + 1);
204
	}
205
}
206