diff --git a/composer.json b/composer.json index 7da855f85..e576d268a 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "type": "project", "license": "AGPL-3.0-only", "require": { - "php": ">=8.1.0 <8.2.0", + "php": ">=8.1.0 <8.4.0", "ext-ctype": "*", "ext-dom": "*", "ext-gd": "*", @@ -23,7 +23,7 @@ "pear/archive_tar": "~1.4.14", "pelago/emogrifier": "^7.2.0", "psr/log": "^3.0.0", - "scssphp/scssphp": "^1.10.3", + "scssphp/scssphp": "^1.12.1", "symfony/console": "~6.4.0", "symfony/dotenv": "~6.4.0", "symfony/framework-bundle": "~6.4.0", diff --git a/composer.lock b/composer.lock index ba67be268..cfbf5130a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ad8759a06560693a5072a1cf4c7738a2", + "content-hash": "3959316b7b3e78409779d7bd4d2f225f", "packages": [ { "name": "apereo/phpcas", @@ -1974,16 +1974,16 @@ }, { "name": "scssphp/scssphp", - "version": "v1.10.5", + "version": "v1.12.1", "source": { "type": "git", "url": "https://github.com/scssphp/scssphp.git", - "reference": "6d44282ccf283e133ab70b6282f8e068ff2f9bf9" + "reference": "394ed1e960138710a60d035c1a85d43d0bf0faeb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/scssphp/scssphp/zipball/6d44282ccf283e133ab70b6282f8e068ff2f9bf9", - "reference": "6d44282ccf283e133ab70b6282f8e068ff2f9bf9", + "url": "https://api.github.com/repos/scssphp/scssphp/zipball/394ed1e960138710a60d035c1a85d43d0bf0faeb", + "reference": "394ed1e960138710a60d035c1a85d43d0bf0faeb", "shasum": "" }, "require": { @@ -2000,7 +2000,7 @@ "thoughtbot/bourbon": "^7.0", "twbs/bootstrap": "~5.0", "twbs/bootstrap4": "4.6.1", - "zurb/foundation": "~6.5" + "zurb/foundation": "~6.7.0" }, "suggest": { "ext-iconv": "Can be used as fallback when ext-mbstring is not available", @@ -2048,9 +2048,9 @@ ], "support": { "issues": "https://github.com/scssphp/scssphp/issues", - "source": "https://github.com/scssphp/scssphp/tree/v1.10.5" + "source": "https://github.com/scssphp/scssphp/tree/v1.12.1" }, - "time": "2022-07-27T15:52:39+00:00" + "time": "2024-01-13T12:36:40+00:00" }, { "name": "symfony/cache", diff --git a/core/action.class.inc.php b/core/action.class.inc.php index 4777a0925..10660003a 100644 --- a/core/action.class.inc.php +++ b/core/action.class.inc.php @@ -369,7 +369,7 @@ class ActionEmail extends ActionNotification protected function FindRecipients($sRecipAttCode, $aArgs) { $sOQL = $this->Get($sRecipAttCode); - if (strlen($sOQL) === 0) return ''; + if (utils::IsNullOrEmptyString($sOQL)) return ''; try { @@ -413,7 +413,7 @@ class ActionEmail extends ActionNotification while ($oObj = $oSet->Fetch()) { $sAddress = trim($oObj->Get($sEmailAttCode)); - if (strlen($sAddress) > 0) + if (utils::IsNotNullOrEmptyString($sAddress)) { $aRecipients[] = $sAddress; $this->m_iRecipients++; diff --git a/lib/bin/pscss b/lib/bin/pscss index fa9a900db..e57bed8fc 100644 --- a/lib/bin/pscss +++ b/lib/bin/pscss @@ -4,15 +4,16 @@ /** * Proxy PHP file generated by Composer * - * This file includes the referenced bin path (../scssphp/scssphp/bin/pscss) using ob_start to remove the shebang if present - * to prevent the shebang from being output on PHP<8 + * This file includes the referenced bin path (../scssphp/scssphp/bin/pscss) + * using a stream wrapper to prevent the shebang from being output on PHP<8 * * @generated */ namespace Composer; -$binPath = __DIR__ . "/" . '../scssphp/scssphp/bin/pscss'; +$GLOBALS['_composer_bin_dir'] = __DIR__; +$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php'; if (PHP_VERSION_ID < 80000) { if (!class_exists('Composer\BinProxyWrapper')) { @@ -23,18 +24,17 @@ if (PHP_VERSION_ID < 80000) { { private $handle; private $position; + private $realpath; public function stream_open($path, $mode, $options, &$opened_path) { - // get rid of composer-bin-proxy:// prefix for __FILE__ & __DIR__ resolution - $opened_path = substr($path, 21); - $opened_path = realpath($opened_path) ?: $opened_path; - $this->handle = fopen($opened_path, $mode); + // get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution + $opened_path = substr($path, 17); + $this->realpath = realpath($opened_path) ?: $opened_path; + $opened_path = $this->realpath; + $this->handle = fopen($this->realpath, $mode); $this->position = 0; - // remove all traces of this stream wrapper once it has been used - stream_wrapper_unregister('composer-bin-proxy'); - return (bool) $this->handle; } @@ -66,6 +66,16 @@ if (PHP_VERSION_ID < 80000) { return $operation ? flock($this->handle, $operation) : true; } + public function stream_seek($offset, $whence) + { + if (0 === fseek($this->handle, $offset, $whence)) { + $this->position = ftell($this->handle); + return true; + } + + return false; + } + public function stream_tell() { return $this->position; @@ -78,20 +88,32 @@ if (PHP_VERSION_ID < 80000) { public function stream_stat() { - return fstat($this->handle); + return array(); } public function stream_set_option($option, $arg1, $arg2) { return true; } + + public function url_stat($path, $flags) + { + $path = substr($path, 17); + if (file_exists($path)) { + return stat($path); + } + + return false; + } } } - if (function_exists('stream_wrapper_register') && stream_wrapper_register('composer-bin-proxy', 'Composer\BinProxyWrapper')) { - include("composer-bin-proxy://" . $binPath); - exit(0); + if ( + (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true)) + || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) + ) { + return include("phpvfscomposer://" . __DIR__ . '/..'.'/scssphp/scssphp/bin/pscss'); } } -include $binPath; +return include __DIR__ . '/..'.'/scssphp/scssphp/bin/pscss'; diff --git a/lib/bin/pscss.bat b/lib/bin/pscss.bat index 1e368dc92..6b83bf27c 100644 --- a/lib/bin/pscss.bat +++ b/lib/bin/pscss.bat @@ -1,4 +1,5 @@ @ECHO OFF setlocal DISABLEDELAYEDEXPANSION -SET BIN_TARGET=%~dp0/../scssphp/scssphp/bin/pscss +SET BIN_TARGET=%~dp0/pscss +SET COMPOSER_RUNTIME_BIN_DIR=%~dp0 php "%BIN_TARGET%" %* diff --git a/lib/composer/installed.json b/lib/composer/installed.json index 1a0187ede..e13c89514 100644 --- a/lib/composer/installed.json +++ b/lib/composer/installed.json @@ -2058,17 +2058,17 @@ }, { "name": "scssphp/scssphp", - "version": "v1.10.5", - "version_normalized": "1.10.5.0", + "version": "v1.12.1", + "version_normalized": "1.12.1.0", "source": { "type": "git", "url": "https://github.com/scssphp/scssphp.git", - "reference": "6d44282ccf283e133ab70b6282f8e068ff2f9bf9" + "reference": "394ed1e960138710a60d035c1a85d43d0bf0faeb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/scssphp/scssphp/zipball/6d44282ccf283e133ab70b6282f8e068ff2f9bf9", - "reference": "6d44282ccf283e133ab70b6282f8e068ff2f9bf9", + "url": "https://api.github.com/repos/scssphp/scssphp/zipball/394ed1e960138710a60d035c1a85d43d0bf0faeb", + "reference": "394ed1e960138710a60d035c1a85d43d0bf0faeb", "shasum": "" }, "require": { @@ -2085,13 +2085,13 @@ "thoughtbot/bourbon": "^7.0", "twbs/bootstrap": "~5.0", "twbs/bootstrap4": "4.6.1", - "zurb/foundation": "~6.5" + "zurb/foundation": "~6.7.0" }, "suggest": { "ext-iconv": "Can be used as fallback when ext-mbstring is not available", "ext-mbstring": "For best performance, mbstring should be installed as it is faster than ext-iconv" }, - "time": "2022-07-27T15:52:39+00:00", + "time": "2024-01-13T12:36:40+00:00", "bin": [ "bin/pscss" ], @@ -2135,7 +2135,7 @@ ], "support": { "issues": "https://github.com/scssphp/scssphp/issues", - "source": "https://github.com/scssphp/scssphp/tree/v1.10.5" + "source": "https://github.com/scssphp/scssphp/tree/v1.12.1" }, "install-path": "../scssphp/scssphp" }, diff --git a/lib/composer/installed.php b/lib/composer/installed.php index 1c7724926..e0eaac9a4 100644 --- a/lib/composer/installed.php +++ b/lib/composer/installed.php @@ -3,7 +3,7 @@ 'name' => 'combodo/itop', 'pretty_version' => 'dev-develop', 'version' => 'dev-develop', - 'reference' => 'befb061f0ddb07527ed1d1fb0dde21e29428db75', + 'reference' => '25c2e34eed5a10c142c237a61a06b6845bb9031c', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -22,7 +22,7 @@ 'combodo/itop' => array( 'pretty_version' => 'dev-develop', 'version' => 'dev-develop', - 'reference' => 'befb061f0ddb07527ed1d1fb0dde21e29428db75', + 'reference' => '25c2e34eed5a10c142c237a61a06b6845bb9031c', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -351,9 +351,9 @@ 'dev_requirement' => false, ), 'scssphp/scssphp' => array( - 'pretty_version' => 'v1.10.5', - 'version' => '1.10.5.0', - 'reference' => '6d44282ccf283e133ab70b6282f8e068ff2f9bf9', + 'pretty_version' => 'v1.12.1', + 'version' => '1.12.1.0', + 'reference' => '394ed1e960138710a60d035c1a85d43d0bf0faeb', 'type' => 'library', 'install_path' => __DIR__ . '/../scssphp/scssphp', 'aliases' => array(), diff --git a/lib/scssphp/scssphp/composer.json b/lib/scssphp/scssphp/composer.json index 269a4732f..d17ffb924 100644 --- a/lib/scssphp/scssphp/composer.json +++ b/lib/scssphp/scssphp/composer.json @@ -43,23 +43,23 @@ "thoughtbot/bourbon": "^7.0", "twbs/bootstrap": "~5.0", "twbs/bootstrap4": "4.6.1", - "zurb/foundation": "~6.5" + "zurb/foundation": "~6.7.0" }, "repositories": [ { "type": "package", "package": { "name": "sass/sass-spec", - "version": "2022.02.24", + "version": "2022.08.19", "source": { "type": "git", "url": "https://github.com/sass/sass-spec.git", - "reference": "f41b9bfb9a3013392f2136c79f7f3356f15fb8ba" + "reference": "2bdc199723a3445d5badac3ac774105698f08861" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sass/sass-spec/zipball/f41b9bfb9a3013392f2136c79f7f3356f15fb8ba", - "reference": "f41b9bfb9a3013392f2136c79f7f3356f15fb8ba", + "url": "https://api.github.com/repos/sass/sass-spec/zipball/2bdc199723a3445d5badac3ac774105698f08861", + "reference": "2bdc199723a3445d5badac3ac774105698f08861", "shasum": "" } } diff --git a/lib/scssphp/scssphp/src/Block/CallableBlock.php b/lib/scssphp/scssphp/src/Block/CallableBlock.php index a18a87c2a..9b32d8ce7 100644 --- a/lib/scssphp/scssphp/src/Block/CallableBlock.php +++ b/lib/scssphp/scssphp/src/Block/CallableBlock.php @@ -14,6 +14,7 @@ namespace ScssPhp\ScssPhp\Block; use ScssPhp\ScssPhp\Block; use ScssPhp\ScssPhp\Compiler\Environment; +use ScssPhp\ScssPhp\Node\Number; /** * @internal @@ -26,7 +27,7 @@ class CallableBlock extends Block public $name; /** - * @var array|null + * @var list|null */ public $args; diff --git a/lib/scssphp/scssphp/src/Compiler.php b/lib/scssphp/scssphp/src/Compiler.php index e66cee3dc..d4e7c6896 100644 --- a/lib/scssphp/scssphp/src/Compiler.php +++ b/lib/scssphp/scssphp/src/Compiler.php @@ -458,10 +458,33 @@ class Compiler } /** - * Compile scss + * Compiles the provided scss file into CSS. + * + * @param string $path + * + * @return CompilationResult + * + * @throws SassException when the source fails to compile + */ + public function compileFile($path) + { + $source = file_get_contents($path); + + if ($source === false) { + throw new \RuntimeException('Could not read the file content'); + } + + return $this->compileString($source, $path); + } + + /** + * Compiles the provided scss source code into CSS. + * + * If provided, the path is considered to be the path from which the source code comes + * from, which will be used to resolve relative imports. * * @param string $source - * @param string|null $path + * @param string|null $path The path for the source, used to resolve relative imports * * @return CompilationResult * @@ -548,7 +571,7 @@ class Compiler $sourceMap = null; - if (! empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + if (! empty($out) && $this->sourceMap !== self::SOURCE_MAP_NONE && $this->sourceMap) { assert($sourceMapGenerator !== null); $sourceMap = $sourceMapGenerator->generateJson($prefix); $sourceMapUrl = null; @@ -1508,6 +1531,7 @@ class Compiler // start from the root while ($scope->parent && $scope->parent->type !== Type::T_ROOT) { array_unshift($childStash, $scope); + \assert($scope->parent !== null); $scope = $scope->parent; } @@ -2090,6 +2114,11 @@ class Compiler foreach ($selector as $node) { $compound = ''; + if (!is_array($node)) { + $output[] = $node; + continue; + } + array_walk_recursive( $node, function ($value, $key) use (&$compound) { @@ -2124,12 +2153,16 @@ class Compiler foreach ($selector as $node) { $compound = ''; - array_walk_recursive( - $node, - function ($value, $key) use (&$compound) { - $compound .= $value; - } - ); + if (!is_array($node)) { + $compound .= $node; + } else { + array_walk_recursive( + $node, + function ($value, $key) use (&$compound) { + $compound .= $value; + } + ); + } if ($this->isImmediateRelationshipCombinator($compound)) { if (\count($output)) { @@ -2885,7 +2918,7 @@ class Compiler { if (isset($child[Parser::SOURCE_LINE])) { $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->sourceLine = $child[Parser::SOURCE_LINE]; $this->sourceColumn = isset($child[Parser::SOURCE_COLUMN]) ? $child[Parser::SOURCE_COLUMN] : -1; } elseif (\is_array($child) && isset($child[1]->sourceLine) && $child[1] instanceof Block) { $this->sourceIndex = $child[1]->sourceIndex; @@ -4529,8 +4562,10 @@ EOL; return $colorName; } - if (is_numeric($alpha)) { + if (\is_int($alpha) || \is_float($alpha)) { $a = new Number($alpha, ''); + } elseif (is_numeric($alpha)) { + $a = new Number((float) $alpha, ''); } else { $a = $alpha; } @@ -5806,13 +5841,13 @@ EOL; if (! \is_null($file)) { if (\is_array($dir)) { - $callableDescription = (\is_object($dir[0]) ? \get_class($dir[0]) : $dir[0]).'::'.$dir[1]; + $callableDescription = (\is_object($dir[0]) ? \get_class($dir[0]) : $dir[0]) . '::' . $dir[1]; } elseif ($dir instanceof \Closure) { $r = new \ReflectionFunction($dir); if (false !== strpos($r->name, '{closure}')) { $callableDescription = sprintf('closure{%s:%s}', $r->getFileName(), $r->getStartLine()); } elseif ($class = $r->getClosureScopeClass()) { - $callableDescription = $class->name.'::'.$r->name; + $callableDescription = $class->name . '::' . $r->name; } else { $callableDescription = $r->name; } @@ -5925,15 +5960,15 @@ EOL; private function tryImportPathWithExtensions($path) { $result = array_merge( - $this->tryImportPath($path.'.sass'), - $this->tryImportPath($path.'.scss') + $this->tryImportPath($path . '.sass'), + $this->tryImportPath($path . '.scss') ); if ($result) { return $result; } - return $this->tryImportPath($path.'.css'); + return $this->tryImportPath($path . '.css'); } /** @@ -5943,7 +5978,7 @@ EOL; */ private function tryImportPath($path) { - $partial = dirname($path).'/_'.basename($path); + $partial = dirname($path) . '/_' . basename($path); $candidates = []; @@ -5969,7 +6004,7 @@ EOL; return null; } - return $this->checkImportPathConflicts($this->tryImportPathWithExtensions($path.'/index')); + return $this->checkImportPathConflicts($this->tryImportPathWithExtensions($path . '/index')); } /** @@ -5984,7 +6019,7 @@ EOL; } $normalizedPath = $path; - $normalizedRootDirectory = $this->rootDirectory.'/'; + $normalizedRootDirectory = $this->rootDirectory . '/'; if (\DIRECTORY_SEPARATOR === '\\') { $normalizedRootDirectory = str_replace('\\', '/', $normalizedRootDirectory); @@ -6371,8 +6406,6 @@ EOL; */ protected function sortNativeFunctionArgs($functionName, $prototypes, $args) { - static $parser = null; - if (! isset($prototypes)) { $keyArgs = []; $posArgs = []; @@ -6526,7 +6559,7 @@ EOL; * * @return array * - * @phpstan-param non-empty-list, rest_argument: string|null}> $prototypes + * @phpstan-param non-empty-array, rest_argument: string|null}> $prototypes * @phpstan-return array{arguments: list, rest_argument: string|null} */ private function selectFunctionPrototype(array $prototypes, $positional, array $names) @@ -6984,10 +7017,14 @@ EOL; return static::$null; } - if (is_numeric($value)) { + if (\is_int($value) || \is_float($value)) { return new Number($value, ''); } + if (is_numeric($value)) { + return new Number((float) $value, ''); + } + if ($value === '') { return static::$emptyString; } @@ -7675,9 +7712,9 @@ EOL; $b = min(1.0 - $w, $b); $rgb = $this->toRGB($hue, 100, 50); - for($i = 1; $i < 4; $i++) { - $rgb[$i] *= (1.0 - $w - $b); - $rgb[$i] = round($rgb[$i] + 255 * $w + 0.0001); + for ($i = 1; $i < 4; $i++) { + $rgb[$i] *= (1.0 - $w - $b); + $rgb[$i] = round($rgb[$i] + 255 * $w + 0.0001); } return $rgb; @@ -7704,7 +7741,6 @@ EOL; if ((int) $d === 0) { $h = 0; } else { - if ($red == $max) { $h = 60 * ($green - $blue) / $d; } elseif ($green == $max) { @@ -7714,7 +7750,7 @@ EOL; } } - return [Type::T_HWB, fmod($h, 360), $min / 255 * 100, 100 - $max / 255 *100]; + return [Type::T_HWB, fmod($h, 360), $min / 255 * 100, 100 - $max / 255 * 100]; } @@ -7923,7 +7959,13 @@ EOL; $scale = $operation === 'scale'; $change = $operation === 'change'; - /** @phpstan-var callable(string, float|int, bool=, bool=): (float|int|null) $getParam */ + /** + * @param string $name + * @param float|int $max + * @param bool $checkPercent + * @param bool $assertPercent + * @return float|int|null + */ $getParam = function ($name, $max, $checkPercent = false, $assertPercent = false) use (&$kwargs, $scale, $change) { if (!isset($kwargs[$name])) { return null; @@ -7947,7 +7989,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); @@ -8061,7 +8107,7 @@ EOL; protected static $libChangeColor = ['color', 'kwargs...']; protected function libChangeColor($args) { - return $this->alterColor($args,'change', function ($base, $alter, $max) { + return $this->alterColor($args, 'change', function ($base, $alter, $max) { if ($alter === null) { return $base; } @@ -8582,7 +8628,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; @@ -8601,7 +8647,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; @@ -9589,7 +9635,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."); diff --git a/lib/scssphp/scssphp/src/Node/Number.php b/lib/scssphp/scssphp/src/Node/Number.php index ca9b5b652..6c0445876 100644 --- a/lib/scssphp/scssphp/src/Node/Number.php +++ b/lib/scssphp/scssphp/src/Node/Number.php @@ -33,7 +33,7 @@ use ScssPhp\ScssPhp\Util; * * @template-implements \ArrayAccess */ -class Number extends Node implements \ArrayAccess +class Number extends Node implements \ArrayAccess, \JsonSerializable { const PRECISION = 10; @@ -131,7 +131,7 @@ class Number extends Node implements \ArrayAccess } /** - * @return string[] + * @return list */ public function getNumeratorUnits() { @@ -139,13 +139,23 @@ class Number extends Node implements \ArrayAccess } /** - * @return string[] + * @return list */ public function getDenominatorUnits() { return $this->denominatorUnits; } + /** + * @return mixed + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + // Passing a compiler instance makes the method output a Sass representation instead of a CSS one, supporting full units. + return $this->output(new Compiler()); + } + /** * @return bool */ @@ -227,6 +237,16 @@ class Number extends Node implements \ArrayAccess 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 +286,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); } } @@ -524,7 +564,7 @@ class Number extends Node implements \ArrayAccess try { return $this->coerceUnits($other, function ($num1, $num2) { - return round($num1,self::PRECISION) == round($num2, self::PRECISION); + return round($num1, self::PRECISION) == round($num2, self::PRECISION); }); } catch (SassScriptException $e) { return false; diff --git a/lib/scssphp/scssphp/src/OutputStyle.php b/lib/scssphp/scssphp/src/OutputStyle.php index c284639c1..a1d8b4255 100644 --- a/lib/scssphp/scssphp/src/OutputStyle.php +++ b/lib/scssphp/scssphp/src/OutputStyle.php @@ -1,9 +1,62 @@ saveEncoding(); $this->extractLineNumbers($buffer); + if ($this->utf8 && !preg_match('//u', $buffer)) { + $message = $this->sourceName ? 'Invalid UTF-8 file: ' . $this->sourceName : 'Invalid UTF-8 file'; + throw new ParserException($message); + } + $this->pushBlock(null); // root block $this->whitespace(); $this->pushBlock(null); @@ -323,6 +328,13 @@ class Parser $list = $this->valueList($out); + if ($this->count !== \strlen($this->buffer)) { + $error = $this->parseError('Expected end of value'); + $message = 'Passing trailing content after the expression when parsing a value is deprecated since Scssphp 1.12.0 and will be an error in 2.0. ' . $error->getMessage(); + + @trigger_error($message, E_USER_DEPRECATED); + } + $this->restoreEncoding(); return $list; @@ -383,10 +395,13 @@ class Parser $this->inParens = false; $this->eatWhiteDefault = true; $this->buffer = (string) $buffer; + $this->discardComments = true; $this->saveEncoding(); $this->extractLineNumbers($this->buffer); + $this->whitespace(); + $isMediaQuery = $this->mediaQueryList($out); $this->restoreEncoding(); @@ -1678,9 +1693,9 @@ class Parser */ protected function appendComment($comment) { - assert($this->env !== null); - if (! $this->discardComments) { + assert($this->env !== null); + $this->env->comments[] = $comment; } } @@ -2461,7 +2476,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]) && @@ -2485,7 +2500,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]; @@ -2804,6 +2819,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; diff --git a/lib/scssphp/scssphp/src/Version.php b/lib/scssphp/scssphp/src/Version.php index 5848a0ed0..89bd59526 100644 --- a/lib/scssphp/scssphp/src/Version.php +++ b/lib/scssphp/scssphp/src/Version.php @@ -19,5 +19,5 @@ namespace ScssPhp\ScssPhp; */ class Version { - const VERSION = '1.10.5'; + const VERSION = '1.12.1'; } diff --git a/setup/setuputils.class.inc.php b/setup/setuputils.class.inc.php index 64f134ee3..5548a477a 100644 --- a/setup/setuputils.class.inc.php +++ b/setup/setuputils.class.inc.php @@ -104,7 +104,7 @@ class SetupUtils const PHP_NEXT_MIN_VERSION = ''; // No new PHP requirement for next iTop version yet const MYSQL_NEXT_MIN_VERSION = ''; // No new MySQL requirement for next iTop version yet // -- First recent version that is not yet validated by Combodo (warning) - const PHP_NOT_VALIDATED_VERSION = '8.2.0'; + const PHP_NOT_VALIDATED_VERSION = '8.4.0'; const MIN_MEMORY_LIMIT = '32M'; const SUHOSIN_GET_MAX_VALUE_LENGTH = 2048;