PluginProbe
MaxButtons – Create buttons / 6.3
MaxButtons – Create buttons v6.3
6.23 6.24 6.25 6.26 6.26.1 6.27 6.28 6.3 6.4 6.5 6.6 6.7 6.8 6.9 7.0 7.1 7.1.1 7.1.2 7.1.3 7.10 7.11 7.13 7.13.1 7.13.2 7.13.3 All 100 releases
maxbuttons / assets / libraries / scssphp / src / Server.php

Server.php in MaxButtons – Create buttons 6.3, at assets/libraries/scssphp/src/Server.php

455 lines 11.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * SCSSPHP
4 *
5 * @copyright 2012-2015 Leaf Corcoran
6 *
7 * @license http://opensource.org/licenses/MIT MIT
8 *
9 * @link http://leafo.github.io/scssphp
10 */
11
12 namespace Leafo\ScssPhp;
13
14 use Leafo\ScssPhp\Compiler;
15 use Leafo\ScssPhp\Version;
16
17 /**
18 * SCSS server
19 *
20 * @author Leaf Corcoran <leafot@gmail.com>
21 */
22 class Server
23 {
24 /**
25 * @var boolean
26 */
27 private $showErrorsAsCSS;
28
29 /**
30 * @var string
31 */
32 private $dir;
33
34 /**
35 * @var string
36 */
37 private $cacheDir;
38
39 /**
40 * @var \Leafo\ScssPhp\Compiler
41 */
42 private $scss;
43
44 /**
45 * Join path components
46 *
47 * @param string $left Path component, left of the directory separator
48 * @param string $right Path component, right of the directory separator
49 *
50 * @return string
51 */
52 protected function join($left, $right)
53 {
54 return rtrim($left, '/\\') . DIRECTORY_SEPARATOR . ltrim($right, '/\\');
55 }
56
57 /**
58 * Get name of requested .scss file
59 *
60 * @return string|null
61 */
62 protected function inputName()
63 {
64 switch (true) {
65 case isset($_GET['p']):
66 return $_GET['p'];
67 case isset($_SERVER['PATH_INFO']):
68 return $_SERVER['PATH_INFO'];
69 case isset($_SERVER['DOCUMENT_URI']):
70 return substr($_SERVER['DOCUMENT_URI'], strlen($_SERVER['SCRIPT_NAME']));
71 }
72 }
73
74 /**
75 * Get path to requested .scss file
76 *
77 * @return string
78 */
79 protected function findInput()
80 {
81 if (($input = $this->inputName())
82 && strpos($input, '..') === false
83 && substr($input, -5) === '.scss'
84 ) {
85 $name = $this->join($this->dir, $input);
86
87 if (is_file($name) && is_readable($name)) {
88 return $name;
89 }
90 }
91
92 return false;
93 }
94
95 /**
96 * Get path to cached .css file
97 *
98 * @return string
99 */
100 protected function cacheName($fname)
101 {
102 return $this->join($this->cacheDir, md5($fname) . '.css');
103 }
104
105 /**
106 * Get path to meta data
107 *
108 * @return string
109 */
110 protected function metadataName($out)
111 {
112 return $out . '.meta';
113 }
114
115 /**
116 * Determine whether .scss file needs to be re-compiled.
117 *
118 * @param string $in Input path
119 * @param string $out Output path
120 * @param string $etag ETag
121 *
122 * @return boolean True if compile required.
123 */
124 protected function needsCompile($in, $out, &$etag)
125 {
126 if (! is_file($out)) {
127 return true;
128 }
129
130 $mtime = filemtime($out);
131
132 if (filemtime($in) > $mtime) {
133 return true;
134 }
135
136 $metadataName = $this->metadataName($out);
137
138 if (is_readable($metadataName)) {
139 $metadata = unserialize(file_get_contents($metadataName));
140
141 foreach ($metadata['imports'] as $import => $importMtime) {
142 if ($importMtime > $mtime) {
143 return true;
144 }
145 }
146
147 $etag = $metadata['etag'];
148
149 return false;
150 }
151
152 return true;
153 }
154
155 /**
156 * Get If-Modified-Since header from client request
157 *
158 * @return string|null
159 */
160 protected function getIfModifiedSinceHeader()
161 {
162 $modifiedSince = null;
163
164 if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
165 $modifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
166
167 if (false !== ($semicolonPos = strpos($modifiedSince, ';'))) {
168 $modifiedSince = substr($modifiedSince, 0, $semicolonPos);
169 }
170 }
171
172 return $modifiedSince;
173 }
174
175 /**
176 * Get If-None-Match header from client request
177 *
178 * @return string|null
179 */
180 protected function getIfNoneMatchHeader()
181 {
182 $noneMatch = null;
183
184 if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
185 $noneMatch = $_SERVER['HTTP_IF_NONE_MATCH'];
186 }
187
188 return $noneMatch;
189 }
190
191 /**
192 * Compile .scss file
193 *
194 * @param string $in Input path (.scss)
195 * @param string $out Output path (.css)
196 *
197 * @return array
198 */
199 protected function compile($in, $out)
200 {
201 $start = microtime(true);
202 $css = $this->scss->compile(file_get_contents($in), $in);
203 $elapsed = round((microtime(true) - $start), 4);
204
205 $v = Version::VERSION;
206 $t = @date('r');
207 $css = "/* compiled by scssphp $v on $t (${elapsed}s) */\n\n" . $css;
208 $etag = md5($css);
209
210 file_put_contents($out, $css);
211 file_put_contents(
212 $this->metadataName($out),
213 serialize(array(
214 'etag' => $etag,
215 'imports' => $this->scss->getParsedFiles(),
216 ))
217 );
218
219 return array($css, $etag);
220 }
221
222 /**
223 * Format error as a pseudo-element in CSS
224 *
225 * @param \Exception $error
226 *
227 * @return string
228 */
229 protected function createErrorCSS($error)
230 {
231 $message = str_replace(
232 array("'", "\n"),
233 array("\\'", "\\A"),
234 $error->getfile() . ":\n\n" . $error->getMessage()
235 );
236
237 return "body { display: none !important; }
238 html:after {
239 background: white;
240 color: black;
241 content: '$message';
242 display: block !important;
243 font-family: mono;
244 padding: 1em;
245 white-space: pre;
246 }";
247 }
248
249 /**
250 * Render errors as a pseudo-element within valid CSS, displaying the errors on any
251 * page that includes this CSS.
252 *
253 * @param boolean $show
254 */
255 public function showErrorsAsCSS($show = true)
256 {
257 $this->showErrorsAsCSS = $show;
258 }
259
260 /**
261 * Compile .scss file
262 *
263 * @param string $in Input file (.scss)
264 * @param string $out Output file (.css) optional
265 *
266 * @return string|bool
267 */
268 public function compileFile($in, $out = null)
269 {
270 if (! is_readable($in)) {
271 throw new \Exception('load error: failed to find ' . $in);
272 }
273
274 $pi = pathinfo($in);
275
276 $this->scss->addImportPath($pi['dirname'] . '/');
277
278 $compiled = $this->scss->compile(file_get_contents($in), $in);
279
280 if ($out !== null) {
281 return file_put_contents($out, $compiled);
282 }
283
284 return $compiled;
285 }
286
287 /**
288 * Check if file need compiling
289 *
290 * @param string $in Input file (.scss)
291 * @param string $out Output file (.css)
292 *
293 * @return bool
294 */
295 public function checkedCompile($in, $out)
296 {
297 if (! is_file($out) || filemtime($in) > filemtime($out)) {
298 $this->compileFile($in, $out);
299
300 return true;
301 }
302
303 return false;
304 }
305
306 /**
307 * Compile requested scss and serve css. Outputs HTTP response.
308 *
309 * @param string $salt Prefix a string to the filename for creating the cache name hash
310 */
311 public function serve($salt = '')
312 {
313 $protocol = isset($_SERVER['SERVER_PROTOCOL'])
314 ? $_SERVER['SERVER_PROTOCOL']
315 : 'HTTP/1.0';
316
317 if ($input = $this->findInput()) {
318 $output = $this->cacheName($salt . $input);
319 $etag = $noneMatch = trim($this->getIfNoneMatchHeader(), '"');
320
321 if ($this->needsCompile($input, $output, $etag)) {
322 try {
323 list($css, $etag) = $this->compile($input, $output);
324
325 $lastModified = gmdate('D, d M Y H:i:s', filemtime($output)) . ' GMT';
326
327 header('Last-Modified: ' . $lastModified);
328 header('Content-type: text/css');
329 header('ETag: "' . $etag . '"');
330
331 echo $css;
332
333 } catch (\Exception $e) {
334 if ($this->showErrorsAsCSS) {
335 header('Content-type: text/css');
336
337 echo $this->createErrorCSS($e);
338 } else {
339 header($protocol . ' 500 Internal Server Error');
340 header('Content-type: text/plain');
341
342 echo 'Parse error: ' . $e->getMessage() . "\n";
343 }
344
345 }
346
347 return;
348 }
349
350 header('X-SCSS-Cache: true');
351 header('Content-type: text/css');
352 header('ETag: "' . $etag . '"');
353
354 if ($etag === $noneMatch) {
355 header($protocol . ' 304 Not Modified');
356
357 return;
358 }
359
360 $modifiedSince = $this->getIfModifiedSinceHeader();
361 $mtime = filemtime($output);
362
363 if (@strtotime($modifiedSince) === $mtime) {
364 header($protocol . ' 304 Not Modified');
365
366 return;
367 }
368
369 $lastModified = gmdate('D, d M Y H:i:s', $mtime) . ' GMT';
370 header('Last-Modified: ' . $lastModified);
371
372 echo file_get_contents($output);
373
374 return;
375 }
376
377 header($protocol . ' 404 Not Found');
378 header('Content-type: text/plain');
379
380 $v = Version::VERSION;
381 echo "/* INPUT NOT FOUND scss $v */\n";
382 }
383
384 /**
385 * Based on explicit input/output files does a full change check on cache before compiling.
386 *
387 * @param string $in
388 * @param string $out
389 * @param boolean $force
390 *
391 * @return string Compiled CSS results
392 *
393 * @throws \Exception
394 */
395 public function checkedCachedCompile($in, $out, $force = false)
396 {
397 if (! is_file($in) || ! is_readable($in)) {
398 throw new \Exception('Invalid or unreadable input file specified.');
399 }
400
401 if (is_dir($out) || ! is_writable(file_exists($out) ? $out : dirname($out))) {
402 throw new \Exception('Invalid or unwritable output file specified.');
403 }
404
405 if ($force || $this->needsCompile($in, $out, $etag)) {
406 list($css, $etag) = $this->compile($in, $out);
407 } else {
408 $css = file_get_contents($out);
409 }
410
411 return $css;
412 }
413
414 /**
415 * Constructor
416 *
417 * @param string $dir Root directory to .scss files
418 * @param string $cacheDir Cache directory
419 * @param \Leafo\ScssPhp\Compiler|null $scss SCSS compiler instance
420 */
421 public function __construct($dir, $cacheDir = null, $scss = null)
422 {
423 $this->dir = $dir;
424
425 if (! isset($cacheDir)) {
426 $cacheDir = $this->join($dir, 'scss_cache');
427 }
428
429 $this->cacheDir = $cacheDir;
430
431 if (! is_dir($this->cacheDir)) {
432 mkdir($this->cacheDir, 0755, true);
433 }
434
435 if (! isset($scss)) {
436 $scss = new Compiler();
437 $scss->setImportPaths($this->dir);
438 }
439
440 $this->scss = $scss;
441 $this->showErrorsAsCSS = false;
442 }
443
444 /**
445 * Helper method to serve compiled scss
446 *
447 * @param string $path Root path
448 */
449 public static function serveFrom($path)
450 {
451 $server = new self($path);
452 $server->serve();
453 }
454 }
455