Merge remote-tracking branch 'origin/support/3.2' into develop

This commit is contained in:
Pierre Goiffon
2024-01-30 11:47:04 +01:00
15 changed files with 292 additions and 92 deletions

View File

@@ -4,7 +4,7 @@
"type": "project", "type": "project",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"require": { "require": {
"php": ">=8.1.0 <8.2.0", "php": ">=8.1.0 <8.4.0",
"ext-ctype": "*", "ext-ctype": "*",
"ext-dom": "*", "ext-dom": "*",
"ext-gd": "*", "ext-gd": "*",
@@ -23,7 +23,7 @@
"pear/archive_tar": "~1.4.14", "pear/archive_tar": "~1.4.14",
"pelago/emogrifier": "^7.2.0", "pelago/emogrifier": "^7.2.0",
"psr/log": "^3.0.0", "psr/log": "^3.0.0",
"scssphp/scssphp": "^1.10.3", "scssphp/scssphp": "^1.12.1",
"symfony/console": "~6.4.0", "symfony/console": "~6.4.0",
"symfony/dotenv": "~6.4.0", "symfony/dotenv": "~6.4.0",
"symfony/framework-bundle": "~6.4.0", "symfony/framework-bundle": "~6.4.0",

16
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "ad8759a06560693a5072a1cf4c7738a2", "content-hash": "3959316b7b3e78409779d7bd4d2f225f",
"packages": [ "packages": [
{ {
"name": "apereo/phpcas", "name": "apereo/phpcas",
@@ -1974,16 +1974,16 @@
}, },
{ {
"name": "scssphp/scssphp", "name": "scssphp/scssphp",
"version": "v1.10.5", "version": "v1.12.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/scssphp/scssphp.git", "url": "https://github.com/scssphp/scssphp.git",
"reference": "6d44282ccf283e133ab70b6282f8e068ff2f9bf9" "reference": "394ed1e960138710a60d035c1a85d43d0bf0faeb"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/6d44282ccf283e133ab70b6282f8e068ff2f9bf9", "url": "https://api.github.com/repos/scssphp/scssphp/zipball/394ed1e960138710a60d035c1a85d43d0bf0faeb",
"reference": "6d44282ccf283e133ab70b6282f8e068ff2f9bf9", "reference": "394ed1e960138710a60d035c1a85d43d0bf0faeb",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2000,7 +2000,7 @@
"thoughtbot/bourbon": "^7.0", "thoughtbot/bourbon": "^7.0",
"twbs/bootstrap": "~5.0", "twbs/bootstrap": "~5.0",
"twbs/bootstrap4": "4.6.1", "twbs/bootstrap4": "4.6.1",
"zurb/foundation": "~6.5" "zurb/foundation": "~6.7.0"
}, },
"suggest": { "suggest": {
"ext-iconv": "Can be used as fallback when ext-mbstring is not available", "ext-iconv": "Can be used as fallback when ext-mbstring is not available",
@@ -2048,9 +2048,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/scssphp/scssphp/issues", "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", "name": "symfony/cache",

View File

@@ -369,7 +369,7 @@ class ActionEmail extends ActionNotification
protected function FindRecipients($sRecipAttCode, $aArgs) protected function FindRecipients($sRecipAttCode, $aArgs)
{ {
$sOQL = $this->Get($sRecipAttCode); $sOQL = $this->Get($sRecipAttCode);
if (strlen($sOQL) === 0) return ''; if (utils::IsNullOrEmptyString($sOQL)) return '';
try try
{ {
@@ -413,7 +413,7 @@ class ActionEmail extends ActionNotification
while ($oObj = $oSet->Fetch()) while ($oObj = $oSet->Fetch())
{ {
$sAddress = trim($oObj->Get($sEmailAttCode)); $sAddress = trim($oObj->Get($sEmailAttCode));
if (strlen($sAddress) > 0) if (utils::IsNotNullOrEmptyString($sAddress))
{ {
$aRecipients[] = $sAddress; $aRecipients[] = $sAddress;
$this->m_iRecipients++; $this->m_iRecipients++;

View File

@@ -4,15 +4,16 @@
/** /**
* Proxy PHP file generated by Composer * 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 * This file includes the referenced bin path (../scssphp/scssphp/bin/pscss)
* to prevent the shebang from being output on PHP<8 * using a stream wrapper to prevent the shebang from being output on PHP<8
* *
* @generated * @generated
*/ */
namespace Composer; 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 (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) { if (!class_exists('Composer\BinProxyWrapper')) {
@@ -23,18 +24,17 @@ if (PHP_VERSION_ID < 80000) {
{ {
private $handle; private $handle;
private $position; private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path) public function stream_open($path, $mode, $options, &$opened_path)
{ {
// get rid of composer-bin-proxy:// prefix for __FILE__ & __DIR__ resolution // get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 21); $opened_path = substr($path, 17);
$opened_path = realpath($opened_path) ?: $opened_path; $this->realpath = realpath($opened_path) ?: $opened_path;
$this->handle = fopen($opened_path, $mode); $opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0; $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; return (bool) $this->handle;
} }
@@ -66,6 +66,16 @@ if (PHP_VERSION_ID < 80000) {
return $operation ? flock($this->handle, $operation) : true; 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() public function stream_tell()
{ {
return $this->position; return $this->position;
@@ -78,20 +88,32 @@ if (PHP_VERSION_ID < 80000) {
public function stream_stat() public function stream_stat()
{ {
return fstat($this->handle); return array();
} }
public function stream_set_option($option, $arg1, $arg2) public function stream_set_option($option, $arg1, $arg2)
{ {
return true; 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')) { if (
include("composer-bin-proxy://" . $binPath); (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
exit(0); || (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';

View File

@@ -1,4 +1,5 @@
@ECHO OFF @ECHO OFF
setlocal DISABLEDELAYEDEXPANSION setlocal DISABLEDELAYEDEXPANSION
SET BIN_TARGET=%~dp0/../scssphp/scssphp/bin/pscss SET BIN_TARGET=%~dp0/pscss
SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
php "%BIN_TARGET%" %* php "%BIN_TARGET%" %*

View File

@@ -2058,17 +2058,17 @@
}, },
{ {
"name": "scssphp/scssphp", "name": "scssphp/scssphp",
"version": "v1.10.5", "version": "v1.12.1",
"version_normalized": "1.10.5.0", "version_normalized": "1.12.1.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/scssphp/scssphp.git", "url": "https://github.com/scssphp/scssphp.git",
"reference": "6d44282ccf283e133ab70b6282f8e068ff2f9bf9" "reference": "394ed1e960138710a60d035c1a85d43d0bf0faeb"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/6d44282ccf283e133ab70b6282f8e068ff2f9bf9", "url": "https://api.github.com/repos/scssphp/scssphp/zipball/394ed1e960138710a60d035c1a85d43d0bf0faeb",
"reference": "6d44282ccf283e133ab70b6282f8e068ff2f9bf9", "reference": "394ed1e960138710a60d035c1a85d43d0bf0faeb",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2085,13 +2085,13 @@
"thoughtbot/bourbon": "^7.0", "thoughtbot/bourbon": "^7.0",
"twbs/bootstrap": "~5.0", "twbs/bootstrap": "~5.0",
"twbs/bootstrap4": "4.6.1", "twbs/bootstrap4": "4.6.1",
"zurb/foundation": "~6.5" "zurb/foundation": "~6.7.0"
}, },
"suggest": { "suggest": {
"ext-iconv": "Can be used as fallback when ext-mbstring is not available", "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" "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": [
"bin/pscss" "bin/pscss"
], ],
@@ -2135,7 +2135,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/scssphp/scssphp/issues", "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" "install-path": "../scssphp/scssphp"
}, },

View File

@@ -3,7 +3,7 @@
'name' => 'combodo/itop', 'name' => 'combodo/itop',
'pretty_version' => 'dev-develop', 'pretty_version' => 'dev-develop',
'version' => 'dev-develop', 'version' => 'dev-develop',
'reference' => 'befb061f0ddb07527ed1d1fb0dde21e29428db75', 'reference' => '25c2e34eed5a10c142c237a61a06b6845bb9031c',
'type' => 'project', 'type' => 'project',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
@@ -22,7 +22,7 @@
'combodo/itop' => array( 'combodo/itop' => array(
'pretty_version' => 'dev-develop', 'pretty_version' => 'dev-develop',
'version' => 'dev-develop', 'version' => 'dev-develop',
'reference' => 'befb061f0ddb07527ed1d1fb0dde21e29428db75', 'reference' => '25c2e34eed5a10c142c237a61a06b6845bb9031c',
'type' => 'project', 'type' => 'project',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
@@ -351,9 +351,9 @@
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'scssphp/scssphp' => array( 'scssphp/scssphp' => array(
'pretty_version' => 'v1.10.5', 'pretty_version' => 'v1.12.1',
'version' => '1.10.5.0', 'version' => '1.12.1.0',
'reference' => '6d44282ccf283e133ab70b6282f8e068ff2f9bf9', 'reference' => '394ed1e960138710a60d035c1a85d43d0bf0faeb',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../scssphp/scssphp', 'install_path' => __DIR__ . '/../scssphp/scssphp',
'aliases' => array(), 'aliases' => array(),

View File

@@ -43,23 +43,23 @@
"thoughtbot/bourbon": "^7.0", "thoughtbot/bourbon": "^7.0",
"twbs/bootstrap": "~5.0", "twbs/bootstrap": "~5.0",
"twbs/bootstrap4": "4.6.1", "twbs/bootstrap4": "4.6.1",
"zurb/foundation": "~6.5" "zurb/foundation": "~6.7.0"
}, },
"repositories": [ "repositories": [
{ {
"type": "package", "type": "package",
"package": { "package": {
"name": "sass/sass-spec", "name": "sass/sass-spec",
"version": "2022.02.24", "version": "2022.08.19",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sass/sass-spec.git", "url": "https://github.com/sass/sass-spec.git",
"reference": "f41b9bfb9a3013392f2136c79f7f3356f15fb8ba" "reference": "2bdc199723a3445d5badac3ac774105698f08861"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sass/sass-spec/zipball/f41b9bfb9a3013392f2136c79f7f3356f15fb8ba", "url": "https://api.github.com/repos/sass/sass-spec/zipball/2bdc199723a3445d5badac3ac774105698f08861",
"reference": "f41b9bfb9a3013392f2136c79f7f3356f15fb8ba", "reference": "2bdc199723a3445d5badac3ac774105698f08861",
"shasum": "" "shasum": ""
} }
} }

View File

@@ -14,6 +14,7 @@ namespace ScssPhp\ScssPhp\Block;
use ScssPhp\ScssPhp\Block; use ScssPhp\ScssPhp\Block;
use ScssPhp\ScssPhp\Compiler\Environment; use ScssPhp\ScssPhp\Compiler\Environment;
use ScssPhp\ScssPhp\Node\Number;
/** /**
* @internal * @internal
@@ -26,7 +27,7 @@ class CallableBlock extends Block
public $name; public $name;
/** /**
* @var array|null * @var list<array{string, array|Number|null, bool}>|null
*/ */
public $args; public $args;

View File

@@ -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 $source
* @param string|null $path * @param string|null $path The path for the source, used to resolve relative imports
* *
* @return CompilationResult * @return CompilationResult
* *
@@ -548,7 +571,7 @@ class Compiler
$sourceMap = null; $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); assert($sourceMapGenerator !== null);
$sourceMap = $sourceMapGenerator->generateJson($prefix); $sourceMap = $sourceMapGenerator->generateJson($prefix);
$sourceMapUrl = null; $sourceMapUrl = null;
@@ -1508,6 +1531,7 @@ class Compiler
// start from the root // start from the root
while ($scope->parent && $scope->parent->type !== Type::T_ROOT) { while ($scope->parent && $scope->parent->type !== Type::T_ROOT) {
array_unshift($childStash, $scope); array_unshift($childStash, $scope);
\assert($scope->parent !== null);
$scope = $scope->parent; $scope = $scope->parent;
} }
@@ -2090,6 +2114,11 @@ class Compiler
foreach ($selector as $node) { foreach ($selector as $node) {
$compound = ''; $compound = '';
if (!is_array($node)) {
$output[] = $node;
continue;
}
array_walk_recursive( array_walk_recursive(
$node, $node,
function ($value, $key) use (&$compound) { function ($value, $key) use (&$compound) {
@@ -2124,12 +2153,16 @@ class Compiler
foreach ($selector as $node) { foreach ($selector as $node) {
$compound = ''; $compound = '';
array_walk_recursive( if (!is_array($node)) {
$node, $compound .= $node;
function ($value, $key) use (&$compound) { } else {
$compound .= $value; array_walk_recursive(
} $node,
); function ($value, $key) use (&$compound) {
$compound .= $value;
}
);
}
if ($this->isImmediateRelationshipCombinator($compound)) { if ($this->isImmediateRelationshipCombinator($compound)) {
if (\count($output)) { if (\count($output)) {
@@ -2885,7 +2918,7 @@ class Compiler
{ {
if (isset($child[Parser::SOURCE_LINE])) { if (isset($child[Parser::SOURCE_LINE])) {
$this->sourceIndex = isset($child[Parser::SOURCE_INDEX]) ? $child[Parser::SOURCE_INDEX] : null; $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; $this->sourceColumn = isset($child[Parser::SOURCE_COLUMN]) ? $child[Parser::SOURCE_COLUMN] : -1;
} elseif (\is_array($child) && isset($child[1]->sourceLine) && $child[1] instanceof Block) { } elseif (\is_array($child) && isset($child[1]->sourceLine) && $child[1] instanceof Block) {
$this->sourceIndex = $child[1]->sourceIndex; $this->sourceIndex = $child[1]->sourceIndex;
@@ -4529,8 +4562,10 @@ EOL;
return $colorName; return $colorName;
} }
if (is_numeric($alpha)) { if (\is_int($alpha) || \is_float($alpha)) {
$a = new Number($alpha, ''); $a = new Number($alpha, '');
} elseif (is_numeric($alpha)) {
$a = new Number((float) $alpha, '');
} else { } else {
$a = $alpha; $a = $alpha;
} }
@@ -5806,13 +5841,13 @@ EOL;
if (! \is_null($file)) { if (! \is_null($file)) {
if (\is_array($dir)) { 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) { } elseif ($dir instanceof \Closure) {
$r = new \ReflectionFunction($dir); $r = new \ReflectionFunction($dir);
if (false !== strpos($r->name, '{closure}')) { if (false !== strpos($r->name, '{closure}')) {
$callableDescription = sprintf('closure{%s:%s}', $r->getFileName(), $r->getStartLine()); $callableDescription = sprintf('closure{%s:%s}', $r->getFileName(), $r->getStartLine());
} elseif ($class = $r->getClosureScopeClass()) { } elseif ($class = $r->getClosureScopeClass()) {
$callableDescription = $class->name.'::'.$r->name; $callableDescription = $class->name . '::' . $r->name;
} else { } else {
$callableDescription = $r->name; $callableDescription = $r->name;
} }
@@ -5925,15 +5960,15 @@ EOL;
private function tryImportPathWithExtensions($path) private function tryImportPathWithExtensions($path)
{ {
$result = array_merge( $result = array_merge(
$this->tryImportPath($path.'.sass'), $this->tryImportPath($path . '.sass'),
$this->tryImportPath($path.'.scss') $this->tryImportPath($path . '.scss')
); );
if ($result) { if ($result) {
return $result; return $result;
} }
return $this->tryImportPath($path.'.css'); return $this->tryImportPath($path . '.css');
} }
/** /**
@@ -5943,7 +5978,7 @@ EOL;
*/ */
private function tryImportPath($path) private function tryImportPath($path)
{ {
$partial = dirname($path).'/_'.basename($path); $partial = dirname($path) . '/_' . basename($path);
$candidates = []; $candidates = [];
@@ -5969,7 +6004,7 @@ EOL;
return null; return null;
} }
return $this->checkImportPathConflicts($this->tryImportPathWithExtensions($path.'/index')); return $this->checkImportPathConflicts($this->tryImportPathWithExtensions($path . '/index'));
} }
/** /**
@@ -5984,7 +6019,7 @@ EOL;
} }
$normalizedPath = $path; $normalizedPath = $path;
$normalizedRootDirectory = $this->rootDirectory.'/'; $normalizedRootDirectory = $this->rootDirectory . '/';
if (\DIRECTORY_SEPARATOR === '\\') { if (\DIRECTORY_SEPARATOR === '\\') {
$normalizedRootDirectory = str_replace('\\', '/', $normalizedRootDirectory); $normalizedRootDirectory = str_replace('\\', '/', $normalizedRootDirectory);
@@ -6371,8 +6406,6 @@ EOL;
*/ */
protected function sortNativeFunctionArgs($functionName, $prototypes, $args) protected function sortNativeFunctionArgs($functionName, $prototypes, $args)
{ {
static $parser = null;
if (! isset($prototypes)) { if (! isset($prototypes)) {
$keyArgs = []; $keyArgs = [];
$posArgs = []; $posArgs = [];
@@ -6526,7 +6559,7 @@ EOL;
* *
* @return array * @return array
* *
* @phpstan-param non-empty-list<array{arguments: list<array{0: string, 1: string, 2: array|Number|null}>, rest_argument: string|null}> $prototypes * @phpstan-param non-empty-array<array{arguments: list<array{0: string, 1: string, 2: array|Number|null}>, rest_argument: string|null}> $prototypes
* @phpstan-return array{arguments: list<array{0: string, 1: string, 2: array|Number|null}>, rest_argument: string|null} * @phpstan-return array{arguments: list<array{0: string, 1: string, 2: array|Number|null}>, rest_argument: string|null}
*/ */
private function selectFunctionPrototype(array $prototypes, $positional, array $names) private function selectFunctionPrototype(array $prototypes, $positional, array $names)
@@ -6984,10 +7017,14 @@ EOL;
return static::$null; return static::$null;
} }
if (is_numeric($value)) { if (\is_int($value) || \is_float($value)) {
return new Number($value, ''); return new Number($value, '');
} }
if (is_numeric($value)) {
return new Number((float) $value, '');
}
if ($value === '') { if ($value === '') {
return static::$emptyString; return static::$emptyString;
} }
@@ -7675,9 +7712,9 @@ EOL;
$b = min(1.0 - $w, $b); $b = min(1.0 - $w, $b);
$rgb = $this->toRGB($hue, 100, 50); $rgb = $this->toRGB($hue, 100, 50);
for($i = 1; $i < 4; $i++) { for ($i = 1; $i < 4; $i++) {
$rgb[$i] *= (1.0 - $w - $b); $rgb[$i] *= (1.0 - $w - $b);
$rgb[$i] = round($rgb[$i] + 255 * $w + 0.0001); $rgb[$i] = round($rgb[$i] + 255 * $w + 0.0001);
} }
return $rgb; return $rgb;
@@ -7704,7 +7741,6 @@ EOL;
if ((int) $d === 0) { if ((int) $d === 0) {
$h = 0; $h = 0;
} else { } else {
if ($red == $max) { if ($red == $max) {
$h = 60 * ($green - $blue) / $d; $h = 60 * ($green - $blue) / $d;
} elseif ($green == $max) { } 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'; $scale = $operation === 'scale';
$change = $operation === 'change'; $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) { $getParam = function ($name, $max, $checkPercent = false, $assertPercent = false) use (&$kwargs, $scale, $change) {
if (!isset($kwargs[$name])) { if (!isset($kwargs[$name])) {
return null; return null;
@@ -7947,7 +7989,11 @@ EOL;
$max = 100; $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); $alpha = $getParam('alpha', 1);
@@ -8061,7 +8107,7 @@ EOL;
protected static $libChangeColor = ['color', 'kwargs...']; protected static $libChangeColor = ['color', 'kwargs...'];
protected function libChangeColor($args) 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) { if ($alter === null) {
return $base; return $base;
} }
@@ -8582,7 +8628,7 @@ EOL;
$color = $this->assertColor($args[0], 'color'); $color = $this->assertColor($args[0], 'color');
$amount = $this->assertNumber($args[1], 'amount'); $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])); $color[4] = min(1, max(0, $color[4]));
return $color; return $color;
@@ -8601,7 +8647,7 @@ EOL;
$color = $this->assertColor($args[0], 'color'); $color = $this->assertColor($args[0], 'color');
$amount = $this->assertNumber($args[1], 'amount'); $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])); $color[4] = min(1, max(0, $color[4]));
return $color; 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) protected function libRandom($args)
{ {
if (isset($args[0]) && $args[0] !== static::$null) { 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 = <<<TXT
random() will no longer ignore \$limit units ($limit) in a future release.
Recommendation: random(\$limit / 1$unitString) * 1$unitString
To preserve current behavior: random(\$limit / 1$unitString)
More info: https://sass-lang.com/d/random-with-units
TXT;
Warn::deprecation($this->addLocationToMessage($message));
}
$n = $this->assertInteger($limit, 'limit');
if ($n < 1) { if ($n < 1) {
throw new SassScriptException("\$limit: Must be greater than 0, was $n."); throw new SassScriptException("\$limit: Must be greater than 0, was $n.");

View File

@@ -33,7 +33,7 @@ use ScssPhp\ScssPhp\Util;
* *
* @template-implements \ArrayAccess<int, mixed> * @template-implements \ArrayAccess<int, mixed>
*/ */
class Number extends Node implements \ArrayAccess class Number extends Node implements \ArrayAccess, \JsonSerializable
{ {
const PRECISION = 10; const PRECISION = 10;
@@ -131,7 +131,7 @@ class Number extends Node implements \ArrayAccess
} }
/** /**
* @return string[] * @return list<string>
*/ */
public function getNumeratorUnits() public function getNumeratorUnits()
{ {
@@ -139,13 +139,23 @@ class Number extends Node implements \ArrayAccess
} }
/** /**
* @return string[] * @return list<string>
*/ */
public function getDenominatorUnits() public function getDenominatorUnits()
{ {
return $this->denominatorUnits; 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 * @return bool
*/ */
@@ -227,6 +237,16 @@ class Number extends Node implements \ArrayAccess
return \count($this->numeratorUnits) === 0 && \count($this->denominatorUnits) === 0; 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 * Checks whether the number has exactly this unit
* *
@@ -266,7 +286,27 @@ class Number extends Node implements \ArrayAccess
try { try {
return Util::checkRange('', new Range($min, $max), $this); return Util::checkRange('', new Range($min, $max), $this);
} catch (RangeException $e) { } 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 { try {
return $this->coerceUnits($other, function ($num1, $num2) { 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) { } catch (SassScriptException $e) {
return false; return false;

View File

@@ -1,9 +1,62 @@
<?php <?php
/**
* SCSSPHP
*
* @copyright 2012-2020 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://scssphp.github.io/scssphp
*/
namespace ScssPhp\ScssPhp; namespace ScssPhp\ScssPhp;
final class OutputStyle final class OutputStyle
{ {
const EXPANDED = 'expanded'; const EXPANDED = 'expanded';
const COMPRESSED = 'compressed'; const COMPRESSED = 'compressed';
/**
* Converts a string to an output style.
*
* Using this method allows to write code which will support both
* versions 1.12+ and 2.0 of Scssphp. In 2.0, OutputStyle will be
* an enum instead of using string constants.
*
* @param string $string
*
* @return self::*
*/
public static function fromString($string)
{
switch ($string) {
case 'expanded':
return self::EXPANDED;
case 'compressed':
return self::COMPRESSED;
default:
throw new \InvalidArgumentException('Invalid output style');
}
}
/**
* Converts an output style to a string supported by {@see OutputStyle::fromString()}.
*
* Using this method allows to write code which will support both
* versions 1.12+ and 2.0 of Scssphp. In 2.0, OutputStyle will be
* an enum instead of using string constants.
* The returned string representation is guaranteed to be compatible
* between 1.12 and 2.0.
*
* @param self::* $outputStyle
*
* @return string
*/
public static function toString($outputStyle)
{
return $outputStyle;
}
} }

View File

@@ -273,6 +273,11 @@ class Parser
$this->saveEncoding(); $this->saveEncoding();
$this->extractLineNumbers($buffer); $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->pushBlock(null); // root block
$this->whitespace(); $this->whitespace();
$this->pushBlock(null); $this->pushBlock(null);
@@ -323,6 +328,13 @@ class Parser
$list = $this->valueList($out); $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(); $this->restoreEncoding();
return $list; return $list;
@@ -383,10 +395,13 @@ class Parser
$this->inParens = false; $this->inParens = false;
$this->eatWhiteDefault = true; $this->eatWhiteDefault = true;
$this->buffer = (string) $buffer; $this->buffer = (string) $buffer;
$this->discardComments = true;
$this->saveEncoding(); $this->saveEncoding();
$this->extractLineNumbers($this->buffer); $this->extractLineNumbers($this->buffer);
$this->whitespace();
$isMediaQuery = $this->mediaQueryList($out); $isMediaQuery = $this->mediaQueryList($out);
$this->restoreEncoding(); $this->restoreEncoding();
@@ -1678,9 +1693,9 @@ class Parser
*/ */
protected function appendComment($comment) protected function appendComment($comment)
{ {
assert($this->env !== null);
if (! $this->discardComments) { if (! $this->discardComments) {
assert($this->env !== null);
$this->env->comments[] = $comment; $this->env->comments[] = $comment;
} }
} }
@@ -2461,7 +2476,7 @@ class Parser
$whiteBefore = isset($this->buffer[$this->count - 1]) && $whiteBefore = isset($this->buffer[$this->count - 1]) &&
ctype_space($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]) && $whiteAfter = isset($this->buffer[$this->count]) &&
ctype_space($this->buffer[$this->count]); ctype_space($this->buffer[$this->count]);
$varAfter = isset($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 // 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]; $lhs = [Type::T_EXPRESSION, $op, $lhs, $rhs, $this->inParens, $whiteBefore, $whiteAfter];
@@ -2804,6 +2819,10 @@ class Parser
$this->argValues($args) && $this->argValues($args) &&
$this->matchChar(')') $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]; $func = [Type::T_FUNCTION_CALL, $name, $args];
return true; return true;

View File

@@ -19,5 +19,5 @@ namespace ScssPhp\ScssPhp;
*/ */
class Version class Version
{ {
const VERSION = '1.10.5'; const VERSION = '1.12.1';
} }

View File

@@ -104,7 +104,7 @@ class SetupUtils
const PHP_NEXT_MIN_VERSION = ''; // No new PHP requirement for next iTop version yet 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 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) // -- 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 MIN_MEMORY_LIMIT = '32M';
const SUHOSIN_GET_MAX_VALUE_LENGTH = 2048; const SUHOSIN_GET_MAX_VALUE_LENGTH = 2048;