MDL-61529 core: update lib scssphp to version 0.7.5

This commit is contained in:
Bas Brands 2018-03-16 10:36:40 +01:00
parent b63a3b04b1
commit c8842113a7
28 changed files with 834 additions and 564 deletions

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2017 Leaf Corcoran
* @copyright 2015-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
@ -28,6 +28,11 @@ class Block
*/
public $parent;
/**
* @var string
*/
public $sourceName;
/**
* @var integer
*/

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
@ -18,6 +18,7 @@ use Leafo\ScssPhp\Compiler\Environment;
use Leafo\ScssPhp\Exception\CompilerException;
use Leafo\ScssPhp\Formatter\OutputBlock;
use Leafo\ScssPhp\Node;
use Leafo\ScssPhp\SourceMap\SourceMapGenerator;
use Leafo\ScssPhp\Type;
use Leafo\ScssPhp\Parser;
use Leafo\ScssPhp\Util;
@ -64,6 +65,10 @@ class Compiler
const WITH_SUPPORTS = 4;
const WITH_ALL = 7;
const SOURCE_MAP_NONE = 0;
const SOURCE_MAP_INLINE = 1;
const SOURCE_MAP_FILE = 2;
/**
* @var array
*/
@ -120,11 +125,20 @@ class Compiler
protected $encoding = null;
protected $lineNumberStyle = null;
protected $sourceMap = self::SOURCE_MAP_NONE;
protected $sourceMapOptions = [];
/**
* @var string|\Leafo\ScssPhp\Formatter
*/
protected $formatter = 'Leafo\ScssPhp\Formatter\Nested';
protected $rootEnv;
protected $rootBlock;
/**
* @var \Leafo\ScssPhp\Compiler\Environment
*/
protected $env;
protected $scope;
protected $storeEnv;
@ -165,9 +179,6 @@ class Compiler
*/
public function compile($code, $path = null)
{
$locale = setlocale(LC_NUMERIC, 0);
setlocale(LC_NUMERIC, 'C');
$this->indentLevel = -1;
$this->commentsSeen = [];
$this->extends = [];
@ -194,9 +205,35 @@ class Compiler
$this->compileRoot($tree);
$this->popEnv();
$out = $this->formatter->format($this->scope);
$sourceMapGenerator = null;
setlocale(LC_NUMERIC, $locale);
if ($this->sourceMap) {
if (is_object($this->sourceMap) && $this->sourceMap instanceof SourceMapGenerator) {
$sourceMapGenerator = $this->sourceMap;
$this->sourceMap = self::SOURCE_MAP_FILE;
} elseif ($this->sourceMap !== self::SOURCE_MAP_NONE) {
$sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions);
}
}
$out = $this->formatter->format($this->scope, $sourceMapGenerator);
if (! empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) {
$sourceMap = $sourceMapGenerator->generateJson();
$sourceMapUrl = null;
switch ($this->sourceMap) {
case self::SOURCE_MAP_INLINE:
$sourceMapUrl = sprintf('data:application/json,%s', Util::encodeURIComponent($sourceMap));
break;
case self::SOURCE_MAP_FILE:
$sourceMapUrl = $sourceMapGenerator->saveMap($sourceMap);
break;
}
$out .= sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl);
}
return $out;
}
@ -273,12 +310,15 @@ class Compiler
protected function makeOutputBlock($type, $selectors = null)
{
$out = new OutputBlock;
$out->type = $type;
$out->lines = [];
$out->children = [];
$out->parent = $this->scope;
$out->selectors = $selectors;
$out->depth = $this->env->depth;
$out->type = $type;
$out->lines = [];
$out->children = [];
$out->parent = $this->scope;
$out->selectors = $selectors;
$out->depth = $this->env->depth;
$out->sourceName = $this->env->block->sourceName;
$out->sourceLine = $this->env->block->sourceLine;
$out->sourceColumn = $this->env->block->sourceColumn;
return $out;
}
@ -661,6 +701,7 @@ class Compiler
if ($needsWrap) {
$wrapped = new Block;
$wrapped->sourceName = $media->sourceName;
$wrapped->sourceIndex = $media->sourceIndex;
$wrapped->sourceLine = $media->sourceLine;
$wrapped->sourceColumn = $media->sourceColumn;
@ -734,6 +775,7 @@ class Compiler
// wrap inline selector
if ($block->selector) {
$wrapped = new Block;
$wrapped->sourceName = $block->sourceName;
$wrapped->sourceIndex = $block->sourceIndex;
$wrapped->sourceLine = $block->sourceLine;
$wrapped->sourceColumn = $block->sourceColumn;
@ -790,6 +832,7 @@ class Compiler
}
$b = new Block;
$b->sourceName = $e->block->sourceName;
$b->sourceIndex = $e->block->sourceIndex;
$b->sourceLine = $e->block->sourceLine;
$b->sourceColumn = $e->block->sourceColumn;
@ -1181,7 +1224,7 @@ class Compiler
/**
* Compile selector to string; self(&) should have been replaced by now
*
* @param array $selector
* @param string|array $selector
*
* @return string
*/
@ -1203,7 +1246,7 @@ class Compiler
/**
* Compile selector part
*
* @param arary $piece
* @param array $piece
*
* @return string
*/
@ -1729,9 +1772,18 @@ class Compiler
list(, $for) = $child;
$start = $this->reduce($for->start, true);
$end = $this->reduce($for->end, true);
if (! ($start[2] == $end[2] || $end->unitless())) {
$this->throwError('Incompatible units: "%s" and "%s".', $start->unitStr(), $end->unitStr());
break;
}
$unit = $start[2];
$start = $start[1];
$end = $this->reduce($for->end, true);
$end = $end[1];
$end = $end[1];
$d = $start < $end ? 1 : -1;
for (;;) {
@ -1741,7 +1793,7 @@ class Compiler
break;
}
$this->set($for->var, new Node\Number($start, ''));
$this->set($for->var, new Node\Number($start, $unit));
$start += $d;
$ret = $this->compileChildren($for->children, $out);
@ -1926,7 +1978,7 @@ class Compiler
*
* @param string $value
*
* @return bool
* @return boolean
*/
protected function isImmediateRelationshipCombinator($value)
{
@ -1963,7 +2015,7 @@ class Compiler
* @param array $value
* @param boolean $inExp
*
* @return array
* @return array|\Leafo\ScssPhp\Node\Number
*/
protected function reduce($value, $inExp = false)
{
@ -2238,7 +2290,7 @@ class Compiler
* @param array $left
* @param array $right
*
* @return array
* @return \Leafo\ScssPhp\Node\Number
*/
protected function opAddNumberNumber($left, $right)
{
@ -2251,7 +2303,7 @@ class Compiler
* @param array $left
* @param array $right
*
* @return array
* @return \Leafo\ScssPhp\Node\Number
*/
protected function opMulNumberNumber($left, $right)
{
@ -2264,7 +2316,7 @@ class Compiler
* @param array $left
* @param array $right
*
* @return array
* @return \Leafo\ScssPhp\Node\Number
*/
protected function opSubNumberNumber($left, $right)
{
@ -2277,7 +2329,7 @@ class Compiler
* @param array $left
* @param array $right
*
* @return array
* @return array|\Leafo\ScssPhp\Node\Number
*/
protected function opDivNumberNumber($left, $right)
{
@ -2294,7 +2346,7 @@ class Compiler
* @param array $left
* @param array $right
*
* @return array
* @return \Leafo\ScssPhp\Node\Number
*/
protected function opModNumberNumber($left, $right)
{
@ -2580,7 +2632,7 @@ class Compiler
* @param array $left
* @param array $right
*
* @return array
* @return \Leafo\ScssPhp\Node\Number
*/
protected function opCmpNumberNumber($left, $right)
{
@ -3304,6 +3356,30 @@ class Compiler
$this->lineNumberStyle = $lineNumberStyle;
}
/**
* Enable/disable source maps
*
* @api
*
* @param integer $sourceMap
*/
public function setSourceMap($sourceMap)
{
$this->sourceMap = $sourceMap;
}
/**
* Set source map options
*
* @api
*
* @param array $sourceMapOptions
*/
public function setSourceMapOptions($sourceMapOptions)
{
$this->sourceMapOptions = $sourceMapOptions;
}
/**
* Register function
*
@ -3505,7 +3581,7 @@ class Compiler
* Call SCSS @function
*
* @param string $name
* @param array $args
* @param array $argValues
* @param array $returnValue
*
* @return boolean Returns true if returnValue is set; otherwise, false
@ -3777,7 +3853,7 @@ class Compiler
*
* @param mixed $value
*
* @return array
* @return array|\Leafo\ScssPhp\Node\Number
*/
private function coerceValue($value)
{
@ -3850,7 +3926,8 @@ class Compiler
/**
* Coerce something to list
*
* @param array $item
* @param array $item
* @param string $delim
*
* @return array
*/
@ -4672,36 +4749,32 @@ class Compiler
protected function libRound($args)
{
$num = $args[0];
$num[1] = round($num[1]);
return $num;
return new Node\Number(round($num[1]), $num[2]);
}
protected static $libFloor = ['value'];
protected function libFloor($args)
{
$num = $args[0];
$num[1] = floor($num[1]);
return $num;
return new Node\Number(floor($num[1]), $num[2]);
}
protected static $libCeil = ['value'];
protected function libCeil($args)
{
$num = $args[0];
$num[1] = ceil($num[1]);
return $num;
return new Node\Number(ceil($num[1]), $num[2]);
}
protected static $libAbs = ['value'];
protected function libAbs($args)
{
$num = $args[0];
$num[1] = abs($num[1]);
return $num;
return new Node\Number(abs($num[1]), $num[2]);
}
protected function libMin($args)
@ -5129,7 +5202,7 @@ class Compiler
$string = $this->coerceString($args[0]);
$stringContent = $this->compileStringContent($string);
$string[2] = [mb_strtolower($stringContent)];
$string[2] = [function_exists('mb_strtolower') ? mb_strtolower($stringContent) : strtolower($stringContent)];
return $string;
}
@ -5140,7 +5213,7 @@ class Compiler
$string = $this->coerceString($args[0]);
$stringContent = $this->compileStringContent($string);
$string[2] = [mb_strtoupper($stringContent)];
$string[2] = [function_exists('mb_strtoupper') ? mb_strtoupper($stringContent) : strtoupper($stringContent)];
return $string;
}
@ -5210,6 +5283,8 @@ class Compiler
* Workaround IE7's content counter bug.
*
* @param array $args
*
* @return array
*/
protected function libCounter($args)
{

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
@ -12,6 +12,7 @@
namespace Leafo\ScssPhp;
use Leafo\ScssPhp\Formatter\OutputBlock;
use Leafo\ScssPhp\SourceMap\SourceMapGenerator;
/**
* Base formatter
@ -56,10 +57,30 @@ abstract class Formatter
public $assignSeparator;
/**
* @var boolea
* @var boolean
*/
public $keepSemicolons;
/**
* @var \Leafo\ScssPhp\Formatter\OutputBlock
*/
protected $currentBlock;
/**
* @var integer
*/
protected $currentLine;
/**
* @var integer
*/
protected $currentColumn;
/**
* @var \Leafo\ScssPhp\SourceMap\SourceMapGenerator
*/
protected $sourceMapGenerator;
/**
* Initialize formatter
*
@ -123,10 +144,10 @@ abstract class Formatter
$glue = $this->break . $inner;
echo $inner . implode($glue, $block->lines);
$this->write($inner . implode($glue, $block->lines));
if (! empty($block->children)) {
echo $this->break;
$this->write($this->break);
}
}
@ -139,9 +160,9 @@ abstract class Formatter
{
$inner = $this->indentStr();
echo $inner
$this->write($inner
. implode($this->tagSeparator, $block->selectors)
. $this->open . $this->break;
. $this->open . $this->break);
}
/**
@ -167,6 +188,8 @@ abstract class Formatter
return;
}
$this->currentBlock = $block;
$pre = $this->indentStr();
if (! empty($block->selectors)) {
@ -187,10 +210,10 @@ abstract class Formatter
$this->indentLevel--;
if (empty($block->children)) {
echo $this->break;
$this->write($this->break);
}
echo $pre . $this->close . $this->break;
$this->write($pre . $this->close . $this->break);
}
}
@ -199,12 +222,21 @@ abstract class Formatter
*
* @api
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $block An abstract syntax tree
* @param \Leafo\ScssPhp\Formatter\OutputBlock $block An abstract syntax tree
* @param \Leafo\ScssPhp\SourceMap\SourceMapGenerator|null $sourceMapGenerator Optional source map generator
*
* @return string
*/
public function format(OutputBlock $block)
public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null)
{
$this->sourceMapGenerator = null;
if ($sourceMapGenerator) {
$this->currentLine = 1;
$this->currentColumn = 0;
$this->sourceMapGenerator = $sourceMapGenerator;
}
ob_start();
$this->block($block);
@ -213,4 +245,30 @@ abstract class Formatter
return $out;
}
/**
* @param string $str
*/
protected function write($str)
{
if ($this->sourceMapGenerator) {
$this->sourceMapGenerator->addMapping(
$this->currentLine,
$this->currentColumn,
$this->currentBlock->sourceLine,
$this->currentBlock->sourceColumn - 1, //columns from parser are off by one
$this->currentBlock->sourceName
);
$lines = explode("\n", $str);
$lineCount = count($lines);
$this->currentLine += $lineCount-1;
$lastLine = array_pop($lines);
$this->currentColumn = ($lineCount === 1 ? $this->currentColumn : 0) + strlen($lastLine);
}
echo $str;
}
}

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
@ -53,10 +53,10 @@ class Compressed extends Formatter
}
}
echo $inner . implode($glue, $block->lines);
$this->write($inner . implode($glue, $block->lines));
if (! empty($block->children)) {
echo $this->break;
$this->write($this->break);
}
}
}

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
@ -51,10 +51,10 @@ class Crunched extends Formatter
}
}
echo $inner . implode($glue, $block->lines);
$this->write($inner . implode($glue, $block->lines));
if (! empty($block->children)) {
echo $this->break;
$this->write($this->break);
}
}
}

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
@ -52,13 +52,13 @@ class Debug extends Formatter
$indent = $this->indentStr();
if (empty($block->lines)) {
echo "{$indent}block->lines: []\n";
$this->write("{$indent}block->lines: []\n");
return;
}
foreach ($block->lines as $index => $line) {
echo "{$indent}block->lines[{$index}]: $line\n";
$this->write("{$indent}block->lines[{$index}]: $line\n");
}
}
@ -70,13 +70,13 @@ class Debug extends Formatter
$indent = $this->indentStr();
if (empty($block->selectors)) {
echo "{$indent}block->selectors: []\n";
$this->write("{$indent}block->selectors: []\n");
return;
}
foreach ($block->selectors as $index => $selector) {
echo "{$indent}block->selectors[{$index}]: $selector\n";
$this->write("{$indent}block->selectors[{$index}]: $selector\n");
}
}
@ -88,7 +88,7 @@ class Debug extends Formatter
$indent = $this->indentStr();
if (empty($block->children)) {
echo "{$indent}block->children: []\n";
$this->write("{$indent}block->children: []\n");
return;
}
@ -109,8 +109,10 @@ class Debug extends Formatter
{
$indent = $this->indentStr();
echo "{$indent}block->type: {$block->type}\n" .
"{$indent}block->depth: {$block->depth}\n";
$this->write("{$indent}block->type: {$block->type}\n" .
"{$indent}block->depth: {$block->depth}\n");
$this->currentBlock = $block;
$this->blockSelectors($block);
$this->blockLines($block);

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
@ -59,10 +59,10 @@ class Expanded extends Formatter
}
}
echo $inner . implode($glue, $block->lines);
$this->write($inner . implode($glue, $block->lines));
if (empty($block->selectors) || ! empty($block->children)) {
echo $this->break;
$this->write($this->break);
}
}
}

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
@ -66,10 +66,10 @@ class Nested extends Formatter
}
}
echo $inner . implode($glue, $block->lines);
$this->write($inner . implode($glue, $block->lines));
if (! empty($block->children)) {
echo $this->break;
$this->write($this->break);
}
}
@ -80,9 +80,9 @@ class Nested extends Formatter
{
$inner = $this->indentStr();
echo $inner
$this->write($inner
. implode($this->tagSeparator, $block->selectors)
. $this->open . $this->break;
. $this->open . $this->break);
}
/**
@ -94,13 +94,13 @@ class Nested extends Formatter
$this->block($child);
if ($i < count($block->children) - 1) {
echo $this->break;
$this->write($this->break);
if (isset($block->children[$i + 1])) {
$next = $block->children[$i + 1];
if ($next->depth === max($block->depth, 1) && $child->depth >= $next->depth) {
echo $this->break;
$this->write($this->break);
}
}
}
@ -120,6 +120,9 @@ class Nested extends Formatter
return;
}
$this->currentBlock = $block;
$this->depth = $block->depth;
if (! empty($block->selectors)) {
@ -139,11 +142,11 @@ class Nested extends Formatter
if (! empty($block->selectors)) {
$this->indentLevel--;
echo $this->close;
$this->write($this->close);
}
if ($block->type === 'root') {
echo $this->break;
$this->write($this->break);
}
}

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
@ -47,4 +47,19 @@ class OutputBlock
* @var \Leafo\ScssPhp\Formatter\OutputBlock
*/
public $parent;
/**
* @var string
*/
public $sourceName;
/**
* @var integer
*/
public $sourceLine;
/**
* @var integer
*/
public $sourceColumn;
}

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
@ -31,7 +31,7 @@ class Number extends Node implements \ArrayAccess
/**
* @var integer
*/
static public $precision = 5;
static public $precision = 10;
/**
* @see http://www.w3.org/TR/2012/WD-css3-values-20120308/
@ -291,8 +291,9 @@ class Number extends Node implements \ArrayAccess
reset($units);
$unit = key($units);
$dimension = number_format($dimension, static::$precision, '.', '');
return (string) $dimension . $unit;
return (static::$precision ? rtrim(rtrim($dimension, '0'), '.') : $dimension) . $unit;
}
/**

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
@ -708,6 +708,7 @@ class Parser
list($line, $column) = $this->getSourcePosition($pos);
$b = new Block;
$b->sourceName = $this->sourceName;
$b->sourceLine = $line;
$b->sourceColumn = $column;
$b->sourceIndex = $this->sourceIndex;
@ -2468,7 +2469,13 @@ class Parser
*/
private function saveEncoding()
{
if (ini_get('mbstring.func_overload') & 2) {
if (version_compare(PHP_VERSION, '7.2.0') >= 0) {
return;
}
$iniDirective = 'mbstring' . '.func_overload'; // deprecated in PHP 7.2
if (ini_get($iniDirective) & 2) {
$this->encoding = mb_internal_encoding();
mb_internal_encoding('iso-8859-1');

View File

@ -1,463 +0,0 @@
<?php
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp;
use Leafo\ScssPhp\Compiler;
use Leafo\ScssPhp\Exception\ServerException;
use Leafo\ScssPhp\Version;
/**
* Server
*
* @author Leaf Corcoran <leafot@gmail.com>
*/
class Server
{
/**
* @var boolean
*/
private $showErrorsAsCSS;
/**
* @var string
*/
private $dir;
/**
* @var string
*/
private $cacheDir;
/**
* @var \Leafo\ScssPhp\Compiler
*/
private $scss;
/**
* Join path components
*
* @param string $left Path component, left of the directory separator
* @param string $right Path component, right of the directory separator
*
* @return string
*/
protected function join($left, $right)
{
return rtrim($left, '/\\') . DIRECTORY_SEPARATOR . ltrim($right, '/\\');
}
/**
* Get name of requested .scss file
*
* @return string|null
*/
protected function inputName()
{
switch (true) {
case isset($_GET['p']):
return $_GET['p'];
case isset($_SERVER['PATH_INFO']):
return $_SERVER['PATH_INFO'];
case isset($_SERVER['DOCUMENT_URI']):
return substr($_SERVER['DOCUMENT_URI'], strlen($_SERVER['SCRIPT_NAME']));
}
}
/**
* Get path to requested .scss file
*
* @return string
*/
protected function findInput()
{
if (($input = $this->inputName())
&& strpos($input, '..') === false
&& substr($input, -5) === '.scss'
) {
$name = $this->join($this->dir, $input);
if (is_file($name) && is_readable($name)) {
return $name;
}
}
return false;
}
/**
* Get path to cached .css file
*
* @return string
*/
protected function cacheName($fname)
{
return $this->join($this->cacheDir, md5($fname) . '.css');
}
/**
* Get path to meta data
*
* @return string
*/
protected function metadataName($out)
{
return $out . '.meta';
}
/**
* Determine whether .scss file needs to be re-compiled.
*
* @param string $out Output path
* @param string $etag ETag
*
* @return boolean True if compile required.
*/
protected function needsCompile($out, &$etag)
{
if (! is_file($out)) {
return true;
}
$mtime = filemtime($out);
$metadataName = $this->metadataName($out);
if (is_readable($metadataName)) {
$metadata = unserialize(file_get_contents($metadataName));
foreach ($metadata['imports'] as $import => $originalMtime) {
$currentMtime = filemtime($import);
if ($currentMtime !== $originalMtime || $currentMtime > $mtime) {
return true;
}
}
$metaVars = crc32(serialize($this->scss->getVariables()));
if ($metaVars !== $metadata['vars']) {
return true;
}
$etag = $metadata['etag'];
return false;
}
return true;
}
/**
* Get If-Modified-Since header from client request
*
* @return string|null
*/
protected function getIfModifiedSinceHeader()
{
$modifiedSince = null;
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
$modifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
if (false !== ($semicolonPos = strpos($modifiedSince, ';'))) {
$modifiedSince = substr($modifiedSince, 0, $semicolonPos);
}
}
return $modifiedSince;
}
/**
* Get If-None-Match header from client request
*
* @return string|null
*/
protected function getIfNoneMatchHeader()
{
$noneMatch = null;
if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$noneMatch = $_SERVER['HTTP_IF_NONE_MATCH'];
}
return $noneMatch;
}
/**
* Compile .scss file
*
* @param string $in Input path (.scss)
* @param string $out Output path (.css)
*
* @return array
*/
protected function compile($in, $out)
{
$start = microtime(true);
$css = $this->scss->compile(file_get_contents($in), $in);
$elapsed = round((microtime(true) - $start), 4);
$v = Version::VERSION;
$t = date('r');
$css = "/* compiled by scssphp $v on $t (${elapsed}s) */\n\n" . $css;
$etag = md5($css);
file_put_contents($out, $css);
file_put_contents(
$this->metadataName($out),
serialize([
'etag' => $etag,
'imports' => $this->scss->getParsedFiles(),
'vars' => crc32(serialize($this->scss->getVariables())),
])
);
return [$css, $etag];
}
/**
* Format error as a pseudo-element in CSS
*
* @param \Exception $error
*
* @return string
*/
protected function createErrorCSS(\Exception $error)
{
$message = str_replace(
["'", "\n"],
["\\'", "\\A"],
$error->getfile() . ":\n\n" . $error->getMessage()
);
return "body { display: none !important; }
html:after {
background: white;
color: black;
content: '$message';
display: block !important;
font-family: mono;
padding: 1em;
white-space: pre;
}";
}
/**
* Render errors as a pseudo-element within valid CSS, displaying the errors on any
* page that includes this CSS.
*
* @param boolean $show
*/
public function showErrorsAsCSS($show = true)
{
$this->showErrorsAsCSS = $show;
}
/**
* Compile .scss file
*
* @param string $in Input file (.scss)
* @param string $out Output file (.css) optional
*
* @return string|bool
*
* @throws \Leafo\ScssPhp\Exception\ServerException
*/
public function compileFile($in, $out = null)
{
if (! is_readable($in)) {
throw new ServerException('load error: failed to find ' . $in);
}
$pi = pathinfo($in);
$this->scss->addImportPath($pi['dirname'] . '/');
$compiled = $this->scss->compile(file_get_contents($in), $in);
if ($out !== null) {
return file_put_contents($out, $compiled);
}
return $compiled;
}
/**
* Check if file need compiling
*
* @param string $in Input file (.scss)
* @param string $out Output file (.css)
*
* @return bool
*/
public function checkedCompile($in, $out)
{
if (! is_file($out) || filemtime($in) > filemtime($out)) {
$this->compileFile($in, $out);
return true;
}
return false;
}
/**
* Compile requested scss and serve css. Outputs HTTP response.
*
* @param string $salt Prefix a string to the filename for creating the cache name hash
*/
public function serve($salt = '')
{
$protocol = isset($_SERVER['SERVER_PROTOCOL'])
? $_SERVER['SERVER_PROTOCOL']
: 'HTTP/1.0';
if ($input = $this->findInput()) {
$output = $this->cacheName($salt . $input);
$etag = $noneMatch = trim($this->getIfNoneMatchHeader(), '"');
if ($this->needsCompile($output, $etag)) {
try {
list($css, $etag) = $this->compile($input, $output);
$lastModified = gmdate('D, d M Y H:i:s', filemtime($output)) . ' GMT';
header('Last-Modified: ' . $lastModified);
header('Content-type: text/css');
header('ETag: "' . $etag . '"');
echo $css;
} catch (\Exception $e) {
if ($this->showErrorsAsCSS) {
header('Content-type: text/css');
echo $this->createErrorCSS($e);
} else {
header($protocol . ' 500 Internal Server Error');
header('Content-type: text/plain');
echo 'Parse error: ' . $e->getMessage() . "\n";
}
}
return;
}
header('X-SCSS-Cache: true');
header('Content-type: text/css');
header('ETag: "' . $etag . '"');
if ($etag === $noneMatch) {
header($protocol . ' 304 Not Modified');
return;
}
$modifiedSince = $this->getIfModifiedSinceHeader();
$mtime = filemtime($output);
if (strtotime($modifiedSince) === $mtime) {
header($protocol . ' 304 Not Modified');
return;
}
$lastModified = gmdate('D, d M Y H:i:s', $mtime) . ' GMT';
header('Last-Modified: ' . $lastModified);
echo file_get_contents($output);
return;
}
header($protocol . ' 404 Not Found');
header('Content-type: text/plain');
$v = Version::VERSION;
echo "/* INPUT NOT FOUND scss $v */\n";
}
/**
* Based on explicit input/output files does a full change check on cache before compiling.
*
* @param string $in
* @param string $out
* @param boolean $force
*
* @return string Compiled CSS results
*
* @throws \Leafo\ScssPhp\Exception\ServerException
*/
public function checkedCachedCompile($in, $out, $force = false)
{
if (! is_file($in) || ! is_readable($in)) {
throw new ServerException('Invalid or unreadable input file specified.');
}
if (is_dir($out) || ! is_writable(file_exists($out) ? $out : dirname($out))) {
throw new ServerException('Invalid or unwritable output file specified.');
}
if ($force || $this->needsCompile($out, $etag)) {
list($css, $etag) = $this->compile($in, $out);
} else {
$css = file_get_contents($out);
}
return $css;
}
/**
* Constructor
*
* @param string $dir Root directory to .scss files
* @param string $cacheDir Cache directory
* @param \Leafo\ScssPhp\Compiler|null $scss SCSS compiler instance
*/
public function __construct($dir, $cacheDir = null, $scss = null)
{
$this->dir = $dir;
if (! isset($cacheDir)) {
$cacheDir = $this->join($dir, 'scss_cache');
}
$this->cacheDir = $cacheDir;
if (! is_dir($this->cacheDir)) {
mkdir($this->cacheDir, 0755, true);
}
if (! isset($scss)) {
$scss = new Compiler();
$scss->setImportPaths($this->dir);
}
$this->scss = $scss;
$this->showErrorsAsCSS = false;
if (! ini_get('date.timezone')) {
date_default_timezone_set('UTC');
}
}
/**
* Helper method to serve compiled scss
*
* @param string $path Root path
*/
public static function serveFrom($path)
{
$server = new static($path);
$server->serve();
}
}

View File

@ -0,0 +1,217 @@
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\SourceMap;
/**
* Base64 VLQ Encoder
*
* {@internal Derivative of oyejorge/less.php's lib/SourceMap/Base64VLQ.php, relicensed with permission. }}
*
* @author Josh Schmidt <oyejorge@gmail.com>
* @author Nicolas FRANÇOIS <nicolas.francois@frog-labs.com>
*/
class Base64VLQEncoder
{
/**
* Shift
*
* @var integer
*/
private $shift = 5;
/**
* Mask
*
* @var integer
*/
private $mask = 0x1F; // == (1 << shift) == 0b00011111
/**
* Continuation bit
*
* @var integer
*/
private $continuationBit = 0x20; // == (mask - 1 ) == 0b00100000
/**
* Char to integer map
*
* @var array
*/
private $charToIntMap = array(
'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6, 'H' => 7,
'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, 'O' => 14, 'P' => 15,
'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, 'V' => 21, 'W' => 22, 'X' => 23,
'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27, 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31,
'g' => 32, 'h' => 33, 'i' => 34, 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39,
'o' => 40, 'p' => 41, 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47,
'w' => 48, 'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55,
4 => 56, 5 => 57, 6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63,
);
/**
* Integer to char map
*
* @var array
*/
private $intToCharMap = array(
0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', 7 => 'H',
8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', 14 => 'O', 15 => 'P',
16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', 21 => 'V', 22 => 'W', 23 => 'X',
24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f',
32 => 'g', 33 => 'h', 34 => 'i', 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n',
40 => 'o', 41 => 'p', 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v',
48 => 'w', 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3',
56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+', 63 => '/',
);
/**
* Constructor
*/
public function __construct()
{
// I leave it here for future reference
// foreach (str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char)
// {
// $this->charToIntMap[$char] = $i;
// $this->intToCharMap[$i] = $char;
// }
}
/**
* Convert from a two-complement value to a value where the sign bit is
* is placed in the least significant bit. For example, as decimals:
* 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
* 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
* We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297,
* even on a 64 bit machine.
*
* @param string $aValue
*/
public function toVLQSigned($aValue)
{
return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0);
}
/**
* Convert to a two-complement value from a value where the sign bit is
* is placed in the least significant bit. For example, as decimals:
* 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
* 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
* We assume that the value was generated with a 32 bit machine in mind.
* Hence
* 1 becomes -2147483648
* even on a 64 bit machine.
*
* @param integer $aValue
*/
public function fromVLQSigned($aValue)
{
return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1);
}
/**
* Return the base 64 VLQ encoded value.
*
* @param string $aValue The value to encode
*
* @return string The encoded value
*/
public function encode($aValue)
{
$encoded = '';
$vlq = $this->toVLQSigned($aValue);
do {
$digit = $vlq & $this->mask;
$vlq = $this->zeroFill($vlq, $this->shift);
if ($vlq > 0) {
$digit |= $this->continuationBit;
}
$encoded .= $this->base64Encode($digit);
} while ($vlq > 0);
return $encoded;
}
/**
* Return the value decoded from base 64 VLQ.
*
* @param string $encoded The encoded value to decode
*
* @return integer The decoded value
*/
public function decode($encoded)
{
$vlq = 0;
$i = 0;
do {
$digit = $this->base64Decode($encoded[$i]);
$vlq |= ($digit & $this->mask) << ($i * $this->shift);
$i++;
} while ($digit & $this->continuationBit);
return $this->fromVLQSigned($vlq);
}
/**
* Right shift with zero fill.
*
* @param integer $a number to shift
* @param integer $b number of bits to shift
*
* @return integer
*/
public function zeroFill($a, $b)
{
return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1));
}
/**
* Encode single 6-bit digit as base64.
*
* @param integer $number
*
* @return string
*
* @throws \Exception If the number is invalid
*/
public function base64Encode($number)
{
if ($number < 0 || $number > 63) {
throw new \Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number));
}
return $this->intToCharMap[$number];
}
/**
* Decode single 6-bit digit from base64
*
* @param string $char
*
* @return integer
*
* @throws \Exception If the number is invalid
*/
public function base64Decode($char)
{
if (! array_key_exists($char, $this->charToIntMap)) {
throw new \Exception(sprintf('Invalid base 64 digit "%s" given.', $char));
}
return $this->charToIntMap[$char];
}
}

View File

@ -0,0 +1,337 @@
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\SourceMap;
use Leafo\ScssPhp\Exception\CompilerException;
/**
* Source Map Generator
*
* {@internal Derivative of oyejorge/less.php's lib/SourceMap/Generator.php, relicensed with permission. }}
*
* @author Josh Schmidt <oyejorge@gmail.com>
* @author Nicolas FRANÇOIS <nicolas.francois@frog-labs.com>
*/
class SourceMapGenerator
{
/**
* What version of source map does the generator generate?
*/
const VERSION = 3;
/**
* Array of default options
*
* @var array
*/
protected $defaultOptions = array(
// an optional source root, useful for relocating source files
// on a server or removing repeated values in the 'sources' entry.
// This value is prepended to the individual entries in the 'source' field.
'sourceRoot' => '',
// an optional name of the generated code that this source map is associated with.
'sourceMapFilename' => null,
// url of the map
'sourceMapURL' => null,
// absolute path to a file to write the map to
'sourceMapWriteTo' => null,
// output source contents?
'outputSourceFiles' => false,
// base path for filename normalization
'sourceMapRootpath' => '',
// base path for filename normalization
'sourceMapBasepath' => ''
);
/**
* The base64 VLQ encoder
*
* @var \Leafo\ScssPhp\SourceMap\Base64VLQEncoder
*/
protected $encoder;
/**
* Array of mappings
*
* @var array
*/
protected $mappings = array();
/**
* Array of contents map
*
* @var array
*/
protected $contentsMap = array();
/**
* File to content map
*
* @var array
*/
protected $sources = array();
protected $source_keys = array();
/**
* @var array
*/
private $options;
public function __construct(array $options = [])
{
$this->options = array_merge($this->defaultOptions, $options);
$this->encoder = new Base64VLQEncoder();
}
/**
* Adds a mapping
*
* @param integer $generatedLine The line number in generated file
* @param integer $generatedColumn The column number in generated file
* @param integer $originalLine The line number in original file
* @param integer $originalColumn The column number in original file
* @param string $sourceFile The original source file
*/
public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile)
{
$this->mappings[] = array(
'generated_line' => $generatedLine,
'generated_column' => $generatedColumn,
'original_line' => $originalLine,
'original_column' => $originalColumn,
'source_file' => $sourceFile
);
$this->sources[$sourceFile] = $sourceFile;
}
/**
* Saves the source map to a file
*
* @param string $file The absolute path to a file
* @param string $content The content to write
*
* @throws \Leafo\ScssPhp\Exception\CompilerException If the file could not be saved
*/
public function saveMap($content)
{
$file = $this->options['sourceMapWriteTo'];
$dir = dirname($file);
// directory does not exist
if (! is_dir($dir)) {
// FIXME: create the dir automatically?
throw new CompilerException(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir));
}
// FIXME: proper saving, with dir write check!
if (file_put_contents($file, $content) === false) {
throw new CompilerException(sprintf('Cannot save the source map to "%s"', $file));
}
return $this->options['sourceMapURL'];
}
/**
* Generates the JSON source map
*
* @return string
*
* @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#
*/
public function generateJson()
{
$sourceMap = array();
$mappings = $this->generateMappings();
// File version (always the first entry in the object) and must be a positive integer.
$sourceMap['version'] = self::VERSION;
// An optional name of the generated code that this source map is associated with.
$file = $this->options['sourceMapFilename'];
if ($file) {
$sourceMap['file'] = $file;
}
// An optional source root, useful for relocating source files on a server or removing repeated values in the
// 'sources' entry. This value is prepended to the individual entries in the 'source' field.
$root = $this->options['sourceRoot'];
if ($root) {
$sourceMap['sourceRoot'] = $root;
}
// A list of original sources used by the 'mappings' entry.
$sourceMap['sources'] = array();
foreach ($this->sources as $source_uri => $source_filename) {
$sourceMap['sources'][] = $this->normalizeFilename($source_filename);
}
// A list of symbol names used by the 'mappings' entry.
$sourceMap['names'] = array();
// A string with the encoded mapping data.
$sourceMap['mappings'] = $mappings;
if ($this->options['outputSourceFiles']) {
// An optional list of source content, useful when the 'source' can't be hosted.
// The contents are listed in the same order as the sources above.
// 'null' may be used if some original sources should be retrieved by name.
$sourceMap['sourcesContent'] = $this->getSourcesContent();
}
// less.js compat fixes
if (count($sourceMap['sources']) && empty($sourceMap['sourceRoot'])) {
unset($sourceMap['sourceRoot']);
}
return json_encode($sourceMap);
}
/**
* Returns the sources contents
*
* @return array|null
*/
protected function getSourcesContent()
{
if (empty($this->sources)) {
return null;
}
$content = array();
foreach ($this->sources as $sourceFile) {
$content[] = file_get_contents($sourceFile);
}
return $content;
}
/**
* Generates the mappings string
*
* @return string
*/
public function generateMappings()
{
if (! count($this->mappings)) {
return '';
}
$this->source_keys = array_flip(array_keys($this->sources));
// group mappings by generated line number.
$groupedMap = $groupedMapEncoded = array();
foreach ($this->mappings as $m) {
$groupedMap[$m['generated_line']][] = $m;
}
ksort($groupedMap);
$lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0;
foreach ($groupedMap as $lineNumber => $line_map) {
while (++$lastGeneratedLine < $lineNumber) {
$groupedMapEncoded[] = ';';
}
$lineMapEncoded = array();
$lastGeneratedColumn = 0;
foreach ($line_map as $m) {
$mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn);
$lastGeneratedColumn = $m['generated_column'];
// find the index
if ($m['source_file']) {
$index = $this->findFileIndex($m['source_file']);
if ($index !== false) {
$mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex);
$lastOriginalIndex = $index;
// lines are stored 0-based in SourceMap spec version 3
$mapEncoded .= $this->encoder->encode($m['original_line'] - 1 - $lastOriginalLine);
$lastOriginalLine = $m['original_line'] - 1;
$mapEncoded .= $this->encoder->encode($m['original_column'] - $lastOriginalColumn);
$lastOriginalColumn = $m['original_column'];
}
}
$lineMapEncoded[] = $mapEncoded;
}
$groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';';
}
return rtrim(implode($groupedMapEncoded), ';');
}
/**
* Finds the index for the filename
*
* @param string $filename
*
* @return integer|false
*/
protected function findFileIndex($filename)
{
return $this->source_keys[$filename];
}
protected function normalizeFilename($filename)
{
$filename = $this->fixWindowsPath($filename);
$rootpath = $this->options['sourceMapRootpath'];
$basePath = $this->options['sourceMapBasepath'];
// "Trim" the 'sourceMapBasepath' from the output filename.
if (strpos($filename, $basePath) === 0) {
$filename = substr($filename, strlen($basePath));
}
// Remove extra leading path separators.
if (strpos($filename, '\\') === 0 || strpos($filename, '/') === 0) {
$filename = substr($filename, 1);
}
return $rootpath . $filename;
}
/**
* Fix windows paths
*
* @param string $path
* @param boolean $addEndSlash
*
* @return string
*/
public function fixWindowsPath($path, $addEndSlash = false)
{
$slash = ($addEndSlash) ? '/' : '';
if (! empty($path)) {
$path = str_replace('\\', '/', $path);
$path = rtrim($path, '/') . $slash;
}
return $path;
}
}

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
@ -15,7 +15,7 @@ use Leafo\ScssPhp\Base\Range;
use Leafo\ScssPhp\Exception\RangeException;
/**
* Utilties
* Utilty functions
*
* @author Anthon Pang <anthon.pang@gmail.com>
*/
@ -25,10 +25,10 @@ class Util
* Asserts that `value` falls within `range` (inclusive), leaving
* room for slight floating-point errors.
*
* @param string $name The name of the value. Used in the error message.
* @param Range $range Range of values.
* @param array $value The value to check.
* @param string $unit The unit of the value. Used in error reporting.
* @param string $name The name of the value. Used in the error message.
* @param \Leafo\ScssPhp\Base\Range $range Range of values.
* @param array $value The value to check.
* @param string $unit The unit of the value. Used in error reporting.
*
* @return mixed `value` adjusted to fall within range, if it was outside by a floating-point margin.
*
@ -53,4 +53,18 @@ class Util
throw new RangeException("$name {$val} must be between {$range->first} and {$range->last}$unit");
}
/**
* Encode URI component
*
* @param string $string
*
* @return string
*/
public static function encodeURIComponent($string)
{
$revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')');
return strtr(rawurlencode($string), $revert);
}
}

View File

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
@ -18,5 +18,5 @@ namespace Leafo\ScssPhp;
*/
class Version
{
const VERSION = 'v0.6.7';
const VERSION = 'v0.7.5';
}

View File

@ -7,6 +7,5 @@ Import procedure:
- Copy all the files from the folder 'src' this directory.
- Copy the license file from the project root.
- Apply path for https://github.com/leafo/scssphp/issues/532 if it is not merged yet
Licensed under MIT, Copyright (c) 2015 Leaf Corcoran.

View File

@ -250,7 +250,7 @@
<location>scssphp</location>
<name>scssphp</name>
<license>MIT</license>
<version>0.6.7</version>
<version>0.7.5</version>
</library>
<library>
<location>spout</location>