mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts: # composer.json # composer.lock # lib/composer/autoload_classmap.php # lib/composer/autoload_files.php # lib/composer/autoload_real.php # lib/composer/autoload_static.php # lib/composer/installed.php
This commit is contained in:
@@ -2,6 +2,11 @@
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
if (PHP_VERSION_ID < 50600) {
|
||||
echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f::getLoader();
|
||||
|
||||
@@ -108,7 +108,10 @@ if (PHP_VERSION_ID < 80000) {
|
||||
}
|
||||
}
|
||||
|
||||
if (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) {
|
||||
if (
|
||||
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||
) {
|
||||
include("phpvfscomposer://" . __DIR__ . '/..'.'/scssphp/scssphp/bin/pscss');
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ class ClassLoader
|
||||
|
||||
/**
|
||||
* @return string[] Array of classname => path
|
||||
* @psalm-var array<string, string>
|
||||
* @psalm-return array<string, string>
|
||||
*/
|
||||
public function getClassMap()
|
||||
{
|
||||
|
||||
@@ -26,8 +26,21 @@ use Composer\Semver\VersionParser;
|
||||
*/
|
||||
class InstalledVersions
|
||||
{
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
* @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
|
||||
*/
|
||||
private static $installed;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private static $canGetVendors;
|
||||
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
|
||||
*/
|
||||
private static $installedByVendor = array();
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
|
||||
@@ -2,25 +2,25 @@
|
||||
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php',
|
||||
'23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php',
|
||||
'7e9bd612cc444b3eed788ebbe46263a0' => $vendorDir . '/laminas/laminas-zendframework-bridge/src/autoload.php',
|
||||
'23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php',
|
||||
'0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
|
||||
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
|
||||
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
|
||||
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
|
||||
);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
|
||||
@@ -25,33 +25,20 @@ class ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
require __DIR__ . '/platform_check.php';
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f', 'loadClassLoader'));
|
||||
|
||||
$includePaths = require __DIR__ . '/include_paths.php';
|
||||
$includePaths[] = get_include_path();
|
||||
set_include_path(implode(PATH_SEPARATOR, $includePaths));
|
||||
|
||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||
if ($useStaticLoader) {
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::getInitializer($loader));
|
||||
} else {
|
||||
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
$loader->addClassMap($classMap);
|
||||
}
|
||||
}
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::getInitializer($loader));
|
||||
|
||||
$loader->setClassMapAuthoritative(true);
|
||||
$loader->register(true);
|
||||
|
||||
if ($useStaticLoader) {
|
||||
$includeFiles = Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::$files;
|
||||
} else {
|
||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||
}
|
||||
$includeFiles = \Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::$files;
|
||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||
composerRequire7f81b4a2a468a061c306af5e447a9a9f($fileIdentifier, $file);
|
||||
}
|
||||
@@ -60,11 +47,16 @@ class ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileIdentifier
|
||||
* @param string $file
|
||||
* @return void
|
||||
*/
|
||||
function composerRequire7f81b4a2a468a061c306af5e447a9a9f($fileIdentifier, $file)
|
||||
{
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
require $file;
|
||||
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
|
||||
require $file;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,23 +7,23 @@ namespace Composer\Autoload;
|
||||
class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
{
|
||||
public static $files = array (
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php',
|
||||
'23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php',
|
||||
'7e9bd612cc444b3eed788ebbe46263a0' => __DIR__ . '/..' . '/laminas/laminas-zendframework-bridge/src/autoload.php',
|
||||
'23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php',
|
||||
'0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
|
||||
'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
|
||||
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
|
||||
'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
@@ -663,17 +663,13 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Composer\\iTopComposer' => __DIR__ . '/../..' . '/sources/Composer/iTopComposer.php',
|
||||
'Combodo\\iTop\\Controller\\AjaxRenderController' => __DIR__ . '/../..' . '/sources/Controller/AjaxRenderController.php',
|
||||
'Combodo\\iTop\\Controller\\Base\\Layout\\ActivityPanelController' => __DIR__ . '/../..' . '/sources/Controller/Base/Layout/ActivityPanelController.php',
|
||||
'Combodo\\iTop\\Controller\\OAuth\\OAuthAjaxController' => __DIR__ . '/../..' . '/sources/Controller/OAuth/OAuthAjaxController.php',
|
||||
'Combodo\\iTop\\Controller\\OAuth\\OAuthLandingController' => __DIR__ . '/../..' . '/sources/Controller/OAuth/OAuthLandingController.php',
|
||||
'Combodo\\iTop\\Controller\\OAuth\\OAuthWizardController' => __DIR__ . '/../..' . '/sources/Controller/OAuth/OAuthWizardController.php',
|
||||
'Combodo\\iTop\\Controller\\PreferencesController' => __DIR__ . '/../..' . '/sources/Controller/PreferencesController.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\IOAuthClientProvider' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/IOAuthClientProvider.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\IOAuthClientResultDisplay' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/IOAuthClientResultDisplay.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderAbstract' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderAbstract.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderAzure' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderAzure.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderFactory' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderFactory.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderGoogle' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderGoogle.php',
|
||||
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientResultDisplayConf' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/OAuthClientResultDisplayConf.php',
|
||||
'Combodo\\iTop\\Core\\CMDBChange\\CMDBChangeOrigin' => __DIR__ . '/../..' . '/sources/Core/CMDBChange/CMDBChangeOrigin.php',
|
||||
'Combodo\\iTop\\Core\\DbConnectionWrapper' => __DIR__ . '/../..' . '/core/DbConnectionWrapper.php',
|
||||
'Combodo\\iTop\\Core\\Email\\EmailFactory' => __DIR__ . '/../..' . '/sources/Core/Email/EmailFactory.php',
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
// include_paths.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
|
||||
@@ -1892,17 +1892,17 @@
|
||||
},
|
||||
{
|
||||
"name": "scssphp/scssphp",
|
||||
"version": "1.0.6",
|
||||
"version_normalized": "1.0.6.0",
|
||||
"version": "v1.10.3",
|
||||
"version_normalized": "1.10.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/scssphp/scssphp.git",
|
||||
"reference": "5b3c9d704950d8f9637f5110c36c281ec47dc13c"
|
||||
"reference": "0f1e1516ed2412ad43e42a6a319e77624ba1f713"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/5b3c9d704950d8f9637f5110c36c281ec47dc13c",
|
||||
"reference": "5b3c9d704950d8f9637f5110c36c281ec47dc13c",
|
||||
"url": "https://api.github.com/repos/scssphp/scssphp/zipball/0f1e1516ed2412ad43e42a6a319e77624ba1f713",
|
||||
"reference": "0f1e1516ed2412ad43e42a6a319e77624ba1f713",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1911,12 +1911,21 @@
|
||||
"php": ">=5.6.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.3",
|
||||
"squizlabs/php_codesniffer": "~2.5",
|
||||
"twbs/bootstrap": "~4.3",
|
||||
"bamarni/composer-bin-plugin": "^1.4",
|
||||
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.3 || ^9.4",
|
||||
"sass/sass-spec": "*",
|
||||
"squizlabs/php_codesniffer": "~3.5",
|
||||
"symfony/phpunit-bridge": "^5.1",
|
||||
"thoughtbot/bourbon": "^7.0",
|
||||
"twbs/bootstrap": "~5.0",
|
||||
"twbs/bootstrap4": "4.6.1",
|
||||
"zurb/foundation": "~6.5"
|
||||
},
|
||||
"time": "2019-12-12T05:00:52+00:00",
|
||||
"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-05-16T07:22:18+00:00",
|
||||
"bin": [
|
||||
"bin/pscss"
|
||||
],
|
||||
@@ -1954,7 +1963,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/scssphp/scssphp/issues",
|
||||
"source": "https://github.com/scssphp/scssphp/tree/master"
|
||||
"source": "https://github.com/scssphp/scssphp/tree/v1.10.3"
|
||||
},
|
||||
"install-path": "../scssphp/scssphp"
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'reference' => '6550a4fd2c2024f20270c230ffcac622eac81788',
|
||||
'reference' => 'b4f827f399a5d20dbe01c4da84a7dba41aaecb15',
|
||||
'name' => '__root__',
|
||||
'dev' => true,
|
||||
),
|
||||
@@ -16,7 +16,7 @@
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'reference' => '6550a4fd2c2024f20270c230ffcac622eac81788',
|
||||
'reference' => 'b4f827f399a5d20dbe01c4da84a7dba41aaecb15',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'combodo/tcpdf' => array(
|
||||
@@ -321,12 +321,12 @@
|
||||
),
|
||||
),
|
||||
'scssphp/scssphp' => array(
|
||||
'pretty_version' => '1.0.6',
|
||||
'version' => '1.0.6.0',
|
||||
'pretty_version' => 'v1.10.3',
|
||||
'version' => '1.10.3.0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../scssphp/scssphp',
|
||||
'aliases' => array(),
|
||||
'reference' => '5b3c9d704950d8f9637f5110c36c281ec47dc13c',
|
||||
'reference' => '0f1e1516ed2412ad43e42a6a319e77624ba1f713',
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/cache' => array(
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# scssphp
|
||||
### <http://scssphp.github.io/scssphp>
|
||||
### <https://scssphp.github.io/scssphp>
|
||||
|
||||
[](http://travis-ci.org/scssphp/scssphp)
|
||||

|
||||
[](https://packagist.org/packages/scssphp/scssphp)
|
||||
|
||||
`scssphp` is a compiler for SCSS written in PHP.
|
||||
|
||||
Checkout the homepage, <http://scssphp.github.io/scssphp>, for directions on how to use.
|
||||
Checkout the homepage, <https://scssphp.github.io/scssphp>, for directions on how to use.
|
||||
|
||||
## Running Tests
|
||||
|
||||
@@ -23,8 +23,7 @@ There are several tests in the `tests/` directory:
|
||||
* `FailingTest.php` contains tests reported in Github issues that demonstrate compatibility bugs.
|
||||
* `InputTest.php` compiles every `.scss` file in the `tests/inputs` directory
|
||||
then compares to the respective `.css` file in the `tests/outputs` directory.
|
||||
* `ScssTest.php` extracts (ruby) `scss` tests from the `tests/scss_test.rb` file.
|
||||
* `ServerTest.php` contains functional tests for the `Server` class.
|
||||
* `SassSpecTest.php` extracts tests from the `sass/sass-spec` repository.
|
||||
|
||||
When changing any of the tests in `tests/inputs`, the tests will most likely
|
||||
fail because the output has changed. Once you verify that the output is correct
|
||||
@@ -32,16 +31,41 @@ you can run the following command to rebuild all the tests:
|
||||
|
||||
BUILD=1 vendor/bin/phpunit tests
|
||||
|
||||
This will compile all the tests, and save results into `tests/outputs`.
|
||||
This will compile all the tests, and save results into `tests/outputs`. It also
|
||||
updates the list of excluded specs from sass-spec.
|
||||
|
||||
To enable the `scss` compatibility tests:
|
||||
To enable the full `sass-spec` compatibility tests:
|
||||
|
||||
TEST_SCSS_COMPAT=1 vendor/bin/phpunit tests
|
||||
TEST_SASS_SPEC=1 vendor/bin/phpunit tests
|
||||
|
||||
## Coding Standard
|
||||
|
||||
`scssphp` source conforms to [PSR2](http://www.php-fig.org/psr/psr-2/).
|
||||
`scssphp` source conforms to [PSR12](https://www.php-fig.org/psr/psr-12/).
|
||||
|
||||
Run the following command from the root directory to check the code for "sniffs".
|
||||
|
||||
vendor/bin/phpcs --standard=PSR2 bin src tests
|
||||
vendor/bin/phpcs --standard=PSR12 --extensions=php bin src tests *.php
|
||||
|
||||
## Static Analysis
|
||||
|
||||
`scssphp` uses [phpstan](https://phpstan.org/) for static analysis.
|
||||
|
||||
Run the following command from the root directory to analyse the codebase:
|
||||
|
||||
make phpstan
|
||||
|
||||
As most of the codebase is composed of legacy code which cannot be type-checked
|
||||
fully, the setup contains a baseline file with all errors we want to ignore. In
|
||||
particular, we ignore all errors related to not specifying the types inside arrays
|
||||
when these arrays correspond to the representation of Sass values and Sass AST nodes
|
||||
in the parser and compiler.
|
||||
When contributing, the proper process to deal with static analysis is the following:
|
||||
|
||||
1. Make your change in the codebase
|
||||
2. Run `make phpstan`
|
||||
3. Fix errors reported by phpstan when possible
|
||||
4. Repeat step 2 and 3 until nothing gets fixed anymore at step 3
|
||||
5. Run `make phpstan-baseline` to regenerate the phpstan baseline
|
||||
|
||||
Additions to the baseline will be reviewed to avoid ignoring errors that should have
|
||||
been fixed.
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -19,26 +20,26 @@ if (version_compare(PHP_VERSION, '5.6') < 0) {
|
||||
include __DIR__ . '/../scss.inc.php';
|
||||
|
||||
use ScssPhp\ScssPhp\Compiler;
|
||||
use ScssPhp\ScssPhp\Exception\SassException;
|
||||
use ScssPhp\ScssPhp\OutputStyle;
|
||||
use ScssPhp\ScssPhp\Parser;
|
||||
use ScssPhp\ScssPhp\Version;
|
||||
|
||||
$style = null;
|
||||
$loadPaths = null;
|
||||
$precision = null;
|
||||
$loadPaths = [];
|
||||
$dumpTree = false;
|
||||
$inputFile = null;
|
||||
$changeDir = false;
|
||||
$debugInfo = false;
|
||||
$lineNumbers = false;
|
||||
$ignoreErrors = false;
|
||||
$encoding = false;
|
||||
$sourceMap = false;
|
||||
$embedSources = false;
|
||||
$embedSourceMap = false;
|
||||
|
||||
/**
|
||||
* Parse argument
|
||||
*
|
||||
* @param integer $i
|
||||
* @param array $options
|
||||
* @param int $i
|
||||
* @param string[] $options
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
@@ -61,25 +62,29 @@ function parseArgument(&$i, $options) {
|
||||
}
|
||||
}
|
||||
|
||||
$arguments = [];
|
||||
|
||||
for ($i = 1; $i < $argc; $i++) {
|
||||
if ($argv[$i] === '-?' || $argv[$i] === '-h' || $argv[$i] === '--help') {
|
||||
$exe = $argv[0];
|
||||
|
||||
$HELP = <<<EOT
|
||||
Usage: $exe [options] [input-file]
|
||||
Usage: $exe [options] [input-file] [output-file]
|
||||
|
||||
Options include:
|
||||
|
||||
--help Show this message [-h, -?]
|
||||
--continue-on-error Continue compilation (as best as possible) when error encountered
|
||||
--debug-info Annotate selectors with CSS referring to the source file and line number [-g]
|
||||
--dump-tree Dump formatted parse tree [-T]
|
||||
--continue-on-error [deprecated] Ignored
|
||||
--debug-info [deprecated] Ignored [-g]
|
||||
--dump-tree [deprecated] Dump formatted parse tree [-T]
|
||||
--iso8859-1 Use iso8859-1 encoding instead of default utf-8
|
||||
--line-numbers Annotate selectors with comments referring to the source file and line number [--line-comments]
|
||||
--line-numbers [deprecated] Ignored [--line-comments]
|
||||
--load-path=PATH Set import path [-I]
|
||||
--precision=N Set decimal number precision (default 10) [-p]
|
||||
--precision=N [deprecated] Ignored. (default 10) [-p]
|
||||
--sourcemap Create source map file
|
||||
--style=FORMAT Set the output format (compact, compressed, crunched, expanded, or nested) [-s, -t]
|
||||
--embed-sources Embed source file contents in source maps
|
||||
--embed-source-map Embed the source map contents in CSS (default if writing to stdout)
|
||||
--style=FORMAT Set the output style (compressed or expanded) [-s, -t]
|
||||
--version Print the version [-v]
|
||||
|
||||
EOT;
|
||||
@@ -90,13 +95,15 @@ EOT;
|
||||
exit(Version::VERSION . "\n");
|
||||
}
|
||||
|
||||
// Keep parsing --continue-on-error to avoid BC breaks for scripts using it
|
||||
if ($argv[$i] === '--continue-on-error') {
|
||||
$ignoreErrors = true;
|
||||
// TODO report it as a warning ?
|
||||
continue;
|
||||
}
|
||||
|
||||
// Keep parsing it to avoid BC breaks for scripts using it
|
||||
if ($argv[$i] === '-g' || $argv[$i] === '--debug-info') {
|
||||
$debugInfo = true;
|
||||
// TODO report it as a warning ?
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -105,8 +112,9 @@ EOT;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Keep parsing it to avoid BC breaks for scripts using it
|
||||
if ($argv[$i] === '--line-numbers' || $argv[$i] === '--line-comments') {
|
||||
$lineNumbers = true;
|
||||
// TODO report it as a warning ?
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -115,6 +123,16 @@ EOT;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($argv[$i] === '--embed-sources') {
|
||||
$embedSources = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($argv[$i] === '--embed-source-map') {
|
||||
$embedSourceMap = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($argv[$i] === '-T' || $argv[$i] === '--dump-tree') {
|
||||
$dumpTree = true;
|
||||
continue;
|
||||
@@ -130,34 +148,25 @@ EOT;
|
||||
$value = parseArgument($i, array('-I', '--load-path'));
|
||||
|
||||
if (isset($value)) {
|
||||
$loadPaths = $value;
|
||||
$loadPaths[] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Keep parsing --precision to avoid BC breaks for scripts using it
|
||||
$value = parseArgument($i, array('-p', '--precision'));
|
||||
|
||||
if (isset($value)) {
|
||||
$precision = $value;
|
||||
// TODO report it as a warning ?
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file_exists($argv[$i])) {
|
||||
$inputFile = $argv[$i];
|
||||
continue;
|
||||
}
|
||||
$arguments[] = $argv[$i];
|
||||
}
|
||||
|
||||
|
||||
if ($inputFile) {
|
||||
if (isset($arguments[0]) && file_exists($arguments[0])) {
|
||||
$inputFile = $arguments[0];
|
||||
$data = file_get_contents($inputFile);
|
||||
|
||||
$newWorkingDir = dirname(realpath($inputFile));
|
||||
$oldWorkingDir = getcwd();
|
||||
|
||||
if ($oldWorkingDir !== $newWorkingDir) {
|
||||
$changeDir = chdir($newWorkingDir);
|
||||
$inputFile = basename($inputFile);
|
||||
}
|
||||
} else {
|
||||
$data = '';
|
||||
|
||||
@@ -171,45 +180,65 @@ if ($dumpTree) {
|
||||
|
||||
print_r(json_decode(json_encode($parser->parse($data)), true));
|
||||
|
||||
fwrite(STDERR, 'Warning: the --dump-tree option is deprecated. Use proper debugging tools instead.');
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
$scss = new Compiler();
|
||||
|
||||
if ($debugInfo) {
|
||||
$scss->setLineNumberStyle(Compiler::DEBUG_INFO);
|
||||
}
|
||||
|
||||
if ($lineNumbers) {
|
||||
$scss->setLineNumberStyle(Compiler::LINE_COMMENTS);
|
||||
}
|
||||
|
||||
if ($ignoreErrors) {
|
||||
$scss->setIgnoreErrors($ignoreErrors);
|
||||
}
|
||||
|
||||
if ($loadPaths) {
|
||||
$scss->setImportPaths(explode(PATH_SEPARATOR, $loadPaths));
|
||||
}
|
||||
|
||||
if ($precision) {
|
||||
$scss->setNumberPrecision($precision);
|
||||
$scss->setImportPaths($loadPaths);
|
||||
}
|
||||
|
||||
if ($style) {
|
||||
$scss->setFormatter('ScssPhp\\ScssPhp\\Formatter\\' . ucfirst($style));
|
||||
if ($style === OutputStyle::COMPRESSED || $style === OutputStyle::EXPANDED) {
|
||||
$scss->setOutputStyle($style);
|
||||
} else {
|
||||
fwrite(STDERR, "WARNING: the $style style is deprecated.\n");
|
||||
$scss->setFormatter('ScssPhp\\ScssPhp\\Formatter\\' . ucfirst($style));
|
||||
}
|
||||
}
|
||||
|
||||
$outputFile = isset($arguments[1]) ? $arguments[1] : null;
|
||||
$sourceMapFile = null;
|
||||
|
||||
if ($sourceMap) {
|
||||
$scss->setSourceMap(Compiler::SOURCE_MAP_INLINE);
|
||||
$sourceMapOptions = array(
|
||||
'outputSourceFiles' => $embedSources,
|
||||
);
|
||||
if ($embedSourceMap || $outputFile === null) {
|
||||
$scss->setSourceMap(Compiler::SOURCE_MAP_INLINE);
|
||||
} else {
|
||||
$sourceMapFile = $outputFile . '.map';
|
||||
$sourceMapOptions['sourceMapWriteTo'] = $sourceMapFile;
|
||||
$sourceMapOptions['sourceMapURL'] = basename($sourceMapFile);
|
||||
$sourceMapOptions['sourceMapBasepath'] = getcwd();
|
||||
$sourceMapOptions['sourceMapFilename'] = basename($outputFile);
|
||||
|
||||
$scss->setSourceMap(Compiler::SOURCE_MAP_FILE);
|
||||
}
|
||||
|
||||
$scss->setSourceMapOptions($sourceMapOptions);
|
||||
}
|
||||
|
||||
if ($encoding) {
|
||||
$scss->setEncoding($encoding);
|
||||
}
|
||||
|
||||
echo $scss->compile($data, $inputFile);
|
||||
|
||||
if ($changeDir) {
|
||||
chdir($oldWorkingDir);
|
||||
try {
|
||||
$result = $scss->compileString($data, $inputFile);
|
||||
} catch (SassException $e) {
|
||||
fwrite(STDERR, 'Error: '.$e->getMessage()."\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ($outputFile) {
|
||||
file_put_contents($outputFile, $result->getCss());
|
||||
|
||||
if ($sourceMapFile !== null && $result->getSourceMap() !== null) {
|
||||
file_put_contents($sourceMapFile, $result->getSourceMap());
|
||||
}
|
||||
} else {
|
||||
echo $result->getCss();
|
||||
}
|
||||
|
||||
@@ -30,22 +30,82 @@
|
||||
"ext-json": "*",
|
||||
"ext-ctype": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "For best performance, mbstring should be installed as it is faster than ext-iconv",
|
||||
"ext-iconv": "Can be used as fallback when ext-mbstring is not available"
|
||||
},
|
||||
"require-dev": {
|
||||
"squizlabs/php_codesniffer": "~2.5",
|
||||
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.3",
|
||||
"twbs/bootstrap": "~4.3",
|
||||
"bamarni/composer-bin-plugin": "^1.4",
|
||||
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.3 || ^9.4",
|
||||
"sass/sass-spec": "*",
|
||||
"squizlabs/php_codesniffer": "~3.5",
|
||||
"symfony/phpunit-bridge": "^5.1",
|
||||
"thoughtbot/bourbon": "^7.0",
|
||||
"twbs/bootstrap": "~5.0",
|
||||
"twbs/bootstrap4": "4.6.1",
|
||||
"zurb/foundation": "~6.5"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"repositories": [
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "sass/sass-spec",
|
||||
"version": "2022.02.24",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sass/sass-spec.git",
|
||||
"reference": "f41b9bfb9a3013392f2136c79f7f3356f15fb8ba"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sass/sass-spec/zipball/f41b9bfb9a3013392f2136c79f7f3356f15fb8ba",
|
||||
"reference": "f41b9bfb9a3013392f2136c79f7f3356f15fb8ba",
|
||||
"shasum": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "thoughtbot/bourbon",
|
||||
"version": "v7.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thoughtbot/bourbon.git",
|
||||
"reference": "fbe338ee6807e7f7aa996d82c8a16f248bb149b3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thoughtbot/bourbon/zipball/fbe338ee6807e7f7aa996d82c8a16f248bb149b3",
|
||||
"reference": "fbe338ee6807e7f7aa996d82c8a16f248bb149b3",
|
||||
"shasum": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "package",
|
||||
"package": {
|
||||
"name": "twbs/bootstrap4",
|
||||
"version": "v4.6.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twbs/bootstrap.git",
|
||||
"reference": "043a03c95a2ad6738f85b65e53b9dbdfb03b8d10"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twbs/bootstrap/zipball/043a03c95a2ad6738f85b65e53b9dbdfb03b8d10",
|
||||
"reference": "043a03c95a2ad6738f85b65e53b9dbdfb03b8d10",
|
||||
"shasum": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"bin": ["bin/pscss"],
|
||||
"archive": {
|
||||
"exclude": [
|
||||
"/Makefile",
|
||||
"/.gitattributes",
|
||||
"/.gitignore",
|
||||
"/.travis.yml",
|
||||
"/phpunit.xml.dist",
|
||||
"/tests"
|
||||
]
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"bamarni/composer-bin-plugin": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,21 @@
|
||||
<?php
|
||||
|
||||
if (version_compare(PHP_VERSION, '5.6') < 0) {
|
||||
throw new \Exception('scssphp requires PHP 5.6 or above');
|
||||
}
|
||||
|
||||
if (! class_exists('ScssPhp\ScssPhp\Version', false)) {
|
||||
include_once __DIR__ . '/src/Base/Range.php';
|
||||
include_once __DIR__ . '/src/Block.php';
|
||||
include_once __DIR__ . '/src/Cache.php';
|
||||
include_once __DIR__ . '/src/Colors.php';
|
||||
include_once __DIR__ . '/src/Compiler.php';
|
||||
include_once __DIR__ . '/src/Compiler/Environment.php';
|
||||
include_once __DIR__ . '/src/Exception/CompilerException.php';
|
||||
include_once __DIR__ . '/src/Exception/ParserException.php';
|
||||
include_once __DIR__ . '/src/Exception/RangeException.php';
|
||||
include_once __DIR__ . '/src/Exception/ServerException.php';
|
||||
include_once __DIR__ . '/src/Formatter.php';
|
||||
include_once __DIR__ . '/src/Formatter/Compact.php';
|
||||
include_once __DIR__ . '/src/Formatter/Compressed.php';
|
||||
include_once __DIR__ . '/src/Formatter/Crunched.php';
|
||||
include_once __DIR__ . '/src/Formatter/Debug.php';
|
||||
include_once __DIR__ . '/src/Formatter/Expanded.php';
|
||||
include_once __DIR__ . '/src/Formatter/Nested.php';
|
||||
include_once __DIR__ . '/src/Formatter/OutputBlock.php';
|
||||
include_once __DIR__ . '/src/Node.php';
|
||||
include_once __DIR__ . '/src/Node/Number.php';
|
||||
include_once __DIR__ . '/src/Parser.php';
|
||||
include_once __DIR__ . '/src/SourceMap/Base64.php';
|
||||
include_once __DIR__ . '/src/SourceMap/Base64VLQ.php';
|
||||
include_once __DIR__ . '/src/SourceMap/SourceMapGenerator.php';
|
||||
include_once __DIR__ . '/src/Type.php';
|
||||
include_once __DIR__ . '/src/Util.php';
|
||||
include_once __DIR__ . '/src/Version.php';
|
||||
if (! class_exists('ScssPhp\ScssPhp\Version')) {
|
||||
spl_autoload_register(function ($class) {
|
||||
if (0 !== strpos($class, 'ScssPhp\ScssPhp\\')) {
|
||||
// Not a ScssPhp class
|
||||
return;
|
||||
}
|
||||
|
||||
$subClass = substr($class, strlen('ScssPhp\ScssPhp\\'));
|
||||
$path = __DIR__ . '/src/' . str_replace('\\', '/', $subClass) . '.php';
|
||||
|
||||
if (file_exists($path)) {
|
||||
require $path;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2015-2019 Leaf Corcoran
|
||||
* @copyright 2015-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -15,17 +16,26 @@ namespace ScssPhp\ScssPhp\Base;
|
||||
* Range
|
||||
*
|
||||
* @author Anthon Pang <anthon.pang@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Range
|
||||
{
|
||||
/**
|
||||
* @var float|int
|
||||
*/
|
||||
public $first;
|
||||
|
||||
/**
|
||||
* @var float|int
|
||||
*/
|
||||
public $last;
|
||||
|
||||
/**
|
||||
* Initialize range
|
||||
*
|
||||
* @param integer|float $first
|
||||
* @param integer|float $last
|
||||
* @param int|float $first
|
||||
* @param int|float $last
|
||||
*/
|
||||
public function __construct($first, $last)
|
||||
{
|
||||
@@ -36,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)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -15,16 +16,18 @@ namespace ScssPhp\ScssPhp;
|
||||
* Block
|
||||
*
|
||||
* @author Anthon Pang <anthon.pang@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Block
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @var \ScssPhp\ScssPhp\Block
|
||||
* @var Block|null
|
||||
*/
|
||||
public $parent;
|
||||
|
||||
@@ -34,22 +37,22 @@ class Block
|
||||
public $sourceName;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
public $sourceIndex;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
public $sourceLine;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
public $sourceColumn;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @var array|null
|
||||
*/
|
||||
public $selectors;
|
||||
|
||||
@@ -64,7 +67,7 @@ class Block
|
||||
public $children;
|
||||
|
||||
/**
|
||||
* @var \ScssPhp\ScssPhp\Block
|
||||
* @var Block|null
|
||||
*/
|
||||
public $selfParent;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -12,6 +13,7 @@
|
||||
namespace ScssPhp\ScssPhp;
|
||||
|
||||
use Exception;
|
||||
use ScssPhp\ScssPhp\Version;
|
||||
|
||||
/**
|
||||
* The scss cache manager.
|
||||
@@ -22,37 +24,60 @@ use Exception;
|
||||
* taking in account options that affects the result
|
||||
*
|
||||
* The cache manager is agnostic about data format and only the operation is expected to be described by string
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* SCSS cache
|
||||
*
|
||||
* @author Cedric Morin
|
||||
* @author Cedric Morin <cedric@yterium.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Cache
|
||||
{
|
||||
const CACHE_VERSION = 1;
|
||||
|
||||
// directory used for storing data
|
||||
/**
|
||||
* directory used for storing data
|
||||
*
|
||||
* @var string|false
|
||||
*/
|
||||
public static $cacheDir = false;
|
||||
|
||||
// prefix for the storing data
|
||||
/**
|
||||
* prefix for the storing data
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $prefix = 'scssphp_';
|
||||
|
||||
// force a refresh : 'once' for refreshing the first hit on a cache only, true to never use the cache in this hit
|
||||
/**
|
||||
* force a refresh : 'once' for refreshing the first hit on a cache only, true to never use the cache in this hit
|
||||
*
|
||||
* @var bool|string
|
||||
*/
|
||||
public static $forceRefresh = false;
|
||||
|
||||
// specifies the number of seconds after which data cached will be seen as 'garbage' and potentially cleaned up
|
||||
/**
|
||||
* specifies the number of seconds after which data cached will be seen as 'garbage' and potentially cleaned up
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public static $gcLifetime = 604800;
|
||||
|
||||
// array of already refreshed cache if $forceRefresh==='once'
|
||||
/**
|
||||
* array of already refreshed cache if $forceRefresh==='once'
|
||||
*
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
protected static $refreshed = [];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $options
|
||||
*
|
||||
* @phpstan-param array{cacheDir?: string, prefix?: string, forceRefresh?: string} $options
|
||||
*/
|
||||
public function __construct($options)
|
||||
{
|
||||
@@ -84,10 +109,10 @@ class Cache
|
||||
* Get the cached result of $operation on $what,
|
||||
* which is known as dependant from the content of $options
|
||||
*
|
||||
* @param string $operation parse, compile...
|
||||
* @param mixed $what content key (e.g., filename to be treated)
|
||||
* @param array $options any option that affect the operation result on the content
|
||||
* @param integer $lastModified last modified timestamp
|
||||
* @param string $operation parse, compile...
|
||||
* @param mixed $what content key (e.g., filename to be treated)
|
||||
* @param array $options any option that affect the operation result on the content
|
||||
* @param int|null $lastModified last modified timestamp
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
@@ -97,18 +122,20 @@ class Cache
|
||||
{
|
||||
$fileCache = self::$cacheDir . self::cacheName($operation, $what, $options);
|
||||
|
||||
if (((self::$forceRefresh === false) || (self::$forceRefresh === 'once' &&
|
||||
if (
|
||||
((self::$forceRefresh === false) || (self::$forceRefresh === 'once' &&
|
||||
isset(self::$refreshed[$fileCache]))) && file_exists($fileCache)
|
||||
) {
|
||||
$cacheTime = filemtime($fileCache);
|
||||
|
||||
if ((is_null($lastModified) || $cacheTime > $lastModified) &&
|
||||
if (
|
||||
(\is_null($lastModified) || $cacheTime > $lastModified) &&
|
||||
$cacheTime + self::$gcLifetime > time()
|
||||
) {
|
||||
$c = file_get_contents($fileCache);
|
||||
$c = unserialize($c);
|
||||
|
||||
if (is_array($c) && isset($c['value'])) {
|
||||
if (\is_array($c) && isset($c['value'])) {
|
||||
return $c['value'];
|
||||
}
|
||||
}
|
||||
@@ -125,6 +152,8 @@ class Cache
|
||||
* @param mixed $what
|
||||
* @param mixed $value
|
||||
* @param array $options
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCache($operation, $what, $value, $options = [])
|
||||
{
|
||||
@@ -132,6 +161,7 @@ class Cache
|
||||
|
||||
$c = ['value' => $value];
|
||||
$c = serialize($c);
|
||||
|
||||
file_put_contents($fileCache, $c);
|
||||
|
||||
if (self::$forceRefresh === 'once') {
|
||||
@@ -153,6 +183,7 @@ class Cache
|
||||
{
|
||||
$t = [
|
||||
'version' => self::CACHE_VERSION,
|
||||
'scssphpVersion' => Version::VERSION,
|
||||
'operation' => $operation,
|
||||
'what' => $what,
|
||||
'options' => $options
|
||||
@@ -169,6 +200,8 @@ class Cache
|
||||
/**
|
||||
* Check that the cache dir exists and is writeable
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function checkCacheDir()
|
||||
@@ -177,9 +210,7 @@ class Cache
|
||||
self::$cacheDir = rtrim(self::$cacheDir, '/') . '/';
|
||||
|
||||
if (! is_dir(self::$cacheDir)) {
|
||||
if (! mkdir(self::$cacheDir)) {
|
||||
throw new Exception('Cache directory couldn\'t be created: ' . self::$cacheDir);
|
||||
}
|
||||
throw new Exception('Cache directory doesn\'t exist: ' . self::$cacheDir);
|
||||
}
|
||||
|
||||
if (! is_writable(self::$cacheDir)) {
|
||||
@@ -189,6 +220,8 @@ class Cache
|
||||
|
||||
/**
|
||||
* Delete unused cached files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function cleanCache()
|
||||
{
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -15,6 +16,8 @@ namespace ScssPhp\ScssPhp;
|
||||
* CSS Colors
|
||||
*
|
||||
* @author Leaf Corcoran <leafot@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Colors
|
||||
{
|
||||
@@ -23,12 +26,13 @@ class Colors
|
||||
*
|
||||
* @see http://www.w3.org/TR/css3-color
|
||||
*
|
||||
* @var array
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected static $cssColors = [
|
||||
'aliceblue' => '240,248,255',
|
||||
'antiquewhite' => '250,235,215',
|
||||
'aqua' => '0,255,255',
|
||||
'cyan' => '0,255,255',
|
||||
'aquamarine' => '127,255,212',
|
||||
'azure' => '240,255,255',
|
||||
'beige' => '245,245,220',
|
||||
@@ -46,13 +50,12 @@ class Colors
|
||||
'cornflowerblue' => '100,149,237',
|
||||
'cornsilk' => '255,248,220',
|
||||
'crimson' => '220,20,60',
|
||||
'cyan' => '0,255,255',
|
||||
'darkblue' => '0,0,139',
|
||||
'darkcyan' => '0,139,139',
|
||||
'darkgoldenrod' => '184,134,11',
|
||||
'darkgray' => '169,169,169',
|
||||
'darkgreen' => '0,100,0',
|
||||
'darkgrey' => '169,169,169',
|
||||
'darkgreen' => '0,100,0',
|
||||
'darkkhaki' => '189,183,107',
|
||||
'darkmagenta' => '139,0,139',
|
||||
'darkolivegreen' => '85,107,47',
|
||||
@@ -75,14 +78,15 @@ class Colors
|
||||
'floralwhite' => '255,250,240',
|
||||
'forestgreen' => '34,139,34',
|
||||
'fuchsia' => '255,0,255',
|
||||
'magenta' => '255,0,255',
|
||||
'gainsboro' => '220,220,220',
|
||||
'ghostwhite' => '248,248,255',
|
||||
'gold' => '255,215,0',
|
||||
'goldenrod' => '218,165,32',
|
||||
'gray' => '128,128,128',
|
||||
'grey' => '128,128,128',
|
||||
'green' => '0,128,0',
|
||||
'greenyellow' => '173,255,47',
|
||||
'grey' => '128,128,128',
|
||||
'honeydew' => '240,255,240',
|
||||
'hotpink' => '255,105,180',
|
||||
'indianred' => '205,92,92',
|
||||
@@ -98,8 +102,8 @@ class Colors
|
||||
'lightcyan' => '224,255,255',
|
||||
'lightgoldenrodyellow' => '250,250,210',
|
||||
'lightgray' => '211,211,211',
|
||||
'lightgreen' => '144,238,144',
|
||||
'lightgrey' => '211,211,211',
|
||||
'lightgreen' => '144,238,144',
|
||||
'lightpink' => '255,182,193',
|
||||
'lightsalmon' => '255,160,122',
|
||||
'lightseagreen' => '32,178,170',
|
||||
@@ -111,7 +115,6 @@ class Colors
|
||||
'lime' => '0,255,0',
|
||||
'limegreen' => '50,205,50',
|
||||
'linen' => '250,240,230',
|
||||
'magenta' => '255,0,255',
|
||||
'maroon' => '128,0,0',
|
||||
'mediumaquamarine' => '102,205,170',
|
||||
'mediumblue' => '0,0,205',
|
||||
@@ -145,7 +148,6 @@ class Colors
|
||||
'plum' => '221,160,221',
|
||||
'powderblue' => '176,224,230',
|
||||
'purple' => '128,0,128',
|
||||
'rebeccapurple' => '102,51,153',
|
||||
'red' => '255,0,0',
|
||||
'rosybrown' => '188,143,143',
|
||||
'royalblue' => '65,105,225',
|
||||
@@ -167,7 +169,6 @@ class Colors
|
||||
'teal' => '0,128,128',
|
||||
'thistle' => '216,191,216',
|
||||
'tomato' => '255,99,71',
|
||||
'transparent' => '0,0,0,0',
|
||||
'turquoise' => '64,224,208',
|
||||
'violet' => '238,130,238',
|
||||
'wheat' => '245,222,179',
|
||||
@@ -175,6 +176,8 @@ class Colors
|
||||
'whitesmoke' => '245,245,245',
|
||||
'yellow' => '255,255,0',
|
||||
'yellowgreen' => '154,205,50',
|
||||
'rebeccapurple' => '102,51,153',
|
||||
'transparent' => '0,0,0,0',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -182,11 +185,11 @@ class Colors
|
||||
*
|
||||
* @param string $colorName
|
||||
*
|
||||
* @return array|null
|
||||
* @return int[]|null
|
||||
*/
|
||||
public static function colorNameToRGBa($colorName)
|
||||
{
|
||||
if (is_string($colorName) && isset(static::$cssColors[$colorName])) {
|
||||
if (\is_string($colorName) && isset(static::$cssColors[$colorName])) {
|
||||
$rgba = explode(',', static::$cssColors[$colorName]);
|
||||
|
||||
// only case with opacity is transparent, with opacity=0, so we can intval on opacity also
|
||||
@@ -201,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 $a
|
||||
* @param int $r
|
||||
* @param int $g
|
||||
* @param int $b
|
||||
* @param int|float $a
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
@@ -217,28 +220,26 @@ class Colors
|
||||
}
|
||||
|
||||
if ($a < 1) {
|
||||
# specific case we dont' revert according to spec
|
||||
#if (! $a && ! $r && ! $g && ! $b) {
|
||||
# return 'transparent';
|
||||
#}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_null($reverseColorTable)) {
|
||||
if (\is_null($reverseColorTable)) {
|
||||
$reverseColorTable = [];
|
||||
|
||||
foreach (static::$cssColors as $name => $rgb_str) {
|
||||
$rgb_str = explode(',', $rgb_str);
|
||||
|
||||
if (count($rgb_str) == 3) {
|
||||
$reverseColorTable[intval($rgb_str[0])][intval($rgb_str[1])][intval($rgb_str[2])] = $name;
|
||||
if (
|
||||
\count($rgb_str) == 3 &&
|
||||
! isset($reverseColorTable[\intval($rgb_str[0])][\intval($rgb_str[1])][\intval($rgb_str[2])])
|
||||
) {
|
||||
$reverseColorTable[\intval($rgb_str[0])][\intval($rgb_str[1])][\intval($rgb_str[2])] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($reverseColorTable[intval($r)][intval($g)][intval($b)])) {
|
||||
return $reverseColorTable[intval($r)][intval($g)][intval($b)];
|
||||
if (isset($reverseColorTable[\intval($r)][\intval($g)][\intval($b)])) {
|
||||
return $reverseColorTable[\intval($r)][\intval($g)][\intval($b)];
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -15,19 +16,41 @@ namespace ScssPhp\ScssPhp\Compiler;
|
||||
* Compiler environment
|
||||
*
|
||||
* @author Anthon Pang <anthon.pang@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Environment
|
||||
{
|
||||
/**
|
||||
* @var \ScssPhp\ScssPhp\Block
|
||||
* @var \ScssPhp\ScssPhp\Block|null
|
||||
*/
|
||||
public $block;
|
||||
|
||||
/**
|
||||
* @var \ScssPhp\ScssPhp\Compiler\Environment
|
||||
* @var \ScssPhp\ScssPhp\Compiler\Environment|null
|
||||
*/
|
||||
public $parent;
|
||||
|
||||
/**
|
||||
* @var Environment|null
|
||||
*/
|
||||
public $declarationScopeParent;
|
||||
|
||||
/**
|
||||
* @var Environment|null
|
||||
*/
|
||||
public $parentStore;
|
||||
|
||||
/**
|
||||
* @var array|null
|
||||
*/
|
||||
public $selectors;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
public $marker;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
@@ -39,7 +62,7 @@ class Environment
|
||||
public $storeUnreduced;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
public $depth;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -15,7 +16,9 @@ namespace ScssPhp\ScssPhp\Exception;
|
||||
* Compiler exception
|
||||
*
|
||||
* @author Oleksandr Savchenko <traveltino@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class CompilerException extends \Exception
|
||||
class CompilerException extends \Exception implements SassException
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -15,7 +16,43 @@ namespace ScssPhp\ScssPhp\Exception;
|
||||
* Parser Exception
|
||||
*
|
||||
* @author Oleksandr Savchenko <traveltino@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ParserException extends \Exception
|
||||
class ParserException extends \Exception implements SassException
|
||||
{
|
||||
/**
|
||||
* @var array|null
|
||||
* @phpstan-var array{string, int, int}|null
|
||||
*/
|
||||
private $sourcePosition;
|
||||
|
||||
/**
|
||||
* Get source position
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @return array|null
|
||||
* @phpstan-return array{string, int, int}|null
|
||||
*/
|
||||
public function getSourcePosition()
|
||||
{
|
||||
return $this->sourcePosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set source position
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param array $sourcePosition
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @phpstan-param array{string, int, int} $sourcePosition
|
||||
*/
|
||||
public function setSourcePosition($sourcePosition)
|
||||
{
|
||||
$this->sourcePosition = $sourcePosition;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -15,7 +16,9 @@ namespace ScssPhp\ScssPhp\Exception;
|
||||
* Range exception
|
||||
*
|
||||
* @author Anthon Pang <anthon.pang@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class RangeException extends \Exception
|
||||
class RangeException extends \Exception implements SassException
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -11,11 +12,15 @@
|
||||
|
||||
namespace ScssPhp\ScssPhp\Exception;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated.', ServerException::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Server Exception
|
||||
*
|
||||
* @author Anthon Pang <anthon.pang@gmail.com>
|
||||
*
|
||||
* @deprecated The Scssphp server should define its own exception instead.
|
||||
*/
|
||||
class ServerException extends \Exception
|
||||
class ServerException extends \Exception implements SassException
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -18,11 +19,13 @@ use ScssPhp\ScssPhp\SourceMap\SourceMapGenerator;
|
||||
* Base formatter
|
||||
*
|
||||
* @author Leaf Corcoran <leafot@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class Formatter
|
||||
{
|
||||
/**
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
public $indentLevel;
|
||||
|
||||
@@ -57,7 +60,7 @@ abstract class Formatter
|
||||
public $assignSeparator;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
* @var bool
|
||||
*/
|
||||
public $keepSemicolons;
|
||||
|
||||
@@ -67,17 +70,17 @@ abstract class Formatter
|
||||
protected $currentBlock;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
protected $currentLine;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
protected $currentColumn;
|
||||
|
||||
/**
|
||||
* @var \ScssPhp\ScssPhp\SourceMap\SourceMapGenerator
|
||||
* @var \ScssPhp\ScssPhp\SourceMap\SourceMapGenerator|null
|
||||
*/
|
||||
protected $sourceMapGenerator;
|
||||
|
||||
@@ -118,16 +121,33 @@ abstract class Formatter
|
||||
return rtrim($name) . $this->assignSeparator . $value . ';';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return custom property assignment
|
||||
* differs in that you have to keep spaces in the value as is
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function customProperty($name, $value)
|
||||
{
|
||||
return rtrim($name) . trim($this->assignSeparator) . $value . ';';
|
||||
}
|
||||
|
||||
/**
|
||||
* Output lines inside a block
|
||||
*
|
||||
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function blockLines(OutputBlock $block)
|
||||
{
|
||||
$inner = $this->indentStr();
|
||||
|
||||
$glue = $this->break . $inner;
|
||||
$glue = $this->break . $inner;
|
||||
|
||||
$this->write($inner . implode($glue, $block->lines));
|
||||
|
||||
@@ -140,9 +160,13 @@ abstract class Formatter
|
||||
* Output block selectors
|
||||
*
|
||||
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function blockSelectors(OutputBlock $block)
|
||||
{
|
||||
assert(! empty($block->selectors));
|
||||
|
||||
$inner = $this->indentStr();
|
||||
|
||||
$this->write($inner
|
||||
@@ -154,6 +178,8 @@ abstract class Formatter
|
||||
* Output block children
|
||||
*
|
||||
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function blockChildren(OutputBlock $block)
|
||||
{
|
||||
@@ -166,6 +192,8 @@ abstract class Formatter
|
||||
* Output non-empty block
|
||||
*
|
||||
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function block(OutputBlock $block)
|
||||
{
|
||||
@@ -211,7 +239,7 @@ abstract class Formatter
|
||||
*
|
||||
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
protected function testEmptyChildren($block)
|
||||
{
|
||||
@@ -258,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;
|
||||
}
|
||||
@@ -269,6 +306,8 @@ abstract class Formatter
|
||||
* Output content
|
||||
*
|
||||
* @param string $str
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function write($str)
|
||||
{
|
||||
@@ -282,7 +321,8 @@ abstract class Formatter
|
||||
* Maybe Strip semi-colon appended by property(); it's a separator, not a terminator
|
||||
* will be striped for real before a closing, otherwise displayed unchanged starting the next write
|
||||
*/
|
||||
if (! $this->keepSemicolons &&
|
||||
if (
|
||||
! $this->keepSemicolons &&
|
||||
$str &&
|
||||
(strpos($str, ';') !== false) &&
|
||||
(substr($str, -1) === ';')
|
||||
@@ -293,22 +333,43 @@ abstract class Formatter
|
||||
}
|
||||
|
||||
if ($this->sourceMapGenerator) {
|
||||
$this->sourceMapGenerator->addMapping(
|
||||
$this->currentLine,
|
||||
$this->currentColumn,
|
||||
$this->currentBlock->sourceLine,
|
||||
//columns from parser are off by one
|
||||
$this->currentBlock->sourceColumn > 0 ? $this->currentBlock->sourceColumn - 1 : 0,
|
||||
$this->currentBlock->sourceName
|
||||
);
|
||||
|
||||
$lines = explode("\n", $str);
|
||||
$lineCount = count($lines);
|
||||
$this->currentLine += $lineCount-1;
|
||||
|
||||
$lastLine = array_pop($lines);
|
||||
|
||||
$this->currentColumn = ($lineCount === 1 ? $this->currentColumn : 0) + strlen($lastLine);
|
||||
foreach ($lines as $line) {
|
||||
// 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,
|
||||
$this->currentBlock->sourceLine,
|
||||
//columns from parser are off by one
|
||||
$this->currentBlock->sourceColumn > 0 ? $this->currentBlock->sourceColumn - 1 : 0,
|
||||
$this->currentBlock->sourceName
|
||||
);
|
||||
}
|
||||
|
||||
$this->currentLine++;
|
||||
$this->currentColumn = 0;
|
||||
}
|
||||
|
||||
if ($lastLine !== '') {
|
||||
assert($this->currentBlock->sourceLine !== null);
|
||||
assert($this->currentBlock->sourceName !== null);
|
||||
$this->sourceMapGenerator->addMapping(
|
||||
$this->currentLine,
|
||||
$this->currentColumn,
|
||||
$this->currentBlock->sourceLine,
|
||||
//columns from parser are off by one
|
||||
$this->currentBlock->sourceColumn > 0 ? $this->currentBlock->sourceColumn - 1 : 0,
|
||||
$this->currentBlock->sourceName
|
||||
);
|
||||
}
|
||||
|
||||
$this->currentColumn += \strlen($lastLine);
|
||||
}
|
||||
|
||||
echo $str;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -17,6 +18,10 @@ use ScssPhp\ScssPhp\Formatter;
|
||||
* Compact formatter
|
||||
*
|
||||
* @author Leaf Corcoran <leafot@gmail.com>
|
||||
*
|
||||
* @deprecated since 1.4.0. Use the Compressed formatter instead.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Compact extends Formatter
|
||||
{
|
||||
@@ -25,6 +30,8 @@ class Compact extends Formatter
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
@trigger_error('The Compact formatter is deprecated since 1.4.0. Use the Compressed formatter instead.', E_USER_DEPRECATED);
|
||||
|
||||
$this->indentLevel = 0;
|
||||
$this->indentChar = '';
|
||||
$this->break = '';
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -12,12 +13,13 @@
|
||||
namespace ScssPhp\ScssPhp\Formatter;
|
||||
|
||||
use ScssPhp\ScssPhp\Formatter;
|
||||
use ScssPhp\ScssPhp\Formatter\OutputBlock;
|
||||
|
||||
/**
|
||||
* Compressed formatter
|
||||
*
|
||||
* @author Leaf Corcoran <leafot@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Compressed extends Formatter
|
||||
{
|
||||
@@ -48,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,8 @@ class Compressed extends Formatter
|
||||
*/
|
||||
protected function blockSelectors(OutputBlock $block)
|
||||
{
|
||||
assert(! empty($block->selectors));
|
||||
|
||||
$inner = $this->indentStr();
|
||||
|
||||
$this->write(
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -12,12 +13,15 @@
|
||||
namespace ScssPhp\ScssPhp\Formatter;
|
||||
|
||||
use ScssPhp\ScssPhp\Formatter;
|
||||
use ScssPhp\ScssPhp\Formatter\OutputBlock;
|
||||
|
||||
/**
|
||||
* Crunched formatter
|
||||
*
|
||||
* @author Anthon Pang <anthon.pang@gmail.com>
|
||||
*
|
||||
* @deprecated since 1.4.0. Use the Compressed formatter instead.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Crunched extends Formatter
|
||||
{
|
||||
@@ -26,6 +30,8 @@ class Crunched extends Formatter
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
@trigger_error('The Crunched formatter is deprecated since 1.4.0. Use the Compressed formatter instead.', E_USER_DEPRECATED);
|
||||
|
||||
$this->indentLevel = 0;
|
||||
$this->indentChar = ' ';
|
||||
$this->break = '';
|
||||
@@ -65,6 +71,8 @@ class Crunched extends Formatter
|
||||
*/
|
||||
protected function blockSelectors(OutputBlock $block)
|
||||
{
|
||||
assert(! empty($block->selectors));
|
||||
|
||||
$inner = $this->indentStr();
|
||||
|
||||
$this->write(
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -12,12 +13,15 @@
|
||||
namespace ScssPhp\ScssPhp\Formatter;
|
||||
|
||||
use ScssPhp\ScssPhp\Formatter;
|
||||
use ScssPhp\ScssPhp\Formatter\OutputBlock;
|
||||
|
||||
/**
|
||||
* Debug formatter
|
||||
*
|
||||
* @author Anthon Pang <anthon.pang@gmail.com>
|
||||
*
|
||||
* @deprecated since 1.4.0.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Debug extends Formatter
|
||||
{
|
||||
@@ -26,6 +30,8 @@ class Debug extends Formatter
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
@trigger_error('The Debug formatter is deprecated since 1.4.0.', E_USER_DEPRECATED);
|
||||
|
||||
$this->indentLevel = 0;
|
||||
$this->indentChar = '';
|
||||
$this->break = "\n";
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -12,12 +13,13 @@
|
||||
namespace ScssPhp\ScssPhp\Formatter;
|
||||
|
||||
use ScssPhp\ScssPhp\Formatter;
|
||||
use ScssPhp\ScssPhp\Formatter\OutputBlock;
|
||||
|
||||
/**
|
||||
* Expanded formatter
|
||||
*
|
||||
* @author Leaf Corcoran <leafot@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Expanded extends Formatter
|
||||
{
|
||||
@@ -55,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]+/', $glue, $line);
|
||||
$replacedLine = preg_replace('/\r\n?|\n|\f/', $this->break, $line);
|
||||
assert($replacedLine !== null);
|
||||
$block->lines[$index] = $replacedLine;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -12,18 +13,21 @@
|
||||
namespace ScssPhp\ScssPhp\Formatter;
|
||||
|
||||
use ScssPhp\ScssPhp\Formatter;
|
||||
use ScssPhp\ScssPhp\Formatter\OutputBlock;
|
||||
use ScssPhp\ScssPhp\Type;
|
||||
|
||||
/**
|
||||
* Nested formatter
|
||||
*
|
||||
* @author Leaf Corcoran <leafot@gmail.com>
|
||||
*
|
||||
* @deprecated since 1.4.0. Use the Expanded formatter instead.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Nested extends Formatter
|
||||
{
|
||||
/**
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
private $depth;
|
||||
|
||||
@@ -32,6 +36,8 @@ class Nested extends Formatter
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
@trigger_error('The Nested formatter is deprecated since 1.4.0. Use the Expanded formatter instead.', E_USER_DEPRECATED);
|
||||
|
||||
$this->indentLevel = 0;
|
||||
$this->indentChar = ' ';
|
||||
$this->break = "\n";
|
||||
@@ -58,12 +64,13 @@ class Nested extends Formatter
|
||||
protected function blockLines(OutputBlock $block)
|
||||
{
|
||||
$inner = $this->indentStr();
|
||||
|
||||
$glue = $this->break . $inner;
|
||||
$glue = $this->break . $inner;
|
||||
|
||||
foreach ($block->lines as $index => $line) {
|
||||
if (substr($line, 0, 2) === '/*') {
|
||||
$block->lines[$index] = preg_replace('/[\r\n]+/', $glue, $line);
|
||||
$replacedLine = preg_replace('/\r\n?|\n|\f/', $this->break, $line);
|
||||
assert($replacedLine !== null);
|
||||
$block->lines[$index] = $replacedLine;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +97,7 @@ class Nested extends Formatter
|
||||
$previousHasSelector = false;
|
||||
}
|
||||
|
||||
$isMediaOrDirective = in_array($block->type, [Type::T_DIRECTIVE, Type::T_MEDIA]);
|
||||
$isMediaOrDirective = \in_array($block->type, [Type::T_DIRECTIVE, Type::T_MEDIA]);
|
||||
$isSupport = ($block->type === Type::T_DIRECTIVE
|
||||
&& $block->selectors && strpos(implode('', $block->selectors), '@supports') !== false);
|
||||
|
||||
@@ -98,7 +105,8 @@ class Nested extends Formatter
|
||||
array_pop($depths);
|
||||
$this->depth--;
|
||||
|
||||
if (! $this->depth && ($block->depth <= 1 || (! $this->indentLevel && $block->type === Type::T_COMMENT)) &&
|
||||
if (
|
||||
! $this->depth && ($block->depth <= 1 || (! $this->indentLevel && $block->type === Type::T_COMMENT)) &&
|
||||
(($block->selectors && ! $isMediaOrDirective) || $previousHasSelector)
|
||||
) {
|
||||
$downLevel = $this->break;
|
||||
@@ -119,10 +127,12 @@ class Nested extends Formatter
|
||||
if ($block->depth > end($depths)) {
|
||||
if (! $previousEmpty || $this->depth < 1) {
|
||||
$this->depth++;
|
||||
|
||||
$depths[] = $block->depth;
|
||||
} else {
|
||||
// keep the current depth unchanged but take the block depth as a new reference for following blocks
|
||||
array_pop($depths);
|
||||
|
||||
$depths[] = $block->depth;
|
||||
}
|
||||
}
|
||||
@@ -213,7 +223,7 @@ class Nested extends Formatter
|
||||
*
|
||||
* @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
private function hasFlatChild($block)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -15,51 +16,53 @@ namespace ScssPhp\ScssPhp\Formatter;
|
||||
* Output block
|
||||
*
|
||||
* @author Anthon Pang <anthon.pang@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class OutputBlock
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
public $depth;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @var array|null
|
||||
*/
|
||||
public $selectors;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @var string[]
|
||||
*/
|
||||
public $lines;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @var OutputBlock[]
|
||||
*/
|
||||
public $children;
|
||||
|
||||
/**
|
||||
* @var \ScssPhp\ScssPhp\Formatter\OutputBlock
|
||||
* @var OutputBlock|null
|
||||
*/
|
||||
public $parent;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
public $sourceName;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
* @var int|null
|
||||
*/
|
||||
public $sourceLine;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
* @var int|null
|
||||
*/
|
||||
public $sourceColumn;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -15,6 +16,8 @@ namespace ScssPhp\ScssPhp;
|
||||
* Base node
|
||||
*
|
||||
* @author Anthon Pang <anthon.pang@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class Node
|
||||
{
|
||||
@@ -24,17 +27,17 @@ abstract class Node
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
public $sourceIndex;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
* @var int|null
|
||||
*/
|
||||
public $sourceLine;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
* @var int|null
|
||||
*/
|
||||
public $sourceColumn;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -11,9 +12,13 @@
|
||||
|
||||
namespace ScssPhp\ScssPhp\Node;
|
||||
|
||||
use ScssPhp\ScssPhp\Base\Range;
|
||||
use ScssPhp\ScssPhp\Compiler;
|
||||
use ScssPhp\ScssPhp\Exception\RangeException;
|
||||
use ScssPhp\ScssPhp\Exception\SassScriptException;
|
||||
use ScssPhp\ScssPhp\Node;
|
||||
use ScssPhp\ScssPhp\Type;
|
||||
use ScssPhp\ScssPhp\Util;
|
||||
|
||||
/**
|
||||
* Dimension + optional units
|
||||
@@ -25,20 +30,26 @@ use ScssPhp\ScssPhp\Type;
|
||||
* }}
|
||||
*
|
||||
* @author Anthon Pang <anthon.pang@gmail.com>
|
||||
*
|
||||
* @template-implements \ArrayAccess<int, mixed>
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
static public $precision = 10;
|
||||
public static $precision = self::PRECISION;
|
||||
|
||||
/**
|
||||
* @see http://www.w3.org/TR/2012/WD-css3-values-20120308/
|
||||
*
|
||||
* @var array
|
||||
* @phpstan-var array<string, array<string, float|int>>
|
||||
*/
|
||||
static protected $unitTable = [
|
||||
protected static $unitTable = [
|
||||
'in' => [
|
||||
'in' => 1,
|
||||
'pc' => 6,
|
||||
@@ -64,91 +75,93 @@ class Number extends Node implements \ArrayAccess
|
||||
],
|
||||
'dpi' => [
|
||||
'dpi' => 1,
|
||||
'dpcm' => 2.54,
|
||||
'dppx' => 96,
|
||||
'dpcm' => 1 / 2.54,
|
||||
'dppx' => 1 / 96,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* @var integer|float
|
||||
* @var int|float
|
||||
*/
|
||||
public $dimension;
|
||||
private $dimension;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @var string[]
|
||||
* @phpstan-var list<string>
|
||||
*/
|
||||
public $units;
|
||||
private $numeratorUnits;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var list<string>
|
||||
*/
|
||||
private $denominatorUnits;
|
||||
|
||||
/**
|
||||
* Initialize number
|
||||
*
|
||||
* @param mixed $dimension
|
||||
* @param mixed $initialUnit
|
||||
* @param int|float $dimension
|
||||
* @param string[]|string $numeratorUnits
|
||||
* @param string[] $denominatorUnits
|
||||
*
|
||||
* @phpstan-param list<string>|string $numeratorUnits
|
||||
* @phpstan-param list<string> $denominatorUnits
|
||||
*/
|
||||
public function __construct($dimension, $initialUnit)
|
||||
public function __construct($dimension, $numeratorUnits, array $denominatorUnits = [])
|
||||
{
|
||||
$this->type = Type::T_NUMBER;
|
||||
if (is_string($numeratorUnits)) {
|
||||
$numeratorUnits = $numeratorUnits ? [$numeratorUnits] : [];
|
||||
} elseif (isset($numeratorUnits['numerator_units'], $numeratorUnits['denominator_units'])) {
|
||||
// TODO get rid of this once `$number[2]` is not used anymore
|
||||
$denominatorUnits = $numeratorUnits['denominator_units'];
|
||||
$numeratorUnits = $numeratorUnits['numerator_units'];
|
||||
}
|
||||
|
||||
$this->dimension = $dimension;
|
||||
$this->units = is_array($initialUnit)
|
||||
? $initialUnit
|
||||
: ($initialUnit ? [$initialUnit => 1]
|
||||
: []);
|
||||
$this->numeratorUnits = $numeratorUnits;
|
||||
$this->denominatorUnits = $denominatorUnits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Coerce number to target units
|
||||
*
|
||||
* @param array $units
|
||||
*
|
||||
* @return \ScssPhp\ScssPhp\Node\Number
|
||||
* @return float|int
|
||||
*/
|
||||
public function coerce($units)
|
||||
public function getDimension()
|
||||
{
|
||||
if ($this->unitless()) {
|
||||
return new Number($this->dimension, $units);
|
||||
}
|
||||
|
||||
$dimension = $this->dimension;
|
||||
|
||||
foreach (static::$unitTable['in'] as $unit => $conv) {
|
||||
$from = isset($this->units[$unit]) ? $this->units[$unit] : 0;
|
||||
$to = isset($units[$unit]) ? $units[$unit] : 0;
|
||||
$factor = pow($conv, $from - $to);
|
||||
$dimension /= $factor;
|
||||
}
|
||||
|
||||
return new Number($dimension, $units);
|
||||
return $this->dimension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize number
|
||||
*
|
||||
* @return \ScssPhp\ScssPhp\Node\Number
|
||||
* @return string[]
|
||||
*/
|
||||
public function normalize()
|
||||
public function getNumeratorUnits()
|
||||
{
|
||||
$dimension = $this->dimension;
|
||||
$units = [];
|
||||
|
||||
$this->normalizeUnits($dimension, $units, 'in');
|
||||
|
||||
return new Number($dimension, $units);
|
||||
return $this->numeratorUnits;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return string[]
|
||||
*/
|
||||
public function getDenominatorUnits()
|
||||
{
|
||||
return $this->denominatorUnits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
if ($offset === -3) {
|
||||
return ! is_null($this->sourceColumn);
|
||||
return ! \is_null($this->sourceColumn);
|
||||
}
|
||||
|
||||
if ($offset === -2) {
|
||||
return ! is_null($this->sourceLine);
|
||||
return ! \is_null($this->sourceLine);
|
||||
}
|
||||
|
||||
if ($offset === -1 ||
|
||||
if (
|
||||
$offset === -1 ||
|
||||
$offset === 0 ||
|
||||
$offset === 1 ||
|
||||
$offset === 2
|
||||
@@ -160,8 +173,9 @@ class Number extends Node implements \ArrayAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
switch ($offset) {
|
||||
@@ -175,60 +189,54 @@ class Number extends Node implements \ArrayAccess
|
||||
return $this->sourceIndex;
|
||||
|
||||
case 0:
|
||||
return $this->type;
|
||||
return Type::T_NUMBER;
|
||||
|
||||
case 1:
|
||||
return $this->dimension;
|
||||
|
||||
case 2:
|
||||
return $this->units;
|
||||
return array('numerator_units' => $this->numeratorUnits, 'denominator_units' => $this->denominatorUnits);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if ($offset === 1) {
|
||||
$this->dimension = $value;
|
||||
} elseif ($offset === 2) {
|
||||
$this->units = $value;
|
||||
} elseif ($offset == -1) {
|
||||
$this->sourceIndex = $value;
|
||||
} elseif ($offset == -2) {
|
||||
$this->sourceLine = $value;
|
||||
} elseif ($offset == -3) {
|
||||
$this->sourceColumn = $value;
|
||||
}
|
||||
throw new \BadMethodCallException('Number is immutable');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
if ($offset === 1) {
|
||||
$this->dimension = null;
|
||||
} elseif ($offset === 2) {
|
||||
$this->units = null;
|
||||
} elseif ($offset === -1) {
|
||||
$this->sourceIndex = null;
|
||||
} elseif ($offset === -2) {
|
||||
$this->sourceLine = null;
|
||||
} elseif ($offset === -3) {
|
||||
$this->sourceColumn = null;
|
||||
}
|
||||
throw new \BadMethodCallException('Number is immutable');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the number is unitless
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
public function unitless()
|
||||
{
|
||||
return ! array_sum($this->units);
|
||||
return \count($this->numeratorUnits) === 0 && \count($this->denominatorUnits) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the number has exactly this unit
|
||||
*
|
||||
* @param string $unit
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasUnit($unit)
|
||||
{
|
||||
return \count($this->numeratorUnits) === 1 && \count($this->denominatorUnits) === 0 && $this->numeratorUnits[0] === $unit;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -238,22 +246,289 @@ class Number extends Node implements \ArrayAccess
|
||||
*/
|
||||
public function unitStr()
|
||||
{
|
||||
$numerators = [];
|
||||
$denominators = [];
|
||||
|
||||
foreach ($this->units as $unit => $unitSize) {
|
||||
if ($unitSize > 0) {
|
||||
$numerators = array_pad($numerators, count($numerators) + $unitSize, $unit);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($unitSize < 0) {
|
||||
$denominators = array_pad($denominators, count($denominators) + $unitSize, $unit);
|
||||
continue;
|
||||
}
|
||||
if ($this->unitless()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return implode('*', $numerators) . (count($denominators) ? '/' . implode('*', $denominators) : '');
|
||||
return self::getUnitString($this->numeratorUnits, $this->denominatorUnits);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float|int $min
|
||||
* @param float|int $max
|
||||
* @param string|null $name
|
||||
*
|
||||
* @return float|int
|
||||
* @throws SassScriptException
|
||||
*/
|
||||
public function valueInRange($min, $max, $name = null)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $varName
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function assertNoUnits($varName = null)
|
||||
{
|
||||
if ($this->unitless()) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw SassScriptException::forArgument(sprintf('Expected %s to have no units.', $this), $varName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $unit
|
||||
* @param string|null $varName
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function assertUnit($unit, $varName = null)
|
||||
{
|
||||
if ($this->hasUnit($unit)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw SassScriptException::forArgument(sprintf('Expected %s to have unit "%s".', $this, $unit), $varName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Number $other
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function assertSameUnitOrUnitless(Number $other)
|
||||
{
|
||||
if ($other->unitless()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->numeratorUnits === $other->numeratorUnits && $this->denominatorUnits === $other->denominatorUnits) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new SassScriptException(sprintf(
|
||||
'Incompatible units %s and %s.',
|
||||
self::getUnitString($this->numeratorUnits, $this->denominatorUnits),
|
||||
self::getUnitString($other->numeratorUnits, $other->denominatorUnits)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this number, converted to the units represented by $newNumeratorUnits and $newDenominatorUnits.
|
||||
*
|
||||
* This does not throw an error if this number is unitless and
|
||||
* $newNumeratorUnits/$newDenominatorUnits are not empty, or vice versa. Instead,
|
||||
* it treats all unitless numbers as convertible to and from all units without
|
||||
* changing the value.
|
||||
*
|
||||
* @param string[] $newNumeratorUnits
|
||||
* @param string[] $newDenominatorUnits
|
||||
*
|
||||
* @return Number
|
||||
*
|
||||
* @phpstan-param list<string> $newNumeratorUnits
|
||||
* @phpstan-param list<string> $newDenominatorUnits
|
||||
*
|
||||
* @throws SassScriptException if this number's units are not compatible with $newNumeratorUnits and $newDenominatorUnits
|
||||
*/
|
||||
public function coerce(array $newNumeratorUnits, array $newDenominatorUnits)
|
||||
{
|
||||
return new Number($this->valueInUnits($newNumeratorUnits, $newDenominatorUnits), $newNumeratorUnits, $newDenominatorUnits);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Number $other
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isComparableTo(Number $other)
|
||||
{
|
||||
if ($this->unitless() || $other->unitless()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->greaterThan($other);
|
||||
return true;
|
||||
} catch (SassScriptException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Number $other
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function lessThan(Number $other)
|
||||
{
|
||||
return $this->coerceUnits($other, function ($num1, $num2) {
|
||||
return $num1 < $num2;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Number $other
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function lessThanOrEqual(Number $other)
|
||||
{
|
||||
return $this->coerceUnits($other, function ($num1, $num2) {
|
||||
return $num1 <= $num2;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Number $other
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function greaterThan(Number $other)
|
||||
{
|
||||
return $this->coerceUnits($other, function ($num1, $num2) {
|
||||
return $num1 > $num2;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Number $other
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function greaterThanOrEqual(Number $other)
|
||||
{
|
||||
return $this->coerceUnits($other, function ($num1, $num2) {
|
||||
return $num1 >= $num2;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Number $other
|
||||
*
|
||||
* @return Number
|
||||
*/
|
||||
public function plus(Number $other)
|
||||
{
|
||||
return $this->coerceNumber($other, function ($num1, $num2) {
|
||||
return $num1 + $num2;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Number $other
|
||||
*
|
||||
* @return Number
|
||||
*/
|
||||
public function minus(Number $other)
|
||||
{
|
||||
return $this->coerceNumber($other, function ($num1, $num2) {
|
||||
return $num1 - $num2;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Number
|
||||
*/
|
||||
public function unaryMinus()
|
||||
{
|
||||
return new Number(-$this->dimension, $this->numeratorUnits, $this->denominatorUnits);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Number $other
|
||||
*
|
||||
* @return Number
|
||||
*/
|
||||
public function modulo(Number $other)
|
||||
{
|
||||
return $this->coerceNumber($other, function ($num1, $num2) {
|
||||
if ($num2 == 0) {
|
||||
return NAN;
|
||||
}
|
||||
|
||||
$result = fmod($num1, $num2);
|
||||
|
||||
if ($result == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($num2 < 0 xor $num1 < 0) {
|
||||
$result += $num2;
|
||||
}
|
||||
|
||||
return $result;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Number $other
|
||||
*
|
||||
* @return Number
|
||||
*/
|
||||
public function times(Number $other)
|
||||
{
|
||||
return $this->multiplyUnits($this->dimension * $other->dimension, $this->numeratorUnits, $this->denominatorUnits, $other->numeratorUnits, $other->denominatorUnits);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Number $other
|
||||
*
|
||||
* @return Number
|
||||
*/
|
||||
public function dividedBy(Number $other)
|
||||
{
|
||||
if ($other->dimension == 0) {
|
||||
if ($this->dimension == 0) {
|
||||
$value = NAN;
|
||||
} elseif ($this->dimension > 0) {
|
||||
$value = INF;
|
||||
} else {
|
||||
$value = -INF;
|
||||
}
|
||||
} else {
|
||||
$value = $this->dimension / $other->dimension;
|
||||
}
|
||||
|
||||
return $this->multiplyUnits($value, $this->numeratorUnits, $this->denominatorUnits, $other->denominatorUnits, $other->numeratorUnits);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Number $other
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function equals(Number $other)
|
||||
{
|
||||
// Unitless numbers are convertable to unit numbers, but not equal, so we special-case unitless here.
|
||||
if ($this->unitless() !== $other->unitless()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// In Sass, neither NaN nor Infinity are equal to themselves, while PHP defines INF==INF
|
||||
if (is_nan($this->dimension) || is_nan($other->dimension) || !is_finite($this->dimension) || !is_finite($other->dimension)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->unitless()) {
|
||||
return round($this->dimension, self::PRECISION) == round($other->dimension, self::PRECISION);
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->coerceUnits($other, function ($num1, $num2) {
|
||||
return round($num1,self::PRECISION) == round($num2, self::PRECISION);
|
||||
});
|
||||
} catch (SassScriptException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,35 +540,31 @@ class Number extends Node implements \ArrayAccess
|
||||
*/
|
||||
public function output(Compiler $compiler = null)
|
||||
{
|
||||
$dimension = round($this->dimension, static::$precision);
|
||||
$dimension = round($this->dimension, self::PRECISION);
|
||||
|
||||
$units = array_filter($this->units, function ($unitSize) {
|
||||
return $unitSize;
|
||||
});
|
||||
|
||||
if (count($units) > 1 && array_sum($units) === 0) {
|
||||
$dimension = $this->dimension;
|
||||
$units = [];
|
||||
|
||||
$this->normalizeUnits($dimension, $units, 'in');
|
||||
|
||||
$dimension = round($dimension, static::$precision);
|
||||
$units = array_filter($units, function ($unitSize) {
|
||||
return $unitSize;
|
||||
});
|
||||
if (is_nan($dimension)) {
|
||||
return 'NaN';
|
||||
}
|
||||
|
||||
$unitSize = array_sum($units);
|
||||
|
||||
if ($compiler && ($unitSize > 1 || $unitSize < 0 || count($units) > 1)) {
|
||||
$compiler->throwError((string) $dimension . $this->unitStr() . " isn't a valid CSS value.");
|
||||
if ($dimension === INF) {
|
||||
return 'Infinity';
|
||||
}
|
||||
|
||||
reset($units);
|
||||
$unit = key($units);
|
||||
$dimension = number_format($dimension, static::$precision, '.', '');
|
||||
if ($dimension === -INF) {
|
||||
return '-Infinity';
|
||||
}
|
||||
|
||||
return (static::$precision ? rtrim(rtrim($dimension, '0'), '.') : $dimension) . $unit;
|
||||
if ($compiler) {
|
||||
$unit = $this->unitStr();
|
||||
} elseif (isset($this->numeratorUnits[0])) {
|
||||
$unit = $this->numeratorUnits[0];
|
||||
} else {
|
||||
$unit = '';
|
||||
}
|
||||
|
||||
$dimension = number_format($dimension, self::PRECISION, '.', '');
|
||||
|
||||
return rtrim(rtrim($dimension, '0'), '.') . $unit;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -305,26 +576,229 @@ class Number extends Node implements \ArrayAccess
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize units
|
||||
* @param Number $other
|
||||
* @param callable $operation
|
||||
*
|
||||
* @param integer|float $dimension
|
||||
* @param array $units
|
||||
* @param string $baseUnit
|
||||
* @return Number
|
||||
*
|
||||
* @phpstan-param callable(int|float, int|float): (int|float) $operation
|
||||
*/
|
||||
private function normalizeUnits(&$dimension, &$units, $baseUnit = 'in')
|
||||
private function coerceNumber(Number $other, $operation)
|
||||
{
|
||||
$dimension = $this->dimension;
|
||||
$units = [];
|
||||
$result = $this->coerceUnits($other, $operation);
|
||||
|
||||
foreach ($this->units as $unit => $exp) {
|
||||
if (isset(static::$unitTable[$baseUnit][$unit])) {
|
||||
$factor = pow(static::$unitTable[$baseUnit][$unit], $exp);
|
||||
if (!$this->unitless()) {
|
||||
return new Number($result, $this->numeratorUnits, $this->denominatorUnits);
|
||||
}
|
||||
|
||||
$unit = $baseUnit;
|
||||
$dimension /= $factor;
|
||||
return new Number($result, $other->numeratorUnits, $other->denominatorUnits);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Number $other
|
||||
* @param callable $operation
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @phpstan-template T
|
||||
* @phpstan-param callable(int|float, int|float): T $operation
|
||||
* @phpstan-return T
|
||||
*/
|
||||
private function coerceUnits(Number $other, $operation)
|
||||
{
|
||||
if (!$this->unitless()) {
|
||||
$num1 = $this->dimension;
|
||||
$num2 = $other->valueInUnits($this->numeratorUnits, $this->denominatorUnits);
|
||||
} else {
|
||||
$num1 = $this->valueInUnits($other->numeratorUnits, $other->denominatorUnits);
|
||||
$num2 = $other->dimension;
|
||||
}
|
||||
|
||||
return \call_user_func($operation, $num1, $num2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $numeratorUnits
|
||||
* @param string[] $denominatorUnits
|
||||
*
|
||||
* @return int|float
|
||||
*
|
||||
* @phpstan-param list<string> $numeratorUnits
|
||||
* @phpstan-param list<string> $denominatorUnits
|
||||
*
|
||||
* @throws SassScriptException if this number's units are not compatible with $numeratorUnits and $denominatorUnits
|
||||
*/
|
||||
private function valueInUnits(array $numeratorUnits, array $denominatorUnits)
|
||||
{
|
||||
if (
|
||||
$this->unitless()
|
||||
|| (\count($numeratorUnits) === 0 && \count($denominatorUnits) === 0)
|
||||
|| ($this->numeratorUnits === $numeratorUnits && $this->denominatorUnits === $denominatorUnits)
|
||||
) {
|
||||
return $this->dimension;
|
||||
}
|
||||
|
||||
$value = $this->dimension;
|
||||
$oldNumerators = $this->numeratorUnits;
|
||||
|
||||
foreach ($numeratorUnits as $newNumerator) {
|
||||
foreach ($oldNumerators as $key => $oldNumerator) {
|
||||
$conversionFactor = self::getConversionFactor($newNumerator, $oldNumerator);
|
||||
|
||||
if (\is_null($conversionFactor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value *= $conversionFactor;
|
||||
unset($oldNumerators[$key]);
|
||||
continue 2;
|
||||
}
|
||||
|
||||
$units[$unit] = $exp + (isset($units[$unit]) ? $units[$unit] : 0);
|
||||
throw new SassScriptException(sprintf(
|
||||
'Incompatible units %s and %s.',
|
||||
self::getUnitString($this->numeratorUnits, $this->denominatorUnits),
|
||||
self::getUnitString($numeratorUnits, $denominatorUnits)
|
||||
));
|
||||
}
|
||||
|
||||
$oldDenominators = $this->denominatorUnits;
|
||||
|
||||
foreach ($denominatorUnits as $newDenominator) {
|
||||
foreach ($oldDenominators as $key => $oldDenominator) {
|
||||
$conversionFactor = self::getConversionFactor($newDenominator, $oldDenominator);
|
||||
|
||||
if (\is_null($conversionFactor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value /= $conversionFactor;
|
||||
unset($oldDenominators[$key]);
|
||||
continue 2;
|
||||
}
|
||||
|
||||
throw new SassScriptException(sprintf(
|
||||
'Incompatible units %s and %s.',
|
||||
self::getUnitString($this->numeratorUnits, $this->denominatorUnits),
|
||||
self::getUnitString($numeratorUnits, $denominatorUnits)
|
||||
));
|
||||
}
|
||||
|
||||
if (\count($oldNumerators) || \count($oldDenominators)) {
|
||||
throw new SassScriptException(sprintf(
|
||||
'Incompatible units %s and %s.',
|
||||
self::getUnitString($this->numeratorUnits, $this->denominatorUnits),
|
||||
self::getUnitString($numeratorUnits, $denominatorUnits)
|
||||
));
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|float $value
|
||||
* @param string[] $numerators1
|
||||
* @param string[] $denominators1
|
||||
* @param string[] $numerators2
|
||||
* @param string[] $denominators2
|
||||
*
|
||||
* @return Number
|
||||
*
|
||||
* @phpstan-param list<string> $numerators1
|
||||
* @phpstan-param list<string> $denominators1
|
||||
* @phpstan-param list<string> $numerators2
|
||||
* @phpstan-param list<string> $denominators2
|
||||
*/
|
||||
private function multiplyUnits($value, array $numerators1, array $denominators1, array $numerators2, array $denominators2)
|
||||
{
|
||||
$newNumerators = array();
|
||||
|
||||
foreach ($numerators1 as $numerator) {
|
||||
foreach ($denominators2 as $key => $denominator) {
|
||||
$conversionFactor = self::getConversionFactor($numerator, $denominator);
|
||||
|
||||
if (\is_null($conversionFactor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value /= $conversionFactor;
|
||||
unset($denominators2[$key]);
|
||||
continue 2;
|
||||
}
|
||||
|
||||
$newNumerators[] = $numerator;
|
||||
}
|
||||
|
||||
foreach ($numerators2 as $numerator) {
|
||||
foreach ($denominators1 as $key => $denominator) {
|
||||
$conversionFactor = self::getConversionFactor($numerator, $denominator);
|
||||
|
||||
if (\is_null($conversionFactor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value /= $conversionFactor;
|
||||
unset($denominators1[$key]);
|
||||
continue 2;
|
||||
}
|
||||
|
||||
$newNumerators[] = $numerator;
|
||||
}
|
||||
|
||||
$newDenominators = array_values(array_merge($denominators1, $denominators2));
|
||||
|
||||
return new Number($value, $newNumerators, $newDenominators);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of [unit1]s per [unit2].
|
||||
*
|
||||
* Equivalently, `1unit1 * conversionFactor(unit1, unit2) = 1unit2`.
|
||||
*
|
||||
* @param string $unit1
|
||||
* @param string $unit2
|
||||
*
|
||||
* @return float|int|null
|
||||
*/
|
||||
private static function getConversionFactor($unit1, $unit2)
|
||||
{
|
||||
if ($unit1 === $unit2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
foreach (static::$unitTable as $unitVariants) {
|
||||
if (isset($unitVariants[$unit1]) && isset($unitVariants[$unit2])) {
|
||||
return $unitVariants[$unit1] / $unitVariants[$unit2];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unit(s) as the product of numerator units divided by the product of denominator units
|
||||
*
|
||||
* @param string[] $numerators
|
||||
* @param string[] $denominators
|
||||
*
|
||||
* @phpstan-param list<string> $numerators
|
||||
* @phpstan-param list<string> $denominators
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function getUnitString(array $numerators, array $denominators)
|
||||
{
|
||||
if (!\count($numerators)) {
|
||||
if (\count($denominators) === 0) {
|
||||
return 'no units';
|
||||
}
|
||||
|
||||
if (\count($denominators) === 1) {
|
||||
return $denominators[0] . '^-1';
|
||||
}
|
||||
|
||||
return '(' . implode('*', $denominators) . ')^-1';
|
||||
}
|
||||
|
||||
return implode('*', $numerators) . (\count($denominators) ? '/' . implode('*', $denominators) : '');
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -15,11 +16,13 @@ namespace ScssPhp\ScssPhp\SourceMap;
|
||||
* Base 64 Encode/Decode
|
||||
*
|
||||
* @author Anthon Pang <anthon.pang@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Base64
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private static $encodingMap = [
|
||||
0 => 'A',
|
||||
@@ -89,7 +92,7 @@ class Base64
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @var array<string|int, int>
|
||||
*/
|
||||
private static $decodingMap = [
|
||||
'A' => 0,
|
||||
@@ -161,7 +164,7 @@ class Base64
|
||||
/**
|
||||
* Convert to base64
|
||||
*
|
||||
* @param integer $value
|
||||
* @param int $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@@ -175,7 +178,7 @@ class Base64
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return integer
|
||||
* @return int
|
||||
*/
|
||||
public static function decode($value)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -11,8 +12,6 @@
|
||||
|
||||
namespace ScssPhp\ScssPhp\SourceMap;
|
||||
|
||||
use ScssPhp\ScssPhp\SourceMap\Base64;
|
||||
|
||||
/**
|
||||
* Base 64 VLQ
|
||||
*
|
||||
@@ -35,6 +34,8 @@ use ScssPhp\ScssPhp\SourceMap\Base64;
|
||||
*
|
||||
* @author John Lenz <johnlenz@google.com>
|
||||
* @author Anthon Pang <anthon.pang@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Base64VLQ
|
||||
{
|
||||
@@ -50,7 +51,7 @@ class Base64VLQ
|
||||
/**
|
||||
* Returns the VLQ encoded value.
|
||||
*
|
||||
* @param integer $value
|
||||
* @param int $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@@ -61,7 +62,9 @@ class Base64VLQ
|
||||
|
||||
do {
|
||||
$digit = $vlq & self::VLQ_BASE_MASK;
|
||||
$vlq >>= self::VLQ_BASE_SHIFT;
|
||||
|
||||
//$vlq >>>= self::VLQ_BASE_SHIFT; // unsigned right shift
|
||||
$vlq = (($vlq >> 1) & PHP_INT_MAX) >> (self::VLQ_BASE_SHIFT - 1);
|
||||
|
||||
if ($vlq > 0) {
|
||||
$digit |= self::VLQ_CONTINUATION_BIT;
|
||||
@@ -77,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)
|
||||
{
|
||||
@@ -104,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)
|
||||
{
|
||||
@@ -123,14 +126,16 @@ 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)
|
||||
{
|
||||
$negate = ($value & 1) === 1;
|
||||
$value = ($value >> 1) & ~(1<<(8 * PHP_INT_SIZE - 1)); // unsigned right shift
|
||||
|
||||
//$value >>>= 1; // unsigned right shift
|
||||
$value = ($value >> 1) & PHP_INT_MAX;
|
||||
|
||||
if (! $negate) {
|
||||
return $value;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -20,6 +21,8 @@ use ScssPhp\ScssPhp\Exception\CompilerException;
|
||||
*
|
||||
* @author Josh Schmidt <oyejorge@gmail.com>
|
||||
* @author Nicolas FRANÇOIS <nicolas.francois@frog-labs.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class SourceMapGenerator
|
||||
{
|
||||
@@ -32,6 +35,7 @@ class SourceMapGenerator
|
||||
* Array of default options
|
||||
*
|
||||
* @var array
|
||||
* @phpstan-var array{sourceRoot: string, sourceMapFilename: string|null, sourceMapURL: string|null, sourceMapWriteTo: string|null, outputSourceFiles: bool, sourceMapRootpath: string, sourceMapBasepath: string}
|
||||
*/
|
||||
protected $defaultOptions = [
|
||||
// an optional source root, useful for relocating source files
|
||||
@@ -69,6 +73,7 @@ class SourceMapGenerator
|
||||
* Array of mappings
|
||||
*
|
||||
* @var array
|
||||
* @phpstan-var list<array{generated_line: int, generated_column: int, original_line: int, original_column: int, source_file: string}>
|
||||
*/
|
||||
protected $mappings = [];
|
||||
|
||||
@@ -82,30 +87,40 @@ class SourceMapGenerator
|
||||
/**
|
||||
* File to content map
|
||||
*
|
||||
* @var array
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $sources = [];
|
||||
|
||||
/**
|
||||
* @var array<string, int>
|
||||
*/
|
||||
protected $sourceKeys = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @phpstan-var array{sourceRoot: string, sourceMapFilename: string|null, sourceMapURL: string|null, sourceMapWriteTo: string|null, outputSourceFiles: bool, sourceMapRootpath: string, sourceMapBasepath: string}
|
||||
*/
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* @phpstan-param array{sourceRoot?: string, sourceMapFilename?: string|null, sourceMapURL?: string|null, sourceMapWriteTo?: string|null, outputSourceFiles?: bool, sourceMapRootpath?: string, sourceMapBasepath?: string} $options
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $sourceFile)
|
||||
{
|
||||
@@ -125,14 +140,16 @@ 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
|
||||
*/
|
||||
public function saveMap($content)
|
||||
{
|
||||
$file = $this->options['sourceMapWriteTo'];
|
||||
$dir = dirname($file);
|
||||
assert($file !== null);
|
||||
$dir = \dirname($file);
|
||||
|
||||
// directory does not exist
|
||||
if (! is_dir($dir)) {
|
||||
@@ -153,14 +170,16 @@ class SourceMapGenerator
|
||||
/**
|
||||
* Generates the JSON source map
|
||||
*
|
||||
* @param string $prefix A prefix added in the output file, which needs to shift mappings
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#
|
||||
*/
|
||||
public function generateJson()
|
||||
public function generateJson($prefix = '')
|
||||
{
|
||||
$sourceMap = [];
|
||||
$mappings = $this->generateMappings();
|
||||
$mappings = $this->generateMappings($prefix);
|
||||
|
||||
// File version (always the first entry in the object) and must be a positive integer.
|
||||
$sourceMap['version'] = self::VERSION;
|
||||
@@ -183,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);
|
||||
}
|
||||
|
||||
@@ -201,17 +220,25 @@ class SourceMapGenerator
|
||||
}
|
||||
|
||||
// less.js compat fixes
|
||||
if (count($sourceMap['sources']) && empty($sourceMap['sourceRoot'])) {
|
||||
if (\count($sourceMap['sources']) && empty($sourceMap['sourceRoot'])) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sources contents
|
||||
*
|
||||
* @return array|null
|
||||
* @return string[]|null
|
||||
*/
|
||||
protected function getSourcesContent()
|
||||
{
|
||||
@@ -231,14 +258,21 @@ class SourceMapGenerator
|
||||
/**
|
||||
* Generates the mappings string
|
||||
*
|
||||
* @param string $prefix A prefix added in the output file, which needs to shift mappings
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateMappings()
|
||||
public function generateMappings($prefix = '')
|
||||
{
|
||||
if (! count($this->mappings)) {
|
||||
if (! \count($this->mappings)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$prefixLines = substr_count($prefix, "\n");
|
||||
$lastPrefixNewLine = strrpos($prefix, "\n");
|
||||
$lastPrefixLineStart = false === $lastPrefixNewLine ? 0 : $lastPrefixNewLine + 1;
|
||||
$prefixColumn = strlen($prefix) - $lastPrefixLineStart;
|
||||
|
||||
$this->sourceKeys = array_flip(array_keys($this->sources));
|
||||
|
||||
// group mappings by generated line number.
|
||||
@@ -249,9 +283,16 @@ class SourceMapGenerator
|
||||
}
|
||||
|
||||
ksort($groupedMap);
|
||||
|
||||
$lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0;
|
||||
|
||||
foreach ($groupedMap as $lineNumber => $lineMap) {
|
||||
if ($lineNumber > 1) {
|
||||
// The prefix only impacts the column for the first line of the original output
|
||||
$prefixColumn = 0;
|
||||
}
|
||||
$lineNumber += $prefixLines;
|
||||
|
||||
while (++$lastGeneratedLine < $lineNumber) {
|
||||
$groupedMapEncoded[] = ';';
|
||||
}
|
||||
@@ -260,8 +301,10 @@ class SourceMapGenerator
|
||||
$lastGeneratedColumn = 0;
|
||||
|
||||
foreach ($lineMap as $m) {
|
||||
$mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn);
|
||||
$lastGeneratedColumn = $m['generated_column'];
|
||||
$generatedColumn = $m['generated_column'] + $prefixColumn;
|
||||
|
||||
$mapEncoded = $this->encoder->encode($generatedColumn - $lastGeneratedColumn);
|
||||
$lastGeneratedColumn = $generatedColumn;
|
||||
|
||||
// find the index
|
||||
if ($m['source_file']) {
|
||||
@@ -292,7 +335,7 @@ class SourceMapGenerator
|
||||
*
|
||||
* @param string $filename
|
||||
*
|
||||
* @return integer|false
|
||||
* @return int|false
|
||||
*/
|
||||
protected function findFileIndex($filename)
|
||||
{
|
||||
@@ -313,8 +356,8 @@ class SourceMapGenerator
|
||||
$basePath = $this->options['sourceMapBasepath'];
|
||||
|
||||
// "Trim" the 'sourceMapBasepath' from the output filename.
|
||||
if (strlen($basePath) && strpos($filename, $basePath) === 0) {
|
||||
$filename = substr($filename, strlen($basePath));
|
||||
if (\strlen($basePath) && strpos($filename, $basePath) === 0) {
|
||||
$filename = substr($filename, \strlen($basePath));
|
||||
}
|
||||
|
||||
// Remove extra leading path separators.
|
||||
@@ -328,8 +371,8 @@ class SourceMapGenerator
|
||||
/**
|
||||
* Fix windows paths
|
||||
*
|
||||
* @param string $path
|
||||
* @param boolean $addEndSlash
|
||||
* @param string $path
|
||||
* @param bool $addEndSlash
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -18,52 +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
|
||||
* @internal
|
||||
*/
|
||||
const T_BREAK = 'break';
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const T_CHARSET = 'charset';
|
||||
const T_COLOR = 'color';
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const T_COMMENT = 'comment';
|
||||
/**
|
||||
* @deprecated
|
||||
* @internal
|
||||
*/
|
||||
const T_CONTINUE = 'continue';
|
||||
/**
|
||||
* @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';
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -13,11 +14,14 @@ namespace ScssPhp\ScssPhp;
|
||||
|
||||
use ScssPhp\ScssPhp\Base\Range;
|
||||
use ScssPhp\ScssPhp\Exception\RangeException;
|
||||
use ScssPhp\ScssPhp\Node\Number;
|
||||
|
||||
/**
|
||||
* Utilty functions
|
||||
* Utility functions
|
||||
*
|
||||
* @author Anthon Pang <anthon.pang@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Util
|
||||
{
|
||||
@@ -25,10 +29,10 @@ class Util
|
||||
* Asserts that `value` falls within `range` (inclusive), leaving
|
||||
* room for slight floating-point errors.
|
||||
*
|
||||
* @param string $name The name of the value. Used in the error message.
|
||||
* @param \ScssPhp\ScssPhp\Base\Range $range Range of values.
|
||||
* @param array $value The value to check.
|
||||
* @param string $unit The unit of the value. Used in error reporting.
|
||||
* @param string $name The name of the value. Used in the error message.
|
||||
* @param Range $range Range of values.
|
||||
* @param array|Number $value The value to check.
|
||||
* @param string $unit The unit of the value. Used in error reporting.
|
||||
*
|
||||
* @return mixed `value` adjusted to fall within range, if it was outside by a floating-point margin.
|
||||
*
|
||||
@@ -39,6 +43,10 @@ class Util
|
||||
$val = $value[1];
|
||||
$grace = new Range(-0.00001, 0.00001);
|
||||
|
||||
if (! \is_numeric($val)) {
|
||||
throw new RangeException("$name {$val} is not a number.");
|
||||
}
|
||||
|
||||
if ($range->includes($val)) {
|
||||
return $val;
|
||||
}
|
||||
@@ -67,4 +75,110 @@ class Util
|
||||
|
||||
return strtr(rawurlencode($string), $revert);
|
||||
}
|
||||
|
||||
/**
|
||||
* mb_chr() wrapper
|
||||
*
|
||||
* @param int $code
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function mbChr($code)
|
||||
{
|
||||
// Use the native implementation if available, but not on PHP 7.2 as mb_chr(0) is buggy there
|
||||
if (\PHP_VERSION_ID > 70300 && \function_exists('mb_chr')) {
|
||||
return mb_chr($code, 'UTF-8');
|
||||
}
|
||||
|
||||
if (0x80 > $code %= 0x200000) {
|
||||
$s = \chr($code);
|
||||
} elseif (0x800 > $code) {
|
||||
$s = \chr(0xC0 | $code >> 6) . \chr(0x80 | $code & 0x3F);
|
||||
} elseif (0x10000 > $code) {
|
||||
$s = \chr(0xE0 | $code >> 12) . \chr(0x80 | $code >> 6 & 0x3F) . \chr(0x80 | $code & 0x3F);
|
||||
} else {
|
||||
$s = \chr(0xF0 | $code >> 18) . \chr(0x80 | $code >> 12 & 0x3F)
|
||||
. \chr(0x80 | $code >> 6 & 0x3F) . \chr(0x80 | $code & 0x3F);
|
||||
}
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* mb_strlen() wrapper
|
||||
*
|
||||
* @param string $string
|
||||
* @return int
|
||||
*/
|
||||
public static function mbStrlen($string)
|
||||
{
|
||||
// Use the native implementation if available.
|
||||
if (\function_exists('mb_strlen')) {
|
||||
return mb_strlen($string, 'UTF-8');
|
||||
}
|
||||
|
||||
if (\function_exists('iconv_strlen')) {
|
||||
return (int) @iconv_strlen($string, 'UTF-8');
|
||||
}
|
||||
|
||||
throw new \LogicException('Either mbstring (recommended) or iconv is necessary to use Scssphp.');
|
||||
}
|
||||
|
||||
/**
|
||||
* mb_substr() wrapper
|
||||
* @param string $string
|
||||
* @param int $start
|
||||
* @param null|int $length
|
||||
* @return string
|
||||
*/
|
||||
public static function mbSubstr($string, $start, $length = null)
|
||||
{
|
||||
// Use the native implementation if available.
|
||||
if (\function_exists('mb_substr')) {
|
||||
return mb_substr($string, $start, $length, 'UTF-8');
|
||||
}
|
||||
|
||||
if (\function_exists('iconv_substr')) {
|
||||
if ($start < 0) {
|
||||
$start = static::mbStrlen($string) + $start;
|
||||
if ($start < 0) {
|
||||
$start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $length) {
|
||||
$length = 2147483647;
|
||||
} elseif ($length < 0) {
|
||||
$length = static::mbStrlen($string) + $length - $start;
|
||||
if ($length < 0) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
return (string)iconv_substr($string, $start, $length, 'UTF-8');
|
||||
}
|
||||
|
||||
throw new \LogicException('Either mbstring (recommended) or iconv is necessary to use Scssphp.');
|
||||
}
|
||||
|
||||
/**
|
||||
* mb_strpos wrapper
|
||||
* @param string $haystack
|
||||
* @param string $needle
|
||||
* @param int $offset
|
||||
*
|
||||
* @return int|false
|
||||
*/
|
||||
public static function mbStrpos($haystack, $needle, $offset = 0)
|
||||
{
|
||||
if (\function_exists('mb_strpos')) {
|
||||
return mb_strpos($haystack, $needle, $offset, 'UTF-8');
|
||||
}
|
||||
|
||||
if (\function_exists('iconv_strpos')) {
|
||||
return iconv_strpos($haystack, $needle, $offset, 'UTF-8');
|
||||
}
|
||||
|
||||
throw new \LogicException('Either mbstring (recommended) or iconv is necessary to use Scssphp.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SCSSPHP
|
||||
*
|
||||
* @copyright 2012-2019 Leaf Corcoran
|
||||
* @copyright 2012-2020 Leaf Corcoran
|
||||
*
|
||||
* @license http://opensource.org/licenses/MIT MIT
|
||||
*
|
||||
@@ -18,5 +19,5 @@ namespace ScssPhp\ScssPhp;
|
||||
*/
|
||||
class Version
|
||||
{
|
||||
const VERSION = 'v1.0.6';
|
||||
const VERSION = '1.10.3';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user