diff --git a/lib/scssphp/composer.json b/lib/scssphp/composer.json index 86cd396..f81203d 100644 --- a/lib/scssphp/composer.json +++ b/lib/scssphp/composer.json @@ -42,7 +42,7 @@ "symfony/phpunit-bridge": "^5.1", "thoughtbot/bourbon": "^7.0", "twbs/bootstrap": "~5.0", - "twbs/bootstrap4": "4.6.0", + "twbs/bootstrap4": "4.6.1", "zurb/foundation": "~6.5" }, "repositories": [ @@ -50,16 +50,16 @@ "type": "package", "package": { "name": "sass/sass-spec", - "version": "2021.09.15", + "version": "2022.08.19", "source": { "type": "git", "url": "https://github.com/sass/sass-spec.git", - "reference": "eb2d7a0865c1faf0b55a39ff962b24aca9b4c955" + "reference": "2bdc199723a3445d5badac3ac774105698f08861" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sass/sass-spec/zipball/eb2d7a0865c1faf0b55a39ff962b24aca9b4c955", - "reference": "eb2d7a0865c1faf0b55a39ff962b24aca9b4c955", + "url": "https://api.github.com/repos/sass/sass-spec/zipball/2bdc199723a3445d5badac3ac774105698f08861", + "reference": "2bdc199723a3445d5badac3ac774105698f08861", "shasum": "" } } @@ -86,16 +86,16 @@ "type": "package", "package": { "name": "twbs/bootstrap4", - "version": "v4.6.0", + "version": "v4.6.1", "source": { "type": "git", "url": "https://github.com/twbs/bootstrap.git", - "reference": "6ffb0b48e455430f8a5359ed689ad64c1143fac2" + "reference": "043a03c95a2ad6738f85b65e53b9dbdfb03b8d10" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twbs/bootstrap/zipball/6ffb0b48e455430f8a5359ed689ad64c1143fac2", - "reference": "6ffb0b48e455430f8a5359ed689ad64c1143fac2", + "url": "https://api.github.com/repos/twbs/bootstrap/zipball/043a03c95a2ad6738f85b65e53b9dbdfb03b8d10", + "reference": "043a03c95a2ad6738f85b65e53b9dbdfb03b8d10", "shasum": "" } } @@ -103,6 +103,15 @@ ], "bin": ["bin/pscss"], "config": { - "sort-packages": true + "sort-packages": true, + "allow-plugins": { + "bamarni/composer-bin-plugin": true + } + }, + "extra": { + "bamarni-bin": { + "forward-command": false, + "bin-links": false + } } } diff --git a/lib/scssphp/src/Base/Range.php b/lib/scssphp/src/Base/Range.php index 0c5f584..31d5ec5 100644 --- a/lib/scssphp/src/Base/Range.php +++ b/lib/scssphp/src/Base/Range.php @@ -34,8 +34,8 @@ class Range /** * Initialize range * - * @param integer|float $first - * @param integer|float $last + * @param int|float $first + * @param int|float $last */ public function __construct($first, $last) { @@ -46,9 +46,9 @@ class Range /** * Test for inclusion in range * - * @param integer|float $value + * @param int|float $value * - * @return boolean + * @return bool */ public function includes($value) { diff --git a/lib/scssphp/src/Block.php b/lib/scssphp/src/Block.php index 3ae49d0..96668dc 100644 --- a/lib/scssphp/src/Block.php +++ b/lib/scssphp/src/Block.php @@ -22,12 +22,12 @@ namespace ScssPhp\ScssPhp; class Block { /** - * @var string + * @var string|null */ public $type; /** - * @var \ScssPhp\ScssPhp\Block + * @var Block|null */ public $parent; @@ -37,17 +37,17 @@ class Block public $sourceName; /** - * @var integer + * @var int */ public $sourceIndex; /** - * @var integer + * @var int */ public $sourceLine; /** - * @var integer + * @var int */ public $sourceColumn; @@ -67,7 +67,7 @@ class Block public $children; /** - * @var \ScssPhp\ScssPhp\Block|null + * @var Block|null */ public $selfParent; } diff --git a/lib/scssphp/src/Block/AtRootBlock.php b/lib/scssphp/src/Block/AtRootBlock.php new file mode 100644 index 0000000..41842c2 --- /dev/null +++ b/lib/scssphp/src/Block/AtRootBlock.php @@ -0,0 +1,37 @@ +type = Type::T_AT_ROOT; + } +} diff --git a/lib/scssphp/src/Block/CallableBlock.php b/lib/scssphp/src/Block/CallableBlock.php new file mode 100644 index 0000000..a18a87c --- /dev/null +++ b/lib/scssphp/src/Block/CallableBlock.php @@ -0,0 +1,45 @@ +type = $type; + } +} diff --git a/lib/scssphp/src/Block/ContentBlock.php b/lib/scssphp/src/Block/ContentBlock.php new file mode 100644 index 0000000..8708498 --- /dev/null +++ b/lib/scssphp/src/Block/ContentBlock.php @@ -0,0 +1,38 @@ +type = Type::T_INCLUDE; + } +} diff --git a/lib/scssphp/src/Block/DirectiveBlock.php b/lib/scssphp/src/Block/DirectiveBlock.php new file mode 100644 index 0000000..b1d3d1a --- /dev/null +++ b/lib/scssphp/src/Block/DirectiveBlock.php @@ -0,0 +1,37 @@ +type = Type::T_DIRECTIVE; + } +} diff --git a/lib/scssphp/src/Block/EachBlock.php b/lib/scssphp/src/Block/EachBlock.php new file mode 100644 index 0000000..b328957 --- /dev/null +++ b/lib/scssphp/src/Block/EachBlock.php @@ -0,0 +1,37 @@ +type = Type::T_EACH; + } +} diff --git a/lib/scssphp/src/Block/ElseBlock.php b/lib/scssphp/src/Block/ElseBlock.php new file mode 100644 index 0000000..6abb4d7 --- /dev/null +++ b/lib/scssphp/src/Block/ElseBlock.php @@ -0,0 +1,27 @@ +type = Type::T_ELSE; + } +} diff --git a/lib/scssphp/src/Block/ElseifBlock.php b/lib/scssphp/src/Block/ElseifBlock.php new file mode 100644 index 0000000..4622bca --- /dev/null +++ b/lib/scssphp/src/Block/ElseifBlock.php @@ -0,0 +1,32 @@ +type = Type::T_ELSEIF; + } +} diff --git a/lib/scssphp/src/Block/ForBlock.php b/lib/scssphp/src/Block/ForBlock.php new file mode 100644 index 0000000..a9cf673 --- /dev/null +++ b/lib/scssphp/src/Block/ForBlock.php @@ -0,0 +1,47 @@ +type = Type::T_FOR; + } +} diff --git a/lib/scssphp/src/Block/IfBlock.php b/lib/scssphp/src/Block/IfBlock.php new file mode 100644 index 0000000..9f21bf8 --- /dev/null +++ b/lib/scssphp/src/Block/IfBlock.php @@ -0,0 +1,37 @@ + + */ + public $cases = []; + + public function __construct() + { + $this->type = Type::T_IF; + } +} diff --git a/lib/scssphp/src/Block/MediaBlock.php b/lib/scssphp/src/Block/MediaBlock.php new file mode 100644 index 0000000..c49ee1b --- /dev/null +++ b/lib/scssphp/src/Block/MediaBlock.php @@ -0,0 +1,37 @@ +type = Type::T_MEDIA; + } +} diff --git a/lib/scssphp/src/Block/NestedPropertyBlock.php b/lib/scssphp/src/Block/NestedPropertyBlock.php new file mode 100644 index 0000000..1ea4a6c --- /dev/null +++ b/lib/scssphp/src/Block/NestedPropertyBlock.php @@ -0,0 +1,37 @@ +type = Type::T_NESTED_PROPERTY; + } +} diff --git a/lib/scssphp/src/Block/WhileBlock.php b/lib/scssphp/src/Block/WhileBlock.php new file mode 100644 index 0000000..ac18d4e --- /dev/null +++ b/lib/scssphp/src/Block/WhileBlock.php @@ -0,0 +1,32 @@ +type = Type::T_WHILE; + } +} diff --git a/lib/scssphp/src/Colors.php b/lib/scssphp/src/Colors.php index e836e3f..2df3999 100644 --- a/lib/scssphp/src/Colors.php +++ b/lib/scssphp/src/Colors.php @@ -204,10 +204,10 @@ class Colors /** * Reverse conversion : from RGBA to a color name if possible * - * @param integer $r - * @param integer $g - * @param integer $b - * @param integer|float $a + * @param int $r + * @param int $g + * @param int $b + * @param int|float $a * * @return string|null */ diff --git a/lib/scssphp/src/Compiler.php b/lib/scssphp/src/Compiler.php index d17d9f2..ecafc8c 100644 --- a/lib/scssphp/src/Compiler.php +++ b/lib/scssphp/src/Compiler.php @@ -13,6 +13,17 @@ namespace ScssPhp\ScssPhp; use ScssPhp\ScssPhp\Base\Range; +use ScssPhp\ScssPhp\Block\AtRootBlock; +use ScssPhp\ScssPhp\Block\CallableBlock; +use ScssPhp\ScssPhp\Block\DirectiveBlock; +use ScssPhp\ScssPhp\Block\EachBlock; +use ScssPhp\ScssPhp\Block\ElseBlock; +use ScssPhp\ScssPhp\Block\ElseifBlock; +use ScssPhp\ScssPhp\Block\ForBlock; +use ScssPhp\ScssPhp\Block\IfBlock; +use ScssPhp\ScssPhp\Block\MediaBlock; +use ScssPhp\ScssPhp\Block\NestedPropertyBlock; +use ScssPhp\ScssPhp\Block\WhileBlock; use ScssPhp\ScssPhp\Compiler\CachedResult; use ScssPhp\ScssPhp\Compiler\Environment; use ScssPhp\ScssPhp\Exception\CompilerException; @@ -155,7 +166,7 @@ class Compiler /** * @var array - * @phpstan-var array + * @phpstan-var array */ protected $userFunctions = []; /** @@ -200,9 +211,15 @@ class Compiler private $charset = true; /** - * @var string|\ScssPhp\ScssPhp\Formatter + * @var Formatter */ - protected $formatter = Expanded::class; + protected $formatter; + + /** + * @var string + * @phpstan-var class-string + */ + private $configuredFormatter = Expanded::class; /** * @var Environment @@ -370,7 +387,7 @@ class Compiler 'encoding' => $this->encoding, 'sourceMap' => serialize($this->sourceMap), 'sourceMapOptions' => $this->sourceMapOptions, - 'formatter' => $this->formatter, + 'formatter' => $this->configuredFormatter, 'legacyImportPath' => $this->legacyCwdImportPath, ]; @@ -491,7 +508,7 @@ class Compiler $tree = $this->parser->parse($source); $this->parser = null; - $this->formatter = new $this->formatter(); + $this->formatter = new $this->configuredFormatter(); $this->rootBlock = null; $this->rootEnv = $this->pushEnv($tree); @@ -518,6 +535,7 @@ class Compiler $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); } } + assert($this->scope !== null); $out = $this->formatter->format($this->scope, $sourceMapGenerator); @@ -531,6 +549,7 @@ class Compiler $sourceMap = null; if (! empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + assert($sourceMapGenerator !== null); $sourceMap = $sourceMapGenerator->generateJson($prefix); $sourceMapUrl = null; @@ -644,7 +663,7 @@ class Compiler * @param array $target * @param array $origin * - * @return boolean + * @return bool */ protected function isSelfExtend($target, $origin) { @@ -660,7 +679,7 @@ class Compiler /** * Push extends * - * @param array $target + * @param string[] $target * @param array $origin * @param array|null $block * @@ -703,9 +722,9 @@ class Compiler $out->sourceLine = $this->env->block->sourceLine; $out->sourceColumn = $this->env->block->sourceColumn; } else { - $out->sourceName = null; - $out->sourceLine = null; - $out->sourceColumn = null; + $out->sourceName = isset($this->sourceNames[$this->sourceIndex]) ? $this->sourceNames[$this->sourceIndex] : '(stdin)'; + $out->sourceLine = $this->sourceLine; + $out->sourceColumn = $this->sourceColumn; } return $out; @@ -723,6 +742,7 @@ class Compiler $this->rootBlock = $this->scope = $this->makeOutputBlock(Type::T_ROOT); $this->compileChildrenNoReturn($rootBlock->children, $this->scope); + assert($this->scope !== null); $this->flattenSelectors($this->scope); $this->missingSelectors(); } @@ -804,6 +824,7 @@ class Compiler } if ($placeholderSelector && 0 === \count($block->selectors) && null !== $parentKey) { + assert($block->parent !== null); unset($block->parent->children[$parentKey]); return; @@ -853,10 +874,10 @@ class Compiler /** * Match extends * - * @param array $selector - * @param array $out - * @param integer $from - * @param boolean $initial + * @param array $selector + * @param array $out + * @param int $from + * @param bool $initial * * @return void */ @@ -989,7 +1010,7 @@ class Compiler * @param string $part * @param array $matches * - * @return boolean + * @return bool */ protected function isPseudoSelector($part, &$matches) { @@ -1051,11 +1072,11 @@ class Compiler /** * Match extends single * - * @param array $rawSingle - * @param array $outOrigin - * @param boolean $initial + * @param array $rawSingle + * @param array $outOrigin + * @param bool $initial * - * @return boolean + * @return bool */ protected function matchExtendsSingle($rawSingle, &$outOrigin, $initial = true) { @@ -1270,11 +1291,13 @@ class Compiler */ protected function compileMedia(Block $media) { + assert($media instanceof MediaBlock); $this->pushEnv($media); $mediaQueries = $this->compileMediaQuery($this->multiplyMedia($this->env)); if (! empty($mediaQueries)) { + assert($this->scope !== null); $previousScope = $this->scope; $parentScope = $this->mediaParent($this->scope); @@ -1347,7 +1370,7 @@ class Compiler /** * Compile directive * - * @param \ScssPhp\ScssPhp\Block|array $directive + * @param DirectiveBlock|array $directive * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out * * @return void @@ -1413,6 +1436,7 @@ class Compiler */ protected function compileAtRoot(Block $block) { + assert($block instanceof AtRootBlock); $env = $this->pushEnv($block); $envs = $this->compactEnv($env); list($with, $without) = $this->compileWith(isset($block->with) ? $block->with : null); @@ -1439,7 +1463,7 @@ class Compiler if ( ! $selfParent->selectors && - isset($block->parent) && $block->parent && + isset($block->parent) && isset($block->parent->selectors) && $block->parent->selectors ) { $selfParent = $block->parent; @@ -1447,13 +1471,15 @@ class Compiler $this->env = $this->filterWithWithout($envs, $with, $without); + assert($this->scope !== null); $saveScope = $this->scope; $this->scope = $this->filterScopeWithWithout($saveScope, $with, $without); // propagate selfParent to the children where they still can be useful $this->compileChildrenNoReturn($block->children, $this->scope, $selfParent); - $this->scope = $this->completeScope($this->scope, $saveScope); + assert($this->scope !== null); + $this->completeScope($this->scope, $saveScope); $this->scope = $saveScope; $this->env = $this->extractEnv($envs); @@ -1461,7 +1487,7 @@ class Compiler } /** - * Filter at-root scope depending of with/without option + * Filter at-root scope depending on with/without option * * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $scope * @param array $with @@ -1477,6 +1503,7 @@ class Compiler if ($scope->type === Type::T_ROOT) { return $scope; } + assert($this->rootBlock !== null); // start from the root while ($scope->parent && $scope->parent->type !== Type::T_ROOT) { @@ -1544,7 +1571,7 @@ class Compiler */ protected function completeScope($scope, $previousScope) { - if (! $scope->type && (! $scope->selectors || ! \count($scope->selectors)) && \count($scope->lines)) { + if (! $scope->type && ! $scope->selectors && \count($scope->lines)) { $scope->selectors = $this->findScopeSelectors($previousScope, $scope->depth); } @@ -1561,7 +1588,7 @@ class Compiler * Find a selector by the depth node in the scope * * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $scope - * @param integer $depth + * @param int $depth * * @return array */ @@ -1585,7 +1612,7 @@ class Compiler /** * Compile @at-root's with: inclusion / without: exclusion into 2 lists uses to filter scope/env later * - * @param array $withCondition + * @param array|null $withCondition * * @return array * @@ -1674,7 +1701,7 @@ class Compiler * @param array $with * @param array $without * - * @return boolean + * @return bool */ protected function isWith($block, $with, $without) { @@ -1684,6 +1711,7 @@ class Compiler } if ($block->type === Type::T_DIRECTIVE) { + assert($block instanceof DirectiveBlock || $block instanceof OutputBlock); if (isset($block->name)) { return $this->testWithWithout($this->compileDirectiveName($block->name), $with, $without); } elseif (isset($block->selectors) && preg_match(',@(\w+),ims', json_encode($block->selectors), $m)) { @@ -1719,7 +1747,7 @@ class Compiler * @param array $with * @param array $without * - * @return boolean + * @return bool * true if the block should be kept, false to reject */ protected function testWithWithout($what, $with, $without) @@ -1754,10 +1782,12 @@ class Compiler $this->scope = $this->makeOutputBlock($block->type, $selectors); $this->scope->depth = 1; + assert($this->scope->parent !== null); $this->scope->parent->children[] = $this->scope; $this->compileChildrenNoReturn($block->children, $this->scope); + assert($this->scope !== null); $this->scope = $this->scope->parent; $this->env = $this->extractEnv($envs); @@ -1774,6 +1804,7 @@ class Compiler */ protected function compileNestedPropertiesBlock(Block $block, OutputBlock $out) { + assert($block instanceof NestedPropertyBlock); $prefix = $this->compileValue($block->prefix) . '-'; $nested = $this->makeOutputBlock($block->type); @@ -1792,6 +1823,7 @@ class Compiler break; case Type::T_NESTED_PROPERTY: + assert($child[1] instanceof NestedPropertyBlock); array_unshift($child[1]->prefix[2], $prefix); break; } @@ -1813,11 +1845,12 @@ class Compiler $this->pushEnv($block); $this->scope = $this->makeOutputBlock($block->type, $selectors); + assert($this->scope->parent !== null); $this->scope->parent->children[] = $this->scope; // wrap assign children in a block // except for @font-face - if ($block->type !== Type::T_DIRECTIVE || $this->compileDirectiveName($block->name) !== 'font-face') { + if (!$block instanceof DirectiveBlock || $this->compileDirectiveName($block->name) !== 'font-face') { // need wrapping? $needWrapping = false; @@ -1846,6 +1879,7 @@ class Compiler $this->compileChildrenNoReturn($block->children, $this->scope); + assert($this->scope !== null); $this->scope = $this->scope->parent; $this->popEnv(); @@ -1874,10 +1908,12 @@ class Compiler protected function compileBlock(Block $block) { $env = $this->pushEnv($block); + assert($block->selectors !== null); $env->selectors = $this->evalSelectors($block->selectors); $out = $this->makeOutputBlock(null); + assert($this->scope !== null); $this->scope->children[] = $out; if (\count($block->children)) { @@ -1895,6 +1931,7 @@ class Compiler // and revert for the following children of the same block if ($selfParentSelectors) { + assert($block->selfParent !== null); $block->selfParent->selectors = $selfParentSelectors; } } @@ -1906,8 +1943,8 @@ class Compiler /** * Compile the value of a comment that can have interpolation * - * @param array $value - * @param boolean $pushEnv + * @param array $value + * @param bool $pushEnv * * @return string */ @@ -1950,6 +1987,7 @@ class Compiler $out = $this->makeOutputBlock(Type::T_COMMENT); $out->lines[] = $this->compileCommentValue($block, true); + assert($this->scope !== null); $this->scope->children[] = $out; } @@ -1964,7 +2002,11 @@ class Compiler { $this->shouldEvaluate = false; - $selectors = array_map([$this, 'evalSelector'], $selectors); + $evaluatedSelectors = []; + foreach ($selectors as $selector) { + $evaluatedSelectors[] = $this->evalSelector($selector); + } + $selectors = $evaluatedSelectors; // after evaluating interpolates, we might need a second pass if ($this->shouldEvaluate) { @@ -1992,6 +2034,8 @@ class Compiler * @param array $selector * * @return array + * + * @phpstan-impure */ protected function evalSelector($selector) { @@ -2004,6 +2048,8 @@ class Compiler * @param array $part * * @return array + * + * @phpstan-impure */ protected function evalSelectorPart($part) { @@ -2017,8 +2063,8 @@ class Compiler } } elseif ( \is_string($p) && \strlen($p) >= 2 && - ($first = $p[0]) && ($first === '"' || $first === "'") && - substr($p, -1) === $first + ($p[0] === '"' || $p[0] === "'") && + substr($p, -1) === $p[0] ) { $p = substr($p, 1, -1); } @@ -2224,7 +2270,7 @@ class Compiler * * @param array $selector * - * @return boolean + * @return bool */ protected function hasSelectorPlaceholder($selector) { @@ -2304,9 +2350,9 @@ class Compiler } /** - * Compile children and throw exception if unexpected `@return` + * Compile children and throw exception if unexpected at-return * - * @param array $stms + * @param array[] $stms * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out * @param \ScssPhp\ScssPhp\Block $selfParent * @param string $traceName @@ -2321,13 +2367,13 @@ class Compiler foreach ($stms as $stm) { if ($selfParent && isset($stm[1]) && \is_object($stm[1]) && $stm[1] instanceof Block) { + $oldSelfParent = $stm[1]->selfParent; $stm[1]->selfParent = $selfParent; $ret = $this->compileChild($stm, $out); - $stm[1]->selfParent = null; + $stm[1]->selfParent = $oldSelfParent; } elseif ($selfParent && \in_array($stm[0], [Type::T_INCLUDE, Type::T_EXTEND])) { $stm['selfParent'] = $selfParent; $ret = $this->compileChild($stm, $out); - unset($stm['selfParent']); } else { $ret = $this->compileChild($stm, $out); } @@ -2385,7 +2431,7 @@ class Compiler $queryString = $this->compileMediaQuery([$queryList[$kql]]); $queryString = reset($queryString); - if (strpos($queryString, '@media ') === 0) { + if ($queryString !== false && strpos($queryString, '@media ') === 0) { $queryString = substr($queryString, 7); $queries = []; @@ -2641,9 +2687,9 @@ class Compiler * * @param array $rawPath * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out - * @param boolean $once + * @param bool $once * - * @return boolean + * @return bool */ protected function compileImport($rawPath, OutputBlock $out, $once = false) { @@ -2778,7 +2824,7 @@ class Compiler // insert the directive as a comment $child = $this->makeOutputBlock(Type::T_COMMENT); $child->lines[] = $line; - $child->sourceName = $this->sourceNames[$this->sourceIndex]; + $child->sourceName = $this->sourceNames[$this->sourceIndex] ?: '(stdin)'; $child->sourceLine = $this->sourceLine; $child->sourceColumn = $this->sourceColumn; @@ -2841,7 +2887,7 @@ class Compiler $this->sourceIndex = isset($child[Parser::SOURCE_INDEX]) ? $child[Parser::SOURCE_INDEX] : null; $this->sourceLine = isset($child[Parser::SOURCE_LINE]) ? $child[Parser::SOURCE_LINE] : -1; $this->sourceColumn = isset($child[Parser::SOURCE_COLUMN]) ? $child[Parser::SOURCE_COLUMN] : -1; - } elseif (\is_array($child) && isset($child[1]->sourceLine)) { + } elseif (\is_array($child) && isset($child[1]->sourceLine) && $child[1] instanceof Block) { $this->sourceIndex = $child[1]->sourceIndex; $this->sourceLine = $child[1]->sourceLine; $this->sourceColumn = $child[1]->sourceColumn; @@ -3049,6 +3095,7 @@ class Compiler case Type::T_MIXIN: case Type::T_FUNCTION: list(, $block) = $child; + assert($block instanceof CallableBlock); // the block need to be able to go up to it's parent env to resolve vars $block->parentEnv = $this->getStoreEnv(); $this->set(static::$namespaces[$block->type] . $block->name, $block, true); @@ -3076,6 +3123,7 @@ class Compiler if (! $selectors && isset($child['selfParent'])) { $selectors = $this->multiplySelectors($this->env, $child['selfParent']); } + assert($selectors !== null); if (\count($result) > 1) { $replacement = implode(', ', $result); @@ -3099,6 +3147,7 @@ EOL; case Type::T_IF: list(, $if) = $child; + assert($if instanceof IfBlock); if ($this->isTruthy($this->reduce($if->cond, true))) { return $this->compileChildren($if->children, $out); @@ -3106,8 +3155,8 @@ EOL; foreach ($if->cases as $case) { if ( - $case->type === Type::T_ELSE || - $case->type === Type::T_ELSEIF && $this->isTruthy($this->reduce($case->cond)) + $case instanceof ElseBlock || + $case instanceof ElseifBlock && $this->isTruthy($this->reduce($case->cond)) ) { return $this->compileChildren($case->children, $out); } @@ -3116,6 +3165,7 @@ EOL; case Type::T_EACH: list(, $each) = $child; + assert($each instanceof EachBlock); $list = $this->coerceList($this->reduce($each->list), ',', true); @@ -3150,6 +3200,7 @@ EOL; case Type::T_WHILE: list(, $while) = $child; + assert($while instanceof WhileBlock); while ($this->isTruthy($this->reduce($while->cond, true))) { $ret = $this->compileChildren($while->children, $out); @@ -3162,6 +3213,7 @@ EOL; case Type::T_FOR: list(, $for) = $child; + assert($for instanceof ForBlock); $startNumber = $this->assertNumber($this->reduce($for->start, true)); $endNumber = $this->assertNumber($this->reduce($for->end, true)); @@ -3222,6 +3274,8 @@ EOL; throw $this->error("Undefined mixin $name"); } + assert($mixin instanceof CallableBlock); + $callingScope = $this->getStoreEnv(); // push scope, apply args @@ -3232,7 +3286,7 @@ EOL; // and assign this fake parent to childs $selfParent = null; - if (isset($child['selfParent']) && isset($child['selfParent']->selectors)) { + if (isset($child['selfParent']) && $child['selfParent'] instanceof Block && isset($child['selfParent']->selectors)) { $selfParent = $child['selfParent']; } else { $parentSelectors = $this->multiplySelectors($this->env); @@ -3242,7 +3296,7 @@ EOL; $parent->selectors = $parentSelectors; foreach ($mixin->children as $k => $child) { - if (isset($child[1]) && \is_object($child[1]) && $child[1] instanceof Block) { + if (isset($child[1]) && $child[1] instanceof Block) { $mixin->children[$k][1]->parent = $parent; } } @@ -3348,6 +3402,8 @@ EOL; default: throw $this->error("unknown child type: $child[0]"); } + + return null; } /** @@ -3394,7 +3450,7 @@ EOL; * * @param array|Number $value * - * @return boolean + * @return bool */ public function isTruthy($value) { @@ -3406,7 +3462,7 @@ EOL; * * @param string $value * - * @return boolean + * @return bool */ protected function isImmediateRelationshipCombinator($value) { @@ -3418,7 +3474,7 @@ EOL; * * @param array $value * - * @return boolean + * @return bool */ protected function shouldEval($value) { @@ -3441,7 +3497,7 @@ EOL; * Reduce value * * @param array|Number $value - * @param boolean $inExp + * @param bool $inExp * * @return array|Number */ @@ -3482,32 +3538,24 @@ EOL; $ucLType = ucfirst($ltype); $ucRType = ucfirst($rtype); + $shouldEval = $inParens || $inExp; + // this tries: // 1. op[op name][left type][right type] - // 2. op[left type][right type] (passing the op as first arg + // 2. op[left type][right type] (passing the op as first arg) // 3. op[op name] - $fn = "op${ucOpName}${ucLType}${ucRType}"; - - if ( - \is_callable([$this, $fn]) || - (($fn = "op${ucLType}${ucRType}") && - \is_callable([$this, $fn]) && - $passOp = true) || - (($fn = "op${ucOpName}") && - \is_callable([$this, $fn]) && - $genOp = true) - ) { - $shouldEval = $inParens || $inExp; - - if (isset($passOp)) { - $out = $this->$fn($op, $left, $right, $shouldEval); - } else { - $out = $this->$fn($left, $right, $shouldEval); - } + if (\is_callable([$this, $fn = "op{$ucOpName}{$ucLType}{$ucRType}"])) { + $out = $this->$fn($left, $right, $shouldEval); + } elseif (\is_callable([$this, $fn = "op{$ucLType}{$ucRType}"])) { + $out = $this->$fn($op, $left, $right, $shouldEval); + } elseif (\is_callable([$this, $fn = "op{$ucOpName}"])) { + $out = $this->$fn($left, $right, $shouldEval); + } else { + $out = null; + } - if (isset($out)) { - return $out; - } + if (isset($out)) { + return $out; } return $this->expToString($value); @@ -3844,11 +3892,12 @@ EOL; // Special functions overriding a CSS function are case-insensitive. We normalize them as lowercase // to avoid the deprecation warning about the wrong case being used. - if ($lowercasedName === 'min' || $lowercasedName === 'max') { + if ($lowercasedName === 'min' || $lowercasedName === 'max' || $lowercasedName === 'rgb' || $lowercasedName === 'rgba' || $lowercasedName === 'hsl' || $lowercasedName === 'hsla') { $normalizedName = $lowercasedName; } if (($f = $this->getBuiltinFunction($normalizedName)) && \is_callable($f)) { + /** @var string $libName */ $libName = $f[1]; $prototype = isset(static::$$libName) ? static::$$libName : null; @@ -3946,6 +3995,10 @@ EOL; unset($value['enclosing']); } + if ($value[1] === '' && count($value[2]) > 1) { + $value[1] = ' '; + } + return $value; case Type::T_STRING: @@ -4061,8 +4114,8 @@ EOL; * Boolean and * * @param array|Number $left - * @param array|Number $right - * @param boolean $shouldEval + * @param array|Number $right + * @param bool $shouldEval * * @return array|Number|null */ @@ -4090,7 +4143,7 @@ EOL; * * @param array|Number $left * @param array|Number $right - * @param boolean $shouldEval + * @param bool $shouldEval * * @return array|Number|null */ @@ -4516,8 +4569,7 @@ EOL; // force double quote as string quote for the output in certain cases if ( $value[1] === "'" && - (strpos($content, '"') === false or strpos($content, "'") !== false) && - strpbrk($content, '{}\\\'') !== false + (strpos($content, '"') === false or strpos($content, "'") !== false) ) { $value[1] = '"'; } elseif ( @@ -4570,6 +4622,8 @@ EOL; } } + $separator = $delim === '/' ? ' /' : $delim; + $prefix_value = ''; if ($delim !== ' ') { @@ -4608,7 +4662,7 @@ EOL; $filtered[] = $compiled; } - return $pre . substr(implode("$delim", $filtered), \strlen($prefix_value)) . $post; + return $pre . substr(implode($separator, $filtered), \strlen($prefix_value)) . $post; case Type::T_MAP: $keys = $value[1]; @@ -4880,10 +4934,10 @@ EOL; /** * Join selectors; looks for & to replace, or append parent before child * - * @param array $parent - * @param array $child - * @param boolean $stillHasSelf - * @param array $selfParentSelectors + * @param array $parent + * @param array $child + * @param bool $stillHasSelf + * @param array $selfParentSelectors * @return array */ @@ -4961,6 +5015,8 @@ EOL; return $this->multiplyMedia($env->parent, $childQueries); } + assert($env->block instanceof MediaBlock); + $parentQueries = isset($env->block->queryList) ? $env->block->queryList : [[[Type::T_MEDIA_VALUE, $env->block->value]]]; @@ -5095,7 +5151,7 @@ EOL; * * @param string $name * @param mixed $value - * @param boolean $shadow + * @param bool $shadow * @param \ScssPhp\ScssPhp\Compiler\Environment $env * @param mixed $valueUnreduced * @@ -5201,9 +5257,9 @@ EOL; * @internal * * @param string $name - * @param boolean $shouldThrow + * @param bool $shouldThrow * @param \ScssPhp\ScssPhp\Compiler\Environment $env - * @param boolean $unreduced + * @param bool $unreduced * * @return mixed|null */ @@ -5270,7 +5326,7 @@ EOL; * @param string $name * @param \ScssPhp\ScssPhp\Compiler\Environment $env * - * @return boolean + * @return bool */ protected function has($name, Environment $env = null) { @@ -5460,7 +5516,7 @@ EOL; * * @api * - * @param integer $numberPrecision + * @param int $numberPrecision * * @return void * @@ -5487,11 +5543,11 @@ EOL; { switch ($style) { case OutputStyle::EXPANDED: - $this->formatter = Expanded::class; + $this->configuredFormatter = Expanded::class; break; case OutputStyle::COMPRESSED: - $this->formatter = Compressed::class; + $this->configuredFormatter = Compressed::class; break; default: @@ -5509,6 +5565,8 @@ EOL; * @return void * * @deprecated Use {@see setOutputStyle} instead. + * + * @phpstan-param class-string $formatterName */ public function setFormatter($formatterName) { @@ -5517,7 +5575,7 @@ EOL; } @trigger_error('The method "setFormatter" is deprecated. Use "setOutputStyle" instead.', E_USER_DEPRECATED); - $this->formatter = $formatterName; + $this->configuredFormatter = $formatterName; } /** @@ -5561,7 +5619,7 @@ EOL; * * @api * - * @param integer $sourceMap + * @param int $sourceMap * * @return void * @@ -5658,6 +5716,10 @@ EOL; // see if tree is cached $realPath = realpath($path); + if ($realPath === false) { + $realPath = $path; + } + if (substr($path, -5) === '.sass') { $this->sourceIndex = \count($this->sourceNames); $this->sourceNames[] = $path; @@ -5963,7 +6025,7 @@ EOL; * * @api * - * @param boolean $ignoreErrors + * @param bool $ignoreErrors * * @return \ScssPhp\ScssPhp\Compiler * @@ -6001,6 +6063,8 @@ EOL; * * @param string $msg Message with optional sprintf()-style vararg parameters * + * @return never + * * @throws \ScssPhp\ScssPhp\Exception\CompilerException * * @deprecated use "error" and throw the exception in the caller instead. @@ -6020,7 +6084,8 @@ EOL; * * @internal * - * @param string $msg Message with optional sprintf()-style vararg parameters + * @param string $msg Message with optional sprintf()-style vararg parameters + * @param bool|float|int|string|null ...$args * * @return CompilerException */ @@ -6102,7 +6167,7 @@ EOL; /** * Beautify call stack for output * - * @param boolean $all + * @param bool $all * @param int|null $limit * * @return string @@ -6138,6 +6203,8 @@ EOL; * * @param string $name * + * @return void + * * @throws \Exception */ protected function handleImportLoop($name) @@ -6162,8 +6229,8 @@ EOL; /** * Call SCSS @function * - * @param Object $func - * @param array $argValues + * @param CallableBlock|null $func + * @param array $argValues * * @return array|Number */ @@ -6332,6 +6399,9 @@ EOL; if (\in_array($functionName, ['libRgb', 'libRgba', 'libHsl', 'libHsla'])) { // notation 100 127 255 / 0 is in fact a simple list of 4 values foreach ($args as $k => $arg) { + if (!isset($arg[1])) { + continue; // This happens when using a trailing comma + } if ($arg[1][0] === Type::T_LIST && \count($arg[1][2]) === 3) { $args[$k][1][2] = $this->extractSlashAlphaInColorFunction($arg[1][2]); } @@ -6737,9 +6807,8 @@ EOL; * * @param array[] $argDef * @param array|null $argValues - * @param boolean $storeInEnv - * @param boolean $reduce - * only used if $storeInEnv = false + * @param bool $storeInEnv + * @param bool $reduce only used if $storeInEnv = false * * @return array * @@ -6765,7 +6834,7 @@ EOL; $prototype = ['arguments' => [], 'rest_argument' => null]; $originalRestArgumentName = null; - foreach ($argDef as $i => $arg) { + foreach ($argDef as $arg) { list($name, $default, $isVariable) = $arg; $normalizedName = str_replace('_', '-', $name); @@ -6983,14 +7052,14 @@ EOL; * * @param array|Number $item * @param string $delim - * @param boolean $removeTrailingNull + * @param bool $removeTrailingNull * * @return array */ protected function coerceList($item, $delim = ',', $removeTrailingNull = false) { if ($item instanceof Number) { - return [Type::T_LIST, $delim, [$item]]; + return [Type::T_LIST, '', [$item]]; } if ($item[0] === Type::T_LIST) { @@ -7013,15 +7082,15 @@ EOL; $list[] = [ Type::T_LIST, - '', + ' ', [$key, $value] ]; } - return [Type::T_LIST, ',', $list]; + return [Type::T_LIST, $list ? ',' : '', $list]; } - return [Type::T_LIST, $delim, [$item]]; + return [Type::T_LIST, '', [$item]]; } /** @@ -7162,10 +7231,10 @@ EOL; } /** - * @param integer|Number $value - * @param boolean $isAlpha + * @param int|Number $value + * @param bool $isAlpha * - * @return integer|mixed + * @return int|mixed */ protected function compileRGBAValue($value, $isAlpha = false) { @@ -7177,12 +7246,12 @@ EOL; } /** - * @param mixed $value - * @param integer|float $min - * @param integer|float $max - * @param boolean $isInt + * @param mixed $value + * @param int|float $min + * @param int|float $max + * @param bool $isInt * - * @return integer|mixed + * @return int|mixed */ protected function compileColorPartValue($value, $min, $max, $isInt = true) { @@ -7233,6 +7302,8 @@ EOL; protected function coerceString($value) { if ($value[0] === Type::T_STRING) { + assert(\is_array($value)); + return $value; } @@ -7276,7 +7347,7 @@ EOL; * * @param array|Number $value * - * @return integer|float + * @return int|float * * @deprecated */ @@ -7336,6 +7407,7 @@ EOL; if ($value[0] !== Type::T_LIST) { throw $this->error('expecting list, %s received', $value[0]); } + assert(\is_array($value)); return $value; } @@ -7414,7 +7486,7 @@ EOL; * @param array|Number $value * @param string|null $varName * - * @return integer + * @return int * * @throws SassScriptException */ @@ -7478,9 +7550,9 @@ EOL; * * @internal * - * @param integer $red - * @param integer $green - * @param integer $blue + * @param int $red + * @param int $green + * @param int $blue * * @return array */ @@ -7505,7 +7577,7 @@ EOL; $h = 60 * ($green - $blue) / $d; } elseif ($green == $max) { $h = 60 * ($blue - $red) / $d + 120; - } elseif ($blue == $max) { + } else { $h = 60 * ($red - $green) / $d + 240; } } @@ -7584,9 +7656,9 @@ EOL; * * @api * - * @param integer $hue H from 0 to 360 - * @param integer $whiteness W from 0 to 100 - * @param integer $blackness B from 0 to 100 + * @param int|float $hue H from 0 to 360 + * @param int|float $whiteness W from 0 to 100 + * @param int|float $blackness B from 0 to 100 * * @return array */ @@ -7616,9 +7688,9 @@ EOL; * * @api * - * @param integer $red - * @param integer $green - * @param integer $blue + * @param int $red + * @param int $green + * @param int $blue * * @return array */ @@ -7637,7 +7709,7 @@ EOL; $h = 60 * ($green - $blue) / $d; } elseif ($green == $max) { $h = 60 * ($blue - $red) / $d + 120; - } elseif ($blue == $max) { + } else { $h = 60 * ($red - $green) / $d + 240; } } @@ -7746,7 +7818,6 @@ EOL; $values = []; - foreach ($list[2] as $item) { $values[] = $this->normalizeValue($item); } @@ -7762,6 +7833,14 @@ EOL; ['channels'], ['red', 'green', 'blue'], ['red', 'green', 'blue', 'alpha'] ]; + + /** + * @param array $args + * @param array $kwargs + * @param string $funcName + * + * @return array + */ protected function libRgb($args, $kwargs, $funcName = 'rgb') { switch (\count($args)) { @@ -7844,14 +7923,7 @@ EOL; $scale = $operation === 'scale'; $change = $operation === 'change'; - /** - * @param string $name - * @param float|int $max - * @param bool $checkPercent - * @param bool $assertPercent - * - * @return float|int|null - */ + /** @phpstan-var callable(string, float|int, bool=, bool=): (float|int|null) $getParam */ $getParam = function ($name, $max, $checkPercent = false, $assertPercent = false) use (&$kwargs, $scale, $change) { if (!isset($kwargs[$name])) { return null; @@ -7875,7 +7947,11 @@ EOL; $max = 100; } - return $number->valueInRange($change ? 0 : -$max, $max, $name); + if ($scale || $assertPercent) { + return $number->valueInRange($change ? 0 : -$max, $max, $name); + } + + return $number->valueInRangeWithUnit($change ? 0 : -$max, $max, $name, $checkPercent ? '%' : ''); }; $alpha = $getParam('alpha', 1); @@ -7910,7 +7986,6 @@ EOL; $hasRgb = $red !== null || $green !== null || $blue !== null; $hasSL = $saturation !== null || $lightness !== null; $hasWB = $whiteness !== null || $blackness !== null; - $found = false; if ($hasRgb && ($hasSL || $hasWB || $hue !== null)) { throw new SassScriptException(sprintf('RGB parameters may not be passed along with %s parameters.', $hasWB ? 'HWB' : 'HSL')); @@ -8131,6 +8206,14 @@ EOL; ['hue', 'saturation'], ['hue', 'saturation', 'lightness'], ['hue', 'saturation', 'lightness', 'alpha'] ]; + + /** + * @param array $args + * @param array $kwargs + * @param string $funcName + * + * @return array|null + */ protected function libHsl($args, $kwargs, $funcName = 'hsl') { $args_to_check = $args; @@ -8155,7 +8238,7 @@ EOL; throw new SassScriptException('Missing argument $lightness.'); } - foreach ($kwargs as $k => $arg) { + foreach ($kwargs as $arg) { if (in_array($arg[0], [Type::T_FUNCTION_CALL, Type::T_FUNCTION]) && in_array($arg[1], ['min', 'max'])) { return null; } @@ -8375,6 +8458,13 @@ EOL; } */ + /** + * @param array $color + * @param int $idx + * @param int|float $amount + * + * @return array + */ protected function adjustHsl($color, $idx, $amount) { $hsl = $this->toHSL($color[1], $color[2], $color[3]); @@ -8496,7 +8586,7 @@ EOL; $color = $this->assertColor($args[0], 'color'); $amount = $this->assertNumber($args[1], 'amount'); - $color[4] = (isset($color[4]) ? $color[4] : 1) + $amount->valueInRange(0, 1, 'amount'); + $color[4] = (isset($color[4]) ? $color[4] : 1) + $amount->valueInRangeWithUnit(0, 1, 'amount', ''); $color[4] = min(1, max(0, $color[4])); return $color; @@ -8515,7 +8605,7 @@ EOL; $color = $this->assertColor($args[0], 'color'); $amount = $this->assertNumber($args[1], 'amount'); - $color[4] = (isset($color[4]) ? $color[4] : 1) - $amount->valueInRange(0, 1, 'amount'); + $color[4] = (isset($color[4]) ? $color[4] : 1) - $amount->valueInRangeWithUnit(0, 1, 'amount', ''); $color[4] = min(1, max(0, $color[4])); return $color; @@ -8664,7 +8754,7 @@ will be an error in future versions of Sass.\n on line $line of $fname"; $list = $this->coerceList($args[0]); - if (\count($list[2]) <= 1 && empty($list['enclosing'])) { + if ($list[1] === '' && \count($list[2]) <= 1 && empty($list['enclosing'])) { return [Type::T_KEYWORD, 'space']; } @@ -8672,6 +8762,10 @@ will be an error in future versions of Sass.\n on line $line of $fname"; return [Type::T_KEYWORD, 'comma']; } + if ($list[1] === '/') { + return [Type::T_KEYWORD, 'slash']; + } + return [Type::T_KEYWORD, 'space']; } @@ -8679,7 +8773,7 @@ will be an error in future versions of Sass.\n on line $line of $fname"; protected function libNth($args) { $list = $this->coerceList($args[0], ',', false); - $n = $this->assertNumber($args[1])->getDimension(); + $n = $this->assertInteger($args[1]); if ($n > 0) { $n--; @@ -8694,7 +8788,7 @@ will be an error in future versions of Sass.\n on line $line of $fname"; protected function libSetNth($args) { $list = $this->coerceList($args[0]); - $n = $this->assertNumber($args[1])->getDimension(); + $n = $this->assertInteger($args[1]); if ($n > 0) { $n--; @@ -9038,9 +9132,13 @@ will be an error in future versions of Sass.\n on line $line of $fname"; * * @return string * @throws CompilerException + * + * @deprecated */ protected function listSeparatorForJoin($list1, $sep) { + @trigger_error(sprintf('The "%s" method is deprecated.', __METHOD__), E_USER_DEPRECATED); + if (! isset($sep)) { return $list1[1]; } @@ -9057,14 +9155,40 @@ will be an error in future versions of Sass.\n on line $line of $fname"; } } - protected static $libJoin = ['list1', 'list2', 'separator:null', 'bracketed:auto']; + protected static $libJoin = ['list1', 'list2', 'separator:auto', 'bracketed:auto']; protected function libJoin($args) { list($list1, $list2, $sep, $bracketed) = $args; $list1 = $this->coerceList($list1, ' ', true); $list2 = $this->coerceList($list2, ' ', true); - $sep = $this->listSeparatorForJoin($list1, $sep); + + switch ($this->compileStringContent($this->assertString($sep, 'separator'))) { + case 'comma': + $separator = ','; + break; + + case 'space': + $separator = ' '; + break; + + case 'slash': + $separator = '/'; + break; + + case 'auto': + if ($list1[1] !== '' || count($list1[2]) > 1 || !empty($list1['enclosing']) && $list1['enclosing'] !== 'parent') { + $separator = $list1[1] ?: ' '; + } elseif ($list2[1] !== '' || count($list2[2]) > 1 || !empty($list2['enclosing']) && $list2['enclosing'] !== 'parent') { + $separator = $list2[1] ?: ' '; + } else { + $separator = ' '; + } + break; + + default: + throw SassScriptException::forArgument('Must be "space", "comma", "slash", or "auto".', 'separator'); + } if ($bracketed === static::$true) { $bracketed = true; @@ -9091,11 +9215,7 @@ will be an error in future versions of Sass.\n on line $line of $fname"; } } - $res = [Type::T_LIST, $sep, array_merge($list1[2], $list2[2])]; - - if (isset($list1['enclosing'])) { - $res['enlcosing'] = $list1['enclosing']; - } + $res = [Type::T_LIST, $separator, array_merge($list1[2], $list2[2])]; if ($bracketed) { $res['enclosing'] = 'bracket'; @@ -9104,14 +9224,35 @@ will be an error in future versions of Sass.\n on line $line of $fname"; return $res; } - protected static $libAppend = ['list', 'val', 'separator:null']; + protected static $libAppend = ['list', 'val', 'separator:auto']; protected function libAppend($args) { list($list1, $value, $sep) = $args; $list1 = $this->coerceList($list1, ' ', true); - $sep = $this->listSeparatorForJoin($list1, $sep); - $res = [Type::T_LIST, $sep, array_merge($list1[2], [$value])]; + + switch ($this->compileStringContent($this->assertString($sep, 'separator'))) { + case 'comma': + $separator = ','; + break; + + case 'space': + $separator = ' '; + break; + + case 'slash': + $separator = '/'; + break; + + case 'auto': + $separator = $list1[1] === '' && \count($list1[2]) <= 1 && (empty($list1['enclosing']) || $list1['enclosing'] === 'parent') ? ' ' : $list1[1]; + break; + + default: + throw SassScriptException::forArgument('Must be "space", "comma", "slash", or "auto".', 'separator'); + } + + $res = [Type::T_LIST, $separator, array_merge($list1[2], [$value])]; if (isset($list1['enclosing'])) { $res['enclosing'] = $list1['enclosing']; @@ -9134,7 +9275,7 @@ will be an error in future versions of Sass.\n on line $line of $fname"; $result = [Type::T_LIST, ',', $lists]; if (! \is_null($firstList)) { foreach ($firstList[2] as $key => $item) { - $list = [Type::T_LIST, '', [$item]]; + $list = [Type::T_LIST, ' ', [$item]]; foreach ($argLists as $arg) { if (isset($arg[2][$key])) { @@ -9264,7 +9405,7 @@ will be an error in future versions of Sass.\n on line $line of $fname"; $index = $index - 1; } if ($index < 0) { - $index = Util::mbStrlen($stringContent) + 1 + $index; + $index = max(Util::mbStrlen($stringContent) + 1 + $index, 0); } $string[2] = [ @@ -9452,7 +9593,25 @@ will be an error in future versions of Sass.\n on line $line of $fname"; protected function libRandom($args) { if (isset($args[0]) && $args[0] !== static::$null) { - $n = $this->assertInteger($args[0], 'limit'); + $limit = $this->assertNumber($args[0], 'limit'); + + if ($limit->hasUnits()) { + $unitString = $limit->unitStr(); + $message = <<addLocationToMessage($message)); + } + + $n = $this->assertInteger($limit, 'limit'); if ($n < 1) { throw new SassScriptException("\$limit: Must be greater than 0, was $n."); @@ -9514,6 +9673,8 @@ will be an error in future versions of Sass.\n on line $line of $fname"; ) { $value['enclosing'] = 'forced_' . $value['enclosing']; $force_enclosing_display = true; + } elseif (! \count($value[2])) { + $value['enclosing'] = 'forced_parent'; } foreach ($value[2] as $k => $listelement) { @@ -9637,7 +9798,7 @@ will be an error in future versions of Sass.\n on line $line of $fname"; * @param array $super * @param array $sub * - * @return boolean + * @return bool */ protected function isSuperSelector($super, $sub) { @@ -9718,7 +9879,7 @@ will be an error in future versions of Sass.\n on line $line of $fname"; * @param array $superParts * @param array $subParts * - * @return boolean + * @return bool */ protected function isSuperPart($superParts, $subParts) { @@ -9785,21 +9946,18 @@ will be an error in future versions of Sass.\n on line $line of $fname"; // do the trick, happening $lastSelector to $previousSelector $appended = []; - foreach ($lastSelectors as $lastSelector) { - $previous = $previousSelectors; - - foreach ($lastSelector as $lastSelectorParts) { - foreach ($lastSelectorParts as $lastSelectorPart) { - foreach ($previous as $i => $previousSelector) { - foreach ($previousSelector as $j => $previousSelectorParts) { - $previous[$i][$j][] = $lastSelectorPart; + foreach ($previousSelectors as $previousSelector) { + foreach ($lastSelectors as $lastSelector) { + $previous = $previousSelector; + foreach ($previousSelector as $j => $previousSelectorParts) { + foreach ($lastSelector as $lastSelectorParts) { + foreach ($lastSelectorParts as $lastSelectorPart) { + $previous[$j][] = $lastSelectorPart; } } } - } - foreach ($previous as $ps) { - $appended[] = $ps; + $appended[] = $previous; } } @@ -9855,10 +10013,10 @@ will be an error in future versions of Sass.\n on line $line of $fname"; * Extend/replace in selectors * used by selector-extend and selector-replace that use the same logic * - * @param array $selectors - * @param array $extendee - * @param array $extender - * @param boolean $replace + * @param array $selectors + * @param array $extendee + * @param array $extender + * @param bool $replace * * @return array */ @@ -9918,6 +10076,8 @@ will be an error in future versions of Sass.\n on line $line of $fname"; $selectorsMap[] = $this->getSelectorArg($arg, 'selector', true); } + assert(!empty($selectorsMap)); + $envs = []; foreach ($selectorsMap as $selectors) { diff --git a/lib/scssphp/src/Compiler/Environment.php b/lib/scssphp/src/Compiler/Environment.php index 306b15a..b205a07 100644 --- a/lib/scssphp/src/Compiler/Environment.php +++ b/lib/scssphp/src/Compiler/Environment.php @@ -31,6 +31,26 @@ class Environment */ public $parent; + /** + * @var Environment|null + */ + public $declarationScopeParent; + + /** + * @var Environment|null + */ + public $parentStore; + + /** + * @var array|null + */ + public $selectors; + + /** + * @var string|null + */ + public $marker; + /** * @var array */ @@ -42,7 +62,7 @@ class Environment public $storeUnreduced; /** - * @var integer + * @var int */ public $depth; } diff --git a/lib/scssphp/src/Exception/ParserException.php b/lib/scssphp/src/Exception/ParserException.php index 00d77ec..f072669 100644 --- a/lib/scssphp/src/Exception/ParserException.php +++ b/lib/scssphp/src/Exception/ParserException.php @@ -22,7 +22,8 @@ namespace ScssPhp\ScssPhp\Exception; class ParserException extends \Exception implements SassException { /** - * @var array + * @var array|null + * @phpstan-var array{string, int, int}|null */ private $sourcePosition; @@ -30,6 +31,9 @@ class ParserException extends \Exception implements SassException * Get source position * * @api + * + * @return array|null + * @phpstan-return array{string, int, int}|null */ public function getSourcePosition() { @@ -42,6 +46,10 @@ class ParserException extends \Exception implements SassException * @api * * @param array $sourcePosition + * + * @return void + * + * @phpstan-param array{string, int, int} $sourcePosition */ public function setSourcePosition($sourcePosition) { diff --git a/lib/scssphp/src/Formatter.php b/lib/scssphp/src/Formatter.php index cc42ae8..6137dc6 100644 --- a/lib/scssphp/src/Formatter.php +++ b/lib/scssphp/src/Formatter.php @@ -25,7 +25,7 @@ use ScssPhp\ScssPhp\SourceMap\SourceMapGenerator; abstract class Formatter { /** - * @var integer + * @var int */ public $indentLevel; @@ -60,7 +60,7 @@ abstract class Formatter public $assignSeparator; /** - * @var boolean + * @var bool */ public $keepSemicolons; @@ -70,12 +70,12 @@ abstract class Formatter protected $currentBlock; /** - * @var integer + * @var int */ protected $currentLine; /** - * @var integer + * @var int */ protected $currentColumn; @@ -239,7 +239,7 @@ abstract class Formatter * * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block * - * @return boolean + * @return bool */ protected function testEmptyChildren($block) { @@ -286,9 +286,18 @@ abstract class Formatter ob_start(); - $this->block($block); + try { + $this->block($block); + } catch (\Exception $e) { + ob_end_clean(); + throw $e; + } catch (\Throwable $e) { + ob_end_clean(); + throw $e; + } $out = ob_get_clean(); + assert($out !== false); return $out; } @@ -331,6 +340,8 @@ abstract class Formatter // If the written line starts is empty, adding a mapping would add it for // a non-existent column as we are at the end of the line if ($line !== '') { + assert($this->currentBlock->sourceLine !== null); + assert($this->currentBlock->sourceName !== null); $this->sourceMapGenerator->addMapping( $this->currentLine, $this->currentColumn, @@ -346,6 +357,8 @@ abstract class Formatter } if ($lastLine !== '') { + assert($this->currentBlock->sourceLine !== null); + assert($this->currentBlock->sourceName !== null); $this->sourceMapGenerator->addMapping( $this->currentLine, $this->currentColumn, diff --git a/lib/scssphp/src/Formatter/Compressed.php b/lib/scssphp/src/Formatter/Compressed.php index de13c18..58ebe3f 100644 --- a/lib/scssphp/src/Formatter/Compressed.php +++ b/lib/scssphp/src/Formatter/Compressed.php @@ -50,8 +50,6 @@ class Compressed extends Formatter foreach ($block->lines as $index => $line) { if (substr($line, 0, 2) === '/*' && substr($line, 2, 1) !== '!') { unset($block->lines[$index]); - } elseif (substr($line, 0, 3) === '/*!') { - $block->lines[$index] = '/*' . substr($line, 3); } } diff --git a/lib/scssphp/src/Formatter/Expanded.php b/lib/scssphp/src/Formatter/Expanded.php index a280416..6eb4a0c 100644 --- a/lib/scssphp/src/Formatter/Expanded.php +++ b/lib/scssphp/src/Formatter/Expanded.php @@ -57,7 +57,9 @@ class Expanded extends Formatter foreach ($block->lines as $index => $line) { if (substr($line, 0, 2) === '/*') { - $block->lines[$index] = preg_replace('/\r\n?|\n|\f/', $this->break, $line); + $replacedLine = preg_replace('/\r\n?|\n|\f/', $this->break, $line); + assert($replacedLine !== null); + $block->lines[$index] = $replacedLine; } } diff --git a/lib/scssphp/src/Formatter/Nested.php b/lib/scssphp/src/Formatter/Nested.php index 9e72956..d5ed85c 100644 --- a/lib/scssphp/src/Formatter/Nested.php +++ b/lib/scssphp/src/Formatter/Nested.php @@ -27,7 +27,7 @@ use ScssPhp\ScssPhp\Type; class Nested extends Formatter { /** - * @var integer + * @var int */ private $depth; @@ -68,7 +68,9 @@ class Nested extends Formatter foreach ($block->lines as $index => $line) { if (substr($line, 0, 2) === '/*') { - $block->lines[$index] = preg_replace('/\r\n?|\n|\f/', $this->break, $line); + $replacedLine = preg_replace('/\r\n?|\n|\f/', $this->break, $line); + assert($replacedLine !== null); + $block->lines[$index] = $replacedLine; } } @@ -221,7 +223,7 @@ class Nested extends Formatter * * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block * - * @return boolean + * @return bool */ private function hasFlatChild($block) { diff --git a/lib/scssphp/src/Formatter/OutputBlock.php b/lib/scssphp/src/Formatter/OutputBlock.php index 88deb2d..2799656 100644 --- a/lib/scssphp/src/Formatter/OutputBlock.php +++ b/lib/scssphp/src/Formatter/OutputBlock.php @@ -22,12 +22,12 @@ namespace ScssPhp\ScssPhp\Formatter; class OutputBlock { /** - * @var string + * @var string|null */ public $type; /** - * @var integer + * @var int */ public $depth; @@ -57,12 +57,12 @@ class OutputBlock public $sourceName; /** - * @var integer|null + * @var int|null */ public $sourceLine; /** - * @var integer|null + * @var int|null */ public $sourceColumn; } diff --git a/lib/scssphp/src/Logger/QuietLogger.php b/lib/scssphp/src/Logger/QuietLogger.php index 0f358c6..ad7c075 100644 --- a/lib/scssphp/src/Logger/QuietLogger.php +++ b/lib/scssphp/src/Logger/QuietLogger.php @@ -14,6 +14,8 @@ namespace ScssPhp\ScssPhp\Logger; /** * A logger that silently ignores all messages. + * + * @final */ class QuietLogger implements LoggerInterface { diff --git a/lib/scssphp/src/Logger/StreamLogger.php b/lib/scssphp/src/Logger/StreamLogger.php index f5da6c9..7db7cc1 100644 --- a/lib/scssphp/src/Logger/StreamLogger.php +++ b/lib/scssphp/src/Logger/StreamLogger.php @@ -14,6 +14,8 @@ namespace ScssPhp\ScssPhp\Logger; /** * A logger that prints to a PHP stream (for instance stderr) + * + * @final */ class StreamLogger implements LoggerInterface { diff --git a/lib/scssphp/src/Node.php b/lib/scssphp/src/Node.php index 5301937..fcaf8a9 100644 --- a/lib/scssphp/src/Node.php +++ b/lib/scssphp/src/Node.php @@ -27,7 +27,7 @@ abstract class Node public $type; /** - * @var integer + * @var int */ public $sourceIndex; diff --git a/lib/scssphp/src/Node/Number.php b/lib/scssphp/src/Node/Number.php index b326906..48e711f 100644 --- a/lib/scssphp/src/Node/Number.php +++ b/lib/scssphp/src/Node/Number.php @@ -38,7 +38,7 @@ class Number extends Node implements \ArrayAccess const PRECISION = 10; /** - * @var integer + * @var int * @deprecated use {Number::PRECISION} instead to read the precision. Configuring it is not supported anymore. */ public static $precision = self::PRECISION; @@ -81,7 +81,7 @@ class Number extends Node implements \ArrayAccess ]; /** - * @var integer|float + * @var int|float */ private $dimension; @@ -100,7 +100,7 @@ class Number extends Node implements \ArrayAccess /** * Initialize number * - * @param integer|float $dimension + * @param int|float $dimension * @param string[]|string $numeratorUnits * @param string[] $denominatorUnits * @@ -147,7 +147,7 @@ class Number extends Node implements \ArrayAccess } /** - * {@inheritdoc} + * @return bool */ #[\ReturnTypeWillChange] public function offsetExists($offset) @@ -173,7 +173,7 @@ class Number extends Node implements \ArrayAccess } /** - * {@inheritdoc} + * @return mixed */ #[\ReturnTypeWillChange] public function offsetGet($offset) @@ -200,7 +200,7 @@ class Number extends Node implements \ArrayAccess } /** - * {@inheritdoc} + * @return void */ #[\ReturnTypeWillChange] public function offsetSet($offset, $value) @@ -209,7 +209,7 @@ class Number extends Node implements \ArrayAccess } /** - * {@inheritdoc} + * @return void */ #[\ReturnTypeWillChange] public function offsetUnset($offset) @@ -220,13 +220,23 @@ class Number extends Node implements \ArrayAccess /** * Returns true if the number is unitless * - * @return boolean + * @return bool */ public function unitless() { return \count($this->numeratorUnits) === 0 && \count($this->denominatorUnits) === 0; } + /** + * Returns true if the number has any units + * + * @return bool + */ + public function hasUnits() + { + return !$this->unitless(); + } + /** * Checks whether the number has exactly this unit * @@ -266,7 +276,27 @@ class Number extends Node implements \ArrayAccess try { return Util::checkRange('', new Range($min, $max), $this); } catch (RangeException $e) { - throw SassScriptException::forArgument(sprintf('Expected %s to be within %s%s and %s%3$s', $this, $min, $this->unitStr(), $max), $name); + throw SassScriptException::forArgument(sprintf('Expected %s to be within %s%s and %s%3$s.', $this, $min, $this->unitStr(), $max), $name); + } + } + + /** + * @param float|int $min + * @param float|int $max + * @param string $name + * @param string $unit + * + * @return float|int + * @throws SassScriptException + * + * @internal + */ + public function valueInRangeWithUnit($min, $max, $name, $unit) + { + try { + return Util::checkRange('', new Range($min, $max), $this); + } catch (RangeException $e) { + throw SassScriptException::forArgument(sprintf('Expected %s to be within %s%s and %s%3$s.', $this, $min, $unit, $max), $name); } } diff --git a/lib/scssphp/src/Parser.php b/lib/scssphp/src/Parser.php index 3ba2f67..1c76e7c 100644 --- a/lib/scssphp/src/Parser.php +++ b/lib/scssphp/src/Parser.php @@ -12,9 +12,22 @@ namespace ScssPhp\ScssPhp; +use ScssPhp\ScssPhp\Block\AtRootBlock; +use ScssPhp\ScssPhp\Block\CallableBlock; +use ScssPhp\ScssPhp\Block\ContentBlock; +use ScssPhp\ScssPhp\Block\DirectiveBlock; +use ScssPhp\ScssPhp\Block\EachBlock; +use ScssPhp\ScssPhp\Block\ElseBlock; +use ScssPhp\ScssPhp\Block\ElseifBlock; +use ScssPhp\ScssPhp\Block\ForBlock; +use ScssPhp\ScssPhp\Block\IfBlock; +use ScssPhp\ScssPhp\Block\MediaBlock; +use ScssPhp\ScssPhp\Block\NestedPropertyBlock; +use ScssPhp\ScssPhp\Block\WhileBlock; use ScssPhp\ScssPhp\Exception\ParserException; use ScssPhp\ScssPhp\Logger\LoggerInterface; use ScssPhp\ScssPhp\Logger\QuietLogger; +use ScssPhp\ScssPhp\Node\Number; /** * Parser @@ -73,10 +86,6 @@ class Parser * @var array */ private $sourcePositions; - /** - * @var array|null - */ - private $charset; /** * The current offset in the buffer * @@ -125,7 +134,7 @@ class Parser * @api * * @param string|null $sourceName - * @param integer $sourceIndex + * @param int $sourceIndex * @param string|null $encoding * @param Cache|null $cache * @param bool $cssOnly @@ -135,11 +144,9 @@ class Parser { $this->sourceName = $sourceName ?: '(stdin)'; $this->sourceIndex = $sourceIndex; - $this->charset = null; $this->utf8 = ! $encoding || strtolower($encoding) === 'utf-8'; $this->patternModifiers = $this->utf8 ? 'Aisu' : 'Ais'; $this->commentsSeen = []; - $this->commentsSeen = []; $this->allowVars = true; $this->cssOnly = $cssOnly; $this->logger = $logger ?: new QuietLogger(); @@ -243,7 +250,6 @@ class Parser if ($this->cache) { $cacheKey = $this->sourceName . ':' . md5($buffer); $parseOptions = [ - 'charset' => $this->charset, 'utf8' => $this->utf8, ]; $v = $this->cache->getCache('parse', $cacheKey, $parseOptions); @@ -284,11 +290,8 @@ class Parser throw $this->parseError('unclosed block'); } - if ($this->charset) { - array_unshift($this->env->children, $this->charset); - } - $this->restoreEncoding(); + assert($this->env !== null); if ($this->cache) { $this->cache->setCache('parse', $cacheKey, $this->env, $parseOptions); @@ -305,7 +308,7 @@ class Parser * @param string $buffer * @param string|array $out * - * @return boolean + * @return bool */ public function parseValue($buffer, &$out) { @@ -334,7 +337,7 @@ class Parser * @param string|array $out * @param bool $shouldValidate * - * @return boolean + * @return bool */ public function parseSelector($buffer, &$out, $shouldValidate = true) { @@ -368,10 +371,10 @@ class Parser * * @api * - * @param string $buffer - * @param string|array $out + * @param string $buffer + * @param array $out * - * @return boolean + * @return bool */ public function parseMediaQueryList($buffer, &$out) { @@ -428,7 +431,7 @@ class Parser * position into $s. Then if a chain fails, use $this->seek($s) to * go back where we started. * - * @return boolean + * @return bool */ protected function parseChunk() { @@ -447,7 +450,8 @@ class Parser ) { ! $this->cssOnly || $this->assertPlainCssValid(false, $s); - $atRoot = $this->pushSpecialBlock(Type::T_AT_ROOT, $s); + $atRoot = new AtRootBlock(); + $this->registerPushedBlock($atRoot, $s); $atRoot->selector = $selector; $atRoot->with = $with; @@ -461,7 +465,8 @@ class Parser $this->mediaQueryList($mediaQueryList) && $this->matchChar('{', false) ) { - $media = $this->pushSpecialBlock(Type::T_MEDIA, $s); + $media = new MediaBlock(); + $this->registerPushedBlock($media, $s); $media->queryList = $mediaQueryList[2]; return true; @@ -477,7 +482,8 @@ class Parser ) { ! $this->cssOnly || $this->assertPlainCssValid(false, $s); - $mixin = $this->pushSpecialBlock(Type::T_MIXIN, $s); + $mixin = new CallableBlock(Type::T_MIXIN); + $this->registerPushedBlock($mixin, $s); $mixin->name = $mixinName; $mixin->args = $args; @@ -509,7 +515,8 @@ class Parser ]; if (! empty($hasBlock)) { - $include = $this->pushSpecialBlock(Type::T_INCLUDE, $s); + $include = new ContentBlock(); + $this->registerPushedBlock($include, $s); $include->child = $child; } else { $this->append($child, $s); @@ -599,7 +606,8 @@ class Parser ) { ! $this->cssOnly || $this->assertPlainCssValid(false, $s); - $func = $this->pushSpecialBlock(Type::T_FUNCTION, $s); + $func = new CallableBlock(Type::T_FUNCTION); + $this->registerPushedBlock($func, $s); $func->name = $fnName; $func->args = $args; @@ -631,7 +639,8 @@ class Parser ) { ! $this->cssOnly || $this->assertPlainCssValid(false, $s); - $each = $this->pushSpecialBlock(Type::T_EACH, $s); + $each = new EachBlock(); + $this->registerPushedBlock($each, $s); foreach ($varNames[2] as $varName) { $each->vars[] = $varName[1]; @@ -660,7 +669,8 @@ class Parser $cond = reset($cond[2]); } - $while = $this->pushSpecialBlock(Type::T_WHILE, $s); + $while = new WhileBlock(); + $this->registerPushedBlock($while, $s); $while->cond = $cond; return true; @@ -680,7 +690,8 @@ class Parser ) { ! $this->cssOnly || $this->assertPlainCssValid(false, $s); - $for = $this->pushSpecialBlock(Type::T_FOR, $s); + $for = new ForBlock(); + $this->registerPushedBlock($for, $s); $for->var = $varName[1]; $for->start = $start; $for->end = $end; @@ -697,7 +708,8 @@ class Parser ) { ! $this->cssOnly || $this->assertPlainCssValid(false, $s); - $if = $this->pushSpecialBlock(Type::T_IF, $s); + $if = new IfBlock(); + $this->registerPushedBlock($if, $s); while ( $cond[0] === Type::T_LIST && @@ -776,20 +788,21 @@ class Parser if (isset($last) && $last[0] === Type::T_IF) { list(, $if) = $last; + assert($if instanceof IfBlock); if ($this->literal('@else', 5)) { if ($this->matchChar('{', false)) { - $else = $this->pushSpecialBlock(Type::T_ELSE, $s); + $else = new ElseBlock(); } elseif ( $this->literal('if', 2) && $this->functionCallArgumentsList($cond, false, '{', false) ) { - $else = $this->pushSpecialBlock(Type::T_ELSEIF, $s); + $else = new ElseifBlock(); $else->cond = $cond; } if (isset($else)) { - $else->dontAppend = true; + $this->registerPushedBlock($else, $s); $if->cases[] = $else; return true; @@ -805,18 +818,6 @@ class Parser $this->valueList($charset) && $this->end() ) { - if (! isset($this->charset)) { - $statement = [Type::T_CHARSET, $charset]; - - list($line, $column) = $this->getSourcePosition($s); - - $statement[static::SOURCE_LINE] = $line; - $statement[static::SOURCE_COLUMN] = $column; - $statement[static::SOURCE_INDEX] = $this->sourceIndex; - - $this->charset = $statement; - } - return true; } @@ -827,7 +828,8 @@ class Parser ($t1 = $this->supportsQuery($supportQuery)) && ($t2 = $this->matchChar('{', false)) ) { - $directive = $this->pushSpecialBlock(Type::T_DIRECTIVE, $s); + $directive = new DirectiveBlock(); + $this->registerPushedBlock($directive, $s); $directive->name = 'supports'; $directive->value = $supportQuery; @@ -848,11 +850,12 @@ class Parser $dirName = [Type::T_STRING, '', $dirName]; } if ($dirName === 'media') { - $directive = $this->pushSpecialBlock(Type::T_MEDIA, $s); + $directive = new MediaBlock(); } else { - $directive = $this->pushSpecialBlock(Type::T_DIRECTIVE, $s); + $directive = new DirectiveBlock(); $directive->name = $dirName; } + $this->registerPushedBlock($directive, $s); if (isset($dirValue)) { ! $this->cssOnly || ($dirValue = $this->assertPlainCssValid($dirValue)); @@ -1028,7 +1031,8 @@ class Parser if ($this->matchChar('{', false)) { ! $this->cssOnly || $this->assertPlainCssValid(false); - $propBlock = $this->pushSpecialBlock(Type::T_NESTED_PROPERTY, $s); + $propBlock = new NestedPropertyBlock(); + $this->registerPushedBlock($propBlock, $s); $propBlock->prefix = $name; $propBlock->hasValue = $foundSomething; @@ -1049,17 +1053,20 @@ class Parser $block = $this->popBlock(); if (! isset($block->type) || $block->type !== Type::T_IF) { + assert($this->env !== null); + if ($this->env->parent) { $this->append(null); // collect comments before next statement if needed } } - if (isset($block->type) && $block->type === Type::T_INCLUDE) { + if ($block instanceof ContentBlock) { $include = $block->child; + assert(\is_array($include)); unset($block->child); $include[3] = $block; $this->append($include, $s); - } elseif (empty($block->dontAppend)) { + } elseif (!$block instanceof ElseBlock && !$block instanceof ElseifBlock) { $type = isset($block->type) ? $block->type : Type::T_BLOCK; $this->append([$type, $block], $s); } @@ -1067,6 +1074,7 @@ class Parser // collect comments just after the block closing if needed if ($this->eatWhiteDefault) { $this->whitespace(); + assert($this->env !== null); if ($this->env->comments) { $this->append(null); @@ -1088,20 +1096,34 @@ class Parser * Push block onto parse tree * * @param array|null $selectors - * @param integer $pos + * @param int $pos * * @return Block */ protected function pushBlock($selectors, $pos = 0) + { + $b = new Block(); + $b->selectors = $selectors; + + $this->registerPushedBlock($b, $pos); + + return $b; + } + + /** + * @param Block $b + * @param int $pos + * + * @return void + */ + private function registerPushedBlock(Block $b, $pos) { list($line, $column) = $this->getSourcePosition($pos); - $b = new Block(); $b->sourceName = $this->sourceName; $b->sourceLine = $line; $b->sourceColumn = $column; $b->sourceIndex = $this->sourceIndex; - $b->selectors = $selectors; $b->comments = []; $b->parent = $this->env; @@ -1121,20 +1143,21 @@ class Parser // collect comments at the beginning of a block if needed if ($this->eatWhiteDefault) { $this->whitespace(); + assert($this->env !== null); if ($this->env->comments) { $this->append(null); } } - - return $b; } /** * Push special (named) block onto parse tree * + * @deprecated + * * @param string $type - * @param integer $pos + * @param int $pos * * @return Block */ @@ -1155,6 +1178,7 @@ class Parser */ protected function popBlock() { + assert($this->env !== null); // collect comments ending just before of a block closing if ($this->env->comments) { @@ -1183,11 +1207,11 @@ class Parser /** * Peek input stream * - * @param string $regex - * @param array $out - * @param integer $from + * @param string $regex + * @param array $out + * @param int $from * - * @return integer + * @return int */ protected function peek($regex, &$out, $from = null) { @@ -1204,7 +1228,9 @@ class Parser /** * Seek to position in input stream (or return current position in input stream) * - * @param integer $where + * @param int $where + * + * @return void */ protected function seek($where) { @@ -1215,7 +1241,10 @@ class Parser * Assert a parsed part is plain CSS Valid * * @param array|false $parsed - * @param int $startPos + * @param int $startPos + * + * @return array + * * @throws ParserException */ protected function assertPlainCssValid($parsed, $startPos = null) @@ -1243,8 +1272,11 @@ class Parser /** * Check a parsed element is plain CSS Valid + * * @param array $parsed - * @return bool|array + * @param bool $allowExpression + * + * @return array|false */ protected function isPlainCssValidElement($parsed, $allowExpression = false) { @@ -1423,7 +1455,9 @@ class Parser * @param array $m Matches (passed by reference) * @param string $delim Delimiter * - * @return boolean True if match; false otherwise + * @return bool True if match; false otherwise + * + * @phpstan-impure */ protected function matchString(&$m, $delim) { @@ -1459,11 +1493,13 @@ class Parser /** * Try to match something on head of buffer * - * @param string $regex - * @param array $out - * @param boolean $eatWhitespace + * @param string $regex + * @param array $out + * @param bool $eatWhitespace + * + * @return bool * - * @return boolean + * @phpstan-impure */ protected function match($regex, &$out, $eatWhitespace = null) { @@ -1489,10 +1525,12 @@ class Parser /** * Match a single string * - * @param string $char - * @param boolean $eatWhitespace + * @param string $char + * @param bool $eatWhitespace + * + * @return bool * - * @return boolean + * @phpstan-impure */ protected function matchChar($char, $eatWhitespace = null) { @@ -1516,11 +1554,13 @@ class Parser /** * Match literal string * - * @param string $what - * @param integer $len - * @param boolean $eatWhitespace + * @param string $what + * @param int $len + * @param bool $eatWhitespace * - * @return boolean + * @return bool + * + * @phpstan-impure */ protected function literal($what, $len, $eatWhitespace = null) { @@ -1544,7 +1584,9 @@ class Parser /** * Match some whitespace * - * @return boolean + * @return bool + * + * @phpstan-impure */ protected function whitespace() { @@ -1596,19 +1638,19 @@ class Parser if (! $comment) { // single part static comment - $this->appendComment([Type::T_COMMENT, $c]); + $commentStatement = [Type::T_COMMENT, $c]; } else { $comment[] = $c; $staticComment = substr($this->buffer, $startCommentCount, $endCommentCount - $startCommentCount); $commentStatement = [Type::T_COMMENT, $staticComment, [Type::T_STRING, '', $comment]]; + } - list($line, $column) = $this->getSourcePosition($startCommentCount); - $commentStatement[self::SOURCE_LINE] = $line; - $commentStatement[self::SOURCE_COLUMN] = $column; - $commentStatement[self::SOURCE_INDEX] = $this->sourceIndex; + list($line, $column) = $this->getSourcePosition($startCommentCount); + $commentStatement[self::SOURCE_LINE] = $line; + $commentStatement[self::SOURCE_COLUMN] = $column; + $commentStatement[self::SOURCE_INDEX] = $this->sourceIndex; - $this->appendComment($commentStatement); - } + $this->appendComment($commentStatement); $this->commentsSeen[$startCommentCount] = true; $this->count = $endCommentCount; @@ -1631,9 +1673,13 @@ class Parser * Append comment to current block * * @param array $comment + * + * @return void */ protected function appendComment($comment) { + assert($this->env !== null); + if (! $this->discardComments) { $this->env->comments[] = $comment; } @@ -1643,10 +1689,14 @@ class Parser * Append statement to current block * * @param array|null $statement - * @param integer $pos + * @param int $pos + * + * @return void */ protected function append($statement, $pos = null) { + assert($this->env !== null); + if (! \is_null($statement)) { ! $this->cssOnly || ($statement = $this->assertPlainCssValid($statement, $pos)); @@ -1676,11 +1726,15 @@ class Parser */ protected function last() { + assert($this->env !== null); + $i = \count($this->env->children) - 1; if (isset($this->env->children[$i])) { return $this->env->children[$i]; } + + return null; } /** @@ -1688,7 +1742,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function mediaQueryList(&$out) { @@ -1700,7 +1754,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function mediaQuery(&$out) { @@ -1754,7 +1808,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function supportsQuery(&$out) { @@ -1887,7 +1941,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function mediaExpression(&$out) { @@ -1920,7 +1974,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function argValues(&$out) { @@ -1945,7 +1999,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function argValue(&$out) { @@ -2025,10 +2079,12 @@ class Parser /** * Parse directive value list that considers $vars as keyword * - * @param array $out - * @param boolean|string $endChar + * @param array $out + * @param string|false $endChar * - * @return boolean + * @return bool + * + * @phpstan-impure */ protected function directiveValue(&$out, $endChar = false) { @@ -2089,7 +2145,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function valueList(&$out) { @@ -2105,10 +2161,11 @@ class Parser * Parse a function call, where externals () are part of the call * and not of the value list * - * @param $out - * @param bool $mandatoryEnclos + * @param array $out + * @param bool $mandatoryEnclos * @param null|string $charAfter - * @param null|bool $eatWhiteSp + * @param null|bool $eatWhiteSp + * * @return bool */ protected function functionCallArgumentsList(&$out, $mandatoryEnclos = true, $charAfter = null, $eatWhiteSp = null) @@ -2145,7 +2202,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function spaceList(&$out) { @@ -2155,17 +2212,18 @@ class Parser /** * Parse generic list * - * @param array $out - * @param string $parseItem The name of the method used to parse items - * @param string $delim - * @param boolean $flatten + * @param array $out + * @param string $parseItem The name of the method used to parse items + * @param string $delim + * @param bool $flatten * - * @return boolean + * @return bool */ protected function genericList(&$out, $parseItem, $delim = '', $flatten = true) { $s = $this->count; $items = []; + /** @var array|Number|null $value */ $value = null; while ($this->$parseItem($value)) { @@ -2179,9 +2237,12 @@ class Parser $trailing_delim = true; } else { + assert(\is_array($value) || $value instanceof Number); // if no delim watch that a keyword didn't eat the single/double quote // from the following starting string if ($value[0] === Type::T_KEYWORD) { + assert(\is_array($value)); + /** @var string $word */ $word = $value[1]; $last_char = substr($word, -1); @@ -2206,8 +2267,10 @@ class Parser $this->count--; } + /** @var array|Number|null $nextValue */ $nextValue = null; if ($this->$parseItem($nextValue)) { + assert(\is_array($nextValue) || $nextValue instanceof Number); if ($nextValue[0] === Type::T_KEYWORD && $nextValue[1] === $last_char) { // bad try, forget it $this->seek($currentCount); @@ -2256,11 +2319,13 @@ class Parser /** * Parse expression * - * @param array $out - * @param boolean $listOnly - * @param boolean $lookForExp + * @param array $out + * @param bool $listOnly + * @param bool $lookForExp * - * @return boolean + * @return bool + * + * @phpstan-impure */ protected function expression(&$out, $listOnly = false, $lookForExp = true) { @@ -2321,12 +2386,14 @@ class Parser /** * Parse expression specifically checking for lists in parenthesis or brackets * - * @param array $out - * @param integer $s - * @param string $closingParen - * @param array $allowedTypes + * @param array $out + * @param int $s + * @param string $closingParen + * @param string[] $allowedTypes + * + * @return bool * - * @return boolean + * @phpstan-param array $allowedTypes */ protected function enclosedExpression(&$out, $s, $closingParen = ')', $allowedTypes = [Type::T_LIST, Type::T_MAP]) { @@ -2381,8 +2448,8 @@ class Parser /** * Parse left-hand side of subexpression * - * @param array $lhs - * @param integer $minP + * @param array $lhs + * @param int $minP * * @return array */ @@ -2394,7 +2461,7 @@ class Parser $whiteBefore = isset($this->buffer[$this->count - 1]) && ctype_space($this->buffer[$this->count - 1]); - while ($this->match($operators, $m, false) && static::$precedence[$m[1]] >= $minP) { + while ($this->match($operators, $m, false) && static::$precedence[strtolower($m[1])] >= $minP) { $whiteAfter = isset($this->buffer[$this->count]) && ctype_space($this->buffer[$this->count]); $varAfter = isset($this->buffer[$this->count]) && @@ -2418,7 +2485,7 @@ class Parser } // consume higher-precedence operators on the right-hand side - $rhs = $this->expHelper($rhs, static::$precedence[$op] + 1); + $rhs = $this->expHelper($rhs, static::$precedence[strtolower($op)] + 1); $lhs = [Type::T_EXPRESSION, $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter]; @@ -2437,7 +2504,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function value(&$out) { @@ -2645,7 +2712,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function parenValue(&$out) { @@ -2684,7 +2751,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function progid(&$out) { @@ -2717,7 +2784,7 @@ class Parser * @param string $name * @param array $func * - * @return boolean + * @return bool */ protected function func($name, &$func) { @@ -2737,6 +2804,10 @@ class Parser $this->argValues($args) && $this->matchChar(')') ) { + if (strtolower($name) === 'var' && \count($args) === 2 && $args[1][0] === Type::T_NULL) { + $args[1] = [null, [Type::T_STRING, '', [' ']], false]; + } + $func = [Type::T_FUNCTION_CALL, $name, $args]; return true; @@ -2771,7 +2842,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function argumentList(&$out) { @@ -2816,7 +2887,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function argumentDef(&$out) { @@ -2878,7 +2949,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function map(&$out) { @@ -2920,7 +2991,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function color(&$out) { @@ -2946,7 +3017,7 @@ class Parser * * @param array $unit * - * @return boolean + * @return bool */ protected function unit(&$unit) { @@ -2971,8 +3042,9 @@ class Parser * Parse string * * @param array $out + * @param bool $keepDelimWithInterpolation * - * @return boolean + * @return bool */ protected function string(&$out, $keepDelimWithInterpolation = false) { @@ -3053,7 +3125,8 @@ class Parser /** * @param string $out - * @param bool $inKeywords + * @param bool $inKeywords + * * @return bool */ protected function matchEscapeCharacter(&$out, $inKeywords = false) @@ -3103,10 +3176,10 @@ class Parser /** * Parse keyword or interpolation * - * @param array $out - * @param boolean $restricted + * @param array $out + * @param bool $restricted * - * @return boolean + * @return bool */ protected function mixedKeyword(&$out, $restricted = false) { @@ -3147,14 +3220,14 @@ class Parser /** * Parse an unbounded string stopped by $end * - * @param string $end - * @param array $out - * @param string $nestOpen - * @param string $nestClose - * @param boolean $rtrim + * @param string $end + * @param array $out + * @param string $nestOpen + * @param string $nestClose + * @param bool $rtrim * @param string $disallow * - * @return boolean + * @return bool */ protected function openString($end, &$out, $nestOpen = null, $nestClose = null, $rtrim = true, $disallow = null) { @@ -3230,9 +3303,9 @@ class Parser * Parser interpolation * * @param string|array $out - * @param boolean $lookWhite save information about whitespace before and after + * @param bool $lookWhite save information about whitespace before and after * - * @return boolean + * @return bool */ protected function interpolation(&$out, $lookWhite = true) { @@ -3287,7 +3360,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function propertyName(&$out) { @@ -3342,7 +3415,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function customProperty(&$out) { @@ -3400,9 +3473,9 @@ class Parser * Parse comma separated selector list * * @param array $out - * @param string|boolean $subSelector + * @param string|bool $subSelector * - * @return boolean + * @return bool */ protected function selectors(&$out, $subSelector = false) { @@ -3436,9 +3509,9 @@ class Parser * Parse whitespace separated selector list * * @param array $out - * @param string|boolean $subSelector + * @param string|bool $subSelector * - * @return boolean + * @return bool */ protected function selector(&$out, $subSelector = false) { @@ -3494,7 +3567,8 @@ class Parser * - but this require a better formal selector representation instead of the array we have now * * @param string $out - * @param bool $keepEscapedNumber + * @param bool $keepEscapedNumber + * * @return bool */ protected function matchEscapeCharacterInSelector(&$out, $keepEscapedNumber = false) @@ -3539,9 +3613,9 @@ class Parser * }} * * @param array $out - * @param string|boolean $subSelector + * @param string|bool $subSelector * - * @return boolean + * @return bool */ protected function selectorSingle(&$out, $subSelector = false) { @@ -3765,7 +3839,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function variable(&$out) { @@ -3792,11 +3866,11 @@ class Parser /** * Parse a keyword * - * @param string $word - * @param boolean $eatWhitespace - * @param boolean $inSelector + * @param string $word + * @param bool $eatWhitespace + * @param bool $inSelector * - * @return boolean + * @return bool */ protected function keyword(&$word, $eatWhitespace = null, $inSelector = false) { @@ -3859,11 +3933,11 @@ class Parser /** * Parse a keyword that should not start with a number * - * @param string $word - * @param boolean $eatWhitespace - * @param boolean $inSelector + * @param string $word + * @param bool $eatWhitespace + * @param bool $inSelector * - * @return boolean + * @return bool */ protected function restrictedKeyword(&$word, $eatWhitespace = null, $inSelector = false) { @@ -3883,7 +3957,7 @@ class Parser * * @param string|array $placeholder * - * @return boolean + * @return bool */ protected function placeholder(&$placeholder) { @@ -3912,7 +3986,7 @@ class Parser * * @param array $out * - * @return boolean + * @return bool */ protected function url(&$out) { @@ -3947,7 +4021,7 @@ class Parser * Consume an end of statement delimiter * @param bool $eatWhitespace * - * @return boolean + * @return bool */ protected function end($eatWhitespace = null) { @@ -3968,7 +4042,7 @@ class Parser * * @param array $value * - * @return array + * @return string[] */ protected function stripAssignmentFlags(&$value) { @@ -3995,7 +4069,7 @@ class Parser * * @param array $selectors * - * @return string + * @return bool */ protected function stripOptionalFlag(&$selectors) { @@ -4044,6 +4118,8 @@ class Parser * Extract line numbers from buffer * * @param string $buffer + * + * @return void */ private function extractLineNumbers($buffer) { @@ -4065,9 +4141,10 @@ class Parser /** * Get source line number and column (given character position in the buffer) * - * @param integer $pos + * @param int $pos * * @return array + * @phpstan-return array{int, int} */ private function getSourcePosition($pos) { diff --git a/lib/scssphp/src/SourceMap/Base64.php b/lib/scssphp/src/SourceMap/Base64.php index 4a5ed8b..00b6b45 100644 --- a/lib/scssphp/src/SourceMap/Base64.php +++ b/lib/scssphp/src/SourceMap/Base64.php @@ -164,7 +164,7 @@ class Base64 /** * Convert to base64 * - * @param integer $value + * @param int $value * * @return string */ @@ -178,7 +178,7 @@ class Base64 * * @param string $value * - * @return integer + * @return int */ public static function decode($value) { diff --git a/lib/scssphp/src/SourceMap/Base64VLQ.php b/lib/scssphp/src/SourceMap/Base64VLQ.php index d47b96a..2a5210c 100644 --- a/lib/scssphp/src/SourceMap/Base64VLQ.php +++ b/lib/scssphp/src/SourceMap/Base64VLQ.php @@ -51,7 +51,7 @@ class Base64VLQ /** * Returns the VLQ encoded value. * - * @param integer $value + * @param int $value * * @return string */ @@ -80,9 +80,9 @@ class Base64VLQ * Decodes VLQValue. * * @param string $str - * @param integer $index + * @param int $index * - * @return integer + * @return int */ public static function decode($str, &$index) { @@ -107,9 +107,9 @@ class Base64VLQ * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) * - * @param integer $value + * @param int $value * - * @return integer + * @return int */ private static function toVLQSigned($value) { @@ -126,9 +126,9 @@ class Base64VLQ * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 * - * @param integer $value + * @param int $value * - * @return integer + * @return int */ private static function fromVLQSigned($value) { diff --git a/lib/scssphp/src/SourceMap/SourceMapGenerator.php b/lib/scssphp/src/SourceMap/SourceMapGenerator.php index 4f14bdc..ccd4f02 100644 --- a/lib/scssphp/src/SourceMap/SourceMapGenerator.php +++ b/lib/scssphp/src/SourceMap/SourceMapGenerator.php @@ -107,18 +107,18 @@ class SourceMapGenerator */ public function __construct(array $options = []) { - $this->options = array_merge($this->defaultOptions, $options); + $this->options = array_replace($this->defaultOptions, $options); $this->encoder = new Base64VLQ(); } /** * 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 + * @param int $generatedLine The line number in generated file + * @param int $generatedColumn The column number in generated file + * @param int $originalLine The line number in original file + * @param int $originalColumn The column number in original file + * @param string $sourceFile The original source file * * @return void */ @@ -140,7 +140,7 @@ class SourceMapGenerator * * @param string $content The content to write * - * @return string + * @return string|null * * @throws \ScssPhp\ScssPhp\Exception\CompilerException If the file could not be saved * @deprecated @@ -148,6 +148,7 @@ class SourceMapGenerator public function saveMap($content) { $file = $this->options['sourceMapWriteTo']; + assert($file !== null); $dir = \dirname($file); // directory does not exist @@ -201,7 +202,7 @@ class SourceMapGenerator // A list of original sources used by the 'mappings' entry. $sourceMap['sources'] = []; - foreach ($this->sources as $sourceUri => $sourceFilename) { + foreach ($this->sources as $sourceFilename) { $sourceMap['sources'][] = $this->normalizeFilename($sourceFilename); } @@ -223,7 +224,15 @@ class SourceMapGenerator unset($sourceMap['sourceRoot']); } - return json_encode($sourceMap, JSON_UNESCAPED_SLASHES); + $jsonSourceMap = json_encode($sourceMap, JSON_UNESCAPED_SLASHES); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw new \RuntimeException(json_last_error_msg()); + } + + assert($jsonSourceMap !== false); + + return $jsonSourceMap; } /** @@ -326,7 +335,7 @@ class SourceMapGenerator * * @param string $filename * - * @return integer|false + * @return int|false */ protected function findFileIndex($filename) { @@ -362,8 +371,8 @@ class SourceMapGenerator /** * Fix windows paths * - * @param string $path - * @param boolean $addEndSlash + * @param string $path + * @param bool $addEndSlash * * @return string */ diff --git a/lib/scssphp/src/Type.php b/lib/scssphp/src/Type.php index fb2a1d7..d430887 100644 --- a/lib/scssphp/src/Type.php +++ b/lib/scssphp/src/Type.php @@ -19,58 +19,190 @@ namespace ScssPhp\ScssPhp; */ class Type { + /** + * @internal + */ const T_ASSIGN = 'assign'; + /** + * @internal + */ const T_AT_ROOT = 'at-root'; + /** + * @internal + */ const T_BLOCK = 'block'; - /** @deprecated */ + /** + * @deprecated + * @internal + */ const T_BREAK = 'break'; + /** + * @internal + */ const T_CHARSET = 'charset'; const T_COLOR = 'color'; + /** + * @internal + */ const T_COMMENT = 'comment'; - /** @deprecated */ + /** + * @deprecated + * @internal + */ const T_CONTINUE = 'continue'; - /** @deprecated */ + /** + * @deprecated + * @internal + */ const T_CONTROL = 'control'; + /** + * @internal + */ const T_CUSTOM_PROPERTY = 'custom'; + /** + * @internal + */ const T_DEBUG = 'debug'; + /** + * @internal + */ const T_DIRECTIVE = 'directive'; + /** + * @internal + */ const T_EACH = 'each'; + /** + * @internal + */ const T_ELSE = 'else'; + /** + * @internal + */ const T_ELSEIF = 'elseif'; + /** + * @internal + */ const T_ERROR = 'error'; + /** + * @internal + */ const T_EXPRESSION = 'exp'; + /** + * @internal + */ const T_EXTEND = 'extend'; + /** + * @internal + */ const T_FOR = 'for'; const T_FUNCTION = 'function'; + /** + * @internal + */ const T_FUNCTION_REFERENCE = 'function-reference'; + /** + * @internal + */ const T_FUNCTION_CALL = 'fncall'; + /** + * @internal + */ const T_HSL = 'hsl'; + /** + * @internal + */ const T_HWB = 'hwb'; + /** + * @internal + */ const T_IF = 'if'; + /** + * @internal + */ const T_IMPORT = 'import'; + /** + * @internal + */ const T_INCLUDE = 'include'; + /** + * @internal + */ const T_INTERPOLATE = 'interpolate'; + /** + * @internal + */ const T_INTERPOLATED = 'interpolated'; + /** + * @internal + */ const T_KEYWORD = 'keyword'; const T_LIST = 'list'; const T_MAP = 'map'; + /** + * @internal + */ const T_MEDIA = 'media'; + /** + * @internal + */ const T_MEDIA_EXPRESSION = 'mediaExp'; + /** + * @internal + */ const T_MEDIA_TYPE = 'mediaType'; + /** + * @internal + */ const T_MEDIA_VALUE = 'mediaValue'; + /** + * @internal + */ const T_MIXIN = 'mixin'; + /** + * @internal + */ const T_MIXIN_CONTENT = 'mixin_content'; + /** + * @internal + */ const T_NESTED_PROPERTY = 'nestedprop'; + /** + * @internal + */ const T_NOT = 'not'; const T_NULL = 'null'; const T_NUMBER = 'number'; + /** + * @internal + */ const T_RETURN = 'return'; + /** + * @internal + */ const T_ROOT = 'root'; + /** + * @internal + */ const T_SCSSPHP_IMPORT_ONCE = 'scssphp-import-once'; + /** + * @internal + */ const T_SELF = 'self'; const T_STRING = 'string'; + /** + * @internal + */ const T_UNARY = 'unary'; + /** + * @internal + */ const T_VARIABLE = 'var'; + /** + * @internal + */ const T_WARN = 'warn'; + /** + * @internal + */ const T_WHILE = 'while'; } diff --git a/lib/scssphp/src/Util.php b/lib/scssphp/src/Util.php index 62cd2a2..ad608ce 100644 --- a/lib/scssphp/src/Util.php +++ b/lib/scssphp/src/Util.php @@ -79,7 +79,7 @@ class Util /** * mb_chr() wrapper * - * @param integer $code + * @param int $code * * @return string */ diff --git a/lib/scssphp/src/Version.php b/lib/scssphp/src/Version.php index be6a310..d604a50 100644 --- a/lib/scssphp/src/Version.php +++ b/lib/scssphp/src/Version.php @@ -19,5 +19,5 @@ namespace ScssPhp\ScssPhp; */ class Version { - const VERSION = '1.9.0'; + const VERSION = '1.11.0'; }