PluginProbe
MaxButtons – Create buttons / 6.28
MaxButtons – Create buttons v6.28
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.28, at assets/libraries/scssphp/src/Server.php

460 lines 11.3 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 $metadataName = $this->metadataName($out);
133
134 if (is_readable($metadataName)) {
135 $metadata = unserialize(file_get_contents($metadataName));
136
137 foreach ($metadata['imports'] as $import => $originalMtime) {
138 $currentMtime = filemtime($import);
139
140 if ($currentMtime !== $originalMtime || $currentMtime > $mtime) {
141 return true;
142 }
143 }
144
145 $metaVars = crc32(serialize($this->scss->getVariables()));
146
147 if ($metaVars !== $metadata['vars']) {
148 return true;
149 }
150
151 $etag = $metadata['etag'];
152
153 return false;
154 }
155
156 return true;
157 }
158
159 /**
160 * Get If-Modified-Since header from client request
161 *
162 * @return string|null
163 */
164 protected function getIfModifiedSinceHeader()
165 {
166 $modifiedSince = null;
167
168 if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
169 $modifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
170
171 if (false !== ($semicolonPos = strpos($modifiedSince, ';'))) {
172 $modifiedSince = substr($modifiedSince, 0, $semicolonPos);
173 }
174 }
175
176 return $modifiedSince;
177 }
178
179 /**
180 * Get If-None-Match header from client request
181 *
182 * @return string|null
183 */
184 protected function getIfNoneMatchHeader()
185 {
186 $noneMatch = null;
187
188 if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
189 $noneMatch = $_SERVER['HTTP_IF_NONE_MATCH'];
190 }
191
192 return $noneMatch;
193 }
194
195 /**
196 * Compile .scss file
197 *
198 * @param string $in Input path (.scss)
199 * @param string $out Output path (.css)
200 *
201 * @return array
202 */
203 protected function compile($in, $out)
204 {
205 $start = microtime(true);
206 $css = $this->scss->compile(file_get_contents($in), $in);
207 $elapsed = round((microtime(true) - $start), 4);
208
209 $v = Version::VERSION;
210 $t = @date('r');
211 $css = "/* compiled by scssphp $v on $t (${elapsed}s) */\n\n" . $css;
212 $etag = md5($css);
213
214 file_put_contents($out, $css);
215 file_put_contents(
216 $this->metadataName($out),
217 serialize(array(
218 'etag' => $etag,
219 'imports' => $this->scss->getParsedFiles(),
220 'vars' => crc32(serialize($this->scss->getVariables())),
221 ))
222 );
223
224 return array($css, $etag);
225 }
226
227 /**
228 * Format error as a pseudo-element in CSS
229 *
230 * @param \Exception $error
231 *
232 * @return string
233 */
234 protected function createErrorCSS($error)
235 {
236 $message = str_replace(
237 array("'", "\n"),
238 array("\\'", "\\A"),
239 $error->getfile() . ":\n\n" . $error->getMessage()
240 );
241
242 return "body { display: none !important; }
243 html:after {
244 background: white;
245 color: black;
246 content: '$message';
247 display: block !important;
248 font-family: mono;
249 padding: 1em;
250 white-space: pre;
251 }";
252 }
253
254 /**
255 * Render errors as a pseudo-element within valid CSS, displaying the errors on any
256 * page that includes this CSS.
257 *
258 * @param boolean $show
259 */
260 public function showErrorsAsCSS($show = true)
261 {
262 $this->showErrorsAsCSS = $show;
263 }
264
265 /**
266 * Compile .scss file
267 *
268 * @param string $in Input file (.scss)
269 * @param string $out Output file (.css) optional
270 *
271 * @return string|bool
272 */
273 public function compileFile($in, $out = null)
274 {
275 if (! is_readable($in)) {
276 throw new \Exception('load error: failed to find ' . $in);
277 }
278
279 $pi = pathinfo($in);
280
281 $this->scss->addImportPath($pi['dirname'] . '/');
282
283 $compiled = $this->scss->compile(file_get_contents($in), $in);
284
285 if ($out !== null) {
286 return file_put_contents($out, $compiled);
287 }
288
289 return $compiled;
290 }
291
292 /**
293 * Check if file need compiling
294 *
295 * @param string $in Input file (.scss)
296 * @param string $out Output file (.css)
297 *
298 * @return bool
299 */
300 public function checkedCompile($in, $out)
301 {
302 if (! is_file($out) || filemtime($in) > filemtime($out)) {
303 $this->compileFile($in, $out);
304
305 return true;
306 }
307
308 return false;
309 }
310
311 /**
312 * Compile requested scss and serve css. Outputs HTTP response.
313 *
314 * @param string $salt Prefix a string to the filename for creating the cache name hash
315 */
316 public function serve($salt = '')
317 {
318 $protocol = isset($_SERVER['SERVER_PROTOCOL'])
319 ? $_SERVER['SERVER_PROTOCOL']
320 : 'HTTP/1.0';
321
322 if ($input = $this->findInput()) {
323 $output = $this->cacheName($salt . $input);
324 $etag = $noneMatch = trim($this->getIfNoneMatchHeader(), '"');
325
326 if ($this->needsCompile($input, $output, $etag)) {
327 try {
328 list($css, $etag) = $this->compile($input, $output);
329
330 $lastModified = gmdate('D, d M Y H:i:s', filemtime($output)) . ' GMT';
331
332 header('Last-Modified: ' . $lastModified);
333 header('Content-type: text/css');
334 header('ETag: "' . $etag . '"');
335
336 echo $css;
337
338 } catch (\Exception $e) {
339 if ($this->showErrorsAsCSS) {
340 header('Content-type: text/css');
341
342 echo $this->createErrorCSS($e);
343 } else {
344 header($protocol . ' 500 Internal Server Error');
345 header('Content-type: text/plain');
346
347 echo 'Parse error: ' . $e->getMessage() . "\n";
348 }
349
350 }
351
352 return;
353 }
354
355 header('X-SCSS-Cache: true');
356 header('Content-type: text/css');
357 header('ETag: "' . $etag . '"');
358
359 if ($etag === $noneMatch) {
360 header($protocol . ' 304 Not Modified');
361
362 return;
363 }
364
365 $modifiedSince = $this->getIfModifiedSinceHeader();
366 $mtime = filemtime($output);
367
368 if (@strtotime($modifiedSince) === $mtime) {
369 header($protocol . ' 304 Not Modified');
370
371 return;
372 }
373
374 $lastModified = gmdate('D, d M Y H:i:s', $mtime) . ' GMT';
375 header('Last-Modified: ' . $lastModified);
376
377 echo file_get_contents($output);
378
379 return;
380 }
381
382 header($protocol . ' 404 Not Found');
383 header('Content-type: text/plain');
384
385 $v = Version::VERSION;
386 echo "/* INPUT NOT FOUND scss $v */\n";
387 }
388
389 /**
390 * Based on explicit input/output files does a full change check on cache before compiling.
391 *
392 * @param string $in
393 * @param string $out
394 * @param boolean $force
395 *
396 * @return string Compiled CSS results
397 *
398 * @throws \Exception
399 */
400 public function checkedCachedCompile($in, $out, $force = false)
401 {
402 if (! is_file($in) || ! is_readable($in)) {
403 throw new \Exception('Invalid or unreadable input file specified.');
404 }
405
406 if (is_dir($out) || ! is_writable(file_exists($out) ? $out : dirname($out))) {
407 throw new \Exception('Invalid or unwritable output file specified.');
408 }
409
410 if ($force || $this->needsCompile($in, $out, $etag)) {
411 list($css, $etag) = $this->compile($in, $out);
412 } else {
413 $css = file_get_contents($out);
414 }
415
416 return $css;
417 }
418
419 /**
420 * Constructor
421 *
422 * @param string $dir Root directory to .scss files
423 * @param string $cacheDir Cache directory
424 * @param \Leafo\ScssPhp\Compiler|null $scss SCSS compiler instance
425 */
426 public function __construct($dir, $cacheDir = null, $scss = null)
427 {
428 $this->dir = $dir;
429
430 if (! isset($cacheDir)) {
431 $cacheDir = $this->join($dir, 'scss_cache');
432 }
433
434 $this->cacheDir = $cacheDir;
435
436 if (! is_dir($this->cacheDir)) {
437 mkdir($this->cacheDir, 0755, true);
438 }
439
440 if (! isset($scss)) {
441 $scss = new Compiler();
442 $scss->setImportPaths($this->dir);
443 }
444
445 $this->scss = $scss;
446 $this->showErrorsAsCSS = false;
447 }
448
449 /**
450 * Helper method to serve compiled scss
451 *
452 * @param string $path Root path
453 */
454 public static function serveFrom($path)
455 {
456 $server = new self($path);
457 $server->serve();
458 }
459 }
460