From 93a69cbc49fd47acfddde7d49ed33be59ed7ab11 Mon Sep 17 00:00:00 2001 From: acognet Date: Mon, 27 Jun 2022 23:38:58 +0200 Subject: [PATCH] =?UTF-8?q?N=C2=B05108=20-=20Update=20embedded=20libs=20fo?= =?UTF-8?q?r=20PHP=208.0=20(3.0=20branch)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/utils.inc.php | 17 +- composer.json | 2 +- composer.lock | 31 +- css/backoffice/components/_field.scss | 3 +- css/backoffice/components/_panel.scss | 2 +- css/backoffice/components/_pill.scss | 4 +- css/backoffice/components/_quick-create.scss | 2 +- .../scss/scss-variables.scss | 7 +- lib/autoload.php | 5 + lib/bin/pscss | 5 +- lib/composer/ClassLoader.php | 2 +- lib/composer/InstalledVersions.php | 23 +- lib/composer/autoload_classmap.php | 141 +- lib/composer/autoload_files.php | 10 +- lib/composer/autoload_namespaces.php | 2 +- lib/composer/autoload_psr4.php | 2 +- lib/composer/autoload_real.php | 30 +- lib/composer/autoload_static.php | 147 +- lib/composer/include_paths.php | 2 +- lib/composer/installed.json | 29 +- lib/composer/installed.php | 142 +- lib/scssphp/scssphp/README.md | 44 +- lib/scssphp/scssphp/bin/pscss | 141 +- lib/scssphp/scssphp/composer.json | 86 +- lib/scssphp/scssphp/scss.inc.php | 43 +- lib/scssphp/scssphp/src/Base/Range.php | 20 +- lib/scssphp/scssphp/src/Block.php | 19 +- lib/scssphp/scssphp/src/Cache.php | 69 +- lib/scssphp/scssphp/src/Colors.php | 51 +- lib/scssphp/scssphp/src/Compiler.php | 6102 ++++++++++++----- .../scssphp/src/Compiler/Environment.php | 31 +- .../src/Exception/CompilerException.php | 7 +- .../scssphp/src/Exception/ParserException.php | 41 +- .../scssphp/src/Exception/RangeException.php | 7 +- .../scssphp/src/Exception/ServerException.php | 9 +- lib/scssphp/scssphp/src/Formatter.php | 109 +- lib/scssphp/scssphp/src/Formatter/Compact.php | 9 +- .../scssphp/src/Formatter/Compressed.php | 10 +- .../scssphp/src/Formatter/Crunched.php | 12 +- lib/scssphp/scssphp/src/Formatter/Debug.php | 10 +- .../scssphp/src/Formatter/Expanded.php | 10 +- lib/scssphp/scssphp/src/Formatter/Nested.php | 28 +- .../scssphp/src/Formatter/OutputBlock.php | 23 +- lib/scssphp/scssphp/src/Node.php | 11 +- lib/scssphp/scssphp/src/Node/Number.php | 736 +- lib/scssphp/scssphp/src/Parser.php | 2076 ++++-- lib/scssphp/scssphp/src/SourceMap/Base64.php | 13 +- .../scssphp/src/SourceMap/Base64VLQ.php | 29 +- .../src/SourceMap/SourceMapGenerator.php | 93 +- lib/scssphp/scssphp/src/Type.php | 141 +- lib/scssphp/scssphp/src/Util.php | 126 +- lib/scssphp/scssphp/src/Version.php | 5 +- setup/compiler.class.inc.php | 12 +- 53 files changed, 8004 insertions(+), 2727 deletions(-) diff --git a/application/utils.inc.php b/application/utils.inc.php index 9a65869c5..3d5b47679 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -21,6 +21,8 @@ use Combodo\iTop\Application\Helper\Session; use Combodo\iTop\Application\UI\Base\iUIBlock; use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock; use ScssPhp\ScssPhp\Compiler; +use ScssPhp\ScssPhp\OutputStyle; +use ScssPhp\ScssPhp\ValueConverter; /** @@ -1940,20 +1942,25 @@ class utils */ public static function CompileCSSFromSASS($sSassContent, $aImportPaths = array(), $aVariables = array()) { - $oSass = new Compiler(); - $oSass->setFormatter('ScssPhp\\ScssPhp\\Formatter\\Compressed'); + $oSass = new Compiler();//['checkImportResolutions'=>true]); + $oSass->setOutputStyle(OutputStyle::EXPANDED); // Setting our variables - $oSass->setVariables($aVariables); + $aCssVariable = []; + foreach ($aVariables as $entry=>$value) { + $aCssVariable[$entry] = ValueConverter::parseValue($value); + } + $oSass->addVariables($aCssVariable); // Setting our imports paths $oSass->setImportPaths($aImportPaths); // Temporary disabling max exec time while compiling $iCurrentMaxExecTime = (int) ini_get('max_execution_time'); set_time_limit(0); // Compiling SASS - $sCss = $oSass->compile($sSassContent); + //checkImportResolutions + $sCss = $oSass->compileString($sSassContent); set_time_limit(intval($iCurrentMaxExecTime)); - return $sCss; + return $sCss->getCss(); } public static function GetImageSize($sImageData) diff --git a/composer.json b/composer.json index fbd23e54f..7d9b540ef 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "nikic/php-parser": "~4.13.2", "pear/archive_tar": "~1.4.14", "pelago/emogrifier": "~3.1.0", - "scssphp/scssphp": "1.0.6", + "scssphp/scssphp": "^1.10.3", "swiftmailer/swiftmailer": "~6.3.0", "symfony/console": "~3.4.47", "symfony/dotenv": "~3.4.47", diff --git a/composer.lock b/composer.lock index ad9188668..d0462fbdc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3bcb18cbb2e063a5ba6c855bbf20d926", + "content-hash": "344b0c488d359d6513240ad9adf4e3a5", "packages": [ { "name": "combodo/tcpdf", @@ -1953,16 +1953,16 @@ }, { "name": "scssphp/scssphp", - "version": "1.0.6", + "version": "v1.10.3", "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": { @@ -1971,11 +1971,20 @@ "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" }, + "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" + }, "bin": [ "bin/pscss" ], @@ -2012,9 +2021,9 @@ ], "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" }, - "time": "2019-12-12T05:00:52+00:00" + "time": "2022-05-16T07:22:18+00:00" }, { "name": "swiftmailer/swiftmailer", @@ -4744,5 +4753,5 @@ "platform-overrides": { "php": "7.1.3" }, - "plugin-api-version": "2.1.0" + "plugin-api-version": "2.3.0" } diff --git a/css/backoffice/components/_field.scss b/css/backoffice/components/_field.scss index f6993b06d..f86e7db85 100644 --- a/css/backoffice/components/_field.scss +++ b/css/backoffice/components/_field.scss @@ -94,14 +94,13 @@ $ibo-field--enable-bulk--checkbox--margin-left: $ibo-spacing-300 !default; max-width: 145px; width: 30%; } - .ibo-field--value { margin-top: $ibo-field--value--margin-top--for-large; /* Mostly used to have a clear separation from elements in .ibo-field--comments */ } /* N°4318 - Visible scrollbar background for large fields overflowing to ease "limits" visualization by the user */ .ibo-field--value > * { - --ibo-scrollbar--scrollbar-track-background-color: $ibo-field--value--scrollbar-track-background-color; + --ibo-scrollbar--scrollbar-track-background-color: #{$ibo-field--value--scrollbar-track-background-color}; } /* Fullscreen mode */ diff --git a/css/backoffice/components/_panel.scss b/css/backoffice/components/_panel.scss index c4afc19a6..ecd22f647 100644 --- a/css/backoffice/components/_panel.scss +++ b/css/backoffice/components/_panel.scss @@ -89,7 +89,7 @@ $ibo-panel--collapsible-toggler--color: $ibo-color-grey-700 !default; .ibo-panel { - --ibo-main-color: map-get($ibo-panel-colors, 'neutral'); /* --ibo-main-color is to allow overload from custom dynamic value from the DM. The overload will be done through an additional CSS class of a particular DM class or DM attribute */ + --ibo-main-color: map-get(#{$ibo-panel-colors}, 'neutral'); /* --ibo-main-color is to allow overload from custom dynamic value from the DM. The overload will be done through an additional CSS class of a particular DM class or DM attribute */ position: relative; diff --git a/css/backoffice/components/_pill.scss b/css/backoffice/components/_pill.scss index d45236c5c..f8ad35b0f 100644 --- a/css/backoffice/components/_pill.scss +++ b/css/backoffice/components/_pill.scss @@ -79,8 +79,8 @@ $ibo-pill-states-colors: ( /* Rules */ .ibo-pill { /* --ibo-main-color-xxx is to allow overload from custom dynamic value from the DM. The overload will be done through an additional CSS class of a particular DM class or DM attribute */ - --ibo-main-color--100: map-get(map-get($ibo-pill-states-colors, 'neutral'), 'primary-color'); - --ibo-main-color--900: map-get(map-get($ibo-pill-states-colors, 'neutral'), 'secondary-color'); + --ibo-main-color--100: map-get(map-get( #{$ibo-pill-states-colors}, 'neutral'), 'primary-color'); + --ibo-main-color--900: map-get(map-get( #{$ibo-pill-states-colors}, 'neutral'), 'secondary-color'); @extend %ibo-fully-centered-content; max-width: $ibo-pill--max-width; diff --git a/css/backoffice/components/_quick-create.scss b/css/backoffice/components/_quick-create.scss index 80c81643a..17534e8e2 100644 --- a/css/backoffice/components/_quick-create.scss +++ b/css/backoffice/components/_quick-create.scss @@ -235,7 +235,7 @@ $ibo-quick-create--compartment--placeholder-hint--text-color: $ibo-color-grey-70 } &:hover{ cursor: pointer; - @extend a:hover; + @extend a; } .highlight{ diff --git a/datamodels/2.x/combodo-backoffice-darkmoon-theme/scss/scss-variables.scss b/datamodels/2.x/combodo-backoffice-darkmoon-theme/scss/scss-variables.scss index d6135be7b..690d3af8b 100644 --- a/datamodels/2.x/combodo-backoffice-darkmoon-theme/scss/scss-variables.scss +++ b/datamodels/2.x/combodo-backoffice-darkmoon-theme/scss/scss-variables.scss @@ -231,13 +231,16 @@ $ibo-keyboard-shortcut--input--background-color: $ibo-input--background-color; $ibo-keyboard-shortcut--input--color: $ibo-input--color; -$ibo-button-colors: ('regular': ('neutral': ('': ($ibo-color-secondary-600, $ibo-color-grey-200, $ibo-button--box-shadow-bottom $ibo-color-secondary-400, $ibo-color-secondary-300, ), ':hover': ( $ibo-color-secondary-500, $ibo-color-grey-100, $ibo-button--box-shadow-bottom $ibo-color-secondary-400, $ibo-color-secondary-300, ), ':active': ( $ibo-color-secondary-500, $ibo-color-grey-200, $ibo-button--box-shadow-top $ibo-color-secondary-400 #{','} $ibo-button--box-shadow-bottom $ibo-color-secondary-500, $ibo-color-secondary-300, ), ':disabled': ($ibo-color-grey-300,$ibo-color-grey-700,$ibo-button--box-shadow-top $ibo-color-grey-300,$ibo-color-grey-500, ), ), /* Primary action does not have the colors from the primary brand color, at least not for now */ 'primary': ( '': ( $ibo-color-primary-700, $ibo-color-white-100, $ibo-button--box-shadow-bottom $ibo-color-primary-400, $ibo-color-primary-300, ), ':hover': ( $ibo-color-primary-600, $ibo-color-white-100, $ibo-button--box-shadow-bottom $ibo-color-primary-400, $ibo-color-primary-300, ), ':active': ( $ibo-color-primary-600, $ibo-color-white-100, $ibo-button--box-shadow-top $ibo-color-primary-400 #{','} $ibo-button--box-shadow-bottom $ibo-color-primary-600, $ibo-color-primary-800, ), ':disabled': ( $ibo-color-grey-300, $ibo-color-grey-700, $ibo-button--box-shadow-top $ibo-color-grey-300, $ibo-color-grey-500, ), ), 'secondary': ( '': ( $ibo-color-secondary-600, $ibo-color-grey-200, $ibo-button--box-shadow-bottom $ibo-color-secondary-400, $ibo-color-secondary-300, ), ':hover': ( $ibo-color-secondary-500, $ibo-color-grey-100, $ibo-button--box-shadow-bottom $ibo-color-secondary-400, $ibo-color-secondary-300, ), ':active': ( $ibo-color-secondary-500, $ibo-color-grey-200, $ibo-button--box-shadow-top $ibo-color-secondary-400 #{','} $ibo-button--box-shadow-bottom $ibo-color-secondary-500, $ibo-color-secondary-200, ), ':disabled': ( $ibo-color-grey-300, $ibo-color-grey-700, $ibo-button--box-shadow-top $ibo-color-grey-300, $ibo-color-grey-500, ), ), 'danger': ( '': ( $ibo-color-danger-700, $ibo-color-white-100, $ibo-button--box-shadow-bottom $ibo-color-danger-500, $ibo-color-danger-700, ), ':hover': ( $ibo-color-danger-600, $ibo-color-white-100, $ibo-button--box-shadow-bottom $ibo-color-danger-500, $ibo-color-danger-700, ), ':active': ( $ibo-color-danger-600, $ibo-color-white-100, $ibo-button--box-shadow-top $ibo-color-danger-500 #{','} $ibo-button--box-shadow-bottom $ibo-color-danger-600, $ibo-color-danger-700, ), ':disabled': ( $ibo-color-grey-300, $ibo-color-grey-700, $ibo-button--box-shadow-top $ibo-color-grey-300, $ibo-color-grey-500, ), ), 'success': ( '': ( $ibo-color-success-700, $ibo-color-white-100, $ibo-button--box-shadow-bottom $ibo-color-success-900, $ibo-color-success-800, ), ':hover': ( $ibo-color-success-800, $ibo-color-white-100, $ibo-button--box-shadow-bottom $ibo-color-success-900, $ibo-color-success-800, ), ':active': ($ibo-color-success-800,$ibo-color-white-100,$ibo-button--box-shadow-top $ibo-color-success-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-success-800,$ibo-color-success-800, ), ':disabled': ($ibo-color-grey-300,$ibo-color-grey-700,$ibo-button--box-shadow-top $ibo-color-grey-300,$ibo-color-grey-500, ), ), /* Colors */ 'red': ( '': ($ibo-color-red-700,$ibo-color-white-100,$ibo-button--box-shadow-bottom $ibo-color-red-500,$ibo-color-red-700, ), ':hover': ($ibo-color-red-600,$ibo-color-white-100,$ibo-button--box-shadow-bottom $ibo-color-red-500,$ibo-color-red-700, ), ':active': ($ibo-color-red-600,$ibo-color-white-100,$ibo-button--box-shadow-top $ibo-color-red-500 #{','} $ibo-button--box-shadow-bottom $ibo-color-red-600,$ibo-color-red-700, ), ':disabled': ($ibo-color-grey-300,$ibo-color-grey-700,$ibo-button--box-shadow-top $ibo-color-grey-300,$ibo-color-grey-500, ), ), 'green': ( '': ($ibo-color-green-700,$ibo-color-white-100,$ibo-button--box-shadow-bottom $ibo-color-green-900,$ibo-color-green-800, ), ':hover': ($ibo-color-green-800,$ibo-color-white-100,$ibo-button--box-shadow-bottom $ibo-color-green-900,$ibo-color-green-800, ), ':active': ($ibo-color-green-800,$ibo-color-white-100,$ibo-button--box-shadow-top $ibo-color-green-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-green-800,$ibo-color-green-800, ), ':disabled': ($ibo-color-grey-300,$ibo-color-grey-700,$ibo-button--box-shadow-top $ibo-color-grey-300,$ibo-color-grey-500, ), ), 'cyan': ( '': ($ibo-color-cyan-500,$ibo-color-white-100,$ibo-button--box-shadow-bottom $ibo-color-cyan-900,$ibo-color-cyan-900, ), ':hover': ($ibo-color-cyan-700,$ibo-color-white-100,$ibo-button--box-shadow-bottom $ibo-color-cyan-900,$ibo-color-cyan-900, ), ':active': ($ibo-color-cyan-700,$ibo-color-white-100,$ibo-button--box-shadow-top $ibo-color-cyan-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-cyan-700,$ibo-color-cyan-700, ), ':disabled': ($ibo-color-grey-300,$ibo-color-grey-700,$ibo-button--box-shadow-top $ibo-color-grey-300,$ibo-color-grey-500, ), ) ), 'alternative': ( /* Semantics */ 'neutral': ( '': (transparent,$ibo-color-grey-300,$ibo-button--box-shadow-bottom transparent,$ibo-color-grey-800, ), ':hover': ($ibo-color-secondary-700,$ibo-color-grey-300,$ibo-button--box-shadow-bottom $ibo-color-secondary-700,$ibo-color-grey-800, ), ':active': ($ibo-color-secondary-700,$ibo-color-grey-300,$ibo-button--box-shadow-top $ibo-color-secondary-700 #{','} $ibo-button--box-shadow-bottom $ibo-color-secondary-700,$ibo-color-grey-800, ), ':disabled': (transparent,$ibo-color-grey-600,$ibo-button--box-shadow-top transparent,$ibo-color-grey-500, ), ), /* Primary action does not have the colors from the primary brand color, at least not for now */ 'primary': ( '': (transparent,$ibo-color-primary-300,$ibo-button--box-shadow-bottom transparent,$ibo-color-cyan-700, ), ':hover': ($ibo-color-primary-900,$ibo-color-primary-300,$ibo-button--box-shadow-bottom $ibo-color-primary-900,$ibo-color-grey-800, ), ':active': ($ibo-color-primary-900,$ibo-color-primary-300,$ibo-button--box-shadow-top $ibo-color-primary-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-primary-900,$ibo-color-grey-800, ), ':disabled': (transparent,$ibo-color-grey-600,$ibo-button--box-shadow-top transparent,$ibo-color-grey-500, ), ), 'secondary': ( '': (transparent,$ibo-color-grey-300,$ibo-button--box-shadow-bottom transparent,$ibo-color-grey-800, ), ':hover': ($ibo-color-secondary-100,$ibo-color-grey-900,$ibo-button--box-shadow-bottom $ibo-color-secondary-100,$ibo-color-grey-800, ), ':active': ($ibo-color-secondary-100,$ibo-color-grey-900,$ibo-button--box-shadow-top $ibo-color-secondary-200 #{','} $ibo-button--box-shadow-bottom $ibo-color-secondary-100,$ibo-color-grey-800, ), ':disabled': (transparent,$ibo-color-grey-600,$ibo-button--box-shadow-top transparent,$ibo-color-grey-500, ), ), 'danger': ( '': (transparent,$ibo-color-danger-700,$ibo-button--box-shadow-bottom transparent,$ibo-color-danger-600, ), ':hover': ($ibo-color-danger-200,$ibo-color-danger-900,$ibo-button--box-shadow-bottom $ibo-color-danger-200,$ibo-color-danger-800, ), ':active': ($ibo-color-danger-200,$ibo-color-danger-900,$ibo-button--box-shadow-top $ibo-color-danger-700 #{','} $ibo-button--box-shadow-bottom $ibo-color-danger-200,$ibo-color-danger-800, ), ':disabled': (transparent,$ibo-color-grey-600,$ibo-button--box-shadow-top transparent,$ibo-color-grey-500, ), ), 'success': ( '': (transparent,$ibo-color-success-900,$ibo-button--box-shadow-bottom transparent,$ibo-color-success-800, ), ':hover': ($ibo-color-success-100,$ibo-color-success-900,$ibo-button--box-shadow-bottom $ibo-color-success-100,$ibo-color-success-800, ), ':active': ($ibo-color-success-100,$ibo-color-success-900,$ibo-button--box-shadow-top $ibo-color-success-700 #{','} $ibo-button--box-shadow-bottom $ibo-color-success-100,$ibo-color-success-800, ), ':disabled': (transparent,$ibo-color-grey-600,$ibo-button--box-shadow-top transparent,$ibo-color-grey-500, ), ), /* Colors */ 'red': ( '': (transparent,$ibo-color-red-700,$ibo-button--box-shadow-bottom transparent,$ibo-color-red-600, ), ':hover': ($ibo-color-red-200,$ibo-color-red-900,$ibo-button--box-shadow-bottom $ibo-color-red-200,$ibo-color-red-800, ), ':active': ($ibo-color-red-200,$ibo-color-red-900,$ibo-button--box-shadow-top $ibo-color-red-700 #{','} $ibo-button--box-shadow-bottom $ibo-color-red-200,$ibo-color-red-800, ), ':disabled': (transparent,$ibo-color-grey-600,$ibo-button--box-shadow-top transparent,$ibo-color-grey-500, ), ), 'green': ( '': (transparent,$ibo-color-green-900,$ibo-button--box-shadow-bottom transparent,$ibo-color-green-800, ), ':hover': ($ibo-color-green-100,$ibo-color-green-900,$ibo-button--box-shadow-bottom $ibo-color-green-100,$ibo-color-green-800, ), ':active': ($ibo-color-green-100,$ibo-color-green-900,$ibo-button--box-shadow-top $ibo-color-green-700 #{','} $ibo-button--box-shadow-bottom $ibo-color-green-100,$ibo-color-green-800, ), ':disabled': (transparent,$ibo-color-grey-600,$ibo-button--box-shadow-top transparent,$ibo-color-grey-500, ), ), 'cyan': ( '': (transparent,$ibo-color-cyan-900,$ibo-button--box-shadow-bottom transparent,$ibo-color-cyan-800, ), ':hover': ($ibo-color-cyan-100,$ibo-color-cyan-900,$ibo-button--box-shadow-bottom $ibo-color-cyan-100,$ibo-color-cyan-800, ), ':active': ($ibo-color-cyan-100,$ibo-color-cyan-900,$ibo-button--box-shadow-top $ibo-color-cyan-800 #{','} $ibo-button--box-shadow-bottom $ibo-color-cyan-100,$ibo-color-cyan-800, ), ':disabled': (transparent,$ibo-color-grey-600,$ibo-button--box-shadow-top transparent,$ibo-color-grey-500, ), ), )); +$ibo-button-colors: ('regular': ('neutral': ('': ($ibo-color-secondary-600, $ibo-color-grey-200, $ibo-button--box-shadow-bottom $ibo-color-secondary-400, $ibo-color-secondary-300, ), ':hover': ( $ibo-color-secondary-500, $ibo-color-grey-100, $ibo-button--box-shadow-bottom $ibo-color-secondary-400, $ibo-color-secondary-300, ), ':active': ( $ibo-color-secondary-500, $ibo-color-grey-200, $ibo-button--box-shadow-top $ibo-color-secondary-400 #{','} $ibo-button--box-shadow-bottom $ibo-color-secondary-500, $ibo-color-secondary-300, ), ':disabled': ($ibo-color-grey-300,$ibo-color-grey-700,$ibo-button--box-shadow-top $ibo-color-grey-300,$ibo-color-grey-500, ), ), 'primary': ( '': ( $ibo-color-primary-700, $ibo-color-white-100, $ibo-button--box-shadow-bottom $ibo-color-primary-400, $ibo-color-primary-300, ), ':hover': ( $ibo-color-primary-600, $ibo-color-white-100, $ibo-button--box-shadow-bottom $ibo-color-primary-400, $ibo-color-primary-300, ), ':active': ( $ibo-color-primary-600, $ibo-color-white-100, $ibo-button--box-shadow-top $ibo-color-primary-400 #{','} $ibo-button--box-shadow-bottom $ibo-color-primary-600, $ibo-color-primary-800, ), ':disabled': ( $ibo-color-grey-300, $ibo-color-grey-700, $ibo-button--box-shadow-top $ibo-color-grey-300, $ibo-color-grey-500, ), ), 'secondary': ( '': ( $ibo-color-secondary-600, $ibo-color-grey-200, $ibo-button--box-shadow-bottom $ibo-color-secondary-400, $ibo-color-secondary-300, ), ':hover': ( $ibo-color-secondary-500, $ibo-color-grey-100, $ibo-button--box-shadow-bottom $ibo-color-secondary-400, $ibo-color-secondary-300, ), ':active': ( $ibo-color-secondary-500, $ibo-color-grey-200, $ibo-button--box-shadow-top $ibo-color-secondary-400 #{','} $ibo-button--box-shadow-bottom $ibo-color-secondary-500, $ibo-color-secondary-200, ), ':disabled': ( $ibo-color-grey-300, $ibo-color-grey-700, $ibo-button--box-shadow-top $ibo-color-grey-300, $ibo-color-grey-500, ), ), 'danger': ( '': ( $ibo-color-danger-700, $ibo-color-white-100, $ibo-button--box-shadow-bottom $ibo-color-danger-500, $ibo-color-danger-700, ), ':hover': ( $ibo-color-danger-600, $ibo-color-white-100, $ibo-button--box-shadow-bottom $ibo-color-danger-500, $ibo-color-danger-700, ), ':active': ( $ibo-color-danger-600, $ibo-color-white-100, $ibo-button--box-shadow-top $ibo-color-danger-500 #{','} $ibo-button--box-shadow-bottom $ibo-color-danger-600, $ibo-color-danger-700, ), ':disabled': ( $ibo-color-grey-300, $ibo-color-grey-700, $ibo-button--box-shadow-top $ibo-color-grey-300, $ibo-color-grey-500, ), ), 'success': ( '': ( $ibo-color-success-700, $ibo-color-white-100, $ibo-button--box-shadow-bottom $ibo-color-success-900, $ibo-color-success-800, ), ':hover': ( $ibo-color-success-800, $ibo-color-white-100, $ibo-button--box-shadow-bottom $ibo-color-success-900, $ibo-color-success-800, ), ':active': ($ibo-color-success-800,$ibo-color-white-100,$ibo-button--box-shadow-top $ibo-color-success-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-success-800,$ibo-color-success-800, ), ':disabled': ($ibo-color-grey-300,$ibo-color-grey-700,$ibo-button--box-shadow-top $ibo-color-grey-300,$ibo-color-grey-500, ), ), 'red': ( '': ($ibo-color-red-700,$ibo-color-white-100,$ibo-button--box-shadow-bottom $ibo-color-red-500,$ibo-color-red-700, ), ':hover': ($ibo-color-red-600,$ibo-color-white-100,$ibo-button--box-shadow-bottom $ibo-color-red-500,$ibo-color-red-700, ), ':active': ($ibo-color-red-600,$ibo-color-white-100,$ibo-button--box-shadow-top $ibo-color-red-500 #{','} $ibo-button--box-shadow-bottom $ibo-color-red-600,$ibo-color-red-700, ), ':disabled': ($ibo-color-grey-300,$ibo-color-grey-700,$ibo-button--box-shadow-top $ibo-color-grey-300,$ibo-color-grey-500, ), ), 'green': ( '': ($ibo-color-green-700,$ibo-color-white-100,$ibo-button--box-shadow-bottom $ibo-color-green-900,$ibo-color-green-800, ), ':hover': ($ibo-color-green-800,$ibo-color-white-100,$ibo-button--box-shadow-bottom $ibo-color-green-900,$ibo-color-green-800, ), ':active': ($ibo-color-green-800,$ibo-color-white-100,$ibo-button--box-shadow-top $ibo-color-green-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-green-800,$ibo-color-green-800, ), ':disabled': ($ibo-color-grey-300,$ibo-color-grey-700,$ibo-button--box-shadow-top $ibo-color-grey-300,$ibo-color-grey-500, ), ), 'cyan': ( '': ($ibo-color-cyan-500,$ibo-color-white-100,$ibo-button--box-shadow-bottom $ibo-color-cyan-900,$ibo-color-cyan-900, ), ':hover': ($ibo-color-cyan-700,$ibo-color-white-100,$ibo-button--box-shadow-bottom $ibo-color-cyan-900,$ibo-color-cyan-900, ), ':active': ($ibo-color-cyan-700,$ibo-color-white-100,$ibo-button--box-shadow-top $ibo-color-cyan-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-cyan-700,$ibo-color-cyan-700, ), ':disabled': ($ibo-color-grey-300,$ibo-color-grey-700,$ibo-button--box-shadow-top $ibo-color-grey-300,$ibo-color-grey-500, ), ) ), 'alternative': ( 'neutral': ( '': (transparent,$ibo-color-grey-300,$ibo-button--box-shadow-bottom transparent,$ibo-color-grey-800, ), ':hover': ($ibo-color-secondary-700,$ibo-color-grey-300,$ibo-button--box-shadow-bottom $ibo-color-secondary-700,$ibo-color-grey-800, ), ':active': ($ibo-color-secondary-700,$ibo-color-grey-300,$ibo-button--box-shadow-top $ibo-color-secondary-700 #{','} $ibo-button--box-shadow-bottom $ibo-color-secondary-700,$ibo-color-grey-800, ), ':disabled': (transparent,$ibo-color-grey-600,$ibo-button--box-shadow-top transparent,$ibo-color-grey-500, ), ), 'primary': ( '': (transparent,$ibo-color-primary-300,$ibo-button--box-shadow-bottom transparent,$ibo-color-cyan-700, ), ':hover': ($ibo-color-primary-900,$ibo-color-primary-300,$ibo-button--box-shadow-bottom $ibo-color-primary-900,$ibo-color-grey-800, ), ':active': ($ibo-color-primary-900,$ibo-color-primary-300,$ibo-button--box-shadow-top $ibo-color-primary-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-primary-900,$ibo-color-grey-800, ), ':disabled': (transparent,$ibo-color-grey-600,$ibo-button--box-shadow-top transparent,$ibo-color-grey-500, ), ), 'secondary': ( '': (transparent,$ibo-color-grey-300,$ibo-button--box-shadow-bottom transparent,$ibo-color-grey-800, ), ':hover': ($ibo-color-secondary-100,$ibo-color-grey-900,$ibo-button--box-shadow-bottom $ibo-color-secondary-100,$ibo-color-grey-800, ), ':active': ($ibo-color-secondary-100,$ibo-color-grey-900,$ibo-button--box-shadow-top $ibo-color-secondary-200 #{','} $ibo-button--box-shadow-bottom $ibo-color-secondary-100,$ibo-color-grey-800, ), ':disabled': (transparent,$ibo-color-grey-600,$ibo-button--box-shadow-top transparent,$ibo-color-grey-500, ), ), 'danger': ( '': (transparent,$ibo-color-danger-700,$ibo-button--box-shadow-bottom transparent,$ibo-color-danger-600, ), ':hover': ($ibo-color-danger-200,$ibo-color-danger-900,$ibo-button--box-shadow-bottom $ibo-color-danger-200,$ibo-color-danger-800, ), ':active': ($ibo-color-danger-200,$ibo-color-danger-900,$ibo-button--box-shadow-top $ibo-color-danger-700 #{','} $ibo-button--box-shadow-bottom $ibo-color-danger-200,$ibo-color-danger-800, ), ':disabled': (transparent,$ibo-color-grey-600,$ibo-button--box-shadow-top transparent,$ibo-color-grey-500, ), ), 'success': ( '': (transparent,$ibo-color-success-900,$ibo-button--box-shadow-bottom transparent,$ibo-color-success-800, ), ':hover': ($ibo-color-success-100,$ibo-color-success-900,$ibo-button--box-shadow-bottom $ibo-color-success-100,$ibo-color-success-800, ), ':active': ($ibo-color-success-100,$ibo-color-success-900,$ibo-button--box-shadow-top $ibo-color-success-700 #{','} $ibo-button--box-shadow-bottom $ibo-color-success-100,$ibo-color-success-800, ), ':disabled': (transparent,$ibo-color-grey-600,$ibo-button--box-shadow-top transparent,$ibo-color-grey-500, ), ), 'red': ( '': (transparent,$ibo-color-red-700,$ibo-button--box-shadow-bottom transparent,$ibo-color-red-600, ), ':hover': ($ibo-color-red-200,$ibo-color-red-900,$ibo-button--box-shadow-bottom $ibo-color-red-200,$ibo-color-red-800, ), ':active': ($ibo-color-red-200,$ibo-color-red-900,$ibo-button--box-shadow-top $ibo-color-red-700 #{','} $ibo-button--box-shadow-bottom $ibo-color-red-200,$ibo-color-red-800, ), ':disabled': (transparent,$ibo-color-grey-600,$ibo-button--box-shadow-top transparent,$ibo-color-grey-500, ), ), 'green': ( '': (transparent,$ibo-color-green-900,$ibo-button--box-shadow-bottom transparent,$ibo-color-green-800, ), ':hover': ($ibo-color-green-100,$ibo-color-green-900,$ibo-button--box-shadow-bottom $ibo-color-green-100,$ibo-color-green-800, ), ':active': ($ibo-color-green-100,$ibo-color-green-900,$ibo-button--box-shadow-top $ibo-color-green-700 #{','} $ibo-button--box-shadow-bottom $ibo-color-green-100,$ibo-color-green-800, ), ':disabled': (transparent,$ibo-color-grey-600,$ibo-button--box-shadow-top transparent,$ibo-color-grey-500, ), ), 'cyan': ( '': (transparent,$ibo-color-cyan-900,$ibo-button--box-shadow-bottom transparent,$ibo-color-cyan-800, ), ':hover': ($ibo-color-cyan-100,$ibo-color-cyan-900,$ibo-button--box-shadow-bottom $ibo-color-cyan-100,$ibo-color-cyan-800, ), ':active': ($ibo-color-cyan-100,$ibo-color-cyan-900,$ibo-button--box-shadow-top $ibo-color-cyan-800 #{','} $ibo-button--box-shadow-bottom $ibo-color-cyan-100,$ibo-color-cyan-800, ), ':disabled': (transparent,$ibo-color-grey-600,$ibo-button--box-shadow-top transparent,$ibo-color-grey-500, ), ), )); -$ibo-alert-colors: ('primary': ($ibo-color-primary-900, $ibo-color-primary-700, $ibo-color-primary-200),'secondary': ($ibo-color-secondary-900, $ibo-color-secondary-700, $ibo-color-secondary-200),'neutral': ($ibo-color-secondary-900, $ibo-color-secondary-700, $ibo-color-secondary-200),'information': ($ibo-color-information-900, $ibo-color-information-700, $ibo-color-information-200),'success': ($ibo-color-success-900, $ibo-color-success-700, $ibo-color-success-100),'failure': ($ibo-color-danger-900, $ibo-color-danger-700, $ibo-color-danger-200),'warning': ($ibo-color-warning-900, $ibo-color-warning-700, $ibo-color-warning-200),'danger': ($ibo-color-danger-900, $ibo-color-danger-700, $ibo-color-danger-200),'grey' : ($ibo-color-grey-900, $ibo-color-grey-700, $ibo-color-grey-200),'blue-grey': ($ibo-color-blue-grey-900, $ibo-color-blue-grey-700, $ibo-color-blue-grey-200),'blue': ($ibo-color-blue-900, $ibo-color-blue-700, $ibo-color-blue-200),'cyan': ($ibo-color-cyan-900, $ibo-color-blue-700, $ibo-color-cyan-100),'green': ($ibo-color-green-900, $ibo-color-green-700, $ibo-color-green-100),'orange' : ($ibo-color-orange-900, $ibo-color-orange-700, $ibo-color-orange-200),'red': ($ibo-color-red-900, $ibo-color-red-700, $ibo-color-red-200),'pink': ($ibo-color-pink-900, $ibo-color-pink-700, $ibo-color-pink-200),); +$ibo-alert-colors: ('primary': ($ibo-color-primary-900, $ibo-color-primary-700, $ibo-color-primary-200), 'secondary': ($ibo-color-secondary-900, $ibo-color-secondary-700, $ibo-color-secondary-200),'neutral': ($ibo-color-secondary-900, $ibo-color-secondary-700, $ibo-color-secondary-200),'information': ($ibo-color-information-900, $ibo-color-information-700, $ibo-color-information-200),'success': ($ibo-color-success-900, $ibo-color-success-700, $ibo-color-success-100),'failure': ($ibo-color-danger-900, $ibo-color-danger-700, $ibo-color-danger-200),'warning': ($ibo-color-warning-900, $ibo-color-warning-700, $ibo-color-warning-200),'danger': ($ibo-color-danger-900, $ibo-color-danger-700, $ibo-color-danger-200),'grey' : ($ibo-color-grey-900, $ibo-color-grey-700, $ibo-color-grey-200),'blue-grey': ($ibo-color-blue-grey-900, $ibo-color-blue-grey-700, $ibo-color-blue-grey-200),'blue': ($ibo-color-blue-900, $ibo-color-blue-700, $ibo-color-blue-200),'cyan': ($ibo-color-cyan-900, $ibo-color-blue-700, $ibo-color-cyan-100),'green': ($ibo-color-green-900, $ibo-color-green-700, $ibo-color-green-100),'orange' : ($ibo-color-orange-900, $ibo-color-orange-700, $ibo-color-orange-200),'red': ($ibo-color-red-900, $ibo-color-red-700, $ibo-color-red-200),'pink': ($ibo-color-pink-900, $ibo-color-pink-700, $ibo-color-pink-200),); $ibo-color-base-lightness-100: 23%; $ibo-color-base-lightness-900: 93%; +$ibo-color-base-opacity-for-lightness-100: 1; +$ibo-color-base-opacity-for-lightness-900: 1; + $ibo-vendors-c3--path--color: $ibo-color-grey-300; $ibo-vendors-c3--tooltip--background-color: $ibo-color-grey-600; $ibo-vendors-c3--tooltip-th--background-color: $ibo-color-grey-600; diff --git a/lib/autoload.php b/lib/autoload.php index f37a5b80e..743c6b6d6 100644 --- a/lib/autoload.php +++ b/lib/autoload.php @@ -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 ComposerAutoloaderInit5e7efdfe4e8f9526eb41991410b96239::getLoader(); diff --git a/lib/bin/pscss b/lib/bin/pscss index f563d0cae..9743992aa 100644 --- a/lib/bin/pscss +++ b/lib/bin/pscss @@ -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); } diff --git a/lib/composer/ClassLoader.php b/lib/composer/ClassLoader.php index 0cd6055d1..afef3fa2a 100644 --- a/lib/composer/ClassLoader.php +++ b/lib/composer/ClassLoader.php @@ -149,7 +149,7 @@ class ClassLoader /** * @return string[] Array of classname => path - * @psalm-var array + * @psalm-return array */ public function getClassMap() { diff --git a/lib/composer/InstalledVersions.php b/lib/composer/InstalledVersions.php index e7552a4a9..c6b54af7b 100644 --- a/lib/composer/InstalledVersions.php +++ b/lib/composer/InstalledVersions.php @@ -26,8 +26,21 @@ use Composer\Semver\VersionParser; */ class InstalledVersions { + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null + */ private static $installed; + + /** + * @var bool|null + */ private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ private static $installedByVendor = array(); /** @@ -230,7 +243,7 @@ class InstalledVersions /** * @return array - * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string} + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} */ public static function getRootPackage() { @@ -244,7 +257,7 @@ class InstalledVersions * * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. * @return array[] - * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array} + * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} */ public static function getRawData() { @@ -267,7 +280,7 @@ class InstalledVersions * Returns the raw data of all installed.php which are currently loaded for custom implementations * * @return array[] - * @psalm-return list}> + * @psalm-return list}> */ public static function getAllRawData() { @@ -290,7 +303,7 @@ class InstalledVersions * @param array[] $data A vendor/composer/installed.php data set * @return void * - * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array} $data + * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data */ public static function reload($data) { @@ -300,7 +313,7 @@ class InstalledVersions /** * @return array[] - * @psalm-return list}> + * @psalm-return list}> */ private static function getInstalled() { diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index a6f22fe52..9742956bf 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -2,7 +2,7 @@ // autoload_classmap.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( @@ -549,6 +549,7 @@ return array( 'FilterPrivateKey' => $baseDir . '/core/filterdef.class.inc.php', 'FindStylesheetObject' => $baseDir . '/application/findstylesheetobject.class.inc.php', 'Firebase\\JWT\\BeforeValidException' => $vendorDir . '/firebase/php-jwt/src/BeforeValidException.php', + 'Firebase\\JWT\\CachedKeySet' => $vendorDir . '/firebase/php-jwt/src/CachedKeySet.php', 'Firebase\\JWT\\ExpiredException' => $vendorDir . '/firebase/php-jwt/src/ExpiredException.php', 'Firebase\\JWT\\JWK' => $vendorDir . '/firebase/php-jwt/src/JWK.php', 'Firebase\\JWT\\JWT' => $vendorDir . '/firebase/php-jwt/src/JWT.php', @@ -630,6 +631,7 @@ return array( 'GuzzleHttp\\Psr7\\StreamWrapper' => $vendorDir . '/guzzlehttp/psr7/src/StreamWrapper.php', 'GuzzleHttp\\Psr7\\UploadedFile' => $vendorDir . '/guzzlehttp/psr7/src/UploadedFile.php', 'GuzzleHttp\\Psr7\\Uri' => $vendorDir . '/guzzlehttp/psr7/src/Uri.php', + 'GuzzleHttp\\Psr7\\UriComparator' => $vendorDir . '/guzzlehttp/psr7/src/UriComparator.php', 'GuzzleHttp\\Psr7\\UriNormalizer' => $vendorDir . '/guzzlehttp/psr7/src/UriNormalizer.php', 'GuzzleHttp\\Psr7\\UriResolver' => $vendorDir . '/guzzlehttp/psr7/src/UriResolver.php', 'GuzzleHttp\\Psr7\\Utils' => $vendorDir . '/guzzlehttp/psr7/src/Utils.php', @@ -1408,13 +1410,29 @@ return array( 'ScalarOqlExpression' => $baseDir . '/core/oql/oqlquery.class.inc.php', 'ScssPhp\\ScssPhp\\Base\\Range' => $vendorDir . '/scssphp/scssphp/src/Base/Range.php', 'ScssPhp\\ScssPhp\\Block' => $vendorDir . '/scssphp/scssphp/src/Block.php', + 'ScssPhp\\ScssPhp\\Block\\AtRootBlock' => $vendorDir . '/scssphp/scssphp/src/Block/AtRootBlock.php', + 'ScssPhp\\ScssPhp\\Block\\CallableBlock' => $vendorDir . '/scssphp/scssphp/src/Block/CallableBlock.php', + 'ScssPhp\\ScssPhp\\Block\\ContentBlock' => $vendorDir . '/scssphp/scssphp/src/Block/ContentBlock.php', + 'ScssPhp\\ScssPhp\\Block\\DirectiveBlock' => $vendorDir . '/scssphp/scssphp/src/Block/DirectiveBlock.php', + 'ScssPhp\\ScssPhp\\Block\\EachBlock' => $vendorDir . '/scssphp/scssphp/src/Block/EachBlock.php', + 'ScssPhp\\ScssPhp\\Block\\ElseBlock' => $vendorDir . '/scssphp/scssphp/src/Block/ElseBlock.php', + 'ScssPhp\\ScssPhp\\Block\\ElseifBlock' => $vendorDir . '/scssphp/scssphp/src/Block/ElseifBlock.php', + 'ScssPhp\\ScssPhp\\Block\\ForBlock' => $vendorDir . '/scssphp/scssphp/src/Block/ForBlock.php', + 'ScssPhp\\ScssPhp\\Block\\IfBlock' => $vendorDir . '/scssphp/scssphp/src/Block/IfBlock.php', + 'ScssPhp\\ScssPhp\\Block\\MediaBlock' => $vendorDir . '/scssphp/scssphp/src/Block/MediaBlock.php', + 'ScssPhp\\ScssPhp\\Block\\NestedPropertyBlock' => $vendorDir . '/scssphp/scssphp/src/Block/NestedPropertyBlock.php', + 'ScssPhp\\ScssPhp\\Block\\WhileBlock' => $vendorDir . '/scssphp/scssphp/src/Block/WhileBlock.php', 'ScssPhp\\ScssPhp\\Cache' => $vendorDir . '/scssphp/scssphp/src/Cache.php', 'ScssPhp\\ScssPhp\\Colors' => $vendorDir . '/scssphp/scssphp/src/Colors.php', + 'ScssPhp\\ScssPhp\\CompilationResult' => $vendorDir . '/scssphp/scssphp/src/CompilationResult.php', 'ScssPhp\\ScssPhp\\Compiler' => $vendorDir . '/scssphp/scssphp/src/Compiler.php', + 'ScssPhp\\ScssPhp\\Compiler\\CachedResult' => $vendorDir . '/scssphp/scssphp/src/Compiler/CachedResult.php', 'ScssPhp\\ScssPhp\\Compiler\\Environment' => $vendorDir . '/scssphp/scssphp/src/Compiler/Environment.php', 'ScssPhp\\ScssPhp\\Exception\\CompilerException' => $vendorDir . '/scssphp/scssphp/src/Exception/CompilerException.php', 'ScssPhp\\ScssPhp\\Exception\\ParserException' => $vendorDir . '/scssphp/scssphp/src/Exception/ParserException.php', 'ScssPhp\\ScssPhp\\Exception\\RangeException' => $vendorDir . '/scssphp/scssphp/src/Exception/RangeException.php', + 'ScssPhp\\ScssPhp\\Exception\\SassException' => $vendorDir . '/scssphp/scssphp/src/Exception/SassException.php', + 'ScssPhp\\ScssPhp\\Exception\\SassScriptException' => $vendorDir . '/scssphp/scssphp/src/Exception/SassScriptException.php', 'ScssPhp\\ScssPhp\\Exception\\ServerException' => $vendorDir . '/scssphp/scssphp/src/Exception/ServerException.php', 'ScssPhp\\ScssPhp\\Formatter' => $vendorDir . '/scssphp/scssphp/src/Formatter.php', 'ScssPhp\\ScssPhp\\Formatter\\Compact' => $vendorDir . '/scssphp/scssphp/src/Formatter/Compact.php', @@ -1424,15 +1442,22 @@ return array( 'ScssPhp\\ScssPhp\\Formatter\\Expanded' => $vendorDir . '/scssphp/scssphp/src/Formatter/Expanded.php', 'ScssPhp\\ScssPhp\\Formatter\\Nested' => $vendorDir . '/scssphp/scssphp/src/Formatter/Nested.php', 'ScssPhp\\ScssPhp\\Formatter\\OutputBlock' => $vendorDir . '/scssphp/scssphp/src/Formatter/OutputBlock.php', + 'ScssPhp\\ScssPhp\\Logger\\LoggerInterface' => $vendorDir . '/scssphp/scssphp/src/Logger/LoggerInterface.php', + 'ScssPhp\\ScssPhp\\Logger\\QuietLogger' => $vendorDir . '/scssphp/scssphp/src/Logger/QuietLogger.php', + 'ScssPhp\\ScssPhp\\Logger\\StreamLogger' => $vendorDir . '/scssphp/scssphp/src/Logger/StreamLogger.php', 'ScssPhp\\ScssPhp\\Node' => $vendorDir . '/scssphp/scssphp/src/Node.php', 'ScssPhp\\ScssPhp\\Node\\Number' => $vendorDir . '/scssphp/scssphp/src/Node/Number.php', + 'ScssPhp\\ScssPhp\\OutputStyle' => $vendorDir . '/scssphp/scssphp/src/OutputStyle.php', 'ScssPhp\\ScssPhp\\Parser' => $vendorDir . '/scssphp/scssphp/src/Parser.php', 'ScssPhp\\ScssPhp\\SourceMap\\Base64' => $vendorDir . '/scssphp/scssphp/src/SourceMap/Base64.php', 'ScssPhp\\ScssPhp\\SourceMap\\Base64VLQ' => $vendorDir . '/scssphp/scssphp/src/SourceMap/Base64VLQ.php', 'ScssPhp\\ScssPhp\\SourceMap\\SourceMapGenerator' => $vendorDir . '/scssphp/scssphp/src/SourceMap/SourceMapGenerator.php', 'ScssPhp\\ScssPhp\\Type' => $vendorDir . '/scssphp/scssphp/src/Type.php', 'ScssPhp\\ScssPhp\\Util' => $vendorDir . '/scssphp/scssphp/src/Util.php', + 'ScssPhp\\ScssPhp\\Util\\Path' => $vendorDir . '/scssphp/scssphp/src/Util/Path.php', + 'ScssPhp\\ScssPhp\\ValueConverter' => $vendorDir . '/scssphp/scssphp/src/ValueConverter.php', 'ScssPhp\\ScssPhp\\Version' => $vendorDir . '/scssphp/scssphp/src/Version.php', + 'ScssPhp\\ScssPhp\\Warn' => $vendorDir . '/scssphp/scssphp/src/Warn.php', 'SearchMenuNode' => $baseDir . '/application/menunode.class.inc.php', 'SecurityException' => $baseDir . '/application/exceptions/SecurityException.php', 'SeparatorPopupMenuItem' => $baseDir . '/application/applicationextension.inc.php', @@ -1461,8 +1486,11 @@ return array( 'Symfony\\Bridge\\Twig\\Command\\DebugCommand' => $vendorDir . '/symfony/twig-bridge/Command/DebugCommand.php', 'Symfony\\Bridge\\Twig\\Command\\LintCommand' => $vendorDir . '/symfony/twig-bridge/Command/LintCommand.php', 'Symfony\\Bridge\\Twig\\DataCollector\\TwigDataCollector' => $vendorDir . '/symfony/twig-bridge/DataCollector/TwigDataCollector.php', + 'Symfony\\Bridge\\Twig\\ErrorRenderer\\TwigErrorRenderer' => $vendorDir . '/symfony/twig-bridge/ErrorRenderer/TwigErrorRenderer.php', 'Symfony\\Bridge\\Twig\\Extension\\AssetExtension' => $vendorDir . '/symfony/twig-bridge/Extension/AssetExtension.php', 'Symfony\\Bridge\\Twig\\Extension\\CodeExtension' => $vendorDir . '/symfony/twig-bridge/Extension/CodeExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\CsrfExtension' => $vendorDir . '/symfony/twig-bridge/Extension/CsrfExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\CsrfRuntime' => $vendorDir . '/symfony/twig-bridge/Extension/CsrfRuntime.php', 'Symfony\\Bridge\\Twig\\Extension\\DumpExtension' => $vendorDir . '/symfony/twig-bridge/Extension/DumpExtension.php', 'Symfony\\Bridge\\Twig\\Extension\\ExpressionExtension' => $vendorDir . '/symfony/twig-bridge/Extension/ExpressionExtension.php', 'Symfony\\Bridge\\Twig\\Extension\\FormExtension' => $vendorDir . '/symfony/twig-bridge/Extension/FormExtension.php', @@ -1483,6 +1511,10 @@ return array( 'Symfony\\Bridge\\Twig\\Form\\TwigRendererEngine' => $vendorDir . '/symfony/twig-bridge/Form/TwigRendererEngine.php', 'Symfony\\Bridge\\Twig\\Form\\TwigRendererEngineInterface' => $vendorDir . '/symfony/twig-bridge/Form/TwigRendererEngineInterface.php', 'Symfony\\Bridge\\Twig\\Form\\TwigRendererInterface' => $vendorDir . '/symfony/twig-bridge/Form/TwigRendererInterface.php', + 'Symfony\\Bridge\\Twig\\Mime\\BodyRenderer' => $vendorDir . '/symfony/twig-bridge/Mime/BodyRenderer.php', + 'Symfony\\Bridge\\Twig\\Mime\\NotificationEmail' => $vendorDir . '/symfony/twig-bridge/Mime/NotificationEmail.php', + 'Symfony\\Bridge\\Twig\\Mime\\TemplatedEmail' => $vendorDir . '/symfony/twig-bridge/Mime/TemplatedEmail.php', + 'Symfony\\Bridge\\Twig\\Mime\\WrappedTemplatedEmail' => $vendorDir . '/symfony/twig-bridge/Mime/WrappedTemplatedEmail.php', 'Symfony\\Bridge\\Twig\\NodeVisitor\\Scope' => $vendorDir . '/symfony/twig-bridge/NodeVisitor/Scope.php', 'Symfony\\Bridge\\Twig\\NodeVisitor\\TranslationDefaultDomainNodeVisitor' => $vendorDir . '/symfony/twig-bridge/NodeVisitor/TranslationDefaultDomainNodeVisitor.php', 'Symfony\\Bridge\\Twig\\NodeVisitor\\TranslationNodeVisitor' => $vendorDir . '/symfony/twig-bridge/NodeVisitor/TranslationNodeVisitor.php', @@ -1647,19 +1679,23 @@ return array( 'Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension' => $vendorDir . '/symfony/web-profiler-bundle/Twig/WebProfilerExtension.php', 'Symfony\\Bundle\\WebProfilerBundle\\WebProfilerBundle' => $vendorDir . '/symfony/web-profiler-bundle/WebProfilerBundle.php', 'Symfony\\Component\\Cache\\Adapter\\AbstractAdapter' => $vendorDir . '/symfony/cache/Adapter/AbstractAdapter.php', + 'Symfony\\Component\\Cache\\Adapter\\AbstractTagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/AbstractTagAwareAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\AdapterInterface' => $vendorDir . '/symfony/cache/Adapter/AdapterInterface.php', 'Symfony\\Component\\Cache\\Adapter\\ApcuAdapter' => $vendorDir . '/symfony/cache/Adapter/ApcuAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\ArrayAdapter' => $vendorDir . '/symfony/cache/Adapter/ArrayAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\ChainAdapter' => $vendorDir . '/symfony/cache/Adapter/ChainAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\DoctrineAdapter' => $vendorDir . '/symfony/cache/Adapter/DoctrineAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\FilesystemAdapter' => $vendorDir . '/symfony/cache/Adapter/FilesystemAdapter.php', + 'Symfony\\Component\\Cache\\Adapter\\FilesystemTagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/FilesystemTagAwareAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\MemcachedAdapter' => $vendorDir . '/symfony/cache/Adapter/MemcachedAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\NullAdapter' => $vendorDir . '/symfony/cache/Adapter/NullAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\PdoAdapter' => $vendorDir . '/symfony/cache/Adapter/PdoAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\PhpArrayAdapter' => $vendorDir . '/symfony/cache/Adapter/PhpArrayAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\PhpFilesAdapter' => $vendorDir . '/symfony/cache/Adapter/PhpFilesAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\ProxyAdapter' => $vendorDir . '/symfony/cache/Adapter/ProxyAdapter.php', + 'Symfony\\Component\\Cache\\Adapter\\Psr16Adapter' => $vendorDir . '/symfony/cache/Adapter/Psr16Adapter.php', 'Symfony\\Component\\Cache\\Adapter\\RedisAdapter' => $vendorDir . '/symfony/cache/Adapter/RedisAdapter.php', + 'Symfony\\Component\\Cache\\Adapter\\RedisTagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/RedisTagAwareAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\SimpleCacheAdapter' => $vendorDir . '/symfony/cache/Adapter/SimpleCacheAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\TagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/TagAwareAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\TagAwareAdapterInterface' => $vendorDir . '/symfony/cache/Adapter/TagAwareAdapterInterface.php', @@ -1667,10 +1703,21 @@ return array( 'Symfony\\Component\\Cache\\Adapter\\TraceableTagAwareAdapter' => $vendorDir . '/symfony/cache/Adapter/TraceableTagAwareAdapter.php', 'Symfony\\Component\\Cache\\CacheItem' => $vendorDir . '/symfony/cache/CacheItem.php', 'Symfony\\Component\\Cache\\DataCollector\\CacheDataCollector' => $vendorDir . '/symfony/cache/DataCollector/CacheDataCollector.php', + 'Symfony\\Component\\Cache\\DependencyInjection\\CacheCollectorPass' => $vendorDir . '/symfony/cache/DependencyInjection/CacheCollectorPass.php', + 'Symfony\\Component\\Cache\\DependencyInjection\\CachePoolClearerPass' => $vendorDir . '/symfony/cache/DependencyInjection/CachePoolClearerPass.php', + 'Symfony\\Component\\Cache\\DependencyInjection\\CachePoolPass' => $vendorDir . '/symfony/cache/DependencyInjection/CachePoolPass.php', + 'Symfony\\Component\\Cache\\DependencyInjection\\CachePoolPrunerPass' => $vendorDir . '/symfony/cache/DependencyInjection/CachePoolPrunerPass.php', 'Symfony\\Component\\Cache\\DoctrineProvider' => $vendorDir . '/symfony/cache/DoctrineProvider.php', 'Symfony\\Component\\Cache\\Exception\\CacheException' => $vendorDir . '/symfony/cache/Exception/CacheException.php', 'Symfony\\Component\\Cache\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/cache/Exception/InvalidArgumentException.php', + 'Symfony\\Component\\Cache\\Exception\\LogicException' => $vendorDir . '/symfony/cache/Exception/LogicException.php', + 'Symfony\\Component\\Cache\\LockRegistry' => $vendorDir . '/symfony/cache/LockRegistry.php', + 'Symfony\\Component\\Cache\\Marshaller\\DefaultMarshaller' => $vendorDir . '/symfony/cache/Marshaller/DefaultMarshaller.php', + 'Symfony\\Component\\Cache\\Marshaller\\DeflateMarshaller' => $vendorDir . '/symfony/cache/Marshaller/DeflateMarshaller.php', + 'Symfony\\Component\\Cache\\Marshaller\\MarshallerInterface' => $vendorDir . '/symfony/cache/Marshaller/MarshallerInterface.php', + 'Symfony\\Component\\Cache\\Marshaller\\TagAwareMarshaller' => $vendorDir . '/symfony/cache/Marshaller/TagAwareMarshaller.php', 'Symfony\\Component\\Cache\\PruneableInterface' => $vendorDir . '/symfony/cache/PruneableInterface.php', + 'Symfony\\Component\\Cache\\Psr16Cache' => $vendorDir . '/symfony/cache/Psr16Cache.php', 'Symfony\\Component\\Cache\\ResettableInterface' => $vendorDir . '/symfony/cache/ResettableInterface.php', 'Symfony\\Component\\Cache\\Simple\\AbstractCache' => $vendorDir . '/symfony/cache/Simple/AbstractCache.php', 'Symfony\\Component\\Cache\\Simple\\ApcuCache' => $vendorDir . '/symfony/cache/Simple/ApcuCache.php', @@ -1686,9 +1733,11 @@ return array( 'Symfony\\Component\\Cache\\Simple\\Psr6Cache' => $vendorDir . '/symfony/cache/Simple/Psr6Cache.php', 'Symfony\\Component\\Cache\\Simple\\RedisCache' => $vendorDir . '/symfony/cache/Simple/RedisCache.php', 'Symfony\\Component\\Cache\\Simple\\TraceableCache' => $vendorDir . '/symfony/cache/Simple/TraceableCache.php', + 'Symfony\\Component\\Cache\\Traits\\AbstractAdapterTrait' => $vendorDir . '/symfony/cache/Traits/AbstractAdapterTrait.php', 'Symfony\\Component\\Cache\\Traits\\AbstractTrait' => $vendorDir . '/symfony/cache/Traits/AbstractTrait.php', 'Symfony\\Component\\Cache\\Traits\\ApcuTrait' => $vendorDir . '/symfony/cache/Traits/ApcuTrait.php', 'Symfony\\Component\\Cache\\Traits\\ArrayTrait' => $vendorDir . '/symfony/cache/Traits/ArrayTrait.php', + 'Symfony\\Component\\Cache\\Traits\\ContractsTrait' => $vendorDir . '/symfony/cache/Traits/ContractsTrait.php', 'Symfony\\Component\\Cache\\Traits\\DoctrineTrait' => $vendorDir . '/symfony/cache/Traits/DoctrineTrait.php', 'Symfony\\Component\\Cache\\Traits\\FilesystemCommonTrait' => $vendorDir . '/symfony/cache/Traits/FilesystemCommonTrait.php', 'Symfony\\Component\\Cache\\Traits\\FilesystemTrait' => $vendorDir . '/symfony/cache/Traits/FilesystemTrait.php', @@ -1697,6 +1746,8 @@ return array( 'Symfony\\Component\\Cache\\Traits\\PhpArrayTrait' => $vendorDir . '/symfony/cache/Traits/PhpArrayTrait.php', 'Symfony\\Component\\Cache\\Traits\\PhpFilesTrait' => $vendorDir . '/symfony/cache/Traits/PhpFilesTrait.php', 'Symfony\\Component\\Cache\\Traits\\ProxyTrait' => $vendorDir . '/symfony/cache/Traits/ProxyTrait.php', + 'Symfony\\Component\\Cache\\Traits\\RedisClusterNodeProxy' => $vendorDir . '/symfony/cache/Traits/RedisClusterNodeProxy.php', + 'Symfony\\Component\\Cache\\Traits\\RedisClusterProxy' => $vendorDir . '/symfony/cache/Traits/RedisClusterProxy.php', 'Symfony\\Component\\Cache\\Traits\\RedisProxy' => $vendorDir . '/symfony/cache/Traits/RedisProxy.php', 'Symfony\\Component\\Cache\\Traits\\RedisTrait' => $vendorDir . '/symfony/cache/Traits/RedisTrait.php', 'Symfony\\Component\\ClassLoader\\ApcClassLoader' => $vendorDir . '/symfony/class-loader/ApcClassLoader.php', @@ -1716,6 +1767,7 @@ return array( 'Symfony\\Component\\Config\\Definition\\BooleanNode' => $vendorDir . '/symfony/config/Definition/BooleanNode.php', 'Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/ArrayNodeDefinition.php', 'Symfony\\Component\\Config\\Definition\\Builder\\BooleanNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/BooleanNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\BuilderAwareInterface' => $vendorDir . '/symfony/config/Definition/Builder/BuilderAwareInterface.php', 'Symfony\\Component\\Config\\Definition\\Builder\\EnumNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/EnumNodeDefinition.php', 'Symfony\\Component\\Config\\Definition\\Builder\\ExprBuilder' => $vendorDir . '/symfony/config/Definition/Builder/ExprBuilder.php', 'Symfony\\Component\\Config\\Definition\\Builder\\FloatNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/FloatNodeDefinition.php', @@ -1741,6 +1793,7 @@ return array( 'Symfony\\Component\\Config\\Definition\\Exception\\InvalidConfigurationException' => $vendorDir . '/symfony/config/Definition/Exception/InvalidConfigurationException.php', 'Symfony\\Component\\Config\\Definition\\Exception\\InvalidDefinitionException' => $vendorDir . '/symfony/config/Definition/Exception/InvalidDefinitionException.php', 'Symfony\\Component\\Config\\Definition\\Exception\\InvalidTypeException' => $vendorDir . '/symfony/config/Definition/Exception/InvalidTypeException.php', + 'Symfony\\Component\\Config\\Definition\\Exception\\TreeWithoutRootNodeException' => $vendorDir . '/symfony/config/Definition/Exception/TreeWithoutRootNodeException.php', 'Symfony\\Component\\Config\\Definition\\Exception\\UnsetKeyException' => $vendorDir . '/symfony/config/Definition/Exception/UnsetKeyException.php', 'Symfony\\Component\\Config\\Definition\\FloatNode' => $vendorDir . '/symfony/config/Definition/FloatNode.php', 'Symfony\\Component\\Config\\Definition\\IntegerNode' => $vendorDir . '/symfony/config/Definition/IntegerNode.php', @@ -1755,6 +1808,7 @@ return array( 'Symfony\\Component\\Config\\Exception\\FileLoaderImportCircularReferenceException' => $vendorDir . '/symfony/config/Exception/FileLoaderImportCircularReferenceException.php', 'Symfony\\Component\\Config\\Exception\\FileLoaderLoadException' => $vendorDir . '/symfony/config/Exception/FileLoaderLoadException.php', 'Symfony\\Component\\Config\\Exception\\FileLocatorFileNotFoundException' => $vendorDir . '/symfony/config/Exception/FileLocatorFileNotFoundException.php', + 'Symfony\\Component\\Config\\Exception\\LoaderLoadException' => $vendorDir . '/symfony/config/Exception/LoaderLoadException.php', 'Symfony\\Component\\Config\\FileLocator' => $vendorDir . '/symfony/config/FileLocator.php', 'Symfony\\Component\\Config\\FileLocatorInterface' => $vendorDir . '/symfony/config/FileLocatorInterface.php', 'Symfony\\Component\\Config\\Loader\\DelegatingLoader' => $vendorDir . '/symfony/config/Loader/DelegatingLoader.php', @@ -1927,8 +1981,11 @@ return array( 'Symfony\\Component\\DependencyInjection\\Argument\\ArgumentInterface' => $vendorDir . '/symfony/dependency-injection/Argument/ArgumentInterface.php', 'Symfony\\Component\\DependencyInjection\\Argument\\BoundArgument' => $vendorDir . '/symfony/dependency-injection/Argument/BoundArgument.php', 'Symfony\\Component\\DependencyInjection\\Argument\\IteratorArgument' => $vendorDir . '/symfony/dependency-injection/Argument/IteratorArgument.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\ReferenceSetArgumentTrait' => $vendorDir . '/symfony/dependency-injection/Argument/ReferenceSetArgumentTrait.php', 'Symfony\\Component\\DependencyInjection\\Argument\\RewindableGenerator' => $vendorDir . '/symfony/dependency-injection/Argument/RewindableGenerator.php', 'Symfony\\Component\\DependencyInjection\\Argument\\ServiceClosureArgument' => $vendorDir . '/symfony/dependency-injection/Argument/ServiceClosureArgument.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\ServiceLocator' => $vendorDir . '/symfony/dependency-injection/Argument/ServiceLocator.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\ServiceLocatorArgument' => $vendorDir . '/symfony/dependency-injection/Argument/ServiceLocatorArgument.php', 'Symfony\\Component\\DependencyInjection\\Argument\\TaggedIteratorArgument' => $vendorDir . '/symfony/dependency-injection/Argument/TaggedIteratorArgument.php', 'Symfony\\Component\\DependencyInjection\\ChildDefinition' => $vendorDir . '/symfony/dependency-injection/ChildDefinition.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\AbstractRecursivePass' => $vendorDir . '/symfony/dependency-injection/Compiler/AbstractRecursivePass.php', @@ -1942,6 +1999,7 @@ return array( 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckDefinitionValidityPass' => $vendorDir . '/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckExceptionOnInvalidReferenceBehaviorPass' => $vendorDir . '/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckReferenceValidityPass' => $vendorDir . '/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckTypeDeclarationsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/CheckTypeDeclarationsPass.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\Compiler' => $vendorDir . '/symfony/dependency-injection/Compiler/Compiler.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface' => $vendorDir . '/symfony/dependency-injection/Compiler/CompilerPassInterface.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\DecoratorServicePass' => $vendorDir . '/symfony/dependency-injection/Compiler/DecoratorServicePass.php', @@ -1954,6 +2012,7 @@ return array( 'Symfony\\Component\\DependencyInjection\\Compiler\\PassConfig' => $vendorDir . '/symfony/dependency-injection/Compiler/PassConfig.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\PriorityTaggedServiceTrait' => $vendorDir . '/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\RegisterEnvVarProcessorsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RegisterEnvVarProcessorsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\RegisterReverseContainerPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RegisterReverseContainerPass.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\RegisterServiceSubscribersPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\RemoveAbstractDefinitionsPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\RemovePrivateAliasesPass' => $vendorDir . '/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.php', @@ -1980,6 +2039,7 @@ return array( 'Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraph' => $vendorDir . '/symfony/dependency-injection/Compiler/ServiceReferenceGraph.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraphEdge' => $vendorDir . '/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraphNode' => $vendorDir . '/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ValidateEnvPlaceholdersPass' => $vendorDir . '/symfony/dependency-injection/Compiler/ValidateEnvPlaceholdersPass.php', 'Symfony\\Component\\DependencyInjection\\Config\\AutowireServiceResource' => $vendorDir . '/symfony/dependency-injection/Config/AutowireServiceResource.php', 'Symfony\\Component\\DependencyInjection\\Config\\ContainerParametersResource' => $vendorDir . '/symfony/dependency-injection/Config/ContainerParametersResource.php', 'Symfony\\Component\\DependencyInjection\\Config\\ContainerParametersResourceChecker' => $vendorDir . '/symfony/dependency-injection/Config/ContainerParametersResourceChecker.php', @@ -1994,8 +2054,10 @@ return array( 'Symfony\\Component\\DependencyInjection\\Dumper\\DumperInterface' => $vendorDir . '/symfony/dependency-injection/Dumper/DumperInterface.php', 'Symfony\\Component\\DependencyInjection\\Dumper\\GraphvizDumper' => $vendorDir . '/symfony/dependency-injection/Dumper/GraphvizDumper.php', 'Symfony\\Component\\DependencyInjection\\Dumper\\PhpDumper' => $vendorDir . '/symfony/dependency-injection/Dumper/PhpDumper.php', + 'Symfony\\Component\\DependencyInjection\\Dumper\\Preloader' => $vendorDir . '/symfony/dependency-injection/Dumper/Preloader.php', 'Symfony\\Component\\DependencyInjection\\Dumper\\XmlDumper' => $vendorDir . '/symfony/dependency-injection/Dumper/XmlDumper.php', 'Symfony\\Component\\DependencyInjection\\Dumper\\YamlDumper' => $vendorDir . '/symfony/dependency-injection/Dumper/YamlDumper.php', + 'Symfony\\Component\\DependencyInjection\\EnvVarLoaderInterface' => $vendorDir . '/symfony/dependency-injection/EnvVarLoaderInterface.php', 'Symfony\\Component\\DependencyInjection\\EnvVarProcessor' => $vendorDir . '/symfony/dependency-injection/EnvVarProcessor.php', 'Symfony\\Component\\DependencyInjection\\EnvVarProcessorInterface' => $vendorDir . '/symfony/dependency-injection/EnvVarProcessorInterface.php', 'Symfony\\Component\\DependencyInjection\\Exception\\AutowiringFailedException' => $vendorDir . '/symfony/dependency-injection/Exception/AutowiringFailedException.php', @@ -2004,6 +2066,7 @@ return array( 'Symfony\\Component\\DependencyInjection\\Exception\\EnvParameterException' => $vendorDir . '/symfony/dependency-injection/Exception/EnvParameterException.php', 'Symfony\\Component\\DependencyInjection\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/dependency-injection/Exception/ExceptionInterface.php', 'Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/dependency-injection/Exception/InvalidArgumentException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\InvalidParameterTypeException' => $vendorDir . '/symfony/dependency-injection/Exception/InvalidParameterTypeException.php', 'Symfony\\Component\\DependencyInjection\\Exception\\LogicException' => $vendorDir . '/symfony/dependency-injection/Exception/LogicException.php', 'Symfony\\Component\\DependencyInjection\\Exception\\OutOfBoundsException' => $vendorDir . '/symfony/dependency-injection/Exception/OutOfBoundsException.php', 'Symfony\\Component\\DependencyInjection\\Exception\\ParameterCircularReferenceException' => $vendorDir . '/symfony/dependency-injection/Exception/ParameterCircularReferenceException.php', @@ -2062,12 +2125,15 @@ return array( 'Symfony\\Component\\DependencyInjection\\Loader\\XmlFileLoader' => $vendorDir . '/symfony/dependency-injection/Loader/XmlFileLoader.php', 'Symfony\\Component\\DependencyInjection\\Loader\\YamlFileLoader' => $vendorDir . '/symfony/dependency-injection/Loader/YamlFileLoader.php', 'Symfony\\Component\\DependencyInjection\\Parameter' => $vendorDir . '/symfony/dependency-injection/Parameter.php', + 'Symfony\\Component\\DependencyInjection\\ParameterBag\\ContainerBag' => $vendorDir . '/symfony/dependency-injection/ParameterBag/ContainerBag.php', + 'Symfony\\Component\\DependencyInjection\\ParameterBag\\ContainerBagInterface' => $vendorDir . '/symfony/dependency-injection/ParameterBag/ContainerBagInterface.php', 'Symfony\\Component\\DependencyInjection\\ParameterBag\\EnvPlaceholderParameterBag' => $vendorDir . '/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php', 'Symfony\\Component\\DependencyInjection\\ParameterBag\\FrozenParameterBag' => $vendorDir . '/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php', 'Symfony\\Component\\DependencyInjection\\ParameterBag\\ParameterBag' => $vendorDir . '/symfony/dependency-injection/ParameterBag/ParameterBag.php', 'Symfony\\Component\\DependencyInjection\\ParameterBag\\ParameterBagInterface' => $vendorDir . '/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php', 'Symfony\\Component\\DependencyInjection\\Reference' => $vendorDir . '/symfony/dependency-injection/Reference.php', 'Symfony\\Component\\DependencyInjection\\ResettableContainerInterface' => $vendorDir . '/symfony/dependency-injection/ResettableContainerInterface.php', + 'Symfony\\Component\\DependencyInjection\\ReverseContainer' => $vendorDir . '/symfony/dependency-injection/ReverseContainer.php', 'Symfony\\Component\\DependencyInjection\\ServiceLocator' => $vendorDir . '/symfony/dependency-injection/ServiceLocator.php', 'Symfony\\Component\\DependencyInjection\\ServiceSubscriberInterface' => $vendorDir . '/symfony/dependency-injection/ServiceSubscriberInterface.php', 'Symfony\\Component\\DependencyInjection\\TaggedContainerInterface' => $vendorDir . '/symfony/dependency-injection/TaggedContainerInterface.php', @@ -2082,6 +2148,7 @@ return array( 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php', 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcherInterface' => $vendorDir . '/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php', 'Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener' => $vendorDir . '/symfony/event-dispatcher/Debug/WrappedListener.php', + 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\AddEventAliasesPass' => $vendorDir . '/symfony/event-dispatcher/DependencyInjection/AddEventAliasesPass.php', 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\RegisterListenersPass' => $vendorDir . '/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php', 'Symfony\\Component\\EventDispatcher\\Event' => $vendorDir . '/symfony/event-dispatcher/Event.php', 'Symfony\\Component\\EventDispatcher\\EventDispatcher' => $vendorDir . '/symfony/event-dispatcher/EventDispatcher.php', @@ -2089,18 +2156,23 @@ return array( 'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => $vendorDir . '/symfony/event-dispatcher/EventSubscriberInterface.php', 'Symfony\\Component\\EventDispatcher\\GenericEvent' => $vendorDir . '/symfony/event-dispatcher/GenericEvent.php', 'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/ImmutableEventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\LegacyEventDispatcherProxy' => $vendorDir . '/symfony/event-dispatcher/LegacyEventDispatcherProxy.php', + 'Symfony\\Component\\EventDispatcher\\LegacyEventProxy' => $vendorDir . '/symfony/event-dispatcher/LegacyEventProxy.php', 'Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/filesystem/Exception/ExceptionInterface.php', 'Symfony\\Component\\Filesystem\\Exception\\FileNotFoundException' => $vendorDir . '/symfony/filesystem/Exception/FileNotFoundException.php', 'Symfony\\Component\\Filesystem\\Exception\\IOException' => $vendorDir . '/symfony/filesystem/Exception/IOException.php', 'Symfony\\Component\\Filesystem\\Exception\\IOExceptionInterface' => $vendorDir . '/symfony/filesystem/Exception/IOExceptionInterface.php', + 'Symfony\\Component\\Filesystem\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/filesystem/Exception/InvalidArgumentException.php', 'Symfony\\Component\\Filesystem\\Filesystem' => $vendorDir . '/symfony/filesystem/Filesystem.php', 'Symfony\\Component\\Filesystem\\LockHandler' => $vendorDir . '/symfony/filesystem/LockHandler.php', 'Symfony\\Component\\Finder\\Comparator\\Comparator' => $vendorDir . '/symfony/finder/Comparator/Comparator.php', 'Symfony\\Component\\Finder\\Comparator\\DateComparator' => $vendorDir . '/symfony/finder/Comparator/DateComparator.php', 'Symfony\\Component\\Finder\\Comparator\\NumberComparator' => $vendorDir . '/symfony/finder/Comparator/NumberComparator.php', 'Symfony\\Component\\Finder\\Exception\\AccessDeniedException' => $vendorDir . '/symfony/finder/Exception/AccessDeniedException.php', + 'Symfony\\Component\\Finder\\Exception\\DirectoryNotFoundException' => $vendorDir . '/symfony/finder/Exception/DirectoryNotFoundException.php', 'Symfony\\Component\\Finder\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/finder/Exception/ExceptionInterface.php', 'Symfony\\Component\\Finder\\Finder' => $vendorDir . '/symfony/finder/Finder.php', + 'Symfony\\Component\\Finder\\Gitignore' => $vendorDir . '/symfony/finder/Gitignore.php', 'Symfony\\Component\\Finder\\Glob' => $vendorDir . '/symfony/finder/Glob.php', 'Symfony\\Component\\Finder\\Iterator\\CustomFilterIterator' => $vendorDir . '/symfony/finder/Iterator/CustomFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\DateRangeFilterIterator' => $vendorDir . '/symfony/finder/Iterator/DateRangeFilterIterator.php', @@ -2110,6 +2182,7 @@ return array( 'Symfony\\Component\\Finder\\Iterator\\FilecontentFilterIterator' => $vendorDir . '/symfony/finder/Iterator/FilecontentFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\FilenameFilterIterator' => $vendorDir . '/symfony/finder/Iterator/FilenameFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\FilterIterator' => $vendorDir . '/symfony/finder/Iterator/FilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\LazyIterator' => $vendorDir . '/symfony/finder/Iterator/LazyIterator.php', 'Symfony\\Component\\Finder\\Iterator\\MultiplePcreFilterIterator' => $vendorDir . '/symfony/finder/Iterator/MultiplePcreFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\PathFilterIterator' => $vendorDir . '/symfony/finder/Iterator/PathFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\RecursiveDirectoryIterator' => $vendorDir . '/symfony/finder/Iterator/RecursiveDirectoryIterator.php', @@ -2127,8 +2200,15 @@ return array( 'Symfony\\Component\\HttpFoundation\\ExpressionRequestMatcher' => $vendorDir . '/symfony/http-foundation/ExpressionRequestMatcher.php', 'Symfony\\Component\\HttpFoundation\\FileBag' => $vendorDir . '/symfony/http-foundation/FileBag.php', 'Symfony\\Component\\HttpFoundation\\File\\Exception\\AccessDeniedException' => $vendorDir . '/symfony/http-foundation/File/Exception/AccessDeniedException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\CannotWriteFileException' => $vendorDir . '/symfony/http-foundation/File/Exception/CannotWriteFileException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\ExtensionFileException' => $vendorDir . '/symfony/http-foundation/File/Exception/ExtensionFileException.php', 'Symfony\\Component\\HttpFoundation\\File\\Exception\\FileException' => $vendorDir . '/symfony/http-foundation/File/Exception/FileException.php', 'Symfony\\Component\\HttpFoundation\\File\\Exception\\FileNotFoundException' => $vendorDir . '/symfony/http-foundation/File/Exception/FileNotFoundException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\FormSizeFileException' => $vendorDir . '/symfony/http-foundation/File/Exception/FormSizeFileException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\IniSizeFileException' => $vendorDir . '/symfony/http-foundation/File/Exception/IniSizeFileException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\NoFileException' => $vendorDir . '/symfony/http-foundation/File/Exception/NoFileException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\NoTmpDirFileException' => $vendorDir . '/symfony/http-foundation/File/Exception/NoTmpDirFileException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\PartialFileException' => $vendorDir . '/symfony/http-foundation/File/Exception/PartialFileException.php', 'Symfony\\Component\\HttpFoundation\\File\\Exception\\UnexpectedTypeException' => $vendorDir . '/symfony/http-foundation/File/Exception/UnexpectedTypeException.php', 'Symfony\\Component\\HttpFoundation\\File\\Exception\\UploadException' => $vendorDir . '/symfony/http-foundation/File/Exception/UploadException.php', 'Symfony\\Component\\HttpFoundation\\File\\File' => $vendorDir . '/symfony/http-foundation/File/File.php', @@ -2142,6 +2222,7 @@ return array( 'Symfony\\Component\\HttpFoundation\\File\\Stream' => $vendorDir . '/symfony/http-foundation/File/Stream.php', 'Symfony\\Component\\HttpFoundation\\File\\UploadedFile' => $vendorDir . '/symfony/http-foundation/File/UploadedFile.php', 'Symfony\\Component\\HttpFoundation\\HeaderBag' => $vendorDir . '/symfony/http-foundation/HeaderBag.php', + 'Symfony\\Component\\HttpFoundation\\HeaderUtils' => $vendorDir . '/symfony/http-foundation/HeaderUtils.php', 'Symfony\\Component\\HttpFoundation\\IpUtils' => $vendorDir . '/symfony/http-foundation/IpUtils.php', 'Symfony\\Component\\HttpFoundation\\JsonResponse' => $vendorDir . '/symfony/http-foundation/JsonResponse.php', 'Symfony\\Component\\HttpFoundation\\ParameterBag' => $vendorDir . '/symfony/http-foundation/ParameterBag.php', @@ -2167,11 +2248,14 @@ return array( 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\AbstractSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MemcacheSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MemcachedSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MigratingSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/MigratingSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MongoDbSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeFileSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NullSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\PdoSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\RedisSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/RedisSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\SessionHandlerFactory' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\StrictSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\WriteCheckSessionHandler' => $vendorDir . '/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\MetadataBag' => $vendorDir . '/symfony/http-foundation/Session/Storage/MetadataBag.php', @@ -2184,6 +2268,15 @@ return array( 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy' => $vendorDir . '/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\SessionStorageInterface' => $vendorDir . '/symfony/http-foundation/Session/Storage/SessionStorageInterface.php', 'Symfony\\Component\\HttpFoundation\\StreamedResponse' => $vendorDir . '/symfony/http-foundation/StreamedResponse.php', + 'Symfony\\Component\\HttpFoundation\\Test\\Constraint\\RequestAttributeValueSame' => $vendorDir . '/symfony/http-foundation/Test/Constraint/RequestAttributeValueSame.php', + 'Symfony\\Component\\HttpFoundation\\Test\\Constraint\\ResponseCookieValueSame' => $vendorDir . '/symfony/http-foundation/Test/Constraint/ResponseCookieValueSame.php', + 'Symfony\\Component\\HttpFoundation\\Test\\Constraint\\ResponseHasCookie' => $vendorDir . '/symfony/http-foundation/Test/Constraint/ResponseHasCookie.php', + 'Symfony\\Component\\HttpFoundation\\Test\\Constraint\\ResponseHasHeader' => $vendorDir . '/symfony/http-foundation/Test/Constraint/ResponseHasHeader.php', + 'Symfony\\Component\\HttpFoundation\\Test\\Constraint\\ResponseHeaderSame' => $vendorDir . '/symfony/http-foundation/Test/Constraint/ResponseHeaderSame.php', + 'Symfony\\Component\\HttpFoundation\\Test\\Constraint\\ResponseIsRedirected' => $vendorDir . '/symfony/http-foundation/Test/Constraint/ResponseIsRedirected.php', + 'Symfony\\Component\\HttpFoundation\\Test\\Constraint\\ResponseIsSuccessful' => $vendorDir . '/symfony/http-foundation/Test/Constraint/ResponseIsSuccessful.php', + 'Symfony\\Component\\HttpFoundation\\Test\\Constraint\\ResponseStatusCodeSame' => $vendorDir . '/symfony/http-foundation/Test/Constraint/ResponseStatusCodeSame.php', + 'Symfony\\Component\\HttpFoundation\\UrlHelper' => $vendorDir . '/symfony/http-foundation/UrlHelper.php', 'Symfony\\Component\\HttpKernel\\Bundle\\Bundle' => $vendorDir . '/symfony/http-kernel/Bundle/Bundle.php', 'Symfony\\Component\\HttpKernel\\Bundle\\BundleInterface' => $vendorDir . '/symfony/http-kernel/Bundle/BundleInterface.php', 'Symfony\\Component\\HttpKernel\\CacheClearer\\CacheClearerInterface' => $vendorDir . '/symfony/http-kernel/CacheClearer/CacheClearerInterface.php', @@ -2202,16 +2295,19 @@ return array( 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolverInterface' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolverInterface.php', 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\DefaultValueResolver' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolver/DefaultValueResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\NotTaggedControllerValueResolver' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolver/NotTaggedControllerValueResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\RequestAttributeValueResolver' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolver/RequestAttributeValueResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\RequestValueResolver' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolver/RequestValueResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\ServiceValueResolver' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolver/ServiceValueResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\SessionValueResolver' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolver/SessionValueResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\TraceableValueResolver' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolver/TraceableValueResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\VariadicValueResolver' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentResolver/VariadicValueResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolverInterface' => $vendorDir . '/symfony/http-kernel/Controller/ArgumentValueResolverInterface.php', 'Symfony\\Component\\HttpKernel\\Controller\\ContainerControllerResolver' => $vendorDir . '/symfony/http-kernel/Controller/ContainerControllerResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ControllerReference' => $vendorDir . '/symfony/http-kernel/Controller/ControllerReference.php', 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver' => $vendorDir . '/symfony/http-kernel/Controller/ControllerResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface' => $vendorDir . '/symfony/http-kernel/Controller/ControllerResolverInterface.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ErrorController' => $vendorDir . '/symfony/http-kernel/Controller/ErrorController.php', 'Symfony\\Component\\HttpKernel\\Controller\\TraceableArgumentResolver' => $vendorDir . '/symfony/http-kernel/Controller/TraceableArgumentResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\TraceableControllerResolver' => $vendorDir . '/symfony/http-kernel/Controller/TraceableControllerResolver.php', 'Symfony\\Component\\HttpKernel\\DataCollector\\AjaxDataCollector' => $vendorDir . '/symfony/http-kernel/DataCollector/AjaxDataCollector.php', @@ -2240,6 +2336,7 @@ return array( 'Symfony\\Component\\HttpKernel\\DependencyInjection\\LoggerPass' => $vendorDir . '/symfony/http-kernel/DependencyInjection/LoggerPass.php', 'Symfony\\Component\\HttpKernel\\DependencyInjection\\MergeExtensionConfigurationPass' => $vendorDir . '/symfony/http-kernel/DependencyInjection/MergeExtensionConfigurationPass.php', 'Symfony\\Component\\HttpKernel\\DependencyInjection\\RegisterControllerArgumentLocatorsPass' => $vendorDir . '/symfony/http-kernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\RegisterLocaleAwareServicesPass' => $vendorDir . '/symfony/http-kernel/DependencyInjection/RegisterLocaleAwareServicesPass.php', 'Symfony\\Component\\HttpKernel\\DependencyInjection\\RemoveEmptyControllerArgumentLocatorsPass' => $vendorDir . '/symfony/http-kernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php', 'Symfony\\Component\\HttpKernel\\DependencyInjection\\ResettableServicePass' => $vendorDir . '/symfony/http-kernel/DependencyInjection/ResettableServicePass.php', 'Symfony\\Component\\HttpKernel\\DependencyInjection\\ServicesResetter' => $vendorDir . '/symfony/http-kernel/DependencyInjection/ServicesResetter.php', @@ -2247,9 +2344,12 @@ return array( 'Symfony\\Component\\HttpKernel\\EventListener\\AbstractTestSessionListener' => $vendorDir . '/symfony/http-kernel/EventListener/AbstractTestSessionListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\AddRequestFormatsListener' => $vendorDir . '/symfony/http-kernel/EventListener/AddRequestFormatsListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\DebugHandlersListener' => $vendorDir . '/symfony/http-kernel/EventListener/DebugHandlersListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\DisallowRobotsIndexingListener' => $vendorDir . '/symfony/http-kernel/EventListener/DisallowRobotsIndexingListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\DumpListener' => $vendorDir . '/symfony/http-kernel/EventListener/DumpListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\ErrorListener' => $vendorDir . '/symfony/http-kernel/EventListener/ErrorListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\ExceptionListener' => $vendorDir . '/symfony/http-kernel/EventListener/ExceptionListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\FragmentListener' => $vendorDir . '/symfony/http-kernel/EventListener/FragmentListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\LocaleAwareListener' => $vendorDir . '/symfony/http-kernel/EventListener/LocaleAwareListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\LocaleListener' => $vendorDir . '/symfony/http-kernel/EventListener/LocaleListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\ProfilerListener' => $vendorDir . '/symfony/http-kernel/EventListener/ProfilerListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener' => $vendorDir . '/symfony/http-kernel/EventListener/ResponseListener.php', @@ -2261,6 +2361,9 @@ return array( 'Symfony\\Component\\HttpKernel\\EventListener\\TestSessionListener' => $vendorDir . '/symfony/http-kernel/EventListener/TestSessionListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\TranslatorListener' => $vendorDir . '/symfony/http-kernel/EventListener/TranslatorListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\ValidateRequestListener' => $vendorDir . '/symfony/http-kernel/EventListener/ValidateRequestListener.php', + 'Symfony\\Component\\HttpKernel\\Event\\ControllerArgumentsEvent' => $vendorDir . '/symfony/http-kernel/Event/ControllerArgumentsEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\ControllerEvent' => $vendorDir . '/symfony/http-kernel/Event/ControllerEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent' => $vendorDir . '/symfony/http-kernel/Event/ExceptionEvent.php', 'Symfony\\Component\\HttpKernel\\Event\\FilterControllerArgumentsEvent' => $vendorDir . '/symfony/http-kernel/Event/FilterControllerArgumentsEvent.php', 'Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent' => $vendorDir . '/symfony/http-kernel/Event/FilterControllerEvent.php', 'Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent' => $vendorDir . '/symfony/http-kernel/Event/FilterResponseEvent.php', @@ -2270,9 +2373,14 @@ return array( 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent' => $vendorDir . '/symfony/http-kernel/Event/GetResponseForExceptionEvent.php', 'Symfony\\Component\\HttpKernel\\Event\\KernelEvent' => $vendorDir . '/symfony/http-kernel/Event/KernelEvent.php', 'Symfony\\Component\\HttpKernel\\Event\\PostResponseEvent' => $vendorDir . '/symfony/http-kernel/Event/PostResponseEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\RequestEvent' => $vendorDir . '/symfony/http-kernel/Event/RequestEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\ResponseEvent' => $vendorDir . '/symfony/http-kernel/Event/ResponseEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\TerminateEvent' => $vendorDir . '/symfony/http-kernel/Event/TerminateEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\ViewEvent' => $vendorDir . '/symfony/http-kernel/Event/ViewEvent.php', 'Symfony\\Component\\HttpKernel\\Exception\\AccessDeniedHttpException' => $vendorDir . '/symfony/http-kernel/Exception/AccessDeniedHttpException.php', 'Symfony\\Component\\HttpKernel\\Exception\\BadRequestHttpException' => $vendorDir . '/symfony/http-kernel/Exception/BadRequestHttpException.php', 'Symfony\\Component\\HttpKernel\\Exception\\ConflictHttpException' => $vendorDir . '/symfony/http-kernel/Exception/ConflictHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\ControllerDoesNotReturnResponseException' => $vendorDir . '/symfony/http-kernel/Exception/ControllerDoesNotReturnResponseException.php', 'Symfony\\Component\\HttpKernel\\Exception\\GoneHttpException' => $vendorDir . '/symfony/http-kernel/Exception/GoneHttpException.php', 'Symfony\\Component\\HttpKernel\\Exception\\HttpException' => $vendorDir . '/symfony/http-kernel/Exception/HttpException.php', 'Symfony\\Component\\HttpKernel\\Exception\\HttpExceptionInterface' => $vendorDir . '/symfony/http-kernel/Exception/HttpExceptionInterface.php', @@ -2305,7 +2413,9 @@ return array( 'Symfony\\Component\\HttpKernel\\HttpCache\\StoreInterface' => $vendorDir . '/symfony/http-kernel/HttpCache/StoreInterface.php', 'Symfony\\Component\\HttpKernel\\HttpCache\\SubRequestHandler' => $vendorDir . '/symfony/http-kernel/HttpCache/SubRequestHandler.php', 'Symfony\\Component\\HttpKernel\\HttpCache\\SurrogateInterface' => $vendorDir . '/symfony/http-kernel/HttpCache/SurrogateInterface.php', + 'Symfony\\Component\\HttpKernel\\HttpClientKernel' => $vendorDir . '/symfony/http-kernel/HttpClientKernel.php', 'Symfony\\Component\\HttpKernel\\HttpKernel' => $vendorDir . '/symfony/http-kernel/HttpKernel.php', + 'Symfony\\Component\\HttpKernel\\HttpKernelBrowser' => $vendorDir . '/symfony/http-kernel/HttpKernelBrowser.php', 'Symfony\\Component\\HttpKernel\\HttpKernelInterface' => $vendorDir . '/symfony/http-kernel/HttpKernelInterface.php', 'Symfony\\Component\\HttpKernel\\Kernel' => $vendorDir . '/symfony/http-kernel/Kernel.php', 'Symfony\\Component\\HttpKernel\\KernelEvents' => $vendorDir . '/symfony/http-kernel/KernelEvents.php', @@ -2329,7 +2439,9 @@ return array( 'Symfony\\Component\\Routing\\Exception\\NoConfigurationException' => $vendorDir . '/symfony/routing/Exception/NoConfigurationException.php', 'Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException' => $vendorDir . '/symfony/routing/Exception/ResourceNotFoundException.php', 'Symfony\\Component\\Routing\\Exception\\RouteNotFoundException' => $vendorDir . '/symfony/routing/Exception/RouteNotFoundException.php', + 'Symfony\\Component\\Routing\\Generator\\CompiledUrlGenerator' => $vendorDir . '/symfony/routing/Generator/CompiledUrlGenerator.php', 'Symfony\\Component\\Routing\\Generator\\ConfigurableRequirementsInterface' => $vendorDir . '/symfony/routing/Generator/ConfigurableRequirementsInterface.php', + 'Symfony\\Component\\Routing\\Generator\\Dumper\\CompiledUrlGeneratorDumper' => $vendorDir . '/symfony/routing/Generator/Dumper/CompiledUrlGeneratorDumper.php', 'Symfony\\Component\\Routing\\Generator\\Dumper\\GeneratorDumper' => $vendorDir . '/symfony/routing/Generator/Dumper/GeneratorDumper.php', 'Symfony\\Component\\Routing\\Generator\\Dumper\\GeneratorDumperInterface' => $vendorDir . '/symfony/routing/Generator/Dumper/GeneratorDumperInterface.php', 'Symfony\\Component\\Routing\\Generator\\Dumper\\PhpGeneratorDumper' => $vendorDir . '/symfony/routing/Generator/Dumper/PhpGeneratorDumper.php', @@ -2345,13 +2457,18 @@ return array( 'Symfony\\Component\\Routing\\Loader\\Configurator\\RoutingConfigurator' => $vendorDir . '/symfony/routing/Loader/Configurator/RoutingConfigurator.php', 'Symfony\\Component\\Routing\\Loader\\Configurator\\Traits\\AddTrait' => $vendorDir . '/symfony/routing/Loader/Configurator/Traits/AddTrait.php', 'Symfony\\Component\\Routing\\Loader\\Configurator\\Traits\\RouteTrait' => $vendorDir . '/symfony/routing/Loader/Configurator/Traits/RouteTrait.php', + 'Symfony\\Component\\Routing\\Loader\\ContainerLoader' => $vendorDir . '/symfony/routing/Loader/ContainerLoader.php', 'Symfony\\Component\\Routing\\Loader\\DependencyInjection\\ServiceRouterLoader' => $vendorDir . '/symfony/routing/Loader/DependencyInjection/ServiceRouterLoader.php', 'Symfony\\Component\\Routing\\Loader\\DirectoryLoader' => $vendorDir . '/symfony/routing/Loader/DirectoryLoader.php', 'Symfony\\Component\\Routing\\Loader\\GlobFileLoader' => $vendorDir . '/symfony/routing/Loader/GlobFileLoader.php', + 'Symfony\\Component\\Routing\\Loader\\ObjectLoader' => $vendorDir . '/symfony/routing/Loader/ObjectLoader.php', 'Symfony\\Component\\Routing\\Loader\\ObjectRouteLoader' => $vendorDir . '/symfony/routing/Loader/ObjectRouteLoader.php', 'Symfony\\Component\\Routing\\Loader\\PhpFileLoader' => $vendorDir . '/symfony/routing/Loader/PhpFileLoader.php', 'Symfony\\Component\\Routing\\Loader\\XmlFileLoader' => $vendorDir . '/symfony/routing/Loader/XmlFileLoader.php', 'Symfony\\Component\\Routing\\Loader\\YamlFileLoader' => $vendorDir . '/symfony/routing/Loader/YamlFileLoader.php', + 'Symfony\\Component\\Routing\\Matcher\\CompiledUrlMatcher' => $vendorDir . '/symfony/routing/Matcher/CompiledUrlMatcher.php', + 'Symfony\\Component\\Routing\\Matcher\\Dumper\\CompiledUrlMatcherDumper' => $vendorDir . '/symfony/routing/Matcher/Dumper/CompiledUrlMatcherDumper.php', + 'Symfony\\Component\\Routing\\Matcher\\Dumper\\CompiledUrlMatcherTrait' => $vendorDir . '/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php', 'Symfony\\Component\\Routing\\Matcher\\Dumper\\DumperCollection' => $vendorDir . '/symfony/routing/Matcher/Dumper/DumperCollection.php', 'Symfony\\Component\\Routing\\Matcher\\Dumper\\DumperRoute' => $vendorDir . '/symfony/routing/Matcher/Dumper/DumperRoute.php', 'Symfony\\Component\\Routing\\Matcher\\Dumper\\MatcherDumper' => $vendorDir . '/symfony/routing/Matcher/Dumper/MatcherDumper.php', @@ -2387,13 +2504,22 @@ return array( 'Symfony\\Component\\VarDumper\\Caster\\DOMCaster' => $vendorDir . '/symfony/var-dumper/Caster/DOMCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\DateCaster' => $vendorDir . '/symfony/var-dumper/Caster/DateCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\DoctrineCaster' => $vendorDir . '/symfony/var-dumper/Caster/DoctrineCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\DsCaster' => $vendorDir . '/symfony/var-dumper/Caster/DsCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\DsPairStub' => $vendorDir . '/symfony/var-dumper/Caster/DsPairStub.php', 'Symfony\\Component\\VarDumper\\Caster\\EnumStub' => $vendorDir . '/symfony/var-dumper/Caster/EnumStub.php', 'Symfony\\Component\\VarDumper\\Caster\\ExceptionCaster' => $vendorDir . '/symfony/var-dumper/Caster/ExceptionCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\FrameStub' => $vendorDir . '/symfony/var-dumper/Caster/FrameStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\GmpCaster' => $vendorDir . '/symfony/var-dumper/Caster/GmpCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\ImagineCaster' => $vendorDir . '/symfony/var-dumper/Caster/ImagineCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\ImgStub' => $vendorDir . '/symfony/var-dumper/Caster/ImgStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\IntlCaster' => $vendorDir . '/symfony/var-dumper/Caster/IntlCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\LinkStub' => $vendorDir . '/symfony/var-dumper/Caster/LinkStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\MemcachedCaster' => $vendorDir . '/symfony/var-dumper/Caster/MemcachedCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\MongoCaster' => $vendorDir . '/symfony/var-dumper/Caster/MongoCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\MysqliCaster' => $vendorDir . '/symfony/var-dumper/Caster/MysqliCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\PdoCaster' => $vendorDir . '/symfony/var-dumper/Caster/PdoCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\PgSqlCaster' => $vendorDir . '/symfony/var-dumper/Caster/PgSqlCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\ProxyManagerCaster' => $vendorDir . '/symfony/var-dumper/Caster/ProxyManagerCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\RedisCaster' => $vendorDir . '/symfony/var-dumper/Caster/RedisCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\ReflectionCaster' => $vendorDir . '/symfony/var-dumper/Caster/ReflectionCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\ResourceCaster' => $vendorDir . '/symfony/var-dumper/Caster/ResourceCaster.php', @@ -2401,6 +2527,7 @@ return array( 'Symfony\\Component\\VarDumper\\Caster\\StubCaster' => $vendorDir . '/symfony/var-dumper/Caster/StubCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\SymfonyCaster' => $vendorDir . '/symfony/var-dumper/Caster/SymfonyCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\TraceStub' => $vendorDir . '/symfony/var-dumper/Caster/TraceStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\UuidCaster' => $vendorDir . '/symfony/var-dumper/Caster/UuidCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\XmlReaderCaster' => $vendorDir . '/symfony/var-dumper/Caster/XmlReaderCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\XmlResourceCaster' => $vendorDir . '/symfony/var-dumper/Caster/XmlResourceCaster.php', 'Symfony\\Component\\VarDumper\\Cloner\\AbstractCloner' => $vendorDir . '/symfony/var-dumper/Cloner/AbstractCloner.php', @@ -2410,11 +2537,23 @@ return array( 'Symfony\\Component\\VarDumper\\Cloner\\DumperInterface' => $vendorDir . '/symfony/var-dumper/Cloner/DumperInterface.php', 'Symfony\\Component\\VarDumper\\Cloner\\Stub' => $vendorDir . '/symfony/var-dumper/Cloner/Stub.php', 'Symfony\\Component\\VarDumper\\Cloner\\VarCloner' => $vendorDir . '/symfony/var-dumper/Cloner/VarCloner.php', + 'Symfony\\Component\\VarDumper\\Command\\Descriptor\\CliDescriptor' => $vendorDir . '/symfony/var-dumper/Command/Descriptor/CliDescriptor.php', + 'Symfony\\Component\\VarDumper\\Command\\Descriptor\\DumpDescriptorInterface' => $vendorDir . '/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php', + 'Symfony\\Component\\VarDumper\\Command\\Descriptor\\HtmlDescriptor' => $vendorDir . '/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php', + 'Symfony\\Component\\VarDumper\\Command\\ServerDumpCommand' => $vendorDir . '/symfony/var-dumper/Command/ServerDumpCommand.php', 'Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper' => $vendorDir . '/symfony/var-dumper/Dumper/AbstractDumper.php', 'Symfony\\Component\\VarDumper\\Dumper\\CliDumper' => $vendorDir . '/symfony/var-dumper/Dumper/CliDumper.php', + 'Symfony\\Component\\VarDumper\\Dumper\\ContextProvider\\CliContextProvider' => $vendorDir . '/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php', + 'Symfony\\Component\\VarDumper\\Dumper\\ContextProvider\\ContextProviderInterface' => $vendorDir . '/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php', + 'Symfony\\Component\\VarDumper\\Dumper\\ContextProvider\\RequestContextProvider' => $vendorDir . '/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php', + 'Symfony\\Component\\VarDumper\\Dumper\\ContextProvider\\SourceContextProvider' => $vendorDir . '/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php', + 'Symfony\\Component\\VarDumper\\Dumper\\ContextualizedDumper' => $vendorDir . '/symfony/var-dumper/Dumper/ContextualizedDumper.php', 'Symfony\\Component\\VarDumper\\Dumper\\DataDumperInterface' => $vendorDir . '/symfony/var-dumper/Dumper/DataDumperInterface.php', 'Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper' => $vendorDir . '/symfony/var-dumper/Dumper/HtmlDumper.php', + 'Symfony\\Component\\VarDumper\\Dumper\\ServerDumper' => $vendorDir . '/symfony/var-dumper/Dumper/ServerDumper.php', 'Symfony\\Component\\VarDumper\\Exception\\ThrowingCasterException' => $vendorDir . '/symfony/var-dumper/Exception/ThrowingCasterException.php', + 'Symfony\\Component\\VarDumper\\Server\\Connection' => $vendorDir . '/symfony/var-dumper/Server/Connection.php', + 'Symfony\\Component\\VarDumper\\Server\\DumpServer' => $vendorDir . '/symfony/var-dumper/Server/DumpServer.php', 'Symfony\\Component\\VarDumper\\VarDumper' => $vendorDir . '/symfony/var-dumper/VarDumper.php', 'Symfony\\Component\\Yaml\\Command\\LintCommand' => $vendorDir . '/symfony/yaml/Command/LintCommand.php', 'Symfony\\Component\\Yaml\\Dumper' => $vendorDir . '/symfony/yaml/Dumper.php', diff --git a/lib/composer/autoload_files.php b/lib/composer/autoload_files.php index 7be757bea..e2cb88955 100644 --- a/lib/composer/autoload_files.php +++ b/lib/composer/autoload_files.php @@ -2,25 +2,25 @@ // autoload_files.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', + '7e9bd612cc444b3eed788ebbe46263a0' => $vendorDir . '/laminas/laminas-zendframework-bridge/src/autoload.php', '5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php', '023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php', - '32dcc8afd4335739640db7d200c1971d' => $vendorDir . '/symfony/polyfill-apcu/bootstrap.php', - '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', - 'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php', - '7e9bd612cc444b3eed788ebbe46263a0' => $vendorDir . '/laminas/laminas-zendframework-bridge/src/autoload.php', 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', '25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php', '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', + 'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php', 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', 'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', + '32dcc8afd4335739640db7d200c1971d' => $vendorDir . '/symfony/polyfill-apcu/bootstrap.php', 'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php', + '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', '2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php', ); diff --git a/lib/composer/autoload_namespaces.php b/lib/composer/autoload_namespaces.php index d12922d08..e6117c750 100644 --- a/lib/composer/autoload_namespaces.php +++ b/lib/composer/autoload_namespaces.php @@ -2,7 +2,7 @@ // autoload_namespaces.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( diff --git a/lib/composer/autoload_psr4.php b/lib/composer/autoload_psr4.php index ca8b4b9f6..651c9f0c1 100644 --- a/lib/composer/autoload_psr4.php +++ b/lib/composer/autoload_psr4.php @@ -2,7 +2,7 @@ // autoload_psr4.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( diff --git a/lib/composer/autoload_real.php b/lib/composer/autoload_real.php index bc3e8fca0..18fec29f2 100644 --- a/lib/composer/autoload_real.php +++ b/lib/composer/autoload_real.php @@ -25,33 +25,20 @@ class ComposerAutoloaderInit5e7efdfe4e8f9526eb41991410b96239 require __DIR__ . '/platform_check.php'; spl_autoload_register(array('ComposerAutoloaderInit5e7efdfe4e8f9526eb41991410b96239', '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('ComposerAutoloaderInit5e7efdfe4e8f9526eb41991410b96239', '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\ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239::getInitializer($loader)); - } else { - $classMap = require __DIR__ . '/autoload_classmap.php'; - if ($classMap) { - $loader->addClassMap($classMap); - } - } + require __DIR__ . '/autoload_static.php'; + call_user_func(\Composer\Autoload\ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239::getInitializer($loader)); $loader->setClassMapAuthoritative(true); $loader->register(true); - if ($useStaticLoader) { - $includeFiles = Composer\Autoload\ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239::$files; - } else { - $includeFiles = require __DIR__ . '/autoload_files.php'; - } + $includeFiles = \Composer\Autoload\ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239::$files; foreach ($includeFiles as $fileIdentifier => $file) { composerRequire5e7efdfe4e8f9526eb41991410b96239($fileIdentifier, $file); } @@ -60,11 +47,16 @@ class ComposerAutoloaderInit5e7efdfe4e8f9526eb41991410b96239 } } +/** + * @param string $fileIdentifier + * @param string $file + * @return void + */ function composerRequire5e7efdfe4e8f9526eb41991410b96239($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { - require $file; - $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + + require $file; } } diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index 4fbf7822b..522b6458c 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -9,20 +9,20 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 public static $files = array ( '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', + '7e9bd612cc444b3eed788ebbe46263a0' => __DIR__ . '/..' . '/laminas/laminas-zendframework-bridge/src/autoload.php', '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php', '023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php', - '32dcc8afd4335739640db7d200c1971d' => __DIR__ . '/..' . '/symfony/polyfill-apcu/bootstrap.php', - '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php', - 'bd9634f2d41831496de0d3dfe4c94881' => __DIR__ . '/..' . '/symfony/polyfill-php56/bootstrap.php', - '7e9bd612cc444b3eed788ebbe46263a0' => __DIR__ . '/..' . '/laminas/laminas-zendframework-bridge/src/autoload.php', 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', '25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php', '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', + 'bd9634f2d41831496de0d3dfe4c94881' => __DIR__ . '/..' . '/symfony/polyfill-php56/bootstrap.php', 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', 'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', + '32dcc8afd4335739640db7d200c1971d' => __DIR__ . '/..' . '/symfony/polyfill-apcu/bootstrap.php', 'def43f6c87e4f8dfd0c9e1b1bab14fe8' => __DIR__ . '/..' . '/symfony/polyfill-iconv/bootstrap.php', + '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php', '2c102faa651ef8ea5874edb585946bce' => __DIR__ . '/..' . '/swiftmailer/swiftmailer/lib/swift_required.php', ); @@ -917,6 +917,7 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'FilterPrivateKey' => __DIR__ . '/../..' . '/core/filterdef.class.inc.php', 'FindStylesheetObject' => __DIR__ . '/../..' . '/application/findstylesheetobject.class.inc.php', 'Firebase\\JWT\\BeforeValidException' => __DIR__ . '/..' . '/firebase/php-jwt/src/BeforeValidException.php', + 'Firebase\\JWT\\CachedKeySet' => __DIR__ . '/..' . '/firebase/php-jwt/src/CachedKeySet.php', 'Firebase\\JWT\\ExpiredException' => __DIR__ . '/..' . '/firebase/php-jwt/src/ExpiredException.php', 'Firebase\\JWT\\JWK' => __DIR__ . '/..' . '/firebase/php-jwt/src/JWK.php', 'Firebase\\JWT\\JWT' => __DIR__ . '/..' . '/firebase/php-jwt/src/JWT.php', @@ -998,6 +999,7 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'GuzzleHttp\\Psr7\\StreamWrapper' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/StreamWrapper.php', 'GuzzleHttp\\Psr7\\UploadedFile' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UploadedFile.php', 'GuzzleHttp\\Psr7\\Uri' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Uri.php', + 'GuzzleHttp\\Psr7\\UriComparator' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UriComparator.php', 'GuzzleHttp\\Psr7\\UriNormalizer' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UriNormalizer.php', 'GuzzleHttp\\Psr7\\UriResolver' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UriResolver.php', 'GuzzleHttp\\Psr7\\Utils' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Utils.php', @@ -1776,13 +1778,29 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'ScalarOqlExpression' => __DIR__ . '/../..' . '/core/oql/oqlquery.class.inc.php', 'ScssPhp\\ScssPhp\\Base\\Range' => __DIR__ . '/..' . '/scssphp/scssphp/src/Base/Range.php', 'ScssPhp\\ScssPhp\\Block' => __DIR__ . '/..' . '/scssphp/scssphp/src/Block.php', + 'ScssPhp\\ScssPhp\\Block\\AtRootBlock' => __DIR__ . '/..' . '/scssphp/scssphp/src/Block/AtRootBlock.php', + 'ScssPhp\\ScssPhp\\Block\\CallableBlock' => __DIR__ . '/..' . '/scssphp/scssphp/src/Block/CallableBlock.php', + 'ScssPhp\\ScssPhp\\Block\\ContentBlock' => __DIR__ . '/..' . '/scssphp/scssphp/src/Block/ContentBlock.php', + 'ScssPhp\\ScssPhp\\Block\\DirectiveBlock' => __DIR__ . '/..' . '/scssphp/scssphp/src/Block/DirectiveBlock.php', + 'ScssPhp\\ScssPhp\\Block\\EachBlock' => __DIR__ . '/..' . '/scssphp/scssphp/src/Block/EachBlock.php', + 'ScssPhp\\ScssPhp\\Block\\ElseBlock' => __DIR__ . '/..' . '/scssphp/scssphp/src/Block/ElseBlock.php', + 'ScssPhp\\ScssPhp\\Block\\ElseifBlock' => __DIR__ . '/..' . '/scssphp/scssphp/src/Block/ElseifBlock.php', + 'ScssPhp\\ScssPhp\\Block\\ForBlock' => __DIR__ . '/..' . '/scssphp/scssphp/src/Block/ForBlock.php', + 'ScssPhp\\ScssPhp\\Block\\IfBlock' => __DIR__ . '/..' . '/scssphp/scssphp/src/Block/IfBlock.php', + 'ScssPhp\\ScssPhp\\Block\\MediaBlock' => __DIR__ . '/..' . '/scssphp/scssphp/src/Block/MediaBlock.php', + 'ScssPhp\\ScssPhp\\Block\\NestedPropertyBlock' => __DIR__ . '/..' . '/scssphp/scssphp/src/Block/NestedPropertyBlock.php', + 'ScssPhp\\ScssPhp\\Block\\WhileBlock' => __DIR__ . '/..' . '/scssphp/scssphp/src/Block/WhileBlock.php', 'ScssPhp\\ScssPhp\\Cache' => __DIR__ . '/..' . '/scssphp/scssphp/src/Cache.php', 'ScssPhp\\ScssPhp\\Colors' => __DIR__ . '/..' . '/scssphp/scssphp/src/Colors.php', + 'ScssPhp\\ScssPhp\\CompilationResult' => __DIR__ . '/..' . '/scssphp/scssphp/src/CompilationResult.php', 'ScssPhp\\ScssPhp\\Compiler' => __DIR__ . '/..' . '/scssphp/scssphp/src/Compiler.php', + 'ScssPhp\\ScssPhp\\Compiler\\CachedResult' => __DIR__ . '/..' . '/scssphp/scssphp/src/Compiler/CachedResult.php', 'ScssPhp\\ScssPhp\\Compiler\\Environment' => __DIR__ . '/..' . '/scssphp/scssphp/src/Compiler/Environment.php', 'ScssPhp\\ScssPhp\\Exception\\CompilerException' => __DIR__ . '/..' . '/scssphp/scssphp/src/Exception/CompilerException.php', 'ScssPhp\\ScssPhp\\Exception\\ParserException' => __DIR__ . '/..' . '/scssphp/scssphp/src/Exception/ParserException.php', 'ScssPhp\\ScssPhp\\Exception\\RangeException' => __DIR__ . '/..' . '/scssphp/scssphp/src/Exception/RangeException.php', + 'ScssPhp\\ScssPhp\\Exception\\SassException' => __DIR__ . '/..' . '/scssphp/scssphp/src/Exception/SassException.php', + 'ScssPhp\\ScssPhp\\Exception\\SassScriptException' => __DIR__ . '/..' . '/scssphp/scssphp/src/Exception/SassScriptException.php', 'ScssPhp\\ScssPhp\\Exception\\ServerException' => __DIR__ . '/..' . '/scssphp/scssphp/src/Exception/ServerException.php', 'ScssPhp\\ScssPhp\\Formatter' => __DIR__ . '/..' . '/scssphp/scssphp/src/Formatter.php', 'ScssPhp\\ScssPhp\\Formatter\\Compact' => __DIR__ . '/..' . '/scssphp/scssphp/src/Formatter/Compact.php', @@ -1792,15 +1810,22 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'ScssPhp\\ScssPhp\\Formatter\\Expanded' => __DIR__ . '/..' . '/scssphp/scssphp/src/Formatter/Expanded.php', 'ScssPhp\\ScssPhp\\Formatter\\Nested' => __DIR__ . '/..' . '/scssphp/scssphp/src/Formatter/Nested.php', 'ScssPhp\\ScssPhp\\Formatter\\OutputBlock' => __DIR__ . '/..' . '/scssphp/scssphp/src/Formatter/OutputBlock.php', + 'ScssPhp\\ScssPhp\\Logger\\LoggerInterface' => __DIR__ . '/..' . '/scssphp/scssphp/src/Logger/LoggerInterface.php', + 'ScssPhp\\ScssPhp\\Logger\\QuietLogger' => __DIR__ . '/..' . '/scssphp/scssphp/src/Logger/QuietLogger.php', + 'ScssPhp\\ScssPhp\\Logger\\StreamLogger' => __DIR__ . '/..' . '/scssphp/scssphp/src/Logger/StreamLogger.php', 'ScssPhp\\ScssPhp\\Node' => __DIR__ . '/..' . '/scssphp/scssphp/src/Node.php', 'ScssPhp\\ScssPhp\\Node\\Number' => __DIR__ . '/..' . '/scssphp/scssphp/src/Node/Number.php', + 'ScssPhp\\ScssPhp\\OutputStyle' => __DIR__ . '/..' . '/scssphp/scssphp/src/OutputStyle.php', 'ScssPhp\\ScssPhp\\Parser' => __DIR__ . '/..' . '/scssphp/scssphp/src/Parser.php', 'ScssPhp\\ScssPhp\\SourceMap\\Base64' => __DIR__ . '/..' . '/scssphp/scssphp/src/SourceMap/Base64.php', 'ScssPhp\\ScssPhp\\SourceMap\\Base64VLQ' => __DIR__ . '/..' . '/scssphp/scssphp/src/SourceMap/Base64VLQ.php', 'ScssPhp\\ScssPhp\\SourceMap\\SourceMapGenerator' => __DIR__ . '/..' . '/scssphp/scssphp/src/SourceMap/SourceMapGenerator.php', 'ScssPhp\\ScssPhp\\Type' => __DIR__ . '/..' . '/scssphp/scssphp/src/Type.php', 'ScssPhp\\ScssPhp\\Util' => __DIR__ . '/..' . '/scssphp/scssphp/src/Util.php', + 'ScssPhp\\ScssPhp\\Util\\Path' => __DIR__ . '/..' . '/scssphp/scssphp/src/Util/Path.php', + 'ScssPhp\\ScssPhp\\ValueConverter' => __DIR__ . '/..' . '/scssphp/scssphp/src/ValueConverter.php', 'ScssPhp\\ScssPhp\\Version' => __DIR__ . '/..' . '/scssphp/scssphp/src/Version.php', + 'ScssPhp\\ScssPhp\\Warn' => __DIR__ . '/..' . '/scssphp/scssphp/src/Warn.php', 'SearchMenuNode' => __DIR__ . '/../..' . '/application/menunode.class.inc.php', 'SecurityException' => __DIR__ . '/../..' . '/application/exceptions/SecurityException.php', 'SeparatorPopupMenuItem' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', @@ -1829,8 +1854,11 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Bridge\\Twig\\Command\\DebugCommand' => __DIR__ . '/..' . '/symfony/twig-bridge/Command/DebugCommand.php', 'Symfony\\Bridge\\Twig\\Command\\LintCommand' => __DIR__ . '/..' . '/symfony/twig-bridge/Command/LintCommand.php', 'Symfony\\Bridge\\Twig\\DataCollector\\TwigDataCollector' => __DIR__ . '/..' . '/symfony/twig-bridge/DataCollector/TwigDataCollector.php', + 'Symfony\\Bridge\\Twig\\ErrorRenderer\\TwigErrorRenderer' => __DIR__ . '/..' . '/symfony/twig-bridge/ErrorRenderer/TwigErrorRenderer.php', 'Symfony\\Bridge\\Twig\\Extension\\AssetExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/AssetExtension.php', 'Symfony\\Bridge\\Twig\\Extension\\CodeExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/CodeExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\CsrfExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/CsrfExtension.php', + 'Symfony\\Bridge\\Twig\\Extension\\CsrfRuntime' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/CsrfRuntime.php', 'Symfony\\Bridge\\Twig\\Extension\\DumpExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/DumpExtension.php', 'Symfony\\Bridge\\Twig\\Extension\\ExpressionExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/ExpressionExtension.php', 'Symfony\\Bridge\\Twig\\Extension\\FormExtension' => __DIR__ . '/..' . '/symfony/twig-bridge/Extension/FormExtension.php', @@ -1851,6 +1879,10 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Bridge\\Twig\\Form\\TwigRendererEngine' => __DIR__ . '/..' . '/symfony/twig-bridge/Form/TwigRendererEngine.php', 'Symfony\\Bridge\\Twig\\Form\\TwigRendererEngineInterface' => __DIR__ . '/..' . '/symfony/twig-bridge/Form/TwigRendererEngineInterface.php', 'Symfony\\Bridge\\Twig\\Form\\TwigRendererInterface' => __DIR__ . '/..' . '/symfony/twig-bridge/Form/TwigRendererInterface.php', + 'Symfony\\Bridge\\Twig\\Mime\\BodyRenderer' => __DIR__ . '/..' . '/symfony/twig-bridge/Mime/BodyRenderer.php', + 'Symfony\\Bridge\\Twig\\Mime\\NotificationEmail' => __DIR__ . '/..' . '/symfony/twig-bridge/Mime/NotificationEmail.php', + 'Symfony\\Bridge\\Twig\\Mime\\TemplatedEmail' => __DIR__ . '/..' . '/symfony/twig-bridge/Mime/TemplatedEmail.php', + 'Symfony\\Bridge\\Twig\\Mime\\WrappedTemplatedEmail' => __DIR__ . '/..' . '/symfony/twig-bridge/Mime/WrappedTemplatedEmail.php', 'Symfony\\Bridge\\Twig\\NodeVisitor\\Scope' => __DIR__ . '/..' . '/symfony/twig-bridge/NodeVisitor/Scope.php', 'Symfony\\Bridge\\Twig\\NodeVisitor\\TranslationDefaultDomainNodeVisitor' => __DIR__ . '/..' . '/symfony/twig-bridge/NodeVisitor/TranslationDefaultDomainNodeVisitor.php', 'Symfony\\Bridge\\Twig\\NodeVisitor\\TranslationNodeVisitor' => __DIR__ . '/..' . '/symfony/twig-bridge/NodeVisitor/TranslationNodeVisitor.php', @@ -2015,19 +2047,23 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Bundle\\WebProfilerBundle\\Twig\\WebProfilerExtension' => __DIR__ . '/..' . '/symfony/web-profiler-bundle/Twig/WebProfilerExtension.php', 'Symfony\\Bundle\\WebProfilerBundle\\WebProfilerBundle' => __DIR__ . '/..' . '/symfony/web-profiler-bundle/WebProfilerBundle.php', 'Symfony\\Component\\Cache\\Adapter\\AbstractAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/AbstractAdapter.php', + 'Symfony\\Component\\Cache\\Adapter\\AbstractTagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/AbstractTagAwareAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\AdapterInterface' => __DIR__ . '/..' . '/symfony/cache/Adapter/AdapterInterface.php', 'Symfony\\Component\\Cache\\Adapter\\ApcuAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ApcuAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\ArrayAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ArrayAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\ChainAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ChainAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\DoctrineAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/DoctrineAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\FilesystemAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/FilesystemAdapter.php', + 'Symfony\\Component\\Cache\\Adapter\\FilesystemTagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/FilesystemTagAwareAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\MemcachedAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/MemcachedAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\NullAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/NullAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\PdoAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/PdoAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\PhpArrayAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/PhpArrayAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\PhpFilesAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/PhpFilesAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\ProxyAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/ProxyAdapter.php', + 'Symfony\\Component\\Cache\\Adapter\\Psr16Adapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/Psr16Adapter.php', 'Symfony\\Component\\Cache\\Adapter\\RedisAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/RedisAdapter.php', + 'Symfony\\Component\\Cache\\Adapter\\RedisTagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/RedisTagAwareAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\SimpleCacheAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/SimpleCacheAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\TagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/TagAwareAdapter.php', 'Symfony\\Component\\Cache\\Adapter\\TagAwareAdapterInterface' => __DIR__ . '/..' . '/symfony/cache/Adapter/TagAwareAdapterInterface.php', @@ -2035,10 +2071,21 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\Cache\\Adapter\\TraceableTagAwareAdapter' => __DIR__ . '/..' . '/symfony/cache/Adapter/TraceableTagAwareAdapter.php', 'Symfony\\Component\\Cache\\CacheItem' => __DIR__ . '/..' . '/symfony/cache/CacheItem.php', 'Symfony\\Component\\Cache\\DataCollector\\CacheDataCollector' => __DIR__ . '/..' . '/symfony/cache/DataCollector/CacheDataCollector.php', + 'Symfony\\Component\\Cache\\DependencyInjection\\CacheCollectorPass' => __DIR__ . '/..' . '/symfony/cache/DependencyInjection/CacheCollectorPass.php', + 'Symfony\\Component\\Cache\\DependencyInjection\\CachePoolClearerPass' => __DIR__ . '/..' . '/symfony/cache/DependencyInjection/CachePoolClearerPass.php', + 'Symfony\\Component\\Cache\\DependencyInjection\\CachePoolPass' => __DIR__ . '/..' . '/symfony/cache/DependencyInjection/CachePoolPass.php', + 'Symfony\\Component\\Cache\\DependencyInjection\\CachePoolPrunerPass' => __DIR__ . '/..' . '/symfony/cache/DependencyInjection/CachePoolPrunerPass.php', 'Symfony\\Component\\Cache\\DoctrineProvider' => __DIR__ . '/..' . '/symfony/cache/DoctrineProvider.php', 'Symfony\\Component\\Cache\\Exception\\CacheException' => __DIR__ . '/..' . '/symfony/cache/Exception/CacheException.php', 'Symfony\\Component\\Cache\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/cache/Exception/InvalidArgumentException.php', + 'Symfony\\Component\\Cache\\Exception\\LogicException' => __DIR__ . '/..' . '/symfony/cache/Exception/LogicException.php', + 'Symfony\\Component\\Cache\\LockRegistry' => __DIR__ . '/..' . '/symfony/cache/LockRegistry.php', + 'Symfony\\Component\\Cache\\Marshaller\\DefaultMarshaller' => __DIR__ . '/..' . '/symfony/cache/Marshaller/DefaultMarshaller.php', + 'Symfony\\Component\\Cache\\Marshaller\\DeflateMarshaller' => __DIR__ . '/..' . '/symfony/cache/Marshaller/DeflateMarshaller.php', + 'Symfony\\Component\\Cache\\Marshaller\\MarshallerInterface' => __DIR__ . '/..' . '/symfony/cache/Marshaller/MarshallerInterface.php', + 'Symfony\\Component\\Cache\\Marshaller\\TagAwareMarshaller' => __DIR__ . '/..' . '/symfony/cache/Marshaller/TagAwareMarshaller.php', 'Symfony\\Component\\Cache\\PruneableInterface' => __DIR__ . '/..' . '/symfony/cache/PruneableInterface.php', + 'Symfony\\Component\\Cache\\Psr16Cache' => __DIR__ . '/..' . '/symfony/cache/Psr16Cache.php', 'Symfony\\Component\\Cache\\ResettableInterface' => __DIR__ . '/..' . '/symfony/cache/ResettableInterface.php', 'Symfony\\Component\\Cache\\Simple\\AbstractCache' => __DIR__ . '/..' . '/symfony/cache/Simple/AbstractCache.php', 'Symfony\\Component\\Cache\\Simple\\ApcuCache' => __DIR__ . '/..' . '/symfony/cache/Simple/ApcuCache.php', @@ -2054,9 +2101,11 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\Cache\\Simple\\Psr6Cache' => __DIR__ . '/..' . '/symfony/cache/Simple/Psr6Cache.php', 'Symfony\\Component\\Cache\\Simple\\RedisCache' => __DIR__ . '/..' . '/symfony/cache/Simple/RedisCache.php', 'Symfony\\Component\\Cache\\Simple\\TraceableCache' => __DIR__ . '/..' . '/symfony/cache/Simple/TraceableCache.php', + 'Symfony\\Component\\Cache\\Traits\\AbstractAdapterTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/AbstractAdapterTrait.php', 'Symfony\\Component\\Cache\\Traits\\AbstractTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/AbstractTrait.php', 'Symfony\\Component\\Cache\\Traits\\ApcuTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/ApcuTrait.php', 'Symfony\\Component\\Cache\\Traits\\ArrayTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/ArrayTrait.php', + 'Symfony\\Component\\Cache\\Traits\\ContractsTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/ContractsTrait.php', 'Symfony\\Component\\Cache\\Traits\\DoctrineTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/DoctrineTrait.php', 'Symfony\\Component\\Cache\\Traits\\FilesystemCommonTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/FilesystemCommonTrait.php', 'Symfony\\Component\\Cache\\Traits\\FilesystemTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/FilesystemTrait.php', @@ -2065,6 +2114,8 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\Cache\\Traits\\PhpArrayTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/PhpArrayTrait.php', 'Symfony\\Component\\Cache\\Traits\\PhpFilesTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/PhpFilesTrait.php', 'Symfony\\Component\\Cache\\Traits\\ProxyTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/ProxyTrait.php', + 'Symfony\\Component\\Cache\\Traits\\RedisClusterNodeProxy' => __DIR__ . '/..' . '/symfony/cache/Traits/RedisClusterNodeProxy.php', + 'Symfony\\Component\\Cache\\Traits\\RedisClusterProxy' => __DIR__ . '/..' . '/symfony/cache/Traits/RedisClusterProxy.php', 'Symfony\\Component\\Cache\\Traits\\RedisProxy' => __DIR__ . '/..' . '/symfony/cache/Traits/RedisProxy.php', 'Symfony\\Component\\Cache\\Traits\\RedisTrait' => __DIR__ . '/..' . '/symfony/cache/Traits/RedisTrait.php', 'Symfony\\Component\\ClassLoader\\ApcClassLoader' => __DIR__ . '/..' . '/symfony/class-loader/ApcClassLoader.php', @@ -2084,6 +2135,7 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\Config\\Definition\\BooleanNode' => __DIR__ . '/..' . '/symfony/config/Definition/BooleanNode.php', 'Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ArrayNodeDefinition.php', 'Symfony\\Component\\Config\\Definition\\Builder\\BooleanNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/BooleanNodeDefinition.php', + 'Symfony\\Component\\Config\\Definition\\Builder\\BuilderAwareInterface' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/BuilderAwareInterface.php', 'Symfony\\Component\\Config\\Definition\\Builder\\EnumNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/EnumNodeDefinition.php', 'Symfony\\Component\\Config\\Definition\\Builder\\ExprBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ExprBuilder.php', 'Symfony\\Component\\Config\\Definition\\Builder\\FloatNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/FloatNodeDefinition.php', @@ -2109,6 +2161,7 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\Config\\Definition\\Exception\\InvalidConfigurationException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/InvalidConfigurationException.php', 'Symfony\\Component\\Config\\Definition\\Exception\\InvalidDefinitionException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/InvalidDefinitionException.php', 'Symfony\\Component\\Config\\Definition\\Exception\\InvalidTypeException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/InvalidTypeException.php', + 'Symfony\\Component\\Config\\Definition\\Exception\\TreeWithoutRootNodeException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/TreeWithoutRootNodeException.php', 'Symfony\\Component\\Config\\Definition\\Exception\\UnsetKeyException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/UnsetKeyException.php', 'Symfony\\Component\\Config\\Definition\\FloatNode' => __DIR__ . '/..' . '/symfony/config/Definition/FloatNode.php', 'Symfony\\Component\\Config\\Definition\\IntegerNode' => __DIR__ . '/..' . '/symfony/config/Definition/IntegerNode.php', @@ -2123,6 +2176,7 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\Config\\Exception\\FileLoaderImportCircularReferenceException' => __DIR__ . '/..' . '/symfony/config/Exception/FileLoaderImportCircularReferenceException.php', 'Symfony\\Component\\Config\\Exception\\FileLoaderLoadException' => __DIR__ . '/..' . '/symfony/config/Exception/FileLoaderLoadException.php', 'Symfony\\Component\\Config\\Exception\\FileLocatorFileNotFoundException' => __DIR__ . '/..' . '/symfony/config/Exception/FileLocatorFileNotFoundException.php', + 'Symfony\\Component\\Config\\Exception\\LoaderLoadException' => __DIR__ . '/..' . '/symfony/config/Exception/LoaderLoadException.php', 'Symfony\\Component\\Config\\FileLocator' => __DIR__ . '/..' . '/symfony/config/FileLocator.php', 'Symfony\\Component\\Config\\FileLocatorInterface' => __DIR__ . '/..' . '/symfony/config/FileLocatorInterface.php', 'Symfony\\Component\\Config\\Loader\\DelegatingLoader' => __DIR__ . '/..' . '/symfony/config/Loader/DelegatingLoader.php', @@ -2295,8 +2349,11 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\DependencyInjection\\Argument\\ArgumentInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/ArgumentInterface.php', 'Symfony\\Component\\DependencyInjection\\Argument\\BoundArgument' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/BoundArgument.php', 'Symfony\\Component\\DependencyInjection\\Argument\\IteratorArgument' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/IteratorArgument.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\ReferenceSetArgumentTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/ReferenceSetArgumentTrait.php', 'Symfony\\Component\\DependencyInjection\\Argument\\RewindableGenerator' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/RewindableGenerator.php', 'Symfony\\Component\\DependencyInjection\\Argument\\ServiceClosureArgument' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/ServiceClosureArgument.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\ServiceLocator' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/ServiceLocator.php', + 'Symfony\\Component\\DependencyInjection\\Argument\\ServiceLocatorArgument' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/ServiceLocatorArgument.php', 'Symfony\\Component\\DependencyInjection\\Argument\\TaggedIteratorArgument' => __DIR__ . '/..' . '/symfony/dependency-injection/Argument/TaggedIteratorArgument.php', 'Symfony\\Component\\DependencyInjection\\ChildDefinition' => __DIR__ . '/..' . '/symfony/dependency-injection/ChildDefinition.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\AbstractRecursivePass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/AbstractRecursivePass.php', @@ -2310,6 +2367,7 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckDefinitionValidityPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckExceptionOnInvalidReferenceBehaviorPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckReferenceValidityPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\CheckTypeDeclarationsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CheckTypeDeclarationsPass.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\Compiler' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/Compiler.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/CompilerPassInterface.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\DecoratorServicePass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/DecoratorServicePass.php', @@ -2322,6 +2380,7 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\DependencyInjection\\Compiler\\PassConfig' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/PassConfig.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\PriorityTaggedServiceTrait' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\RegisterEnvVarProcessorsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RegisterEnvVarProcessorsPass.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\RegisterReverseContainerPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RegisterReverseContainerPass.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\RegisterServiceSubscribersPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\RemoveAbstractDefinitionsPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\RemovePrivateAliasesPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.php', @@ -2348,6 +2407,7 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraph' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ServiceReferenceGraph.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraphEdge' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.php', 'Symfony\\Component\\DependencyInjection\\Compiler\\ServiceReferenceGraphNode' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.php', + 'Symfony\\Component\\DependencyInjection\\Compiler\\ValidateEnvPlaceholdersPass' => __DIR__ . '/..' . '/symfony/dependency-injection/Compiler/ValidateEnvPlaceholdersPass.php', 'Symfony\\Component\\DependencyInjection\\Config\\AutowireServiceResource' => __DIR__ . '/..' . '/symfony/dependency-injection/Config/AutowireServiceResource.php', 'Symfony\\Component\\DependencyInjection\\Config\\ContainerParametersResource' => __DIR__ . '/..' . '/symfony/dependency-injection/Config/ContainerParametersResource.php', 'Symfony\\Component\\DependencyInjection\\Config\\ContainerParametersResourceChecker' => __DIR__ . '/..' . '/symfony/dependency-injection/Config/ContainerParametersResourceChecker.php', @@ -2362,8 +2422,10 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\DependencyInjection\\Dumper\\DumperInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/DumperInterface.php', 'Symfony\\Component\\DependencyInjection\\Dumper\\GraphvizDumper' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/GraphvizDumper.php', 'Symfony\\Component\\DependencyInjection\\Dumper\\PhpDumper' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/PhpDumper.php', + 'Symfony\\Component\\DependencyInjection\\Dumper\\Preloader' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/Preloader.php', 'Symfony\\Component\\DependencyInjection\\Dumper\\XmlDumper' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/XmlDumper.php', 'Symfony\\Component\\DependencyInjection\\Dumper\\YamlDumper' => __DIR__ . '/..' . '/symfony/dependency-injection/Dumper/YamlDumper.php', + 'Symfony\\Component\\DependencyInjection\\EnvVarLoaderInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/EnvVarLoaderInterface.php', 'Symfony\\Component\\DependencyInjection\\EnvVarProcessor' => __DIR__ . '/..' . '/symfony/dependency-injection/EnvVarProcessor.php', 'Symfony\\Component\\DependencyInjection\\EnvVarProcessorInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/EnvVarProcessorInterface.php', 'Symfony\\Component\\DependencyInjection\\Exception\\AutowiringFailedException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/AutowiringFailedException.php', @@ -2372,6 +2434,7 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\DependencyInjection\\Exception\\EnvParameterException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/EnvParameterException.php', 'Symfony\\Component\\DependencyInjection\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/ExceptionInterface.php', 'Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/InvalidArgumentException.php', + 'Symfony\\Component\\DependencyInjection\\Exception\\InvalidParameterTypeException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/InvalidParameterTypeException.php', 'Symfony\\Component\\DependencyInjection\\Exception\\LogicException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/LogicException.php', 'Symfony\\Component\\DependencyInjection\\Exception\\OutOfBoundsException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/OutOfBoundsException.php', 'Symfony\\Component\\DependencyInjection\\Exception\\ParameterCircularReferenceException' => __DIR__ . '/..' . '/symfony/dependency-injection/Exception/ParameterCircularReferenceException.php', @@ -2430,12 +2493,15 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\DependencyInjection\\Loader\\XmlFileLoader' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/XmlFileLoader.php', 'Symfony\\Component\\DependencyInjection\\Loader\\YamlFileLoader' => __DIR__ . '/..' . '/symfony/dependency-injection/Loader/YamlFileLoader.php', 'Symfony\\Component\\DependencyInjection\\Parameter' => __DIR__ . '/..' . '/symfony/dependency-injection/Parameter.php', + 'Symfony\\Component\\DependencyInjection\\ParameterBag\\ContainerBag' => __DIR__ . '/..' . '/symfony/dependency-injection/ParameterBag/ContainerBag.php', + 'Symfony\\Component\\DependencyInjection\\ParameterBag\\ContainerBagInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/ParameterBag/ContainerBagInterface.php', 'Symfony\\Component\\DependencyInjection\\ParameterBag\\EnvPlaceholderParameterBag' => __DIR__ . '/..' . '/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php', 'Symfony\\Component\\DependencyInjection\\ParameterBag\\FrozenParameterBag' => __DIR__ . '/..' . '/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php', 'Symfony\\Component\\DependencyInjection\\ParameterBag\\ParameterBag' => __DIR__ . '/..' . '/symfony/dependency-injection/ParameterBag/ParameterBag.php', 'Symfony\\Component\\DependencyInjection\\ParameterBag\\ParameterBagInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php', 'Symfony\\Component\\DependencyInjection\\Reference' => __DIR__ . '/..' . '/symfony/dependency-injection/Reference.php', 'Symfony\\Component\\DependencyInjection\\ResettableContainerInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/ResettableContainerInterface.php', + 'Symfony\\Component\\DependencyInjection\\ReverseContainer' => __DIR__ . '/..' . '/symfony/dependency-injection/ReverseContainer.php', 'Symfony\\Component\\DependencyInjection\\ServiceLocator' => __DIR__ . '/..' . '/symfony/dependency-injection/ServiceLocator.php', 'Symfony\\Component\\DependencyInjection\\ServiceSubscriberInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/ServiceSubscriberInterface.php', 'Symfony\\Component\\DependencyInjection\\TaggedContainerInterface' => __DIR__ . '/..' . '/symfony/dependency-injection/TaggedContainerInterface.php', @@ -2450,6 +2516,7 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php', 'Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcherInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php', 'Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener' => __DIR__ . '/..' . '/symfony/event-dispatcher/Debug/WrappedListener.php', + 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\AddEventAliasesPass' => __DIR__ . '/..' . '/symfony/event-dispatcher/DependencyInjection/AddEventAliasesPass.php', 'Symfony\\Component\\EventDispatcher\\DependencyInjection\\RegisterListenersPass' => __DIR__ . '/..' . '/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php', 'Symfony\\Component\\EventDispatcher\\Event' => __DIR__ . '/..' . '/symfony/event-dispatcher/Event.php', 'Symfony\\Component\\EventDispatcher\\EventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventDispatcher.php', @@ -2457,18 +2524,23 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventSubscriberInterface.php', 'Symfony\\Component\\EventDispatcher\\GenericEvent' => __DIR__ . '/..' . '/symfony/event-dispatcher/GenericEvent.php', 'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/ImmutableEventDispatcher.php', + 'Symfony\\Component\\EventDispatcher\\LegacyEventDispatcherProxy' => __DIR__ . '/..' . '/symfony/event-dispatcher/LegacyEventDispatcherProxy.php', + 'Symfony\\Component\\EventDispatcher\\LegacyEventProxy' => __DIR__ . '/..' . '/symfony/event-dispatcher/LegacyEventProxy.php', 'Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/filesystem/Exception/ExceptionInterface.php', 'Symfony\\Component\\Filesystem\\Exception\\FileNotFoundException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/FileNotFoundException.php', 'Symfony\\Component\\Filesystem\\Exception\\IOException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/IOException.php', 'Symfony\\Component\\Filesystem\\Exception\\IOExceptionInterface' => __DIR__ . '/..' . '/symfony/filesystem/Exception/IOExceptionInterface.php', + 'Symfony\\Component\\Filesystem\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/InvalidArgumentException.php', 'Symfony\\Component\\Filesystem\\Filesystem' => __DIR__ . '/..' . '/symfony/filesystem/Filesystem.php', 'Symfony\\Component\\Filesystem\\LockHandler' => __DIR__ . '/..' . '/symfony/filesystem/LockHandler.php', 'Symfony\\Component\\Finder\\Comparator\\Comparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/Comparator.php', 'Symfony\\Component\\Finder\\Comparator\\DateComparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/DateComparator.php', 'Symfony\\Component\\Finder\\Comparator\\NumberComparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/NumberComparator.php', 'Symfony\\Component\\Finder\\Exception\\AccessDeniedException' => __DIR__ . '/..' . '/symfony/finder/Exception/AccessDeniedException.php', + 'Symfony\\Component\\Finder\\Exception\\DirectoryNotFoundException' => __DIR__ . '/..' . '/symfony/finder/Exception/DirectoryNotFoundException.php', 'Symfony\\Component\\Finder\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/finder/Exception/ExceptionInterface.php', 'Symfony\\Component\\Finder\\Finder' => __DIR__ . '/..' . '/symfony/finder/Finder.php', + 'Symfony\\Component\\Finder\\Gitignore' => __DIR__ . '/..' . '/symfony/finder/Gitignore.php', 'Symfony\\Component\\Finder\\Glob' => __DIR__ . '/..' . '/symfony/finder/Glob.php', 'Symfony\\Component\\Finder\\Iterator\\CustomFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/CustomFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\DateRangeFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/DateRangeFilterIterator.php', @@ -2478,6 +2550,7 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\Finder\\Iterator\\FilecontentFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/FilecontentFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\FilenameFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/FilenameFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\FilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/FilterIterator.php', + 'Symfony\\Component\\Finder\\Iterator\\LazyIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/LazyIterator.php', 'Symfony\\Component\\Finder\\Iterator\\MultiplePcreFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/MultiplePcreFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\PathFilterIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/PathFilterIterator.php', 'Symfony\\Component\\Finder\\Iterator\\RecursiveDirectoryIterator' => __DIR__ . '/..' . '/symfony/finder/Iterator/RecursiveDirectoryIterator.php', @@ -2495,8 +2568,15 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\HttpFoundation\\ExpressionRequestMatcher' => __DIR__ . '/..' . '/symfony/http-foundation/ExpressionRequestMatcher.php', 'Symfony\\Component\\HttpFoundation\\FileBag' => __DIR__ . '/..' . '/symfony/http-foundation/FileBag.php', 'Symfony\\Component\\HttpFoundation\\File\\Exception\\AccessDeniedException' => __DIR__ . '/..' . '/symfony/http-foundation/File/Exception/AccessDeniedException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\CannotWriteFileException' => __DIR__ . '/..' . '/symfony/http-foundation/File/Exception/CannotWriteFileException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\ExtensionFileException' => __DIR__ . '/..' . '/symfony/http-foundation/File/Exception/ExtensionFileException.php', 'Symfony\\Component\\HttpFoundation\\File\\Exception\\FileException' => __DIR__ . '/..' . '/symfony/http-foundation/File/Exception/FileException.php', 'Symfony\\Component\\HttpFoundation\\File\\Exception\\FileNotFoundException' => __DIR__ . '/..' . '/symfony/http-foundation/File/Exception/FileNotFoundException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\FormSizeFileException' => __DIR__ . '/..' . '/symfony/http-foundation/File/Exception/FormSizeFileException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\IniSizeFileException' => __DIR__ . '/..' . '/symfony/http-foundation/File/Exception/IniSizeFileException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\NoFileException' => __DIR__ . '/..' . '/symfony/http-foundation/File/Exception/NoFileException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\NoTmpDirFileException' => __DIR__ . '/..' . '/symfony/http-foundation/File/Exception/NoTmpDirFileException.php', + 'Symfony\\Component\\HttpFoundation\\File\\Exception\\PartialFileException' => __DIR__ . '/..' . '/symfony/http-foundation/File/Exception/PartialFileException.php', 'Symfony\\Component\\HttpFoundation\\File\\Exception\\UnexpectedTypeException' => __DIR__ . '/..' . '/symfony/http-foundation/File/Exception/UnexpectedTypeException.php', 'Symfony\\Component\\HttpFoundation\\File\\Exception\\UploadException' => __DIR__ . '/..' . '/symfony/http-foundation/File/Exception/UploadException.php', 'Symfony\\Component\\HttpFoundation\\File\\File' => __DIR__ . '/..' . '/symfony/http-foundation/File/File.php', @@ -2510,6 +2590,7 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\HttpFoundation\\File\\Stream' => __DIR__ . '/..' . '/symfony/http-foundation/File/Stream.php', 'Symfony\\Component\\HttpFoundation\\File\\UploadedFile' => __DIR__ . '/..' . '/symfony/http-foundation/File/UploadedFile.php', 'Symfony\\Component\\HttpFoundation\\HeaderBag' => __DIR__ . '/..' . '/symfony/http-foundation/HeaderBag.php', + 'Symfony\\Component\\HttpFoundation\\HeaderUtils' => __DIR__ . '/..' . '/symfony/http-foundation/HeaderUtils.php', 'Symfony\\Component\\HttpFoundation\\IpUtils' => __DIR__ . '/..' . '/symfony/http-foundation/IpUtils.php', 'Symfony\\Component\\HttpFoundation\\JsonResponse' => __DIR__ . '/..' . '/symfony/http-foundation/JsonResponse.php', 'Symfony\\Component\\HttpFoundation\\ParameterBag' => __DIR__ . '/..' . '/symfony/http-foundation/ParameterBag.php', @@ -2535,11 +2616,14 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\AbstractSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MemcacheSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/MemcacheSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MemcachedSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MigratingSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/MigratingSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MongoDbSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeFileSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/NativeSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NullSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\PdoSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\RedisSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/RedisSessionHandler.php', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\SessionHandlerFactory' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\StrictSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\WriteCheckSessionHandler' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Handler/WriteCheckSessionHandler.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\MetadataBag' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/MetadataBag.php', @@ -2552,6 +2636,15 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php', 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\SessionStorageInterface' => __DIR__ . '/..' . '/symfony/http-foundation/Session/Storage/SessionStorageInterface.php', 'Symfony\\Component\\HttpFoundation\\StreamedResponse' => __DIR__ . '/..' . '/symfony/http-foundation/StreamedResponse.php', + 'Symfony\\Component\\HttpFoundation\\Test\\Constraint\\RequestAttributeValueSame' => __DIR__ . '/..' . '/symfony/http-foundation/Test/Constraint/RequestAttributeValueSame.php', + 'Symfony\\Component\\HttpFoundation\\Test\\Constraint\\ResponseCookieValueSame' => __DIR__ . '/..' . '/symfony/http-foundation/Test/Constraint/ResponseCookieValueSame.php', + 'Symfony\\Component\\HttpFoundation\\Test\\Constraint\\ResponseHasCookie' => __DIR__ . '/..' . '/symfony/http-foundation/Test/Constraint/ResponseHasCookie.php', + 'Symfony\\Component\\HttpFoundation\\Test\\Constraint\\ResponseHasHeader' => __DIR__ . '/..' . '/symfony/http-foundation/Test/Constraint/ResponseHasHeader.php', + 'Symfony\\Component\\HttpFoundation\\Test\\Constraint\\ResponseHeaderSame' => __DIR__ . '/..' . '/symfony/http-foundation/Test/Constraint/ResponseHeaderSame.php', + 'Symfony\\Component\\HttpFoundation\\Test\\Constraint\\ResponseIsRedirected' => __DIR__ . '/..' . '/symfony/http-foundation/Test/Constraint/ResponseIsRedirected.php', + 'Symfony\\Component\\HttpFoundation\\Test\\Constraint\\ResponseIsSuccessful' => __DIR__ . '/..' . '/symfony/http-foundation/Test/Constraint/ResponseIsSuccessful.php', + 'Symfony\\Component\\HttpFoundation\\Test\\Constraint\\ResponseStatusCodeSame' => __DIR__ . '/..' . '/symfony/http-foundation/Test/Constraint/ResponseStatusCodeSame.php', + 'Symfony\\Component\\HttpFoundation\\UrlHelper' => __DIR__ . '/..' . '/symfony/http-foundation/UrlHelper.php', 'Symfony\\Component\\HttpKernel\\Bundle\\Bundle' => __DIR__ . '/..' . '/symfony/http-kernel/Bundle/Bundle.php', 'Symfony\\Component\\HttpKernel\\Bundle\\BundleInterface' => __DIR__ . '/..' . '/symfony/http-kernel/Bundle/BundleInterface.php', 'Symfony\\Component\\HttpKernel\\CacheClearer\\CacheClearerInterface' => __DIR__ . '/..' . '/symfony/http-kernel/CacheClearer/CacheClearerInterface.php', @@ -2570,16 +2663,19 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolverInterface' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolverInterface.php', 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\DefaultValueResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolver/DefaultValueResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\NotTaggedControllerValueResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolver/NotTaggedControllerValueResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\RequestAttributeValueResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolver/RequestAttributeValueResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\RequestValueResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolver/RequestValueResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\ServiceValueResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolver/ServiceValueResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\SessionValueResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolver/SessionValueResolver.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\TraceableValueResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolver/TraceableValueResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\VariadicValueResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentResolver/VariadicValueResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolverInterface' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ArgumentValueResolverInterface.php', 'Symfony\\Component\\HttpKernel\\Controller\\ContainerControllerResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ContainerControllerResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ControllerReference' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ControllerReference.php', 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ControllerResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ControllerResolverInterface.php', + 'Symfony\\Component\\HttpKernel\\Controller\\ErrorController' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/ErrorController.php', 'Symfony\\Component\\HttpKernel\\Controller\\TraceableArgumentResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/TraceableArgumentResolver.php', 'Symfony\\Component\\HttpKernel\\Controller\\TraceableControllerResolver' => __DIR__ . '/..' . '/symfony/http-kernel/Controller/TraceableControllerResolver.php', 'Symfony\\Component\\HttpKernel\\DataCollector\\AjaxDataCollector' => __DIR__ . '/..' . '/symfony/http-kernel/DataCollector/AjaxDataCollector.php', @@ -2608,6 +2704,7 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\HttpKernel\\DependencyInjection\\LoggerPass' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/LoggerPass.php', 'Symfony\\Component\\HttpKernel\\DependencyInjection\\MergeExtensionConfigurationPass' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/MergeExtensionConfigurationPass.php', 'Symfony\\Component\\HttpKernel\\DependencyInjection\\RegisterControllerArgumentLocatorsPass' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php', + 'Symfony\\Component\\HttpKernel\\DependencyInjection\\RegisterLocaleAwareServicesPass' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/RegisterLocaleAwareServicesPass.php', 'Symfony\\Component\\HttpKernel\\DependencyInjection\\RemoveEmptyControllerArgumentLocatorsPass' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php', 'Symfony\\Component\\HttpKernel\\DependencyInjection\\ResettableServicePass' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/ResettableServicePass.php', 'Symfony\\Component\\HttpKernel\\DependencyInjection\\ServicesResetter' => __DIR__ . '/..' . '/symfony/http-kernel/DependencyInjection/ServicesResetter.php', @@ -2615,9 +2712,12 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\HttpKernel\\EventListener\\AbstractTestSessionListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/AbstractTestSessionListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\AddRequestFormatsListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/AddRequestFormatsListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\DebugHandlersListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/DebugHandlersListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\DisallowRobotsIndexingListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/DisallowRobotsIndexingListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\DumpListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/DumpListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\ErrorListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/ErrorListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\ExceptionListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/ExceptionListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\FragmentListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/FragmentListener.php', + 'Symfony\\Component\\HttpKernel\\EventListener\\LocaleAwareListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/LocaleAwareListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\LocaleListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/LocaleListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\ProfilerListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/ProfilerListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/ResponseListener.php', @@ -2629,6 +2729,9 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\HttpKernel\\EventListener\\TestSessionListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/TestSessionListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\TranslatorListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/TranslatorListener.php', 'Symfony\\Component\\HttpKernel\\EventListener\\ValidateRequestListener' => __DIR__ . '/..' . '/symfony/http-kernel/EventListener/ValidateRequestListener.php', + 'Symfony\\Component\\HttpKernel\\Event\\ControllerArgumentsEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/ControllerArgumentsEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\ControllerEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/ControllerEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/ExceptionEvent.php', 'Symfony\\Component\\HttpKernel\\Event\\FilterControllerArgumentsEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/FilterControllerArgumentsEvent.php', 'Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/FilterControllerEvent.php', 'Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/FilterResponseEvent.php', @@ -2638,9 +2741,14 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/GetResponseForExceptionEvent.php', 'Symfony\\Component\\HttpKernel\\Event\\KernelEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/KernelEvent.php', 'Symfony\\Component\\HttpKernel\\Event\\PostResponseEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/PostResponseEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\RequestEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/RequestEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\ResponseEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/ResponseEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\TerminateEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/TerminateEvent.php', + 'Symfony\\Component\\HttpKernel\\Event\\ViewEvent' => __DIR__ . '/..' . '/symfony/http-kernel/Event/ViewEvent.php', 'Symfony\\Component\\HttpKernel\\Exception\\AccessDeniedHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/AccessDeniedHttpException.php', 'Symfony\\Component\\HttpKernel\\Exception\\BadRequestHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/BadRequestHttpException.php', 'Symfony\\Component\\HttpKernel\\Exception\\ConflictHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/ConflictHttpException.php', + 'Symfony\\Component\\HttpKernel\\Exception\\ControllerDoesNotReturnResponseException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/ControllerDoesNotReturnResponseException.php', 'Symfony\\Component\\HttpKernel\\Exception\\GoneHttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/GoneHttpException.php', 'Symfony\\Component\\HttpKernel\\Exception\\HttpException' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/HttpException.php', 'Symfony\\Component\\HttpKernel\\Exception\\HttpExceptionInterface' => __DIR__ . '/..' . '/symfony/http-kernel/Exception/HttpExceptionInterface.php', @@ -2673,7 +2781,9 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\HttpKernel\\HttpCache\\StoreInterface' => __DIR__ . '/..' . '/symfony/http-kernel/HttpCache/StoreInterface.php', 'Symfony\\Component\\HttpKernel\\HttpCache\\SubRequestHandler' => __DIR__ . '/..' . '/symfony/http-kernel/HttpCache/SubRequestHandler.php', 'Symfony\\Component\\HttpKernel\\HttpCache\\SurrogateInterface' => __DIR__ . '/..' . '/symfony/http-kernel/HttpCache/SurrogateInterface.php', + 'Symfony\\Component\\HttpKernel\\HttpClientKernel' => __DIR__ . '/..' . '/symfony/http-kernel/HttpClientKernel.php', 'Symfony\\Component\\HttpKernel\\HttpKernel' => __DIR__ . '/..' . '/symfony/http-kernel/HttpKernel.php', + 'Symfony\\Component\\HttpKernel\\HttpKernelBrowser' => __DIR__ . '/..' . '/symfony/http-kernel/HttpKernelBrowser.php', 'Symfony\\Component\\HttpKernel\\HttpKernelInterface' => __DIR__ . '/..' . '/symfony/http-kernel/HttpKernelInterface.php', 'Symfony\\Component\\HttpKernel\\Kernel' => __DIR__ . '/..' . '/symfony/http-kernel/Kernel.php', 'Symfony\\Component\\HttpKernel\\KernelEvents' => __DIR__ . '/..' . '/symfony/http-kernel/KernelEvents.php', @@ -2697,7 +2807,9 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\Routing\\Exception\\NoConfigurationException' => __DIR__ . '/..' . '/symfony/routing/Exception/NoConfigurationException.php', 'Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException' => __DIR__ . '/..' . '/symfony/routing/Exception/ResourceNotFoundException.php', 'Symfony\\Component\\Routing\\Exception\\RouteNotFoundException' => __DIR__ . '/..' . '/symfony/routing/Exception/RouteNotFoundException.php', + 'Symfony\\Component\\Routing\\Generator\\CompiledUrlGenerator' => __DIR__ . '/..' . '/symfony/routing/Generator/CompiledUrlGenerator.php', 'Symfony\\Component\\Routing\\Generator\\ConfigurableRequirementsInterface' => __DIR__ . '/..' . '/symfony/routing/Generator/ConfigurableRequirementsInterface.php', + 'Symfony\\Component\\Routing\\Generator\\Dumper\\CompiledUrlGeneratorDumper' => __DIR__ . '/..' . '/symfony/routing/Generator/Dumper/CompiledUrlGeneratorDumper.php', 'Symfony\\Component\\Routing\\Generator\\Dumper\\GeneratorDumper' => __DIR__ . '/..' . '/symfony/routing/Generator/Dumper/GeneratorDumper.php', 'Symfony\\Component\\Routing\\Generator\\Dumper\\GeneratorDumperInterface' => __DIR__ . '/..' . '/symfony/routing/Generator/Dumper/GeneratorDumperInterface.php', 'Symfony\\Component\\Routing\\Generator\\Dumper\\PhpGeneratorDumper' => __DIR__ . '/..' . '/symfony/routing/Generator/Dumper/PhpGeneratorDumper.php', @@ -2713,13 +2825,18 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\Routing\\Loader\\Configurator\\RoutingConfigurator' => __DIR__ . '/..' . '/symfony/routing/Loader/Configurator/RoutingConfigurator.php', 'Symfony\\Component\\Routing\\Loader\\Configurator\\Traits\\AddTrait' => __DIR__ . '/..' . '/symfony/routing/Loader/Configurator/Traits/AddTrait.php', 'Symfony\\Component\\Routing\\Loader\\Configurator\\Traits\\RouteTrait' => __DIR__ . '/..' . '/symfony/routing/Loader/Configurator/Traits/RouteTrait.php', + 'Symfony\\Component\\Routing\\Loader\\ContainerLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/ContainerLoader.php', 'Symfony\\Component\\Routing\\Loader\\DependencyInjection\\ServiceRouterLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/DependencyInjection/ServiceRouterLoader.php', 'Symfony\\Component\\Routing\\Loader\\DirectoryLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/DirectoryLoader.php', 'Symfony\\Component\\Routing\\Loader\\GlobFileLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/GlobFileLoader.php', + 'Symfony\\Component\\Routing\\Loader\\ObjectLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/ObjectLoader.php', 'Symfony\\Component\\Routing\\Loader\\ObjectRouteLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/ObjectRouteLoader.php', 'Symfony\\Component\\Routing\\Loader\\PhpFileLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/PhpFileLoader.php', 'Symfony\\Component\\Routing\\Loader\\XmlFileLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/XmlFileLoader.php', 'Symfony\\Component\\Routing\\Loader\\YamlFileLoader' => __DIR__ . '/..' . '/symfony/routing/Loader/YamlFileLoader.php', + 'Symfony\\Component\\Routing\\Matcher\\CompiledUrlMatcher' => __DIR__ . '/..' . '/symfony/routing/Matcher/CompiledUrlMatcher.php', + 'Symfony\\Component\\Routing\\Matcher\\Dumper\\CompiledUrlMatcherDumper' => __DIR__ . '/..' . '/symfony/routing/Matcher/Dumper/CompiledUrlMatcherDumper.php', + 'Symfony\\Component\\Routing\\Matcher\\Dumper\\CompiledUrlMatcherTrait' => __DIR__ . '/..' . '/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php', 'Symfony\\Component\\Routing\\Matcher\\Dumper\\DumperCollection' => __DIR__ . '/..' . '/symfony/routing/Matcher/Dumper/DumperCollection.php', 'Symfony\\Component\\Routing\\Matcher\\Dumper\\DumperRoute' => __DIR__ . '/..' . '/symfony/routing/Matcher/Dumper/DumperRoute.php', 'Symfony\\Component\\Routing\\Matcher\\Dumper\\MatcherDumper' => __DIR__ . '/..' . '/symfony/routing/Matcher/Dumper/MatcherDumper.php', @@ -2755,13 +2872,22 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\VarDumper\\Caster\\DOMCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/DOMCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\DateCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/DateCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\DoctrineCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/DoctrineCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\DsCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/DsCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\DsPairStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/DsPairStub.php', 'Symfony\\Component\\VarDumper\\Caster\\EnumStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/EnumStub.php', 'Symfony\\Component\\VarDumper\\Caster\\ExceptionCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ExceptionCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\FrameStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/FrameStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\GmpCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/GmpCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\ImagineCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ImagineCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\ImgStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ImgStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\IntlCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/IntlCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\LinkStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/LinkStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\MemcachedCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/MemcachedCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\MongoCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/MongoCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\MysqliCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/MysqliCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\PdoCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/PdoCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\PgSqlCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/PgSqlCaster.php', + 'Symfony\\Component\\VarDumper\\Caster\\ProxyManagerCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ProxyManagerCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\RedisCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/RedisCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\ReflectionCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ReflectionCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\ResourceCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/ResourceCaster.php', @@ -2769,6 +2895,7 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\VarDumper\\Caster\\StubCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/StubCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\SymfonyCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/SymfonyCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\TraceStub' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/TraceStub.php', + 'Symfony\\Component\\VarDumper\\Caster\\UuidCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/UuidCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\XmlReaderCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/XmlReaderCaster.php', 'Symfony\\Component\\VarDumper\\Caster\\XmlResourceCaster' => __DIR__ . '/..' . '/symfony/var-dumper/Caster/XmlResourceCaster.php', 'Symfony\\Component\\VarDumper\\Cloner\\AbstractCloner' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/AbstractCloner.php', @@ -2778,11 +2905,23 @@ class ComposerStaticInit5e7efdfe4e8f9526eb41991410b96239 'Symfony\\Component\\VarDumper\\Cloner\\DumperInterface' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/DumperInterface.php', 'Symfony\\Component\\VarDumper\\Cloner\\Stub' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/Stub.php', 'Symfony\\Component\\VarDumper\\Cloner\\VarCloner' => __DIR__ . '/..' . '/symfony/var-dumper/Cloner/VarCloner.php', + 'Symfony\\Component\\VarDumper\\Command\\Descriptor\\CliDescriptor' => __DIR__ . '/..' . '/symfony/var-dumper/Command/Descriptor/CliDescriptor.php', + 'Symfony\\Component\\VarDumper\\Command\\Descriptor\\DumpDescriptorInterface' => __DIR__ . '/..' . '/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php', + 'Symfony\\Component\\VarDumper\\Command\\Descriptor\\HtmlDescriptor' => __DIR__ . '/..' . '/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php', + 'Symfony\\Component\\VarDumper\\Command\\ServerDumpCommand' => __DIR__ . '/..' . '/symfony/var-dumper/Command/ServerDumpCommand.php', 'Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/AbstractDumper.php', 'Symfony\\Component\\VarDumper\\Dumper\\CliDumper' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/CliDumper.php', + 'Symfony\\Component\\VarDumper\\Dumper\\ContextProvider\\CliContextProvider' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php', + 'Symfony\\Component\\VarDumper\\Dumper\\ContextProvider\\ContextProviderInterface' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php', + 'Symfony\\Component\\VarDumper\\Dumper\\ContextProvider\\RequestContextProvider' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php', + 'Symfony\\Component\\VarDumper\\Dumper\\ContextProvider\\SourceContextProvider' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php', + 'Symfony\\Component\\VarDumper\\Dumper\\ContextualizedDumper' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/ContextualizedDumper.php', 'Symfony\\Component\\VarDumper\\Dumper\\DataDumperInterface' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/DataDumperInterface.php', 'Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/HtmlDumper.php', + 'Symfony\\Component\\VarDumper\\Dumper\\ServerDumper' => __DIR__ . '/..' . '/symfony/var-dumper/Dumper/ServerDumper.php', 'Symfony\\Component\\VarDumper\\Exception\\ThrowingCasterException' => __DIR__ . '/..' . '/symfony/var-dumper/Exception/ThrowingCasterException.php', + 'Symfony\\Component\\VarDumper\\Server\\Connection' => __DIR__ . '/..' . '/symfony/var-dumper/Server/Connection.php', + 'Symfony\\Component\\VarDumper\\Server\\DumpServer' => __DIR__ . '/..' . '/symfony/var-dumper/Server/DumpServer.php', 'Symfony\\Component\\VarDumper\\VarDumper' => __DIR__ . '/..' . '/symfony/var-dumper/VarDumper.php', 'Symfony\\Component\\Yaml\\Command\\LintCommand' => __DIR__ . '/..' . '/symfony/yaml/Command/LintCommand.php', 'Symfony\\Component\\Yaml\\Dumper' => __DIR__ . '/..' . '/symfony/yaml/Dumper.php', diff --git a/lib/composer/include_paths.php b/lib/composer/include_paths.php index d4fb96718..af33c1491 100644 --- a/lib/composer/include_paths.php +++ b/lib/composer/include_paths.php @@ -2,7 +2,7 @@ // include_paths.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( diff --git a/lib/composer/installed.json b/lib/composer/installed.json index b91aa4420..2f357f207 100644 --- a/lib/composer/installed.json +++ b/lib/composer/installed.json @@ -2037,17 +2037,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": { @@ -2056,12 +2056,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" ], @@ -2099,7 +2108,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" }, diff --git a/lib/composer/installed.php b/lib/composer/installed.php index 9e9ad2068..d29622692 100644 --- a/lib/composer/installed.php +++ b/lib/composer/installed.php @@ -1,40 +1,40 @@ array( + 'name' => '__root__', 'pretty_version' => 'dev-develop', 'version' => 'dev-develop', + 'reference' => '15b9ea45a020f2b526354bcea31e5d6c7dcdba68', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => 'e72030e52fc219089dcdcae6033b53b843d12a60', - 'name' => '__root__', 'dev' => true, ), 'versions' => array( '__root__' => array( 'pretty_version' => 'dev-develop', 'version' => 'dev-develop', + 'reference' => '15b9ea45a020f2b526354bcea31e5d6c7dcdba68', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => 'e72030e52fc219089dcdcae6033b53b843d12a60', 'dev_requirement' => false, ), 'combodo/tcpdf' => array( 'pretty_version' => '6.4.4', 'version' => '6.4.4.0', + 'reference' => '0e31c013ccd000aa6762e9186778aa6e259ac8e8', 'type' => 'library', 'install_path' => __DIR__ . '/../combodo/tcpdf', 'aliases' => array(), - 'reference' => '0e31c013ccd000aa6762e9186778aa6e259ac8e8', 'dev_requirement' => false, ), 'container-interop/container-interop' => array( 'pretty_version' => '1.2.0', 'version' => '1.2.0.0', + 'reference' => '79cbf1341c22ec75643d841642dd5d6acd83bdb8', 'type' => 'library', 'install_path' => __DIR__ . '/../container-interop/container-interop', 'aliases' => array(), - 'reference' => '79cbf1341c22ec75643d841642dd5d6acd83bdb8', 'dev_requirement' => false, ), 'container-interop/container-interop-implementation' => array( @@ -46,208 +46,208 @@ 'doctrine/lexer' => array( 'pretty_version' => '1.2.3', 'version' => '1.2.3.0', + 'reference' => 'c268e882d4dbdd85e36e4ad69e02dc284f89d229', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/lexer', 'aliases' => array(), - 'reference' => 'c268e882d4dbdd85e36e4ad69e02dc284f89d229', 'dev_requirement' => false, ), 'egulias/email-validator' => array( 'pretty_version' => '2.1.25', 'version' => '2.1.25.0', + 'reference' => '0dbf5d78455d4d6a41d186da50adc1122ec066f4', 'type' => 'library', 'install_path' => __DIR__ . '/../egulias/email-validator', 'aliases' => array(), - 'reference' => '0dbf5d78455d4d6a41d186da50adc1122ec066f4', 'dev_requirement' => false, ), 'firebase/php-jwt' => array( 'pretty_version' => 'v5.5.1', 'version' => '5.5.1.0', + 'reference' => '83b609028194aa042ea33b5af2d41a7427de80e6', 'type' => 'library', 'install_path' => __DIR__ . '/../firebase/php-jwt', 'aliases' => array(), - 'reference' => '83b609028194aa042ea33b5af2d41a7427de80e6', 'dev_requirement' => false, ), 'guzzlehttp/guzzle' => array( 'pretty_version' => '6.5.6', 'version' => '6.5.6.0', + 'reference' => 'f092dd734083473658de3ee4bef093ed77d2689c', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/guzzle', 'aliases' => array(), - 'reference' => 'f092dd734083473658de3ee4bef093ed77d2689c', 'dev_requirement' => false, ), 'guzzlehttp/promises' => array( 'pretty_version' => '1.5.1', 'version' => '1.5.1.0', + 'reference' => 'fe752aedc9fd8fcca3fe7ad05d419d32998a06da', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/promises', 'aliases' => array(), - 'reference' => 'fe752aedc9fd8fcca3fe7ad05d419d32998a06da', 'dev_requirement' => false, ), 'guzzlehttp/psr7' => array( 'pretty_version' => '1.8.5', 'version' => '1.8.5.0', + 'reference' => '337e3ad8e5716c15f9657bd214d16cc5e69df268', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/psr7', 'aliases' => array(), - 'reference' => '337e3ad8e5716c15f9657bd214d16cc5e69df268', 'dev_requirement' => false, ), 'laminas/laminas-loader' => array( 'pretty_version' => '2.6.1', 'version' => '2.6.1.0', + 'reference' => '5d01c2c237ae9e68bec262f339947e2ea18979bc', 'type' => 'library', 'install_path' => __DIR__ . '/../laminas/laminas-loader', 'aliases' => array(), - 'reference' => '5d01c2c237ae9e68bec262f339947e2ea18979bc', 'dev_requirement' => false, ), 'laminas/laminas-mail' => array( 'pretty_version' => '2.12.5', 'version' => '2.12.5.0', + 'reference' => 'ed5b36a0deef4ffafe6138c2ae9cafcffafab856', 'type' => 'library', 'install_path' => __DIR__ . '/../laminas/laminas-mail', 'aliases' => array(), - 'reference' => 'ed5b36a0deef4ffafe6138c2ae9cafcffafab856', 'dev_requirement' => false, ), 'laminas/laminas-mime' => array( 'pretty_version' => '2.7.4', 'version' => '2.7.4.0', + 'reference' => 'e45a7d856bf7b4a7b5bd00d6371f9961dc233add', 'type' => 'library', 'install_path' => __DIR__ . '/../laminas/laminas-mime', 'aliases' => array(), - 'reference' => 'e45a7d856bf7b4a7b5bd00d6371f9961dc233add', 'dev_requirement' => false, ), 'laminas/laminas-servicemanager' => array( 'pretty_version' => '3.5.2', 'version' => '3.5.2.0', + 'reference' => '0669e1eec8d9f61e35a5bc5012796d49f418b259', 'type' => 'library', 'install_path' => __DIR__ . '/../laminas/laminas-servicemanager', 'aliases' => array(), - 'reference' => '0669e1eec8d9f61e35a5bc5012796d49f418b259', 'dev_requirement' => false, ), 'laminas/laminas-stdlib' => array( 'pretty_version' => '3.2.1', 'version' => '3.2.1.0', + 'reference' => '2b18347625a2f06a1a485acfbc870f699dbe51c6', 'type' => 'library', 'install_path' => __DIR__ . '/../laminas/laminas-stdlib', 'aliases' => array(), - 'reference' => '2b18347625a2f06a1a485acfbc870f699dbe51c6', 'dev_requirement' => false, ), 'laminas/laminas-validator' => array( 'pretty_version' => '2.13.5', 'version' => '2.13.5.0', + 'reference' => 'd334dddda43af263d6a7e5024fd2b013cb6981f7', 'type' => 'library', 'install_path' => __DIR__ . '/../laminas/laminas-validator', 'aliases' => array(), - 'reference' => 'd334dddda43af263d6a7e5024fd2b013cb6981f7', 'dev_requirement' => false, ), 'laminas/laminas-zendframework-bridge' => array( 'pretty_version' => '1.1.1', 'version' => '1.1.1.0', + 'reference' => '6ede70583e101030bcace4dcddd648f760ddf642', 'type' => 'library', 'install_path' => __DIR__ . '/../laminas/laminas-zendframework-bridge', 'aliases' => array(), - 'reference' => '6ede70583e101030bcace4dcddd648f760ddf642', 'dev_requirement' => false, ), 'league/oauth2-client' => array( 'pretty_version' => '2.6.1', 'version' => '2.6.1.0', + 'reference' => '2334c249907190c132364f5dae0287ab8666aa19', 'type' => 'library', 'install_path' => __DIR__ . '/../league/oauth2-client', 'aliases' => array(), - 'reference' => '2334c249907190c132364f5dae0287ab8666aa19', 'dev_requirement' => false, ), 'league/oauth2-google' => array( 'pretty_version' => '3.0.4', 'version' => '3.0.4.0', + 'reference' => '6b79441f244040760bed5fdcd092a2bda7cf34c6', 'type' => 'library', 'install_path' => __DIR__ . '/../league/oauth2-google', 'aliases' => array(), - 'reference' => '6b79441f244040760bed5fdcd092a2bda7cf34c6', 'dev_requirement' => false, ), 'nikic/php-parser' => array( 'pretty_version' => 'v4.13.2', 'version' => '4.13.2.0', + 'reference' => '210577fe3cf7badcc5814d99455df46564f3c077', 'type' => 'library', 'install_path' => __DIR__ . '/../nikic/php-parser', 'aliases' => array(), - 'reference' => '210577fe3cf7badcc5814d99455df46564f3c077', 'dev_requirement' => false, ), 'paragonie/random_compat' => array( 'pretty_version' => 'v2.0.18', 'version' => '2.0.18.0', + 'reference' => '0a58ef6e3146256cc3dc7cc393927bcc7d1b72db', 'type' => 'library', 'install_path' => __DIR__ . '/../paragonie/random_compat', 'aliases' => array(), - 'reference' => '0a58ef6e3146256cc3dc7cc393927bcc7d1b72db', 'dev_requirement' => false, ), 'pear/archive_tar' => array( 'pretty_version' => '1.4.14', 'version' => '1.4.14.0', + 'reference' => '4d761c5334c790e45ef3245f0864b8955c562caa', 'type' => 'library', 'install_path' => __DIR__ . '/../pear/archive_tar', 'aliases' => array(), - 'reference' => '4d761c5334c790e45ef3245f0864b8955c562caa', 'dev_requirement' => false, ), 'pear/console_getopt' => array( 'pretty_version' => 'v1.4.3', 'version' => '1.4.3.0', + 'reference' => 'a41f8d3e668987609178c7c4a9fe48fecac53fa0', 'type' => 'library', 'install_path' => __DIR__ . '/../pear/console_getopt', 'aliases' => array(), - 'reference' => 'a41f8d3e668987609178c7c4a9fe48fecac53fa0', 'dev_requirement' => false, ), 'pear/pear-core-minimal' => array( 'pretty_version' => 'v1.10.10', 'version' => '1.10.10.0', + 'reference' => '625a3c429d9b2c1546438679074cac1b089116a7', 'type' => 'library', 'install_path' => __DIR__ . '/../pear/pear-core-minimal', 'aliases' => array(), - 'reference' => '625a3c429d9b2c1546438679074cac1b089116a7', 'dev_requirement' => false, ), 'pear/pear_exception' => array( 'pretty_version' => 'v1.0.2', 'version' => '1.0.2.0', + 'reference' => 'b14fbe2ddb0b9f94f5b24cf08783d599f776fff0', 'type' => 'class', 'install_path' => __DIR__ . '/../pear/pear_exception', 'aliases' => array(), - 'reference' => 'b14fbe2ddb0b9f94f5b24cf08783d599f776fff0', 'dev_requirement' => false, ), 'pelago/emogrifier' => array( 'pretty_version' => 'v3.1.0', 'version' => '3.1.0.0', + 'reference' => 'f6a5c7d44612d86c3901c93f1592f5440e6b2cd8', 'type' => 'library', 'install_path' => __DIR__ . '/../pelago/emogrifier', 'aliases' => array(), - 'reference' => 'f6a5c7d44612d86c3901c93f1592f5440e6b2cd8', 'dev_requirement' => false, ), 'psr/cache' => array( 'pretty_version' => '1.0.1', 'version' => '1.0.1.0', + 'reference' => 'd11b50ad223250cf17b86e38383413f5a6764bf8', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/cache', 'aliases' => array(), - 'reference' => 'd11b50ad223250cf17b86e38383413f5a6764bf8', 'dev_requirement' => false, ), 'psr/cache-implementation' => array( @@ -259,10 +259,10 @@ 'psr/container' => array( 'pretty_version' => '1.0.0', 'version' => '1.0.0.0', + 'reference' => 'b7ce3b176482dbbc1245ebf52b181af44c2cf55f', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/container', 'aliases' => array(), - 'reference' => 'b7ce3b176482dbbc1245ebf52b181af44c2cf55f', 'dev_requirement' => false, ), 'psr/container-implementation' => array( @@ -275,10 +275,10 @@ 'psr/http-message' => array( 'pretty_version' => '1.0.1', 'version' => '1.0.1.0', + 'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/http-message', 'aliases' => array(), - 'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363', 'dev_requirement' => false, ), 'psr/http-message-implementation' => array( @@ -290,10 +290,10 @@ 'psr/log' => array( 'pretty_version' => '1.1.2', 'version' => '1.1.2.0', + 'reference' => '446d54b4cb6bf489fc9d75f55843658e6f25d801', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/log', 'aliases' => array(), - 'reference' => '446d54b4cb6bf489fc9d75f55843658e6f25d801', 'dev_requirement' => false, ), 'psr/log-implementation' => array( @@ -305,10 +305,10 @@ 'psr/simple-cache' => array( 'pretty_version' => '1.0.1', 'version' => '1.0.1.0', + 'reference' => '408d5eafb83c57f6365a3ca330ff23aa4a5fa39b', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/simple-cache', 'aliases' => array(), - 'reference' => '408d5eafb83c57f6365a3ca330ff23aa4a5fa39b', 'dev_requirement' => false, ), 'psr/simple-cache-implementation' => array( @@ -320,10 +320,10 @@ 'ralouphie/getallheaders' => array( 'pretty_version' => '3.0.3', 'version' => '3.0.3.0', + 'reference' => '120b605dfeb996808c31b6477290a714d356e822', 'type' => 'library', 'install_path' => __DIR__ . '/../ralouphie/getallheaders', 'aliases' => array(), - 'reference' => '120b605dfeb996808c31b6477290a714d356e822', 'dev_requirement' => false, ), 'rsky/pear-core-min' => array( @@ -333,300 +333,300 @@ ), ), 'scssphp/scssphp' => array( - 'pretty_version' => '1.0.6', - 'version' => '1.0.6.0', + 'pretty_version' => 'v1.10.3', + 'version' => '1.10.3.0', + 'reference' => '0f1e1516ed2412ad43e42a6a319e77624ba1f713', 'type' => 'library', 'install_path' => __DIR__ . '/../scssphp/scssphp', 'aliases' => array(), - 'reference' => '5b3c9d704950d8f9637f5110c36c281ec47dc13c', 'dev_requirement' => false, ), 'swiftmailer/swiftmailer' => array( 'pretty_version' => 'v6.3.0', 'version' => '6.3.0.0', + 'reference' => '8a5d5072dca8f48460fce2f4131fcc495eec654c', 'type' => 'library', 'install_path' => __DIR__ . '/../swiftmailer/swiftmailer', 'aliases' => array(), - 'reference' => '8a5d5072dca8f48460fce2f4131fcc495eec654c', 'dev_requirement' => false, ), 'symfony/cache' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => 'a7a14c4832760bd1fbd31be2859ffedc9b6ff813', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/cache', 'aliases' => array(), - 'reference' => 'a7a14c4832760bd1fbd31be2859ffedc9b6ff813', 'dev_requirement' => false, ), 'symfony/class-loader' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => 'a22265a9f3511c0212bf79f54910ca5a77c0e92c', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/class-loader', 'aliases' => array(), - 'reference' => 'a22265a9f3511c0212bf79f54910ca5a77c0e92c', 'dev_requirement' => false, ), 'symfony/config' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => 'bc6b3fd3930d4b53a60b42fe2ed6fc466b75f03f', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/config', 'aliases' => array(), - 'reference' => 'bc6b3fd3930d4b53a60b42fe2ed6fc466b75f03f', 'dev_requirement' => false, ), 'symfony/console' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => 'a10b1da6fc93080c180bba7219b5ff5b7518fe81', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/console', 'aliases' => array(), - 'reference' => 'a10b1da6fc93080c180bba7219b5ff5b7518fe81', 'dev_requirement' => false, ), 'symfony/css-selector' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => 'da3d9da2ce0026771f5fe64cb332158f1bd2bc33', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/css-selector', 'aliases' => array(), - 'reference' => 'da3d9da2ce0026771f5fe64cb332158f1bd2bc33', 'dev_requirement' => false, ), 'symfony/debug' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => 'ab42889de57fdfcfcc0759ab102e2fd4ea72dcae', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/debug', 'aliases' => array(), - 'reference' => 'ab42889de57fdfcfcc0759ab102e2fd4ea72dcae', 'dev_requirement' => false, ), 'symfony/dependency-injection' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => '51d2a2708c6ceadad84393f8581df1dcf9e5e84b', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/dependency-injection', 'aliases' => array(), - 'reference' => '51d2a2708c6ceadad84393f8581df1dcf9e5e84b', 'dev_requirement' => false, ), 'symfony/dotenv' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => '1022723ac4f56b001d99691d96c6025dbf1404f1', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/dotenv', 'aliases' => array(), - 'reference' => '1022723ac4f56b001d99691d96c6025dbf1404f1', 'dev_requirement' => false, ), 'symfony/event-dispatcher' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => '31fde73757b6bad247c54597beef974919ec6860', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/event-dispatcher', 'aliases' => array(), - 'reference' => '31fde73757b6bad247c54597beef974919ec6860', 'dev_requirement' => false, ), 'symfony/filesystem' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => 'e58d7841cddfed6e846829040dca2cca0ebbbbb3', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/filesystem', 'aliases' => array(), - 'reference' => 'e58d7841cddfed6e846829040dca2cca0ebbbbb3', 'dev_requirement' => false, ), 'symfony/finder' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => 'b6b6ad3db3edb1b4b1c1896b1975fb684994de6e', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/finder', 'aliases' => array(), - 'reference' => 'b6b6ad3db3edb1b4b1c1896b1975fb684994de6e', 'dev_requirement' => false, ), 'symfony/framework-bundle' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => '6c95e747b75ddd2af61152ce93bf87299d15710e', 'type' => 'symfony-bundle', 'install_path' => __DIR__ . '/../symfony/framework-bundle', 'aliases' => array(), - 'reference' => '6c95e747b75ddd2af61152ce93bf87299d15710e', 'dev_requirement' => false, ), 'symfony/http-foundation' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => 'b9885fcce6fe494201da4f70a9309770e9d13dc8', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/http-foundation', 'aliases' => array(), - 'reference' => 'b9885fcce6fe494201da4f70a9309770e9d13dc8', 'dev_requirement' => false, ), 'symfony/http-kernel' => array( 'pretty_version' => 'v3.4.49', 'version' => '3.4.49.0', + 'reference' => '5aa72405f5bd5583c36ed6e756acb17d3f98ac40', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/http-kernel', 'aliases' => array(), - 'reference' => '5aa72405f5bd5583c36ed6e756acb17d3f98ac40', 'dev_requirement' => false, ), 'symfony/polyfill-apcu' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', + 'reference' => 'b44b51e7814c23bfbd793a16ead5d7ce43ed23c5', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-apcu', 'aliases' => array(), - 'reference' => 'b44b51e7814c23bfbd793a16ead5d7ce43ed23c5', 'dev_requirement' => false, ), 'symfony/polyfill-ctype' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', + 'reference' => 'aed596913b70fae57be53d86faa2e9ef85a2297b', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-ctype', 'aliases' => array(), - 'reference' => 'aed596913b70fae57be53d86faa2e9ef85a2297b', 'dev_requirement' => false, ), 'symfony/polyfill-iconv' => array( 'pretty_version' => 'v1.25.0', 'version' => '1.25.0.0', + 'reference' => 'f1aed619e28cb077fc83fac8c4c0383578356e40', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-iconv', 'aliases' => array(), - 'reference' => 'f1aed619e28cb077fc83fac8c4c0383578356e40', 'dev_requirement' => false, ), 'symfony/polyfill-intl-idn' => array( 'pretty_version' => 'v1.25.0', 'version' => '1.25.0.0', + 'reference' => '749045c69efb97c70d25d7463abba812e91f3a44', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-intl-idn', 'aliases' => array(), - 'reference' => '749045c69efb97c70d25d7463abba812e91f3a44', 'dev_requirement' => false, ), 'symfony/polyfill-intl-normalizer' => array( 'pretty_version' => 'v1.25.0', 'version' => '1.25.0.0', + 'reference' => '8590a5f561694770bdcd3f9b5c69dde6945028e8', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer', 'aliases' => array(), - 'reference' => '8590a5f561694770bdcd3f9b5c69dde6945028e8', 'dev_requirement' => false, ), 'symfony/polyfill-mbstring' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', + 'reference' => 'b5f7b932ee6fa802fc792eabd77c4c88084517ce', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-mbstring', 'aliases' => array(), - 'reference' => 'b5f7b932ee6fa802fc792eabd77c4c88084517ce', 'dev_requirement' => false, ), 'symfony/polyfill-php56' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', + 'reference' => 'ea19621731cbd973a6702cfedef3419768bf3372', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-php56', 'aliases' => array(), - 'reference' => 'ea19621731cbd973a6702cfedef3419768bf3372', 'dev_requirement' => false, ), 'symfony/polyfill-php70' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', + 'reference' => '3fe414077251a81a1b15b1c709faf5c2fbae3d4e', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-php70', 'aliases' => array(), - 'reference' => '3fe414077251a81a1b15b1c709faf5c2fbae3d4e', 'dev_requirement' => false, ), 'symfony/polyfill-php72' => array( 'pretty_version' => 'v1.25.0', 'version' => '1.25.0.0', + 'reference' => '9a142215a36a3888e30d0a9eeea9766764e96976', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-php72', 'aliases' => array(), - 'reference' => '9a142215a36a3888e30d0a9eeea9766764e96976', 'dev_requirement' => false, ), 'symfony/polyfill-util' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', + 'reference' => '8df0c3e6a4b85df9a5c6f3f2f46fba5c5c47058a', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-util', 'aliases' => array(), - 'reference' => '8df0c3e6a4b85df9a5c6f3f2f46fba5c5c47058a', 'dev_requirement' => false, ), 'symfony/routing' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => '3e522ac69cadffd8131cc2b22157fa7662331a6c', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/routing', 'aliases' => array(), - 'reference' => '3e522ac69cadffd8131cc2b22157fa7662331a6c', 'dev_requirement' => false, ), 'symfony/stopwatch' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => '298b81faad4ce60e94466226b2abbb8c9bca7462', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/stopwatch', 'aliases' => array(), - 'reference' => '298b81faad4ce60e94466226b2abbb8c9bca7462', 'dev_requirement' => true, ), 'symfony/twig-bridge' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => '090d19d6f1ea5b9e1d79f372785aa5e5c9cd4042', 'type' => 'symfony-bridge', 'install_path' => __DIR__ . '/../symfony/twig-bridge', 'aliases' => array(), - 'reference' => '090d19d6f1ea5b9e1d79f372785aa5e5c9cd4042', 'dev_requirement' => false, ), 'symfony/twig-bundle' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => '977b3096e2df96bc8a8d2329e83466cfc30c373d', 'type' => 'symfony-bundle', 'install_path' => __DIR__ . '/../symfony/twig-bundle', 'aliases' => array(), - 'reference' => '977b3096e2df96bc8a8d2329e83466cfc30c373d', 'dev_requirement' => false, ), 'symfony/var-dumper' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => '0719f6cf4633a38b2c1585140998579ce23b4b7d', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/var-dumper', 'aliases' => array(), - 'reference' => '0719f6cf4633a38b2c1585140998579ce23b4b7d', 'dev_requirement' => true, ), 'symfony/web-profiler-bundle' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => 'ccb83b3a508f4a683e44f571f127beebdc315ff9', 'type' => 'symfony-bundle', 'install_path' => __DIR__ . '/../symfony/web-profiler-bundle', 'aliases' => array(), - 'reference' => 'ccb83b3a508f4a683e44f571f127beebdc315ff9', 'dev_requirement' => true, ), 'symfony/yaml' => array( 'pretty_version' => 'v3.4.47', 'version' => '3.4.47.0', + 'reference' => '88289caa3c166321883f67fe5130188ebbb47094', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/yaml', 'aliases' => array(), - 'reference' => '88289caa3c166321883f67fe5130188ebbb47094', 'dev_requirement' => false, ), 'tecnickcom/tcpdf' => array( @@ -638,28 +638,28 @@ 'thenetworg/oauth2-azure' => array( 'pretty_version' => 'v2.0.1', 'version' => '2.0.1.0', + 'reference' => '2649422a0dc74af32d21d9d738d37abcd5b03998', 'type' => 'library', 'install_path' => __DIR__ . '/../thenetworg/oauth2-azure', 'aliases' => array(), - 'reference' => '2649422a0dc74af32d21d9d738d37abcd5b03998', 'dev_requirement' => false, ), 'true/punycode' => array( 'pretty_version' => 'v2.1.1', 'version' => '2.1.1.0', + 'reference' => 'a4d0c11a36dd7f4e7cd7096076cab6d3378a071e', 'type' => 'library', 'install_path' => __DIR__ . '/../true/punycode', 'aliases' => array(), - 'reference' => 'a4d0c11a36dd7f4e7cd7096076cab6d3378a071e', 'dev_requirement' => false, ), 'twig/twig' => array( 'pretty_version' => 'v1.42.5', 'version' => '1.42.5.0', + 'reference' => '87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e', 'type' => 'library', 'install_path' => __DIR__ . '/../twig/twig', 'aliases' => array(), - 'reference' => '87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e', 'dev_requirement' => false, ), 'zendframework/zend-loader' => array( diff --git a/lib/scssphp/scssphp/README.md b/lib/scssphp/scssphp/README.md index f541e4888..65bb93ea7 100644 --- a/lib/scssphp/scssphp/README.md +++ b/lib/scssphp/scssphp/README.md @@ -1,12 +1,12 @@ # scssphp -### +### -[![Build](https://travis-ci.org/scssphp/scssphp.svg?branch=master)](http://travis-ci.org/scssphp/scssphp) +![Build](https://github.com/scssphp/scssphp/workflows/CI/badge.svg) [![License](https://poser.pugx.org/scssphp/scssphp/license)](https://packagist.org/packages/scssphp/scssphp) `scssphp` is a compiler for SCSS written in PHP. -Checkout the homepage, , for directions on how to use. +Checkout the homepage, , 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. diff --git a/lib/scssphp/scssphp/bin/pscss b/lib/scssphp/scssphp/bin/pscss index f944b6325..0f009d6bd 100644 --- a/lib/scssphp/scssphp/bin/pscss +++ b/lib/scssphp/scssphp/bin/pscss @@ -1,9 +1,10 @@ #!/usr/bin/env php 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(); } diff --git a/lib/scssphp/scssphp/composer.json b/lib/scssphp/scssphp/composer.json index ff0590fbd..3c60ca897 100644 --- a/lib/scssphp/scssphp/composer.json +++ b/lib/scssphp/scssphp/composer.json @@ -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 + } } } diff --git a/lib/scssphp/scssphp/scss.inc.php b/lib/scssphp/scssphp/scss.inc.php index e4ec7f181..459837805 100644 --- a/lib/scssphp/scssphp/scss.inc.php +++ b/lib/scssphp/scssphp/scss.inc.php @@ -1,34 +1,21 @@ + * + * @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) { diff --git a/lib/scssphp/scssphp/src/Block.php b/lib/scssphp/scssphp/src/Block.php index bb1afdd4b..96668dc66 100644 --- a/lib/scssphp/scssphp/src/Block.php +++ b/lib/scssphp/scssphp/src/Block.php @@ -1,8 +1,9 @@ + * + * @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; } diff --git a/lib/scssphp/scssphp/src/Cache.php b/lib/scssphp/scssphp/src/Cache.php index 422497f49..9731c60a7 100644 --- a/lib/scssphp/scssphp/src/Cache.php +++ b/lib/scssphp/scssphp/src/Cache.php @@ -1,8 +1,9 @@ + * + * @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 + */ 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() { diff --git a/lib/scssphp/scssphp/src/Colors.php b/lib/scssphp/scssphp/src/Colors.php index ef6409aca..2df39992b 100644 --- a/lib/scssphp/scssphp/src/Colors.php +++ b/lib/scssphp/scssphp/src/Colors.php @@ -1,8 +1,9 @@ + * + * @internal */ class Colors { @@ -23,12 +26,13 @@ class Colors * * @see http://www.w3.org/TR/css3-color * - * @var array + * @var array */ 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; diff --git a/lib/scssphp/scssphp/src/Compiler.php b/lib/scssphp/scssphp/src/Compiler.php index 711d3382f..eecfa481a 100644 --- a/lib/scssphp/scssphp/src/Compiler.php +++ b/lib/scssphp/scssphp/src/Compiler.php @@ -1,8 +1,9 @@ + * + * @final Extending the Compiler is deprecated */ class Compiler { + /** + * @deprecated + */ const LINE_COMMENTS = 1; + /** + * @deprecated + */ const DEBUG_INFO = 2; + /** + * @deprecated + */ const WITH_RULE = 1; + /** + * @deprecated + */ const WITH_MEDIA = 2; + /** + * @deprecated + */ const WITH_SUPPORTS = 4; + /** + * @deprecated + */ const WITH_ALL = 7; const SOURCE_MAP_NONE = 0; @@ -71,9 +106,9 @@ class Compiler const SOURCE_MAP_FILE = 2; /** - * @var array + * @var array */ - static protected $operatorNames = [ + protected static $operatorNames = [ '+' => 'add', '-' => 'sub', '*' => 'mul', @@ -87,102 +122,261 @@ class Compiler '<=' => 'lte', '>=' => 'gte', - '<=>' => 'cmp', ]; /** - * @var array + * @var array */ - static protected $namespaces = [ + protected static $namespaces = [ 'special' => '%', 'mixin' => '@', 'function' => '^', ]; - static public $true = [Type::T_KEYWORD, 'true']; - static public $false = [Type::T_KEYWORD, 'false']; - static public $null = [Type::T_NULL]; - static public $nullString = [Type::T_STRING, '', []]; - static public $defaultValue = [Type::T_KEYWORD, '']; - static public $selfSelector = [Type::T_SELF]; - static public $emptyList = [Type::T_LIST, '', []]; - static public $emptyMap = [Type::T_MAP, [], []]; - static public $emptyString = [Type::T_STRING, '"', []]; - static public $with = [Type::T_KEYWORD, 'with']; - static public $without = [Type::T_KEYWORD, 'without']; + public static $true = [Type::T_KEYWORD, 'true']; + public static $false = [Type::T_KEYWORD, 'false']; + /** @deprecated */ + public static $NaN = [Type::T_KEYWORD, 'NaN']; + /** @deprecated */ + public static $Infinity = [Type::T_KEYWORD, 'Infinity']; + public static $null = [Type::T_NULL]; + public static $nullString = [Type::T_STRING, '', []]; + public static $defaultValue = [Type::T_KEYWORD, '']; + public static $selfSelector = [Type::T_SELF]; + public static $emptyList = [Type::T_LIST, '', []]; + public static $emptyMap = [Type::T_MAP, [], []]; + public static $emptyString = [Type::T_STRING, '"', []]; + public static $with = [Type::T_KEYWORD, 'with']; + public static $without = [Type::T_KEYWORD, 'without']; + private static $emptyArgumentList = [Type::T_LIST, '', [], []]; - protected $importPaths = ['']; + /** + * @var array + */ + protected $importPaths = []; + /** + * @var array + */ protected $importCache = []; + + /** + * @var string[] + */ protected $importedFiles = []; + + /** + * @var array + * @phpstan-var array + */ protected $userFunctions = []; + /** + * @var array + */ protected $registeredVars = []; + /** + * @var array + */ protected $registeredFeatures = [ 'extend-selector-pseudoclass' => false, 'at-error' => true, - 'units-level-3' => false, + 'units-level-3' => true, 'global-variable-shadowing' => false, ]; + /** + * @var string|null + */ protected $encoding = null; + /** + * @var null + * @deprecated + */ protected $lineNumberStyle = null; + /** + * @var int|SourceMapGenerator + * @phpstan-var self::SOURCE_MAP_*|SourceMapGenerator + */ protected $sourceMap = self::SOURCE_MAP_NONE; + + /** + * @var array + * @phpstan-var array{sourceRoot?: string, sourceMapFilename?: string|null, sourceMapURL?: string|null, sourceMapWriteTo?: string|null, outputSourceFiles?: bool, sourceMapRootpath?: string, sourceMapBasepath?: string} + */ protected $sourceMapOptions = []; /** - * @var string|\ScssPhp\ScssPhp\Formatter + * @var bool */ - protected $formatter = 'ScssPhp\ScssPhp\Formatter\Nested'; + private $charset = true; + /** + * @var Formatter + */ + protected $formatter; + + /** + * @var string + * @phpstan-var class-string + */ + private $configuredFormatter = Expanded::class; + + /** + * @var Environment + */ protected $rootEnv; + /** + * @var OutputBlock|null + */ protected $rootBlock; /** * @var \ScssPhp\ScssPhp\Compiler\Environment */ protected $env; + /** + * @var OutputBlock|null + */ protected $scope; + /** + * @var Environment|null + */ protected $storeEnv; + /** + * @var bool|null + * + * @deprecated + */ protected $charsetSeen; + /** + * @var array + */ protected $sourceNames; + /** + * @var Cache|null + */ protected $cache; - protected $indentLevel; - protected $extends; - protected $extendsMap; - protected $parsedFiles; - protected $parser; - protected $sourceIndex; - protected $sourceLine; - protected $sourceColumn; - protected $stderr; - protected $shouldEvaluate; - protected $ignoreErrors; + /** + * @var bool + */ + protected $cacheCheckImportResolutions = false; + /** + * @var int + */ + protected $indentLevel; + /** + * @var array[] + */ + protected $extends; + /** + * @var array + */ + protected $extendsMap; + + /** + * @var array + */ + protected $parsedFiles = []; + + /** + * @var Parser|null + */ + protected $parser; + /** + * @var int|null + */ + protected $sourceIndex; + /** + * @var int|null + */ + protected $sourceLine; + /** + * @var int|null + */ + protected $sourceColumn; + /** + * @var bool|null + */ + protected $shouldEvaluate; + /** + * @var null + * @deprecated + */ + protected $ignoreErrors; + /** + * @var bool + */ + protected $ignoreCallStackMessage = false; + + /** + * @var array[] + */ protected $callStack = []; + /** + * @var array + * @phpstan-var list + */ + private $resolvedImports = []; + + /** + * The directory of the currently processed file + * + * @var string|null + */ + private $currentDirectory; + + /** + * The directory of the input file + * + * @var string + */ + private $rootDirectory; + + /** + * @var bool + */ + private $legacyCwdImportPath = true; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var array + */ + private $warnedChildFunctions = []; + /** * Constructor * * @param array|null $cacheOptions + * @phpstan-param array{cacheDir?: string, prefix?: string, forceRefresh?: string, checkImportResolutions?: bool}|null $cacheOptions */ public function __construct($cacheOptions = null) { - $this->parsedFiles = []; $this->sourceNames = []; if ($cacheOptions) { $this->cache = new Cache($cacheOptions); + if (!empty($cacheOptions['checkImportResolutions'])) { + $this->cacheCheckImportResolutions = true; + } } - $this->stderr = fopen('php://stderr', 'w'); + $this->logger = new StreamLogger(fopen('php://stderr', 'w'), true); } /** * Get compiler options * - * @return array + * @return array + * + * @internal */ public function getCompileOptions() { @@ -193,54 +387,97 @@ class Compiler 'encoding' => $this->encoding, 'sourceMap' => serialize($this->sourceMap), 'sourceMapOptions' => $this->sourceMapOptions, - 'formatter' => $this->formatter, + 'formatter' => $this->configuredFormatter, + 'legacyImportPath' => $this->legacyCwdImportPath, ]; return $options; } + /** + * Sets an alternative logger. + * + * Changing the logger in the middle of the compilation is not + * supported and will result in an undefined behavior. + * + * @param LoggerInterface $logger + * + * @return void + */ + public function setLogger(LoggerInterface $logger) + { + $this->logger = $logger; + } + /** * Set an alternative error output stream, for testing purpose only * * @param resource $handle + * + * @return void + * + * @deprecated Use {@see setLogger} instead */ public function setErrorOuput($handle) { - $this->stderr = $handle; + @trigger_error('The method "setErrorOuput" is deprecated. Use "setLogger" instead.', E_USER_DEPRECATED); + + $this->logger = new StreamLogger($handle); } /** * Compile scss * - * @api - * - * @param string $code - * @param string $path + * @param string $code + * @param string|null $path * * @return string + * + * @throws SassException when the source fails to compile + * + * @deprecated Use {@see compileString} instead. */ public function compile($code, $path = null) { - if ($this->cache) { - $cacheKey = ($path ? $path : "(stdin)") . ":" . md5($code); - $compileOptions = $this->getCompileOptions(); - $cache = $this->cache->getCache("compile", $cacheKey, $compileOptions); + @trigger_error(sprintf('The "%s" method is deprecated. Use "compileString" instead.', __METHOD__), E_USER_DEPRECATED); - if (is_array($cache) && isset($cache['dependencies']) && isset($cache['out'])) { - // check if any dependency file changed before accepting the cache - foreach ($cache['dependencies'] as $file => $mtime) { - if (! is_file($file) || filemtime($file) !== $mtime) { - unset($cache); - break; - } - } + $result = $this->compileString($code, $path); - if (isset($cache)) { - return $cache['out']; - } + $sourceMap = $result->getSourceMap(); + + if ($sourceMap !== null) { + if ($this->sourceMap instanceof SourceMapGenerator) { + $this->sourceMap->saveMap($sourceMap); + } elseif ($this->sourceMap === self::SOURCE_MAP_FILE) { + $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); + $sourceMapGenerator->saveMap($sourceMap); } } + return $result->getCss(); + } + + /** + * Compile scss + * + * @param string $source + * @param string|null $path + * + * @return CompilationResult + * + * @throws SassException when the source fails to compile + */ + public function compileString($source, $path = null) + { + if ($this->cache) { + $cacheKey = ($path ? $path : '(stdin)') . ':' . md5($source); + $compileOptions = $this->getCompileOptions(); + $cachedResult = $this->cache->getCache('compile', $cacheKey, $compileOptions); + + if ($cachedResult instanceof CachedResult && $this->isFreshCachedResult($cachedResult)) { + return $cachedResult->getResult(); + } + } $this->indentLevel = -1; $this->extends = []; @@ -251,73 +488,168 @@ class Compiler $this->env = null; $this->scope = null; $this->storeEnv = null; - $this->charsetSeen = null; $this->shouldEvaluate = null; + $this->ignoreCallStackMessage = false; + $this->parsedFiles = []; + $this->importedFiles = []; + $this->resolvedImports = []; - $this->parser = $this->parserFactory($path); - $tree = $this->parser->parse($code); - $this->parser = null; - - $this->formatter = new $this->formatter(); - $this->rootBlock = null; - $this->rootEnv = $this->pushEnv($tree); - - $this->injectVariables($this->registeredVars); - $this->compileRoot($tree); - $this->popEnv(); - - $sourceMapGenerator = null; - - if ($this->sourceMap) { - if (is_object($this->sourceMap) && $this->sourceMap instanceof SourceMapGenerator) { - $sourceMapGenerator = $this->sourceMap; - $this->sourceMap = self::SOURCE_MAP_FILE; - } elseif ($this->sourceMap !== self::SOURCE_MAP_NONE) { - $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); - } + if (!\is_null($path) && is_file($path)) { + $path = realpath($path) ?: $path; + $this->currentDirectory = dirname($path); + $this->rootDirectory = $this->currentDirectory; + } else { + $this->currentDirectory = null; + $this->rootDirectory = getcwd(); } - $out = $this->formatter->format($this->scope, $sourceMapGenerator); + try { + $this->parser = $this->parserFactory($path); + $tree = $this->parser->parse($source); + $this->parser = null; - if (! empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { - $sourceMap = $sourceMapGenerator->generateJson(); - $sourceMapUrl = null; + $this->formatter = new $this->configuredFormatter(); + $this->rootBlock = null; + $this->rootEnv = $this->pushEnv($tree); - switch ($this->sourceMap) { - case self::SOURCE_MAP_INLINE: - $sourceMapUrl = sprintf('data:application/json,%s', Util::encodeURIComponent($sourceMap)); - break; + $warnCallback = function ($message, $deprecation) { + $this->logger->warn($message, $deprecation); + }; + $previousWarnCallback = Warn::setCallback($warnCallback); - case self::SOURCE_MAP_FILE: - $sourceMapUrl = $sourceMapGenerator->saveMap($sourceMap); - break; + try { + $this->injectVariables($this->registeredVars); + $this->compileRoot($tree); + $this->popEnv(); + } finally { + Warn::setCallback($previousWarnCallback); } - $out .= sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl); + $sourceMapGenerator = null; + + if ($this->sourceMap) { + if (\is_object($this->sourceMap) && $this->sourceMap instanceof SourceMapGenerator) { + $sourceMapGenerator = $this->sourceMap; + $this->sourceMap = self::SOURCE_MAP_FILE; + } elseif ($this->sourceMap !== self::SOURCE_MAP_NONE) { + $sourceMapGenerator = new SourceMapGenerator($this->sourceMapOptions); + } + } + assert($this->scope !== null); + + $out = $this->formatter->format($this->scope, $sourceMapGenerator); + + $prefix = ''; + + if ($this->charset && strlen($out) !== Util::mbStrlen($out)) { + $prefix = '@charset "UTF-8";' . "\n"; + $out = $prefix . $out; + } + + $sourceMap = null; + + if (! empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) { + assert($sourceMapGenerator !== null); + $sourceMap = $sourceMapGenerator->generateJson($prefix); + $sourceMapUrl = null; + + switch ($this->sourceMap) { + case self::SOURCE_MAP_INLINE: + $sourceMapUrl = sprintf('data:application/json,%s', Util::encodeURIComponent($sourceMap)); + break; + + case self::SOURCE_MAP_FILE: + if (isset($this->sourceMapOptions['sourceMapURL'])) { + $sourceMapUrl = $this->sourceMapOptions['sourceMapURL']; + } + break; + } + + if ($sourceMapUrl !== null) { + $out .= sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl); + } + } + } catch (SassScriptException $e) { + throw new CompilerException($this->addLocationToMessage($e->getMessage()), 0, $e); } + $includedFiles = []; + + foreach ($this->resolvedImports as $resolvedImport) { + $includedFiles[$resolvedImport['filePath']] = $resolvedImport['filePath']; + } + + $result = new CompilationResult($out, $sourceMap, array_values($includedFiles)); + if ($this->cache && isset($cacheKey) && isset($compileOptions)) { - $v = [ - 'dependencies' => $this->getParsedFiles(), - 'out' => &$out, - ]; - - $this->cache->setCache("compile", $cacheKey, $v, $compileOptions); + $this->cache->setCache('compile', $cacheKey, new CachedResult($result, $this->parsedFiles, $this->resolvedImports), $compileOptions); } - return $out; + // Reset state to free memory + // TODO in 2.0, reset parsedFiles as well when the getter is removed. + $this->resolvedImports = []; + $this->importedFiles = []; + + return $result; + } + + /** + * @param CachedResult $result + * + * @return bool + */ + private function isFreshCachedResult(CachedResult $result) + { + // check if any dependency file changed since the result was compiled + foreach ($result->getParsedFiles() as $file => $mtime) { + if (! is_file($file) || filemtime($file) !== $mtime) { + return false; + } + } + + if ($this->cacheCheckImportResolutions) { + $resolvedImports = []; + + foreach ($result->getResolvedImports() as $import) { + $currentDir = $import['currentDir']; + $path = $import['path']; + // store the check across all the results in memory to avoid multiple findImport() on the same path + // with same context. + // this is happening in a same hit with multiple compilations (especially with big frameworks) + if (empty($resolvedImports[$currentDir][$path])) { + $resolvedImports[$currentDir][$path] = $this->findImport($path, $currentDir); + } + + if ($resolvedImports[$currentDir][$path] !== $import['filePath']) { + return false; + } + } + } + + return true; } /** * Instantiate parser * - * @param string $path + * @param string|null $path * * @return \ScssPhp\ScssPhp\Parser */ protected function parserFactory($path) { - $parser = new Parser($path, count($this->sourceNames), $this->encoding, $this->cache); + // https://sass-lang.com/documentation/at-rules/import + // CSS files imported by Sass don’t allow any special Sass features. + // In order to make sure authors don’t accidentally write Sass in their CSS, + // all Sass features that aren’t also valid CSS will produce errors. + // Otherwise, the CSS will be rendered as-is. It can even be extended! + $cssOnly = false; + + if ($path !== null && substr($path, -4) === '.css') { + $cssOnly = true; + } + + $parser = new Parser($path, \count($this->sourceNames), $this->encoding, $this->cache, $cssOnly, $this->logger); $this->sourceNames[] = $path; $this->addParsedFile($path); @@ -331,12 +663,12 @@ class Compiler * @param array $target * @param array $origin * - * @return boolean + * @return bool */ protected function isSelfExtend($target, $origin) { foreach ($origin as $sel) { - if (in_array($target, $sel)) { + if (\in_array($target, $sel)) { return true; } } @@ -347,17 +679,15 @@ class Compiler /** * Push extends * - * @param array $target + * @param string[] $target * @param array $origin * @param array|null $block + * + * @return void */ protected function pushExtends($target, $origin, $block) { - if ($this->isSelfExtend($target, $origin)) { - return; - } - - $i = count($this->extends); + $i = \count($this->extends); $this->extends[] = [$target, $origin, $block]; foreach ($target as $part) { @@ -372,14 +702,14 @@ class Compiler /** * Make output block * - * @param string $type - * @param array $selectors + * @param string|null $type + * @param string[]|null $selectors * * @return \ScssPhp\ScssPhp\Formatter\OutputBlock */ protected function makeOutputBlock($type, $selectors = null) { - $out = new OutputBlock; + $out = new OutputBlock(); $out->type = $type; $out->lines = []; $out->children = []; @@ -392,9 +722,9 @@ class Compiler $out->sourceLine = $this->env->block->sourceLine; $out->sourceColumn = $this->env->block->sourceColumn; } else { - $out->sourceName = null; - $out->sourceLine = null; - $out->sourceColumn = null; + $out->sourceName = isset($this->sourceNames[$this->sourceIndex]) ? $this->sourceNames[$this->sourceIndex] : '(stdin)'; + $out->sourceLine = $this->sourceLine; + $out->sourceColumn = $this->sourceColumn; } return $out; @@ -404,18 +734,23 @@ class Compiler * Compile root * * @param \ScssPhp\ScssPhp\Block $rootBlock + * + * @return void */ protected function compileRoot(Block $rootBlock) { $this->rootBlock = $this->scope = $this->makeOutputBlock(Type::T_ROOT); $this->compileChildrenNoReturn($rootBlock->children, $this->scope); + assert($this->scope !== null); $this->flattenSelectors($this->scope); $this->missingSelectors(); } /** * Report missing selectors + * + * @return void */ protected function missingSelectors() { @@ -435,7 +770,7 @@ class Compiler $origin = $this->collapseSelectors($origin); $this->sourceLine = $block[Parser::SOURCE_LINE]; - $this->throwError("\"$origin\" failed to @extend \"$target\". The selector \"$target\" was not found."); + throw $this->error("\"$origin\" failed to @extend \"$target\". The selector \"$target\" was not found."); } } @@ -444,6 +779,8 @@ class Compiler * * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block * @param string $parentKey + * + * @return void */ protected function flattenSelectors(OutputBlock $block, $parentKey = null) { @@ -453,7 +790,7 @@ class Compiler foreach ($block->selectors as $s) { $selectors[] = $s; - if (! is_array($s)) { + if (! \is_array($s)) { continue; } @@ -486,7 +823,8 @@ class Compiler $block->selectors[] = $this->compileSelector($selector); } - if ($placeholderSelector && 0 === count($block->selectors) && null !== $parentKey) { + if ($placeholderSelector && 0 === \count($block->selectors) && null !== $parentKey) { + assert($block->parent !== null); unset($block->parent->children[$parentKey]); return; @@ -499,7 +837,7 @@ class Compiler } /** - * Glue parts of :not( or :nth-child( ... that are in general splitted in selectors parts + * Glue parts of :not( or :nth-child( ... that are in general split in selectors parts * * @param array $parts * @@ -510,19 +848,20 @@ class Compiler $new = []; foreach ($parts as $part) { - if (is_array($part)) { + if (\is_array($part)) { $part = $this->glueFunctionSelectors($part); $new[] = $part; } else { // a selector part finishing with a ) is the last part of a :not( or :nth-child( // and need to be joined to this - if (count($new) && is_string($new[count($new) - 1]) && - strlen($part) && substr($part, -1) === ')' && strpos($part, '(') === false + if ( + \count($new) && \is_string($new[\count($new) - 1]) && + \strlen($part) && substr($part, -1) === ')' && strpos($part, '(') === false ) { - while (count($new)>1 && substr($new[count($new) - 1], -1) !== '(') { + while (\count($new) > 1 && substr($new[\count($new) - 1], -1) !== '(') { $part = array_pop($new) . $part; } - $new[count($new) - 1] .= $part; + $new[\count($new) - 1] .= $part; } else { $new[] = $part; } @@ -535,21 +874,24 @@ class Compiler /** * Match extends * - * @param array $selector - * @param array $out - * @param integer $from - * @param boolean $initial + * @param array $selector + * @param array $out + * @param int $from + * @param bool $initial + * + * @return void */ protected function matchExtends($selector, &$out, $from = 0, $initial = true) { static $partsPile = []; $selector = $this->glueFunctionSelectors($selector); - if (count($selector) == 1 && in_array(reset($selector), $partsPile)) { + if (\count($selector) == 1 && \in_array(reset($selector), $partsPile)) { return; } $outRecurs = []; + foreach ($selector as $i => $part) { if ($i < $from) { continue; @@ -557,41 +899,43 @@ class Compiler // check that we are not building an infinite loop of extensions // if the new part is just including a previous part don't try to extend anymore - if (count($part) > 1) { + if (\count($part) > 1) { foreach ($partsPile as $previousPart) { - if (! count(array_diff($previousPart, $part))) { + if (! \count(array_diff($previousPart, $part))) { continue 2; } } } $partsPile[] = $part; + if ($this->matchExtendsSingle($part, $origin, $initial)) { - $after = array_slice($selector, $i + 1); - $before = array_slice($selector, 0, $i); + $after = \array_slice($selector, $i + 1); + $before = \array_slice($selector, 0, $i); list($before, $nonBreakableBefore) = $this->extractRelationshipFromFragment($before); foreach ($origin as $new) { $k = 0; // remove shared parts - if (count($new) > 1) { + if (\count($new) > 1) { while ($k < $i && isset($new[$k]) && $selector[$k] === $new[$k]) { $k++; } } - if (count($nonBreakableBefore) and $k == count($new)) { + + if (\count($nonBreakableBefore) && $k === \count($new)) { $k--; } $replacement = []; - $tempReplacement = $k > 0 ? array_slice($new, $k) : $new; + $tempReplacement = $k > 0 ? \array_slice($new, $k) : $new; - for ($l = count($tempReplacement) - 1; $l >= 0; $l--) { + for ($l = \count($tempReplacement) - 1; $l >= 0; $l--) { $slice = []; foreach ($tempReplacement[$l] as $chunk) { - if (! in_array($chunk, $slice)) { + if (! \in_array($chunk, $slice)) { $slice[] = $chunk; } } @@ -603,7 +947,7 @@ class Compiler } } - $afterBefore = $l != 0 ? array_slice($tempReplacement, 0, $l) : []; + $afterBefore = $l != 0 ? \array_slice($tempReplacement, 0, $l) : []; // Merge shared direct relationships. $mergedBefore = $this->mergeDirectRelationships($afterBefore, $nonBreakableBefore); @@ -622,17 +966,18 @@ class Compiler $this->pushOrMergeExtentedSelector($out, $result); // recursively check for more matches - $startRecurseFrom = count($before) + min(count($nonBreakableBefore), count($mergedBefore)); - if (count($origin) > 1) { + $startRecurseFrom = \count($before) + min(\count($nonBreakableBefore), \count($mergedBefore)); + + if (\count($origin) > 1) { $this->matchExtends($result, $out, $startRecurseFrom, false); } else { $this->matchExtends($result, $outRecurs, $startRecurseFrom, false); } // selector sequence merging - if (! empty($before) && count($new) > 1) { - $preSharedParts = $k > 0 ? array_slice($before, 0, $k) : []; - $postSharedParts = $k > 0 ? array_slice($before, $k) : $before; + if (! empty($before) && \count($new) > 1) { + $preSharedParts = $k > 0 ? \array_slice($before, 0, $k) : []; + $postSharedParts = $k > 0 ? \array_slice($before, $k) : $before; list($betweenSharedParts, $nonBreakabl2) = $this->extractRelationshipFromFragment($afterBefore); @@ -652,7 +997,8 @@ class Compiler } array_pop($partsPile); } - while (count($outRecurs)) { + + while (\count($outRecurs)) { $result = array_shift($outRecurs); $this->pushOrMergeExtentedSelector($out, $result); } @@ -660,16 +1006,21 @@ class Compiler /** * Test a part for being a pseudo selector + * * @param string $part - * @param array $matches + * @param array $matches + * * @return bool */ protected function isPseudoSelector($part, &$matches) { - if (strpos($part, ":") === 0 - && preg_match(",^::?([\w-]+)\((.+)\)$,", $part, $matches)) { + if ( + strpos($part, ':') === 0 && + preg_match(",^::?([\w-]+)\((.+)\)$,", $part, $matches) + ) { return true; } + return false; } @@ -679,25 +1030,35 @@ class Compiler * - same as previous * - in a white list * in this case we merge the pseudo selector content + * * @param array $out * @param array $extended + * + * @return void */ protected function pushOrMergeExtentedSelector(&$out, $extended) { - if (count($out) && count($extended) === 1 && count(reset($extended)) === 1) { + if (\count($out) && \count($extended) === 1 && \count(reset($extended)) === 1) { $single = reset($extended); $part = reset($single); - if ($this->isPseudoSelector($part, $matchesExtended) - && in_array($matchesExtended[1], [ 'slotted' ])) { + + if ( + $this->isPseudoSelector($part, $matchesExtended) && + \in_array($matchesExtended[1], [ 'slotted' ]) + ) { $prev = end($out); $prev = $this->glueFunctionSelectors($prev); - if (count($prev) === 1 && count(reset($prev)) === 1) { + + if (\count($prev) === 1 && \count(reset($prev)) === 1) { $single = reset($prev); $part = reset($single); - if ($this->isPseudoSelector($part, $matchesPrev) - && $matchesPrev[1] === $matchesExtended[1]) { + + if ( + $this->isPseudoSelector($part, $matchesPrev) && + $matchesPrev[1] === $matchesExtended[1] + ) { $extended = explode($matchesExtended[1] . '(', $matchesExtended[0], 2); - $extended[1] = $matchesPrev[2] . ", " . $extended[1]; + $extended[1] = $matchesPrev[2] . ', ' . $extended[1]; $extended = implode($matchesExtended[1] . '(', $extended); $extended = [ [ $extended ]]; array_pop($out); @@ -713,9 +1074,9 @@ class Compiler * * @param array $rawSingle * @param array $outOrigin - * @param bool $initial + * @param bool $initial * - * @return boolean + * @return bool */ protected function matchExtendsSingle($rawSingle, &$outOrigin, $initial = true) { @@ -723,18 +1084,18 @@ class Compiler $single = []; // simple usual cases, no need to do the whole trick - if (in_array($rawSingle, [['>'],['+'],['~']])) { + if (\in_array($rawSingle, [['>'],['+'],['~']])) { return false; } foreach ($rawSingle as $part) { // matches Number - if (! is_string($part)) { + if (! \is_string($part)) { return false; } - if (! preg_match('/^[\[.:#%]/', $part) && count($single)) { - $single[count($single) - 1] .= $part; + if (! preg_match('/^[\[.:#%]/', $part) && \count($single)) { + $single[\count($single) - 1] .= $part; } else { $single[] = $part; } @@ -742,7 +1103,7 @@ class Compiler $extendingDecoratedTag = false; - if (count($single) > 1) { + if (\count($single) > 1) { $matches = null; $extendingDecoratedTag = preg_match('/^[a-z0-9]+$/i', $single[0], $matches) ? $matches[0] : false; } @@ -756,24 +1117,31 @@ class Compiler $counts[$idx] = isset($counts[$idx]) ? $counts[$idx] + 1 : 1; } } - if ($initial - && $this->isPseudoSelector($part, $matches) - && ! in_array($matches[1], [ 'not' ])) { + + if ( + $initial && + $this->isPseudoSelector($part, $matches) && + ! \in_array($matches[1], [ 'not' ]) + ) { $buffer = $matches[2]; $parser = $this->parserFactory(__METHOD__); - if ($parser->parseSelector($buffer, $subSelectors)) { + + if ($parser->parseSelector($buffer, $subSelectors, false)) { foreach ($subSelectors as $ksub => $subSelector) { $subExtended = []; $this->matchExtends($subSelector, $subExtended, 0, false); + if ($subExtended) { $subSelectorsExtended = $subSelectors; $subSelectorsExtended[$ksub] = $subExtended; + foreach ($subSelectorsExtended as $ksse => $sse) { $subSelectorsExtended[$ksse] = $this->collapseSelectors($sse); } + $subSelectorsExtended = implode(', ', $subSelectorsExtended); $singleExtended = $single; - $singleExtended[$k] = str_replace("(".$buffer.")", "($subSelectorsExtended)", $part); + $singleExtended[$k] = str_replace('(' . $buffer . ')', "($subSelectorsExtended)", $part); $outOrigin[] = [ $singleExtended ]; $found = true; } @@ -788,7 +1156,7 @@ class Compiler $origin = $this->glueFunctionSelectors($origin); // check count - if ($count !== count($target)) { + if ($count !== \count($target)) { continue; } @@ -798,14 +1166,15 @@ class Compiler foreach ($origin as $j => $new) { // prevent infinite loop when target extends itself - if ($this->isSelfExtend($single, $origin)) { + if ($this->isSelfExtend($single, $origin) && ! $initial) { return false; } $replacement = end($new); // Extending a decorated tag with another tag is not possible. - if ($extendingDecoratedTag && $replacement[0] != $extendingDecoratedTag && + if ( + $extendingDecoratedTag && $replacement[0] != $extendingDecoratedTag && preg_match('/^[a-z0-9]+$/i', $replacement[0]) ) { unset($origin[$j]); @@ -814,8 +1183,8 @@ class Compiler $combined = $this->combineSelectorSingle($replacement, $rem); - if (count(array_diff($combined, $origin[$j][count($origin[$j]) - 1]))) { - $origin[$j][count($origin[$j]) - 1] = $combined; + if (\count(array_diff($combined, $origin[$j][\count($origin[$j]) - 1]))) { + $origin[$j][\count($origin[$j]) - 1] = $combined; } } @@ -844,11 +1213,11 @@ class Compiler $parents = []; $children = []; - $j = $i = count($fragment); + $j = $i = \count($fragment); for (;;) { - $children = $j != $i ? array_slice($fragment, $j, $i - $j) : []; - $parents = array_slice($fragment, 0, $j); + $children = $j != $i ? \array_slice($fragment, $j, $i - $j) : []; + $parents = \array_slice($fragment, 0, $j); $slice = end($parents); if (empty($slice) || ! $this->isImmediateRelationshipCombinator($slice[0])) { @@ -874,8 +1243,15 @@ class Compiler $tag = []; $out = []; $wasTag = false; + $pseudo = []; + + while (\count($other) && strpos(end($other), ':') === 0) { + array_unshift($pseudo, array_pop($other)); + } foreach ([array_reverse($base), array_reverse($other)] as $single) { + $rang = count($single); + foreach ($single as $part) { if (preg_match('/^[\[:]/', $part)) { $out[] = $part; @@ -883,21 +1259,26 @@ class Compiler } elseif (preg_match('/^[\.#]/', $part)) { array_unshift($out, $part); $wasTag = false; - } elseif (preg_match('/^[^_-]/', $part)) { + } elseif (preg_match('/^[^_-]/', $part) && $rang === 1) { $tag[] = $part; $wasTag = true; } elseif ($wasTag) { - $tag[count($tag) - 1] .= $part; + $tag[\count($tag) - 1] .= $part; } else { - $out[] = $part; + array_unshift($out, $part); } + $rang--; } } - if (count($tag)) { + if (\count($tag)) { array_unshift($out, $tag[0]); } + while (\count($pseudo)) { + $out[] = array_shift($pseudo); + } + return $out; } @@ -905,14 +1286,18 @@ class Compiler * Compile media * * @param \ScssPhp\ScssPhp\Block $media + * + * @return void */ protected function compileMedia(Block $media) { + assert($media instanceof MediaBlock); $this->pushEnv($media); $mediaQueries = $this->compileMediaQuery($this->multiplyMedia($this->env)); - if (! empty($mediaQueries) && $mediaQueries) { + if (! empty($mediaQueries)) { + assert($this->scope !== null); $previousScope = $this->scope; $parentScope = $this->mediaParent($this->scope); @@ -929,7 +1314,8 @@ class Compiler foreach ($media->children as $child) { $type = $child[0]; - if ($type !== Type::T_BLOCK && + if ( + $type !== Type::T_BLOCK && $type !== Type::T_MEDIA && $type !== Type::T_DIRECTIVE && $type !== Type::T_IMPORT @@ -940,7 +1326,7 @@ class Compiler } if ($needsWrap) { - $wrapped = new Block; + $wrapped = new Block(); $wrapped->sourceName = $media->sourceName; $wrapped->sourceIndex = $media->sourceIndex; $wrapped->sourceLine = $media->sourceLine; @@ -951,30 +1337,6 @@ class Compiler $wrapped->children = $media->children; $media->children = [[Type::T_BLOCK, $wrapped]]; - - if (isset($this->lineNumberStyle)) { - $annotation = $this->makeOutputBlock(Type::T_COMMENT); - $annotation->depth = 0; - - $file = $this->sourceNames[$media->sourceIndex]; - $line = $media->sourceLine; - - switch ($this->lineNumberStyle) { - case static::LINE_COMMENTS: - $annotation->lines[] = '/* line ' . $line - . ($file ? ', ' . $file : '') - . ' */'; - break; - - case static::DEBUG_INFO: - $annotation->lines[] = '@media -sass-debug-info{' - . ($file ? 'filename{font-family:"' . $file . '"}' : '') - . 'line{font-family:' . $line . '}}'; - break; - } - - $this->scope->children[] = $annotation; - } } $this->compileChildrenNoReturn($media->children, $this->scope); @@ -1008,18 +1370,33 @@ class Compiler /** * Compile directive * - * @param \ScssPhp\ScssPhp\Block|array $block + * @param DirectiveBlock|array $directive * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out + * + * @return void */ protected function compileDirective($directive, OutputBlock $out) { - if (is_array($directive)) { - $s = '@' . $directive[0]; + if (\is_array($directive)) { + $directiveName = $this->compileDirectiveName($directive[0]); + $s = '@' . $directiveName; + if (! empty($directive[1])) { $s .= ' ' . $this->compileValue($directive[1]); } - $this->appendRootDirective($s . ';', $out); + // sass-spec compliance on newline after directives, a bit tricky :/ + $appendNewLine = (! empty($directive[2]) || strpos($s, "\n")) ? "\n" : ""; + if (\is_array($directive[0]) && empty($directive[1])) { + $appendNewLine = "\n"; + } + + if (empty($directive[3])) { + $this->appendRootDirective($s . ';' . $appendNewLine, $out, [Type::T_COMMENT, Type::T_DIRECTIVE]); + } else { + $this->appendOutputLine($out, Type::T_DIRECTIVE, $s . ';'); + } } else { + $directive->name = $this->compileDirectiveName($directive->name); $s = '@' . $directive->name; if (! empty($directive->value)) { @@ -1034,20 +1411,39 @@ class Compiler } } + /** + * directive names can include some interpolation + * + * @param string|array $directiveName + * @return string + * @throws CompilerException + */ + protected function compileDirectiveName($directiveName) + { + if (is_string($directiveName)) { + return $directiveName; + } + + return $this->compileValue($directiveName); + } + /** * Compile at-root * * @param \ScssPhp\ScssPhp\Block $block + * + * @return void */ protected function compileAtRoot(Block $block) { + assert($block instanceof AtRootBlock); $env = $this->pushEnv($block); $envs = $this->compactEnv($env); list($with, $without) = $this->compileWith(isset($block->with) ? $block->with : null); // wrap inline selector if ($block->selector) { - $wrapped = new Block; + $wrapped = new Block(); $wrapped->sourceName = $block->sourceName; $wrapped->sourceIndex = $block->sourceIndex; $wrapped->sourceLine = $block->sourceLine; @@ -1063,8 +1459,11 @@ class Compiler } $selfParent = $block->selfParent; + assert($selfParent !== null, 'at-root blocks must have a selfParent set.'); - if (! $block->selfParent->selectors && isset($block->parent) && $block->parent && + if ( + ! $selfParent->selectors && + isset($block->parent) && isset($block->parent->selectors) && $block->parent->selectors ) { $selfParent = $block->parent; @@ -1072,13 +1471,15 @@ class Compiler $this->env = $this->filterWithWithout($envs, $with, $without); + assert($this->scope !== null); $saveScope = $this->scope; $this->scope = $this->filterScopeWithWithout($saveScope, $with, $without); // propagate selfParent to the children where they still can be useful $this->compileChildrenNoReturn($block->children, $this->scope, $selfParent); - $this->scope = $this->completeScope($this->scope, $saveScope); + assert($this->scope !== null); + $this->completeScope($this->scope, $saveScope); $this->scope = $saveScope; $this->env = $this->extractEnv($envs); @@ -1086,25 +1487,26 @@ class Compiler } /** - * Filter at-root scope depending of with/without option + * Filter at-root scope depending on with/without option * * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $scope * @param array $with * @param array $without * - * @return mixed + * @return OutputBlock */ protected function filterScopeWithWithout($scope, $with, $without) { $filteredScopes = []; $childStash = []; - if ($scope->type === TYPE::T_ROOT) { + if ($scope->type === Type::T_ROOT) { return $scope; } + assert($this->rootBlock !== null); // start from the root - while ($scope->parent && $scope->parent->type !== TYPE::T_ROOT) { + while ($scope->parent && $scope->parent->type !== Type::T_ROOT) { array_unshift($childStash, $scope); $scope = $scope->parent; } @@ -1127,7 +1529,7 @@ class Compiler $filteredScopes[] = $s; } - if (count($childStash)) { + if (\count($childStash)) { $scope = array_shift($childStash); } elseif ($scope->children) { $scope = end($scope->children); @@ -1136,7 +1538,7 @@ class Compiler } } - if (! count($filteredScopes)) { + if (! \count($filteredScopes)) { return $this->rootBlock; } @@ -1147,7 +1549,7 @@ class Compiler $p = &$newScope; - while (count($filteredScopes)) { + while (\count($filteredScopes)) { $s = array_shift($filteredScopes); $s->parent = $p; $p->children[] = $s; @@ -1165,11 +1567,11 @@ class Compiler * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $scope * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $previousScope * - * @return mixed + * @return OutputBlock */ protected function completeScope($scope, $previousScope) { - if (! $scope->type && (! $scope->selectors || ! count($scope->selectors)) && count($scope->lines)) { + if (! $scope->type && ! $scope->selectors && \count($scope->lines)) { $scope->selectors = $this->findScopeSelectors($previousScope, $scope->depth); } @@ -1186,7 +1588,7 @@ class Compiler * Find a selector by the depth node in the scope * * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $scope - * @param integer $depth + * @param int $depth * * @return array */ @@ -1210,9 +1612,11 @@ class Compiler /** * Compile @at-root's with: inclusion / without: exclusion into 2 lists uses to filter scope/env later * - * @param array $withCondition + * @param array|null $withCondition * * @return array + * + * @phpstan-return array{array, array} */ protected function compileWith($withCondition) { @@ -1221,9 +1625,21 @@ class Compiler $without = ['rule' => true]; if ($withCondition) { - if ($this->libMapHasKey([$withCondition, static::$with])) { + if ($withCondition[0] === Type::T_INTERPOLATE) { + $w = $this->compileValue($withCondition); + + $buffer = "($w)"; + $parser = $this->parserFactory(__METHOD__); + + if ($parser->parseValue($buffer, $reParsedWith)) { + $withCondition = $reParsedWith; + } + } + + $withConfig = $this->mapGet($withCondition, static::$with); + if ($withConfig !== null) { $without = []; // cancel the default - $list = $this->coerceList($this->libMapGet([$withCondition, static::$with])); + $list = $this->coerceList($withConfig); foreach ($list[2] as $item) { $keyword = $this->compileStringContent($this->coerceString($item)); @@ -1232,9 +1648,10 @@ class Compiler } } - if ($this->libMapHasKey([$withCondition, static::$without])) { + $withoutConfig = $this->mapGet($withCondition, static::$without); + if ($withoutConfig !== null) { $without = []; // cancel the default - $list = $this->coerceList($this->libMapGet([$withCondition, static::$without])); + $list = $this->coerceList($withoutConfig); foreach ($list[2] as $item) { $keyword = $this->compileStringContent($this->coerceString($item)); @@ -1250,11 +1667,13 @@ class Compiler /** * Filter env stack * - * @param array $envs + * @param Environment[] $envs * @param array $with * @param array $without * - * @return \ScssPhp\ScssPhp\Compiler\Environment + * @return Environment + * + * @phpstan-param non-empty-array $envs */ protected function filterWithWithout($envs, $with, $without) { @@ -1282,7 +1701,7 @@ class Compiler * @param array $with * @param array $without * - * @return boolean + * @return bool */ protected function isWith($block, $with, $without) { @@ -1292,8 +1711,9 @@ class Compiler } if ($block->type === Type::T_DIRECTIVE) { + assert($block instanceof DirectiveBlock || $block instanceof OutputBlock); if (isset($block->name)) { - return $this->testWithWithout($block->name, $with, $without); + return $this->testWithWithout($this->compileDirectiveName($block->name), $with, $without); } elseif (isset($block->selectors) && preg_match(',@(\w+),ims', json_encode($block->selectors), $m)) { return $this->testWithWithout($m[1], $with, $without); } else { @@ -1302,14 +1722,14 @@ class Compiler } } elseif (isset($block->selectors)) { // a selector starting with number is a keyframe rule - if (count($block->selectors)) { + if (\count($block->selectors)) { $s = reset($block->selectors); - while (is_array($s)) { + while (\is_array($s)) { $s = reset($s); } - if (is_object($s) && $s instanceof Node\Number) { + if (\is_object($s) && $s instanceof Number) { return $this->testWithWithout('keyframes', $with, $without); } } @@ -1327,14 +1747,13 @@ class Compiler * @param array $with * @param array $without * - * @return boolean + * @return bool * true if the block should be kept, false to reject */ protected function testWithWithout($what, $with, $without) { - // if without, reject only if in the list (or 'all' is in the list) - if (count($without)) { + if (\count($without)) { return (isset($without[$what]) || isset($without['all'])) ? false : true; } @@ -1347,7 +1766,9 @@ class Compiler * Compile keyframe block * * @param \ScssPhp\ScssPhp\Block $block - * @param array $selectors + * @param string[] $selectors + * + * @return void */ protected function compileKeyframeBlock(Block $block, $selectors) { @@ -1361,10 +1782,12 @@ class Compiler $this->scope = $this->makeOutputBlock($block->type, $selectors); $this->scope->depth = 1; + assert($this->scope->parent !== null); $this->scope->parent->children[] = $this->scope; $this->compileChildrenNoReturn($block->children, $this->scope); + assert($this->scope !== null); $this->scope = $this->scope->parent; $this->env = $this->extractEnv($envs); @@ -1374,11 +1797,14 @@ class Compiler /** * Compile nested properties lines * - * @param \ScssPhp\ScssPhp\Block $block - * @param OutputBlock $out + * @param \ScssPhp\ScssPhp\Block $block + * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out + * + * @return void */ protected function compileNestedPropertiesBlock(Block $block, OutputBlock $out) { + assert($block instanceof NestedPropertyBlock); $prefix = $this->compileValue($block->prefix) . '-'; $nested = $this->makeOutputBlock($block->type); @@ -1397,6 +1823,7 @@ class Compiler break; case Type::T_NESTED_PROPERTY: + assert($child[1] instanceof NestedPropertyBlock); array_unshift($child[1]->prefix[2], $prefix); break; } @@ -1409,18 +1836,21 @@ class Compiler * Compile nested block * * @param \ScssPhp\ScssPhp\Block $block - * @param array $selectors + * @param string[] $selectors + * + * @return void */ protected function compileNestedBlock(Block $block, $selectors) { $this->pushEnv($block); $this->scope = $this->makeOutputBlock($block->type, $selectors); + assert($this->scope->parent !== null); $this->scope->parent->children[] = $this->scope; // wrap assign children in a block // except for @font-face - if ($block->type !== Type::T_DIRECTIVE || $block->name !== "font-face") { + if (!$block instanceof DirectiveBlock || $this->compileDirectiveName($block->name) !== 'font-face') { // need wrapping? $needWrapping = false; @@ -1432,7 +1862,7 @@ class Compiler } if ($needWrapping) { - $wrapped = new Block; + $wrapped = new Block(); $wrapped->sourceName = $block->sourceName; $wrapped->sourceIndex = $block->sourceIndex; $wrapped->sourceLine = $block->sourceLine; @@ -1449,6 +1879,7 @@ class Compiler $this->compileChildrenNoReturn($block->children, $this->scope); + assert($this->scope !== null); $this->scope = $this->scope->parent; $this->popEnv(); @@ -1471,41 +1902,21 @@ class Compiler * @see Compiler::compileChild() * * @param \ScssPhp\ScssPhp\Block $block + * + * @return void */ protected function compileBlock(Block $block) { $env = $this->pushEnv($block); + assert($block->selectors !== null); $env->selectors = $this->evalSelectors($block->selectors); $out = $this->makeOutputBlock(null); - if (isset($this->lineNumberStyle) && count($env->selectors) && count($block->children)) { - $annotation = $this->makeOutputBlock(Type::T_COMMENT); - $annotation->depth = 0; - - $file = $this->sourceNames[$block->sourceIndex]; - $line = $block->sourceLine; - - switch ($this->lineNumberStyle) { - case static::LINE_COMMENTS: - $annotation->lines[] = '/* line ' . $line - . ($file ? ', ' . $file : '') - . ' */'; - break; - - case static::DEBUG_INFO: - $annotation->lines[] = '@media -sass-debug-info{' - . ($file ? 'filename{font-family:"' . $file . '"}' : '') - . 'line{font-family:' . $line . '}}'; - break; - } - - $this->scope->children[] = $annotation; - } - + assert($this->scope !== null); $this->scope->children[] = $out; - if (count($block->children)) { + if (\count($block->children)) { $out->selectors = $this->multiplySelectors($env, $block->selfParent); // propagate selfParent to the children where they still can be useful @@ -1520,6 +1931,7 @@ class Compiler // and revert for the following children of the same block if ($selfParentSelectors) { + assert($block->selfParent !== null); $block->selfParent->selectors = $selfParentSelectors; } } @@ -1531,10 +1943,10 @@ class Compiler /** * Compile the value of a comment that can have interpolation * - * @param array $value - * @param boolean $pushEnv + * @param array $value + * @param bool $pushEnv * - * @return array|mixed|string + * @return string */ protected function compileCommentValue($value, $pushEnv = false) { @@ -1543,18 +1955,19 @@ class Compiler if (isset($value[2])) { if ($pushEnv) { $this->pushEnv(); - $storeEnv = $this->storeEnv; - $this->storeEnv = $this->env; } try { $c = $this->compileValue($value[2]); - } catch (\Exception $e) { + } catch (SassScriptException $e) { + $this->logger->warn('Ignoring interpolation errors in multiline comments is deprecated and will be removed in ScssPhp 2.0. ' . $this->addLocationToMessage($e->getMessage()), true); + // ignore error in comment compilation which are only interpolation + } catch (SassException $e) { + $this->logger->warn('Ignoring interpolation errors in multiline comments is deprecated and will be removed in ScssPhp 2.0. ' . $e->getMessage(), true); // ignore error in comment compilation which are only interpolation } if ($pushEnv) { - $this->storeEnv = $storeEnv; $this->popEnv(); } } @@ -1566,12 +1979,15 @@ class Compiler * Compile root level comment * * @param array $block + * + * @return void */ protected function compileComment($block) { $out = $this->makeOutputBlock(Type::T_COMMENT); $out->lines[] = $this->compileCommentValue($block, true); + assert($this->scope !== null); $this->scope->children[] = $out; } @@ -1586,15 +2002,25 @@ class Compiler { $this->shouldEvaluate = false; - $selectors = array_map([$this, 'evalSelector'], $selectors); + $evaluatedSelectors = []; + foreach ($selectors as $selector) { + $evaluatedSelectors[] = $this->evalSelector($selector); + } + $selectors = $evaluatedSelectors; // after evaluating interpolates, we might need a second pass if ($this->shouldEvaluate) { - $selectors = $this->revertSelfSelector($selectors); + $selectors = $this->replaceSelfSelector($selectors, '&'); $buffer = $this->collapseSelectors($selectors); $parser = $this->parserFactory(__METHOD__); - if ($parser->parseSelector($buffer, $newSelectors)) { + try { + $isValid = $parser->parseSelector($buffer, $newSelectors, true); + } catch (ParserException $e) { + throw $this->error($e->getMessage()); + } + + if ($isValid) { $selectors = array_map([$this, 'evalSelector'], $newSelectors); } } @@ -1608,6 +2034,8 @@ class Compiler * @param array $selector * * @return array + * + * @phpstan-impure */ protected function evalSelector($selector) { @@ -1620,20 +2048,23 @@ class Compiler * @param array $part * * @return array + * + * @phpstan-impure */ protected function evalSelectorPart($part) { foreach ($part as &$p) { - if (is_array($p) && ($p[0] === Type::T_INTERPOLATE || $p[0] === Type::T_STRING)) { + if (\is_array($p) && ($p[0] === Type::T_INTERPOLATE || $p[0] === Type::T_STRING)) { $p = $this->compileValue($p); - // force re-evaluation - if (strpos($p, '&') !== false || strpos($p, ',') !== false) { + // force re-evaluation if self char or non standard char + if (preg_match(',[^\w-],', $p)) { $this->shouldEvaluate = true; } - } elseif (is_string($p) && strlen($p) >= 2 && - ($first = $p[0]) && ($first === '"' || $first === "'") && - substr($p, -1) === $first + } elseif ( + \is_string($p) && \strlen($p) >= 2 && + ($p[0] === '"' || $p[0] === "'") && + substr($p, -1) === $p[0] ) { $p = substr($p, 1, -1); } @@ -1645,14 +2076,44 @@ class Compiler /** * Collapse selectors * - * @param array $selectors - * @param boolean $selectorFormat - * if false return a collapsed string - * if true return an array description of a structured selector + * @param array $selectors * * @return string */ - protected function collapseSelectors($selectors, $selectorFormat = false) + protected function collapseSelectors($selectors) + { + $parts = []; + + foreach ($selectors as $selector) { + $output = []; + + foreach ($selector as $node) { + $compound = ''; + + array_walk_recursive( + $node, + function ($value, $key) use (&$compound) { + $compound .= $value; + } + ); + + $output[] = $compound; + } + + $parts[] = implode(' ', $output); + } + + return implode(', ', $parts); + } + + /** + * Collapse selectors + * + * @param array $selectors + * + * @return array + */ + private function collapseSelectorsAsList($selectors) { $parts = []; @@ -1670,59 +2131,52 @@ class Compiler } ); - if ($selectorFormat && $this->isImmediateRelationshipCombinator($compound)) { - if (count($output)) { - $output[count($output) - 1] .= ' ' . $compound; + if ($this->isImmediateRelationshipCombinator($compound)) { + if (\count($output)) { + $output[\count($output) - 1] .= ' ' . $compound; } else { $output[] = $compound; } $glueNext = true; } elseif ($glueNext) { - $output[count($output) - 1] .= ' ' . $compound; + $output[\count($output) - 1] .= ' ' . $compound; $glueNext = false; } else { $output[] = $compound; } } - if ($selectorFormat) { - foreach ($output as &$o) { - $o = [Type::T_STRING, '', [$o]]; - } - - $output = [Type::T_LIST, ' ', $output]; - } else { - $output = implode(' ', $output); + foreach ($output as &$o) { + $o = [Type::T_STRING, '', [$o]]; } - $parts[] = $output; + $parts[] = [Type::T_LIST, ' ', $output]; } - if ($selectorFormat) { - $parts = [Type::T_LIST, ',', $parts]; - } else { - $parts = implode(', ', $parts); - } - - return $parts; + return [Type::T_LIST, ',', $parts]; } /** * Parse down the selector and revert [self] to "&" before a reparsing * - * @param array $selectors + * @param array $selectors + * @param string|null $replace * * @return array */ - protected function revertSelfSelector($selectors) + protected function replaceSelfSelector($selectors, $replace = null) { foreach ($selectors as &$part) { - if (is_array($part)) { + if (\is_array($part)) { if ($part === [Type::T_SELF]) { - $part = '&'; + if (\is_null($replace)) { + $replace = $this->reduce([Type::T_SELF]); + $replace = $this->compileValue($replace); + } + $part = $replace; } else { - $part = $this->revertSelfSelector($part); + $part = $this->replaceSelfSelector($part, $replace); } } } @@ -1742,18 +2196,19 @@ class Compiler $joined = []; foreach ($single as $part) { - if (empty($joined) || - ! is_string($part) || + if ( + empty($joined) || + ! \is_string($part) || preg_match('/[\[.:#%]/', $part) ) { $joined[] = $part; continue; } - if (is_array(end($joined))) { + if (\is_array(end($joined))) { $joined[] = $part; } else { - $joined[count($joined) - 1] .= $part; + $joined[\count($joined) - 1] .= $part; } } @@ -1769,7 +2224,7 @@ class Compiler */ protected function compileSelector($selector) { - if (! is_array($selector)) { + if (! \is_array($selector)) { return $selector; // media and the like } @@ -1792,7 +2247,7 @@ class Compiler protected function compileSelectorPart($piece) { foreach ($piece as &$p) { - if (! is_array($p)) { + if (! \is_array($p)) { continue; } @@ -1815,17 +2270,17 @@ class Compiler * * @param array $selector * - * @return boolean + * @return bool */ protected function hasSelectorPlaceholder($selector) { - if (! is_array($selector)) { + if (! \is_array($selector)) { return false; } foreach ($selector as $parts) { foreach ($parts as $part) { - if (strlen($part) && '%' === $part[0]) { + if (\strlen($part) && '%' === $part[0]) { return true; } } @@ -1834,6 +2289,11 @@ class Compiler return false; } + /** + * @param string $name + * + * @return void + */ protected function pushCallStack($name = '') { $this->callStack[] = [ @@ -1844,15 +2304,18 @@ class Compiler ]; // infinite calling loop - if (count($this->callStack) > 25000) { + if (\count($this->callStack) > 25000) { // not displayed but you can var_dump it to deep debug $msg = $this->callStackMessage(true, 100); - $msg = "Infinite calling loop"; + $msg = 'Infinite calling loop'; - $this->throwError($msg); + throw $this->error($msg); } } + /** + * @return void + */ protected function popCallStack() { array_pop($this->callStack); @@ -1865,7 +2328,7 @@ class Compiler * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out * @param string $traceName * - * @return array|null + * @return array|Number|null */ protected function compileChildren($stms, OutputBlock $out, $traceName = '') { @@ -1875,6 +2338,8 @@ class Compiler $ret = $this->compileChild($stm, $out); if (isset($ret)) { + $this->popCallStack(); + return $ret; } } @@ -1885,13 +2350,15 @@ class Compiler } /** - * Compile children and throw exception if unexpected @return + * Compile children and throw exception if unexpected at-return * - * @param array $stms + * @param array[] $stms * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out * @param \ScssPhp\ScssPhp\Block $selfParent * @param string $traceName * + * @return void + * * @throws \Exception */ protected function compileChildrenNoReturn($stms, OutputBlock $out, $selfParent = null, $traceName = '') @@ -1899,22 +2366,20 @@ class Compiler $this->pushCallStack($traceName); foreach ($stms as $stm) { - if ($selfParent && isset($stm[1]) && is_object($stm[1]) && $stm[1] instanceof Block) { + if ($selfParent && isset($stm[1]) && \is_object($stm[1]) && $stm[1] instanceof Block) { + $oldSelfParent = $stm[1]->selfParent; $stm[1]->selfParent = $selfParent; $ret = $this->compileChild($stm, $out); - $stm[1]->selfParent = null; - } elseif ($selfParent && in_array($stm[0], [TYPE::T_INCLUDE, TYPE::T_EXTEND])) { + $stm[1]->selfParent = $oldSelfParent; + } elseif ($selfParent && \in_array($stm[0], [Type::T_INCLUDE, Type::T_EXTEND])) { $stm['selfParent'] = $selfParent; $ret = $this->compileChild($stm, $out); - unset($stm['selfParent']); } else { $ret = $this->compileChild($stm, $out); } if (isset($ret)) { - $this->throwError('@return may only be used within a function'); - - return; + throw $this->error('@return may only be used within a function'); } } @@ -1923,7 +2388,7 @@ class Compiler /** - * evaluate media query : compile internal value keeping the structure inchanged + * evaluate media query : compile internal value keeping the structure unchanged * * @param array $queryList * @@ -1939,12 +2404,13 @@ class Compiler $shouldReparse = false; foreach ($query as $kq => $q) { - for ($i = 1; $i < count($q); $i++) { + for ($i = 1; $i < \count($q); $i++) { $value = $this->compileValue($q[$i]); // the parser had no mean to know if media type or expression if it was an interpolation // so you need to reparse if the T_MEDIA_TYPE looks like anything else a media type - if ($q[0] == Type::T_MEDIA_TYPE && + if ( + $q[0] == Type::T_MEDIA_TYPE && (strpos($value, '(') !== false || strpos($value, ')') !== false || strpos($value, ':') !== false || @@ -1958,21 +2424,21 @@ class Compiler } if ($shouldReparse) { - if (is_null($parser)) { + if (\is_null($parser)) { $parser = $this->parserFactory(__METHOD__); } $queryString = $this->compileMediaQuery([$queryList[$kql]]); $queryString = reset($queryString); - if (strpos($queryString, '@media ') === 0) { + if ($queryString !== false && strpos($queryString, '@media ') === 0) { $queryString = substr($queryString, 7); $queries = []; if ($parser->parseMediaQueryList($queryString, $queries)) { $queries = $this->evaluateMediaQuery($queries[2]); - while (count($queries)) { + while (\count($queries)) { $outQueryList[] = array_shift($queries); } @@ -1992,14 +2458,14 @@ class Compiler * * @param array $queryList * - * @return array + * @return string[] */ protected function compileMediaQuery($queryList) { $start = '@media '; $default = trim($start); $out = []; - $current = ""; + $current = ''; foreach ($queryList as $query) { $type = null; @@ -2017,17 +2483,17 @@ class Compiler foreach ($query as $q) { switch ($q[0]) { case Type::T_MEDIA_TYPE: - $newType = array_map([$this, 'compileValue'], array_slice($q, 1)); + $newType = array_map([$this, 'compileValue'], \array_slice($q, 1)); // combining not and anything else than media type is too risky and should be avoided if (! $mediaTypeOnly) { - if (in_array(Type::T_NOT, $newType) || ($type && in_array(Type::T_NOT, $type) )) { + if (\in_array(Type::T_NOT, $newType) || ($type && \in_array(Type::T_NOT, $type) )) { if ($type) { array_unshift($parts, implode(' ', array_filter($type))); } if (! empty($parts)) { - if (strlen($current)) { + if (\strlen($current)) { $current .= $this->formatter->tagSeparator; } @@ -2038,7 +2504,7 @@ class Compiler $out[] = $start . $current; } - $current = ""; + $current = ''; $type = null; $parts = []; } @@ -2090,7 +2556,7 @@ class Compiler } if (! empty($parts)) { - if (strlen($current)) { + if (\strlen($current)) { $current .= $this->formatter->tagSeparator; } @@ -2172,7 +2638,7 @@ class Compiler return $type1; } - if (count($type1) > 1) { + if (\count($type1) > 1) { $m1 = strtolower($type1[0]); $t1 = strtolower($type1[1]); } else { @@ -2180,7 +2646,7 @@ class Compiler $t1 = strtolower($type1[0]); } - if (count($type2) > 1) { + if (\count($type2) > 1) { $m2 = strtolower($type2[0]); $t2 = strtolower($type2[1]); } else { @@ -2213,7 +2679,7 @@ class Compiler } // t1 == t2, neither m1 nor m2 are "not" - return [empty($m1)? $m2 : $m1, $t1]; + return [empty($m1) ? $m2 : $m1, $t1]; } /** @@ -2221,38 +2687,40 @@ class Compiler * * @param array $rawPath * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out - * @param boolean $once + * @param bool $once * - * @return boolean + * @return bool */ protected function compileImport($rawPath, OutputBlock $out, $once = false) { if ($rawPath[0] === Type::T_STRING) { $path = $this->compileStringContent($rawPath); - if ($path = $this->findImport($path)) { - if (! $once || ! in_array($path, $this->importedFiles)) { - $this->importFile($path, $out); - $this->importedFiles[] = $path; + if (strpos($path, 'url(') !== 0 && $filePath = $this->findImport($path, $this->currentDirectory)) { + $this->registerImport($this->currentDirectory, $path, $filePath); + + if (! $once || ! \in_array($filePath, $this->importedFiles)) { + $this->importFile($filePath, $out); + $this->importedFiles[] = $filePath; } return true; } - $this->appendRootDirective('@import ' . $this->compileValue($rawPath). ';', $out); + $this->appendRootDirective('@import ' . $this->compileImportPath($rawPath) . ';', $out); return false; } if ($rawPath[0] === Type::T_LIST) { // handle a list of strings - if (count($rawPath[2]) === 0) { + if (\count($rawPath[2]) === 0) { return false; } foreach ($rawPath[2] as $path) { if ($path[0] !== Type::T_STRING) { - $this->appendRootDirective('@import ' . $this->compileValue($rawPath) . ';', $out); + $this->appendRootDirective('@import ' . $this->compileImportPath($rawPath) . ';', $out); return false; } @@ -2265,19 +2733,68 @@ class Compiler return true; } - $this->appendRootDirective('@import ' . $this->compileValue($rawPath) . ';', $out); + $this->appendRootDirective('@import ' . $this->compileImportPath($rawPath) . ';', $out); return false; } + /** + * @param array $rawPath + * @return string + * @throws CompilerException + */ + protected function compileImportPath($rawPath) + { + $path = $this->compileValue($rawPath); + + // case url() without quotes : suppress \r \n remaining in the path + // if this is a real string there can not be CR or LF char + if (strpos($path, 'url(') === 0) { + $path = str_replace(array("\r", "\n"), array('', ' '), $path); + } else { + // if this is a file name in a string, spaces should be escaped + $path = $this->reduce($rawPath); + $path = $this->escapeImportPathString($path); + $path = $this->compileValue($path); + } + + return $path; + } + + /** + * @param array $path + * @return array + * @throws CompilerException + */ + protected function escapeImportPathString($path) + { + switch ($path[0]) { + case Type::T_LIST: + foreach ($path[2] as $k => $v) { + $path[2][$k] = $this->escapeImportPathString($v); + } + break; + case Type::T_STRING: + if ($path[1]) { + $path = $this->compileValue($path); + $path = str_replace(' ', '\\ ', $path); + $path = [Type::T_KEYWORD, $path]; + } + break; + } + + return $path; + } /** * Append a root directive like @import or @charset as near as the possible from the source code * (keeping before comments, @import and @charset coming before in the source code) * - * @param string $line - * @param @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out - * @param array $allowed + * @param string $line + * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out + * @param array $allowed + * + * @return void */ protected function appendRootDirective($line, $out, $allowed = [Type::T_COMMENT]) { @@ -2289,8 +2806,8 @@ class Compiler $i = 0; - while ($i < count($root->children)) { - if (! isset($root->children[$i]->type) || ! in_array($root->children[$i]->type, $allowed)) { + while ($i < \count($root->children)) { + if (! isset($root->children[$i]->type) || ! \in_array($root->children[$i]->type, $allowed)) { break; } @@ -2300,21 +2817,21 @@ class Compiler // remove incompatible children from the bottom of the list $saveChildren = []; - while ($i < count($root->children)) { + while ($i < \count($root->children)) { $saveChildren[] = array_pop($root->children); } // insert the directive as a comment $child = $this->makeOutputBlock(Type::T_COMMENT); $child->lines[] = $line; - $child->sourceName = $this->sourceNames[$this->sourceIndex]; + $child->sourceName = $this->sourceNames[$this->sourceIndex] ?: '(stdin)'; $child->sourceLine = $this->sourceLine; $child->sourceColumn = $this->sourceColumn; $root->children[] = $child; // repush children - while (count($saveChildren)) { + while (\count($saveChildren)) { $root->children[] = array_pop($saveChildren); } } @@ -2325,25 +2842,23 @@ class Compiler * * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out * @param string $type - * @param string|mixed $line + * @param string $line + * + * @return void */ protected function appendOutputLine(OutputBlock $out, $type, $line) { $outWrite = &$out; - if ($type === Type::T_COMMENT) { - $parent = $out->parent; - - if (end($parent->children) !== $out) { - $outWrite = &$parent->children[count($parent->children) - 1]; - } - } - // check if it's a flat output or not - if (count($out->children)) { - $lastChild = &$out->children[count($out->children) - 1]; + if (\count($out->children)) { + $lastChild = &$out->children[\count($out->children) - 1]; - if ($lastChild->depth === $out->depth && is_null($lastChild->selectors) && ! count($lastChild->children)) { + if ( + $lastChild->depth === $out->depth && + \is_null($lastChild->selectors) && + ! \count($lastChild->children) + ) { $outWrite = $lastChild; } else { $nextLines = $this->makeOutputBlock($type); @@ -2364,7 +2879,7 @@ class Compiler * @param array $child * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out * - * @return array + * @return array|Number|null */ protected function compileChild($child, OutputBlock $out) { @@ -2372,18 +2887,19 @@ class Compiler $this->sourceIndex = isset($child[Parser::SOURCE_INDEX]) ? $child[Parser::SOURCE_INDEX] : null; $this->sourceLine = isset($child[Parser::SOURCE_LINE]) ? $child[Parser::SOURCE_LINE] : -1; $this->sourceColumn = isset($child[Parser::SOURCE_COLUMN]) ? $child[Parser::SOURCE_COLUMN] : -1; - } elseif (is_array($child) && isset($child[1]->sourceLine)) { + } elseif (\is_array($child) && isset($child[1]->sourceLine) && $child[1] instanceof Block) { $this->sourceIndex = $child[1]->sourceIndex; $this->sourceLine = $child[1]->sourceLine; $this->sourceColumn = $child[1]->sourceColumn; } elseif (! empty($out->sourceLine) && ! empty($out->sourceName)) { $this->sourceLine = $out->sourceLine; - $this->sourceIndex = array_search($out->sourceName, $this->sourceNames); + $sourceIndex = array_search($out->sourceName, $this->sourceNames); $this->sourceColumn = $out->sourceColumn; - if ($this->sourceIndex === false) { - $this->sourceIndex = null; + if ($sourceIndex === false) { + $sourceIndex = null; } + $this->sourceIndex = $sourceIndex; } switch ($child[0]) { @@ -2416,10 +2932,30 @@ class Compiler break; case Type::T_CHARSET: - if (! $this->charsetSeen) { - $this->charsetSeen = true; - $this->appendRootDirective('@charset ' . $this->compileValue($child[1]) . ';', $out); + break; + + case Type::T_CUSTOM_PROPERTY: + list(, $name, $value) = $child; + $compiledName = $this->compileValue($name); + + // if the value reduces to null from something else then + // the property should be discarded + if ($value[0] !== Type::T_NULL) { + $value = $this->reduce($value); + + if ($value[0] === Type::T_NULL || $value === static::$nullString) { + break; + } } + + $compiledValue = $this->compileValue($value); + + $line = $this->formatter->customProperty( + $compiledName, + $compiledValue + ); + + $this->appendOutputLine($out, Type::T_ASSIGN, $line); break; case Type::T_ASSIGN: @@ -2427,8 +2963,8 @@ class Compiler if ($name[0] === Type::T_VARIABLE) { $flags = isset($child[3]) ? $child[3] : []; - $isDefault = in_array('!default', $flags); - $isGlobal = in_array('!global', $flags); + $isDefault = \in_array('!default', $flags); + $isGlobal = \in_array('!global', $flags); if ($isGlobal) { $this->set($name[1], $this->reduce($value), false, $this->rootEnv, $value); @@ -2436,7 +2972,7 @@ class Compiler } $shouldSet = $isDefault && - (is_null($result = $this->get($name[1], false)) || + (\is_null($result = $this->get($name[1], false)) || $result === static::$null); if (! $isDefault || $shouldSet) { @@ -2448,7 +2984,7 @@ class Compiler $compiledName = $this->compileValue($name); // handle shorthand syntaxes : size / line-height... - if (in_array($compiledName, ['font', 'grid-row', 'grid-column', 'border-radius'])) { + if (\in_array($compiledName, ['font', 'grid-row', 'grid-column', 'border-radius'])) { if ($value[0] === Type::T_VARIABLE) { // if the font value comes from variable, the content is already reduced // (i.e., formulas were already calculated), so we need the original unreduced value @@ -2468,7 +3004,7 @@ class Compiler break; } - if ($compiledName === 'font' and $value[0] === Type::T_LIST && $value[1]==',') { + if ($compiledName === 'font' && $value[0] === Type::T_LIST && $value[1] === ',') { // this is the case if more than one font is given: example: "font: 400 1em/1.3 arial,helvetica" // we need to handle the first list element $shorthandValue=&$value[2][0]; @@ -2480,11 +3016,11 @@ class Compiler if ($shorthandDividerNeedsUnit) { $divider = $shorthandValue[3]; - if (is_array($divider)) { + if (\is_array($divider)) { $divider = $this->reduce($divider, true); } - if (intval($divider->dimension) and !count($divider->units)) { + if ($divider instanceof Number && \intval($divider->getDimension()) && $divider->unitless()) { $revert = false; } } @@ -2497,17 +3033,18 @@ class Compiler if ($item[0] === Type::T_EXPRESSION && $item[1] === '/') { if ($maxShorthandDividers > 0) { $revert = true; + // if the list of values is too long, this has to be a shorthand, // otherwise it could be a real division - if (is_null($maxListElements) or count($shorthandValue[2]) <= $maxListElements) { + if (\is_null($maxListElements) || \count($shorthandValue[2]) <= $maxListElements) { if ($shorthandDividerNeedsUnit) { $divider = $item[3]; - if (is_array($divider)) { + if (\is_array($divider)) { $divider = $this->reduce($divider, true); } - if (intval($divider->dimension) and !count($divider->units)) { + if ($divider instanceof Number && \intval($divider->getDimension()) && $divider->unitless()) { $revert = false; } } @@ -2535,11 +3072,14 @@ class Compiler $compiledValue = $this->compileValue($value); - $line = $this->formatter->property( - $compiledName, - $compiledValue - ); - $this->appendOutputLine($out, Type::T_ASSIGN, $line); + // ignore empty value + if (\strlen($compiledValue)) { + $line = $this->formatter->property( + $compiledName, + $compiledValue + ); + $this->appendOutputLine($out, Type::T_ASSIGN, $line); + } break; case Type::T_COMMENT: @@ -2555,6 +3095,7 @@ class Compiler case Type::T_MIXIN: case Type::T_FUNCTION: list(, $block) = $child; + assert($block instanceof CallableBlock); // the block need to be able to go up to it's parent env to resolve vars $block->parentEnv = $this->getStoreEnv(); $this->set(static::$namespaces[$block->type] . $block->name, $block, true); @@ -2562,16 +3103,42 @@ class Compiler case Type::T_EXTEND: foreach ($child[1] as $sel) { + $replacedSel = $this->replaceSelfSelector($sel); + + if ($replacedSel !== $sel) { + throw $this->error('Parent selectors aren\'t allowed here.'); + } + $results = $this->evalSelectors([$sel]); foreach ($results as $result) { + if (\count($result) !== 1) { + throw $this->error('complex selectors may not be extended.'); + } + // only use the first one - $result = current($result); + $result = $result[0]; $selectors = $out->selectors; if (! $selectors && isset($child['selfParent'])) { $selectors = $this->multiplySelectors($this->env, $child['selfParent']); } + assert($selectors !== null); + + if (\count($result) > 1) { + $replacement = implode(', ', $result); + $fname = $this->getPrettyPath($this->sourceNames[$this->sourceIndex]); + $line = $this->sourceLine; + + $message = <<logger->warn($message); + } $this->pushExtends($result, $selectors, $child); } @@ -2580,14 +3147,16 @@ class Compiler case Type::T_IF: list(, $if) = $child; + assert($if instanceof IfBlock); if ($this->isTruthy($this->reduce($if->cond, true))) { return $this->compileChildren($if->children, $out); } foreach ($if->cases as $case) { - if ($case->type === Type::T_ELSE || - $case->type === Type::T_ELSEIF && $this->isTruthy($this->reduce($case->cond)) + if ( + $case instanceof ElseBlock || + $case instanceof ElseifBlock && $this->isTruthy($this->reduce($case->cond)) ) { return $this->compileChildren($case->children, $out); } @@ -2596,13 +3165,14 @@ class Compiler case Type::T_EACH: list(, $each) = $child; + assert($each instanceof EachBlock); - $list = $this->coerceList($this->reduce($each->list)); + $list = $this->coerceList($this->reduce($each->list), ',', true); $this->pushEnv(); foreach ($list[2] as $item) { - if (count($each->vars) === 1) { + if (\count($each->vars) === 1) { $this->set($each->vars[0], $item, true); } else { list(,, $values) = $this->coerceList($item); @@ -2615,87 +3185,78 @@ class Compiler $ret = $this->compileChildren($each->children, $out); if ($ret) { - if ($ret[0] !== Type::T_CONTROL) { - $this->popEnv(); + $store = $this->env->store; + $this->popEnv(); + $this->backPropagateEnv($store, $each->vars); - return $ret; - } - - if ($ret[1]) { - break; - } + return $ret; } } - + $store = $this->env->store; $this->popEnv(); + $this->backPropagateEnv($store, $each->vars); + break; case Type::T_WHILE: list(, $while) = $child; + assert($while instanceof WhileBlock); while ($this->isTruthy($this->reduce($while->cond, true))) { $ret = $this->compileChildren($while->children, $out); if ($ret) { - if ($ret[0] !== Type::T_CONTROL) { - return $ret; - } - - if ($ret[1]) { - break; - } + return $ret; } } break; case Type::T_FOR: list(, $for) = $child; + assert($for instanceof ForBlock); - $start = $this->reduce($for->start, true); - $end = $this->reduce($for->end, true); + $startNumber = $this->assertNumber($this->reduce($for->start, true)); + $endNumber = $this->assertNumber($this->reduce($for->end, true)); - if (! ($start[2] == $end[2] || $end->unitless())) { - $this->throwError('Incompatible units: "%s" and "%s".', $start->unitStr(), $end->unitStr()); + $start = $this->assertInteger($startNumber); - break; - } + $numeratorUnits = $startNumber->getNumeratorUnits(); + $denominatorUnits = $startNumber->getDenominatorUnits(); - $unit = $start[2]; - $start = $start[1]; - $end = $end[1]; + $end = $this->assertInteger($endNumber->coerce($numeratorUnits, $denominatorUnits)); $d = $start < $end ? 1 : -1; + $this->pushEnv(); + for (;;) { - if ((! $for->until && $start - $d == $end) || + if ( + (! $for->until && $start - $d == $end) || ($for->until && $start == $end) ) { break; } - $this->set($for->var, new Node\Number($start, $unit)); + $this->set($for->var, new Number($start, $numeratorUnits, $denominatorUnits)); $start += $d; $ret = $this->compileChildren($for->children, $out); if ($ret) { - if ($ret[0] !== Type::T_CONTROL) { - return $ret; - } + $store = $this->env->store; + $this->popEnv(); + $this->backPropagateEnv($store, [$for->var]); - if ($ret[1]) { - break; - } + return $ret; } } + + $store = $this->env->store; + $this->popEnv(); + $this->backPropagateEnv($store, [$for->var]); + break; - case Type::T_BREAK: - return [Type::T_CONTROL, true]; - - case Type::T_CONTINUE: - return [Type::T_CONTROL, false]; - case Type::T_RETURN: return $this->reduce($child[1], true); @@ -2710,24 +3271,22 @@ class Compiler $mixin = $this->get(static::$namespaces['mixin'] . $name, false); if (! $mixin) { - $this->throwError("Undefined mixin $name"); - break; + throw $this->error("Undefined mixin $name"); } + assert($mixin instanceof CallableBlock); + $callingScope = $this->getStoreEnv(); // push scope, apply args $this->pushEnv(); $this->env->depth--; - $storeEnv = $this->storeEnv; - $this->storeEnv = $this->env; - // Find the parent selectors in the env to be able to know what '&' refers to in the mixin // and assign this fake parent to childs $selfParent = null; - if (isset($child['selfParent']) && isset($child['selfParent']->selectors)) { + if (isset($child['selfParent']) && $child['selfParent'] instanceof Block && isset($child['selfParent']->selectors)) { $selfParent = $child['selfParent']; } else { $parentSelectors = $this->multiplySelectors($this->env); @@ -2737,7 +3296,7 @@ class Compiler $parent->selectors = $parentSelectors; foreach ($mixin->children as $k => $child) { - if (isset($child[1]) && is_object($child[1]) && $child[1] instanceof Block) { + if (isset($child[1]) && $child[1] instanceof Block) { $mixin->children[$k][1]->parent = $parent; } } @@ -2771,12 +3330,10 @@ class Compiler if (! empty($mixin->parentEnv)) { $this->env->declarationScopeParent = $mixin->parentEnv; } else { - $this->throwError("@mixin $name() without parentEnv"); + throw $this->error("@mixin $name() without parentEnv"); } - $this->compileChildrenNoReturn($mixin->children, $out, $selfParent, $this->env->marker . " " . $name); - - $this->storeEnv = $storeEnv; + $this->compileChildrenNoReturn($mixin->children, $out, $selfParent, $this->env->marker . ' ' . $name); $this->popEnv(); break; @@ -2788,9 +3345,6 @@ class Compiler $argContent = $child[1]; if (! $content) { - $content = new \stdClass(); - $content->scope = new \stdClass(); - $content->children = $env->parent->block->children; break; } @@ -2799,7 +3353,7 @@ class Compiler if (isset($argUsing) && isset($argContent)) { // Get the arguments provided for the content with the names provided in the "using" argument list - $this->storeEnv = $this->env; + $this->storeEnv = null; $varsUsing = $this->applyArguments($argUsing, $argContent, false); } @@ -2819,54 +3373,58 @@ class Compiler case Type::T_DEBUG: list(, $value) = $child; - $fname = $this->sourceNames[$this->sourceIndex]; + $fname = $this->getPrettyPath($this->sourceNames[$this->sourceIndex]); $line = $this->sourceLine; - $value = $this->compileValue($this->reduce($value, true)); + $value = $this->compileDebugValue($value); - fwrite($this->stderr, "File $fname on line $line DEBUG: $value\n"); + $this->logger->debug("$fname:$line DEBUG: $value"); break; case Type::T_WARN: list(, $value) = $child; - $fname = $this->sourceNames[$this->sourceIndex]; + $fname = $this->getPrettyPath($this->sourceNames[$this->sourceIndex]); $line = $this->sourceLine; - $value = $this->compileValue($this->reduce($value, true)); + $value = $this->compileDebugValue($value); - fwrite($this->stderr, "File $fname on line $line WARN: $value\n"); + $this->logger->warn("$value\n on line $line of $fname"); break; case Type::T_ERROR: list(, $value) = $child; - $fname = $this->sourceNames[$this->sourceIndex]; + $fname = $this->getPrettyPath($this->sourceNames[$this->sourceIndex]); $line = $this->sourceLine; $value = $this->compileValue($this->reduce($value, true)); - $this->throwError("File $fname on line $line ERROR: $value\n"); - break; - - case Type::T_CONTROL: - $this->throwError('@break/@continue not permitted in this scope'); - break; + throw $this->error("File $fname on line $line ERROR: $value\n"); default: - $this->throwError("unknown child type: $child[0]"); + throw $this->error("unknown child type: $child[0]"); } + + return null; } /** * Reduce expression to string * * @param array $exp + * @param bool $keepParens * * @return array */ - protected function expToString($exp) + protected function expToString($exp, $keepParens = false) { - list(, $op, $left, $right, /* $inParens */, $whiteLeft, $whiteRight) = $exp; + list(, $op, $left, $right, $inParens, $whiteLeft, $whiteRight) = $exp; - $content = [$this->reduce($left)]; + $content = []; + + if ($keepParens && $inParens) { + $content[] = '('; + } + + $content[] = $this->reduce($left); if ($whiteLeft) { $content[] = ' '; @@ -2880,17 +3438,21 @@ class Compiler $content[] = $this->reduce($right); + if ($keepParens && $inParens) { + $content[] = ')'; + } + return [Type::T_STRING, '', $content]; } /** * Is truthy? * - * @param array $value + * @param array|Number $value * - * @return boolean + * @return bool */ - protected function isTruthy($value) + public function isTruthy($value) { return $value !== static::$false && $value !== static::$null; } @@ -2900,7 +3462,7 @@ class Compiler * * @param string $value * - * @return boolean + * @return bool */ protected function isImmediateRelationshipCombinator($value) { @@ -2912,7 +3474,7 @@ class Compiler * * @param array $value * - * @return boolean + * @return bool */ protected function shouldEval($value) { @@ -2934,15 +3496,15 @@ class Compiler /** * Reduce value * - * @param array $value - * @param boolean $inExp + * @param array|Number $value + * @param bool $inExp * - * @return null|string|array|\ScssPhp\ScssPhp\Node\Number + * @return array|Number */ protected function reduce($value, $inExp = false) { - if (is_null($value)) { - return null; + if ($value instanceof Number) { + return $value; } switch ($value[0]) { @@ -2959,8 +3521,9 @@ class Compiler } // special case: looks like css shorthand - if ($opName == 'div' && ! $inParens && ! $inExp && isset($right[2]) && - (($right[0] !== Type::T_NUMBER && $right[2] != '') || + if ( + $opName == 'div' && ! $inParens && ! $inExp && + (($right[0] !== Type::T_NUMBER && isset($right[2]) && $right[2] != '') || ($right[0] === Type::T_NUMBER && ! $right->unitless())) ) { return $this->expToString($value); @@ -2975,73 +3538,24 @@ class Compiler $ucLType = ucfirst($ltype); $ucRType = ucfirst($rtype); + $shouldEval = $inParens || $inExp; + // this tries: // 1. op[op name][left type][right type] - // 2. op[left type][right type] (passing the op as first arg + // 2. op[left type][right type] (passing the op as first arg) // 3. op[op name] - $fn = "op${ucOpName}${ucLType}${ucRType}"; + if (\is_callable([$this, $fn = "op${ucOpName}${ucLType}${ucRType}"])) { + $out = $this->$fn($left, $right, $shouldEval); + } elseif (\is_callable([$this, $fn = "op${ucLType}${ucRType}"])) { + $out = $this->$fn($op, $left, $right, $shouldEval); + } elseif (\is_callable([$this, $fn = "op${ucOpName}"])) { + $out = $this->$fn($left, $right, $shouldEval); + } else { + $out = null; + } - if (is_callable([$this, $fn]) || - (($fn = "op${ucLType}${ucRType}") && - is_callable([$this, $fn]) && - $passOp = true) || - (($fn = "op${ucOpName}") && - is_callable([$this, $fn]) && - $genOp = true) - ) { - $coerceUnit = false; - - if (! isset($genOp) && - $left[0] === Type::T_NUMBER && $right[0] === Type::T_NUMBER - ) { - $coerceUnit = true; - - switch ($opName) { - case 'mul': - $targetUnit = $left[2]; - - foreach ($right[2] as $unit => $exp) { - $targetUnit[$unit] = (isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) + $exp; - } - break; - - case 'div': - $targetUnit = $left[2]; - - foreach ($right[2] as $unit => $exp) { - $targetUnit[$unit] = (isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) - $exp; - } - break; - - case 'mod': - $targetUnit = $left[2]; - break; - - default: - $targetUnit = $left->unitless() ? $right[2] : $left[2]; - } - - if (! $left->unitless() && ! $right->unitless()) { - $left = $left->normalize(); - $right = $right->normalize(); - } - } - - $shouldEval = $inParens || $inExp; - - if (isset($passOp)) { - $out = $this->$fn($op, $left, $right, $shouldEval); - } else { - $out = $this->$fn($left, $right, $shouldEval); - } - - if (isset($out)) { - if ($coerceUnit && $out[0] === Type::T_NUMBER) { - $out = $out->coerce($targetUnit); - } - - return $out; - } + if (isset($out)) { + return $out; } return $this->expToString($value); @@ -3052,13 +3566,13 @@ class Compiler $inExp = $inExp || $this->shouldEval($exp); $exp = $this->reduce($exp); - if ($exp[0] === Type::T_NUMBER) { + if ($exp instanceof Number) { switch ($op) { case '+': - return new Node\Number($exp[1], $exp[2]); + return $exp; case '-': - return new Node\Number(-$exp[1], $exp[2]); + return $exp->unaryMinus(); } } @@ -3083,6 +3597,14 @@ class Compiler foreach ($value[2] as &$item) { $item = $this->reduce($item); } + unset($item); + + if (isset($value[3]) && \is_array($value[3])) { + foreach ($value[3] as &$item) { + $item = $this->reduce($item); + } + unset($item); + } return $value; @@ -3099,7 +3621,7 @@ class Compiler case Type::T_STRING: foreach ($value[2] as &$item) { - if (is_array($item) || $item instanceof \ArrayAccess) { + if (\is_array($item) || $item instanceof Number) { $item = $this->reduce($item); } } @@ -3110,7 +3632,7 @@ class Compiler $value[1] = $this->reduce($value[1]); if ($inExp) { - return $value[1]; + return [Type::T_KEYWORD, $this->compileValue($value, false)]; } return $value; @@ -3119,8 +3641,9 @@ class Compiler return $this->fncall($value[1], $value[2]); case Type::T_SELF: - $selfSelector = $this->multiplySelectors($this->env); - $selfSelector = $this->collapseSelectors($selfSelector, true); + $selfParent = ! empty($this->env->block->selfParent) ? $this->env->block->selfParent : null; + $selfSelector = $this->multiplySelectors($this->env, $selfParent); + $selfSelector = $this->collapseSelectorsAsList($selfSelector); return $selfSelector; @@ -3132,35 +3655,301 @@ class Compiler /** * Function caller * - * @param string $name - * @param array $argValues + * @param string|array $functionReference + * @param array $argValues * - * @return array|null + * @return array|Number */ - protected function fncall($name, $argValues) + protected function fncall($functionReference, $argValues) { - // SCSS @function - if ($this->callScssFunction($name, $argValues, $returnValue)) { - return $returnValue; - } + // a string means this is a static hard reference coming from the parsing + if (is_string($functionReference)) { + $name = $functionReference; - // native PHP functions - if ($this->callNativeFunction($name, $argValues, $returnValue)) { - return $returnValue; - } - - // for CSS functions, simply flatten the arguments into a list - $listArgs = []; - - foreach ((array) $argValues as $arg) { - if (empty($arg[0])) { - $listArgs[] = $this->reduce($arg[1]); + $functionReference = $this->getFunctionReference($name); + if ($functionReference === static::$null || $functionReference[0] !== Type::T_FUNCTION_REFERENCE) { + $functionReference = [Type::T_FUNCTION, $name, [Type::T_LIST, ',', []]]; } } - return [Type::T_FUNCTION, $name, [Type::T_LIST, ',', $listArgs]]; + // a function type means we just want a plain css function call + if ($functionReference[0] === Type::T_FUNCTION) { + // for CSS functions, simply flatten the arguments into a list + $listArgs = []; + + foreach ((array) $argValues as $arg) { + if (empty($arg[0]) || count($argValues) === 1) { + $listArgs[] = $this->reduce($this->stringifyFncallArgs($arg[1])); + } + } + + return [Type::T_FUNCTION, $functionReference[1], [Type::T_LIST, ',', $listArgs]]; + } + + if ($functionReference === static::$null || $functionReference[0] !== Type::T_FUNCTION_REFERENCE) { + return static::$defaultValue; + } + + + switch ($functionReference[1]) { + // SCSS @function + case 'scss': + return $this->callScssFunction($functionReference[3], $argValues); + + // native PHP functions + case 'user': + case 'native': + list(,,$name, $fn, $prototype) = $functionReference; + + // special cases of css valid functions min/max + $name = strtolower($name); + if (\in_array($name, ['min', 'max']) && count($argValues) >= 1) { + $cssFunction = $this->cssValidArg( + [Type::T_FUNCTION_CALL, $name, $argValues], + ['min', 'max', 'calc', 'env', 'var'] + ); + if ($cssFunction !== false) { + return $cssFunction; + } + } + $returnValue = $this->callNativeFunction($name, $fn, $prototype, $argValues); + + if (! isset($returnValue)) { + return $this->fncall([Type::T_FUNCTION, $name, [Type::T_LIST, ',', []]], $argValues); + } + + return $returnValue; + + default: + return static::$defaultValue; + } } + /** + * @param array|Number $arg + * @param string[] $allowed_function + * @param bool $inFunction + * + * @return array|Number|false + */ + protected function cssValidArg($arg, $allowed_function = [], $inFunction = false) + { + if ($arg instanceof Number) { + return $this->stringifyFncallArgs($arg); + } + + switch ($arg[0]) { + case Type::T_INTERPOLATE: + return [Type::T_KEYWORD, $this->CompileValue($arg)]; + + case Type::T_FUNCTION: + if (! \in_array($arg[1], $allowed_function)) { + return false; + } + if ($arg[2][0] === Type::T_LIST) { + foreach ($arg[2][2] as $k => $subarg) { + $arg[2][2][$k] = $this->cssValidArg($subarg, $allowed_function, $arg[1]); + if ($arg[2][2][$k] === false) { + return false; + } + } + } + return $arg; + + case Type::T_FUNCTION_CALL: + if (! \in_array($arg[1], $allowed_function)) { + return false; + } + $cssArgs = []; + foreach ($arg[2] as $argValue) { + if ($argValue === static::$null) { + return false; + } + $cssArg = $this->cssValidArg($argValue[1], $allowed_function, $arg[1]); + if (empty($argValue[0]) && $cssArg !== false) { + $cssArgs[] = [$argValue[0], $cssArg]; + } else { + return false; + } + } + + return $this->fncall([Type::T_FUNCTION, $arg[1], [Type::T_LIST, ',', []]], $cssArgs); + + case Type::T_STRING: + case Type::T_KEYWORD: + if (!$inFunction or !\in_array($inFunction, ['calc', 'env', 'var'])) { + return false; + } + return $this->stringifyFncallArgs($arg); + + case Type::T_LIST: + if (!$inFunction) { + return false; + } + if (empty($arg['enclosing']) and $arg[1] === '') { + foreach ($arg[2] as $k => $subarg) { + $arg[2][$k] = $this->cssValidArg($subarg, $allowed_function, $inFunction); + if ($arg[2][$k] === false) { + return false; + } + } + $arg[0] = Type::T_STRING; + return $arg; + } + return false; + + case Type::T_EXPRESSION: + if (! \in_array($arg[1], ['+', '-', '/', '*'])) { + return false; + } + $arg[2] = $this->cssValidArg($arg[2], $allowed_function, $inFunction); + $arg[3] = $this->cssValidArg($arg[3], $allowed_function, $inFunction); + if ($arg[2] === false || $arg[3] === false) { + return false; + } + return $this->expToString($arg, true); + + case Type::T_VARIABLE: + case Type::T_SELF: + default: + return false; + } + } + + + /** + * Reformat fncall arguments to proper css function output + * + * @param array|Number $arg + * + * @return array|Number + */ + protected function stringifyFncallArgs($arg) + { + if ($arg instanceof Number) { + return $arg; + } + + switch ($arg[0]) { + case Type::T_LIST: + foreach ($arg[2] as $k => $v) { + $arg[2][$k] = $this->stringifyFncallArgs($v); + } + break; + + case Type::T_EXPRESSION: + if ($arg[1] === '/') { + $arg[2] = $this->stringifyFncallArgs($arg[2]); + $arg[3] = $this->stringifyFncallArgs($arg[3]); + $arg[5] = $arg[6] = false; // no space around / + $arg = $this->expToString($arg); + } + break; + + case Type::T_FUNCTION_CALL: + $name = strtolower($arg[1]); + + if (in_array($name, ['max', 'min', 'calc'])) { + $args = $arg[2]; + $arg = $this->fncall([Type::T_FUNCTION, $name, [Type::T_LIST, ',', []]], $args); + } + break; + } + + return $arg; + } + + /** + * Find a function reference + * @param string $name + * @param bool $safeCopy + * @return array + */ + protected function getFunctionReference($name, $safeCopy = false) + { + // SCSS @function + if ($func = $this->get(static::$namespaces['function'] . $name, false)) { + if ($safeCopy) { + $func = clone $func; + } + + return [Type::T_FUNCTION_REFERENCE, 'scss', $name, $func]; + } + + // native PHP functions + + // try to find a native lib function + $normalizedName = $this->normalizeName($name); + + if (isset($this->userFunctions[$normalizedName])) { + // see if we can find a user function + list($f, $prototype) = $this->userFunctions[$normalizedName]; + + return [Type::T_FUNCTION_REFERENCE, 'user', $name, $f, $prototype]; + } + + $lowercasedName = strtolower($normalizedName); + + // Special functions overriding a CSS function are case-insensitive. We normalize them as lowercase + // to avoid the deprecation warning about the wrong case being used. + if ($lowercasedName === 'min' || $lowercasedName === 'max') { + $normalizedName = $lowercasedName; + } + + if (($f = $this->getBuiltinFunction($normalizedName)) && \is_callable($f)) { + /** @var string $libName */ + $libName = $f[1]; + $prototype = isset(static::$$libName) ? static::$$libName : null; + + // All core functions have a prototype defined. Not finding the + // prototype can mean 2 things: + // - the function comes from a child class (deprecated just after) + // - the function was found with a different case, which relates to calling the + // wrong Sass function due to our camelCase usage (`fade-in()` vs `fadein()`), + // because PHP method names are case-insensitive while property names are + // case-sensitive. + if ($prototype === null || strtolower($normalizedName) !== $normalizedName) { + $r = new \ReflectionMethod($this, $libName); + $actualLibName = $r->name; + + if ($actualLibName !== $libName || strtolower($normalizedName) !== $normalizedName) { + $kebabCaseName = preg_replace('~(?<=\\w)([A-Z])~', '-$1', substr($actualLibName, 3)); + assert($kebabCaseName !== null); + $originalName = strtolower($kebabCaseName); + $warning = "Calling built-in functions with a non-standard name is deprecated since Scssphp 1.8.0 and will not work anymore in 2.0 (they will be treated as CSS function calls instead).\nUse \"$originalName\" instead of \"$name\"."; + @trigger_error($warning, E_USER_DEPRECATED); + $fname = $this->getPrettyPath($this->sourceNames[$this->sourceIndex]); + $line = $this->sourceLine; + Warn::deprecation("$warning\n on line $line of $fname"); + + // Use the actual function definition + $prototype = isset(static::$$actualLibName) ? static::$$actualLibName : null; + $f[1] = $libName = $actualLibName; + } + } + + if (\get_class($this) !== __CLASS__ && !isset($this->warnedChildFunctions[$libName])) { + $r = new \ReflectionMethod($this, $libName); + $declaringClass = $r->getDeclaringClass()->name; + + $needsWarning = $this->warnedChildFunctions[$libName] = $declaringClass !== __CLASS__; + + if ($needsWarning) { + if (method_exists(__CLASS__, $libName)) { + @trigger_error(sprintf('Overriding the "%s" core function by extending the Compiler is deprecated and will be unsupported in 2.0. Remove the "%s::%s" method.', $normalizedName, $declaringClass, $libName), E_USER_DEPRECATED); + } else { + @trigger_error(sprintf('Registering custom functions by extending the Compiler and using the lib* discovery mechanism is deprecated and will be removed in 2.0. Replace the "%s::%s" method with registering the "%s" function through "Compiler::registerFunction".', $declaringClass, $libName, $normalizedName), E_USER_DEPRECATED); + } + } + } + + return [Type::T_FUNCTION_REFERENCE, 'native', $name, $f, $prototype]; + } + + return static::$null; + } + + /** * Normalize name * @@ -3176,14 +3965,20 @@ class Compiler /** * Normalize value * - * @param array $value + * @internal * - * @return array + * @param array|Number $value + * + * @return array|Number */ public function normalizeValue($value) { $value = $this->coerceForExpression($this->reduce($value)); + if ($value instanceof Number) { + return $value; + } + switch ($value[0]) { case Type::T_LIST: $value = $this->extractInterpolation($value); @@ -3200,14 +3995,15 @@ class Compiler unset($value['enclosing']); } + if ($value[1] === '' && count($value[2]) > 1) { + $value[1] = ' '; + } + return $value; case Type::T_STRING: return [$value[0], '"', [$this->compileStringContent($value)]]; - case Type::T_NUMBER: - return $value->normalize(); - case Type::T_INTERPOLATE: return [Type::T_KEYWORD, $this->compileValue($value)]; @@ -3219,70 +4015,66 @@ class Compiler /** * Add numbers * - * @param array $left - * @param array $right + * @param Number $left + * @param Number $right * - * @return \ScssPhp\ScssPhp\Node\Number + * @return Number */ - protected function opAddNumberNumber($left, $right) + protected function opAddNumberNumber(Number $left, Number $right) { - return new Node\Number($left[1] + $right[1], $left[2]); + return $left->plus($right); } /** * Multiply numbers * - * @param array $left - * @param array $right + * @param Number $left + * @param Number $right * - * @return \ScssPhp\ScssPhp\Node\Number + * @return Number */ - protected function opMulNumberNumber($left, $right) + protected function opMulNumberNumber(Number $left, Number $right) { - return new Node\Number($left[1] * $right[1], $left[2]); + return $left->times($right); } /** * Subtract numbers * - * @param array $left - * @param array $right + * @param Number $left + * @param Number $right * - * @return \ScssPhp\ScssPhp\Node\Number + * @return Number */ - protected function opSubNumberNumber($left, $right) + protected function opSubNumberNumber(Number $left, Number $right) { - return new Node\Number($left[1] - $right[1], $left[2]); + return $left->minus($right); } /** * Divide numbers * - * @param array $left - * @param array $right + * @param Number $left + * @param Number $right * - * @return array|\ScssPhp\ScssPhp\Node\Number + * @return Number */ - protected function opDivNumberNumber($left, $right) + protected function opDivNumberNumber(Number $left, Number $right) { - if ($right[1] == 0) { - return [Type::T_STRING, '', [$left[1] . $left[2] . '/' . $right[1] . $right[2]]]; - } - - return new Node\Number($left[1] / $right[1], $left[2]); + return $left->dividedBy($right); } /** * Mod numbers * - * @param array $left - * @param array $right + * @param Number $left + * @param Number $right * - * @return \ScssPhp\ScssPhp\Node\Number + * @return Number */ - protected function opModNumberNumber($left, $right) + protected function opModNumberNumber(Number $left, Number $right) { - return new Node\Number($left[1] % $right[1], $left[2]); + return $left->modulo($right); } /** @@ -3321,11 +4113,11 @@ class Compiler /** * Boolean and * - * @param array $left - * @param array $right - * @param boolean $shouldEval + * @param array|Number $left + * @param array|Number $right + * @param bool $shouldEval * - * @return array|null + * @return array|Number|null */ protected function opAnd($left, $right, $shouldEval) { @@ -3349,11 +4141,11 @@ class Compiler /** * Boolean or * - * @param array $left - * @param array $right - * @param boolean $shouldEval + * @param array|Number $left + * @param array|Number $right + * @param bool $shouldEval * - * @return array|null + * @return array|Number|null */ protected function opOr($left, $right, $shouldEval) { @@ -3385,6 +4177,15 @@ class Compiler */ protected function opColorColor($op, $left, $right) { + if ($op !== '==' && $op !== '!=') { + $warning = "Color arithmetic is deprecated and will be an error in future versions.\n" + . "Consider using Sass's color functions instead."; + $fname = $this->getPrettyPath($this->sourceNames[$this->sourceIndex]); + $line = $this->sourceLine; + + Warn::deprecation("$warning\n on line $line of $fname"); + } + $out = [Type::T_COLOR]; foreach ([1, 2, 3] as $i) { @@ -3405,13 +4206,16 @@ class Compiler break; case '%': + if ($rval == 0) { + throw $this->error("color: Can't take modulo by zero"); + } + $out[] = $lval % $rval; break; case '/': if ($rval == 0) { - $this->throwError("color: Can't divide by zero"); - break 2; + throw $this->error("color: Can't divide by zero"); } $out[] = (int) ($lval / $rval); @@ -3424,8 +4228,7 @@ class Compiler return $this->opNeq($left, $right); default: - $this->throwError("color: unknown op $op"); - break 2; + throw $this->error("color: unknown op $op"); } } @@ -3443,13 +4246,21 @@ class Compiler * * @param string $op * @param array $left - * @param array $right + * @param Number $right * * @return array */ - protected function opColorNumber($op, $left, $right) + protected function opColorNumber($op, $left, Number $right) { - $value = $right[1]; + if ($op === '==') { + return static::$false; + } + + if ($op === '!=') { + return static::$true; + } + + $value = $right->getDimension(); return $this->opColorColor( $op, @@ -3462,14 +4273,22 @@ class Compiler * Compare number and color * * @param string $op - * @param array $left + * @param Number $left * @param array $right * * @return array */ - protected function opNumberColor($op, $left, $right) + protected function opNumberColor($op, Number $left, $right) { - $value = $left[1]; + if ($op === '==') { + return static::$false; + } + + if ($op === '!=') { + return static::$true; + } + + $value = $left->getDimension(); return $this->opColorColor( $op, @@ -3481,8 +4300,8 @@ class Compiler /** * Compare number1 == number2 * - * @param array $left - * @param array $right + * @param array|Number $left + * @param array|Number $right * * @return array */ @@ -3502,8 +4321,8 @@ class Compiler /** * Compare number1 != number2 * - * @param array $left - * @param array $right + * @param array|Number $left + * @param array|Number $right * * @return array */ @@ -3521,70 +4340,81 @@ class Compiler } /** - * Compare number1 >= number2 + * Compare number1 == number2 * - * @param array $left - * @param array $right + * @param Number $left + * @param Number $right * * @return array */ - protected function opGteNumberNumber($left, $right) + protected function opEqNumberNumber(Number $left, Number $right) { - return $this->toBool($left[1] >= $right[1]); + return $this->toBool($left->equals($right)); + } + + /** + * Compare number1 != number2 + * + * @param Number $left + * @param Number $right + * + * @return array + */ + protected function opNeqNumberNumber(Number $left, Number $right) + { + return $this->toBool(!$left->equals($right)); + } + + /** + * Compare number1 >= number2 + * + * @param Number $left + * @param Number $right + * + * @return array + */ + protected function opGteNumberNumber(Number $left, Number $right) + { + return $this->toBool($left->greaterThanOrEqual($right)); } /** * Compare number1 > number2 * - * @param array $left - * @param array $right + * @param Number $left + * @param Number $right * * @return array */ - protected function opGtNumberNumber($left, $right) + protected function opGtNumberNumber(Number $left, Number $right) { - return $this->toBool($left[1] > $right[1]); + return $this->toBool($left->greaterThan($right)); } /** * Compare number1 <= number2 * - * @param array $left - * @param array $right + * @param Number $left + * @param Number $right * * @return array */ - protected function opLteNumberNumber($left, $right) + protected function opLteNumberNumber(Number $left, Number $right) { - return $this->toBool($left[1] <= $right[1]); + return $this->toBool($left->lessThanOrEqual($right)); } /** * Compare number1 < number2 * - * @param array $left - * @param array $right + * @param Number $left + * @param Number $right * * @return array */ - protected function opLtNumberNumber($left, $right) + protected function opLtNumberNumber(Number $left, Number $right) { - return $this->toBool($left[1] < $right[1]); - } - - /** - * Three-way comparison, aka spaceship operator - * - * @param array $left - * @param array $right - * - * @return \ScssPhp\ScssPhp\Node\Number - */ - protected function opCmpNumberNumber($left, $right) - { - $n = $left[1] - $right[1]; - - return new Node\Number($n ? $n / abs($n) : 0, ''); + return $this->toBool($left->lessThan($right)); } /** @@ -3592,7 +4422,7 @@ class Compiler * * @api * - * @param mixed $thing + * @param bool $thing * * @return array */ @@ -3601,6 +4431,53 @@ class Compiler return $thing ? static::$true : static::$false; } + /** + * Escape non printable chars in strings output as in dart-sass + * + * @internal + * + * @param string $string + * @param bool $inKeyword + * + * @return string + */ + public function escapeNonPrintableChars($string, $inKeyword = false) + { + static $replacement = []; + if (empty($replacement[$inKeyword])) { + for ($i = 0; $i < 32; $i++) { + if ($i !== 9 || $inKeyword) { + $replacement[$inKeyword][chr($i)] = '\\' . dechex($i) . ($inKeyword ? ' ' : chr(0)); + } + } + } + $string = str_replace(array_keys($replacement[$inKeyword]), array_values($replacement[$inKeyword]), $string); + // chr(0) is not a possible char from the input, so any chr(0) comes from our escaping replacement + if (strpos($string, chr(0)) !== false) { + if (substr($string, -1) === chr(0)) { + $string = substr($string, 0, -1); + } + $string = str_replace( + [chr(0) . '\\',chr(0) . ' '], + [ '\\', ' '], + $string + ); + if (strpos($string, chr(0)) !== false) { + $parts = explode(chr(0), $string); + $string = array_shift($parts); + while (count($parts)) { + $next = array_shift($parts); + if (strpos("0123456789abcdefABCDEF" . chr(9), $next[0]) !== false) { + $string .= " "; + } + $string .= $next; + } + } + } + + return $string; + } + /** * Compiles a primitive value into a CSS property value. * @@ -3614,17 +4491,22 @@ class Compiler * * @api * - * @param array $value + * @param array|Number $value + * @param bool $quote * - * @return string|array + * @return string */ - public function compileValue($value) + public function compileValue($value, $quote = true) { $value = $this->reduce($value); + if ($value instanceof Number) { + return $value->output($this); + } + switch ($value[0]) { case Type::T_KEYWORD: - return $value[1]; + return $this->escapeNonPrintableChars($value[1], true); case Type::T_COLOR: // [1] - red component (either number for a %) @@ -3637,18 +4519,18 @@ class Compiler $g = $this->compileRGBAValue($g); $b = $this->compileRGBAValue($b); - if (count($value) === 5) { + if (\count($value) === 5) { $alpha = $this->compileRGBAValue($value[4], true); if (! is_numeric($alpha) || $alpha < 1) { $colorName = Colors::RGBaToColorName($r, $g, $b, $alpha); - if (! is_null($colorName)) { + if (! \is_null($colorName)) { return $colorName; } if (is_numeric($alpha)) { - $a = new Node\Number($alpha, ''); + $a = new Number($alpha, ''); } else { $a = $alpha; } @@ -3663,7 +4545,7 @@ class Compiler $colorName = Colors::RGBaToColorName($r, $g, $b); - if (! is_null($colorName)) { + if (! \is_null($colorName)) { return $colorName; } @@ -3676,72 +4558,119 @@ class Compiler return $h; - case Type::T_NUMBER: - return $value->output($this); - case Type::T_STRING: - return $value[1] . $this->compileStringContent($value) . $value[1]; + $content = $this->compileStringContent($value, $quote); + + if ($value[1] && $quote) { + $content = str_replace('\\', '\\\\', $content); + + $content = $this->escapeNonPrintableChars($content); + + // force double quote as string quote for the output in certain cases + if ( + $value[1] === "'" && + (strpos($content, '"') === false or strpos($content, "'") !== false) + ) { + $value[1] = '"'; + } elseif ( + $value[1] === '"' && + (strpos($content, '"') !== false and strpos($content, "'") === false) + ) { + $value[1] = "'"; + } + + $content = str_replace($value[1], '\\' . $value[1], $content); + } + + return $value[1] . $content . $value[1]; case Type::T_FUNCTION: - $args = ! empty($value[2]) ? $this->compileValue($value[2]) : ''; + $args = ! empty($value[2]) ? $this->compileValue($value[2], $quote) : ''; return "$value[1]($args)"; + case Type::T_FUNCTION_REFERENCE: + $name = ! empty($value[2]) ? $value[2] : ''; + + return "get-function(\"$name\")"; + case Type::T_LIST: $value = $this->extractInterpolation($value); if ($value[0] !== Type::T_LIST) { - return $this->compileValue($value); + return $this->compileValue($value, $quote); } list(, $delim, $items) = $value; - $pre = $post = ""; + $pre = $post = ''; + if (! empty($value['enclosing'])) { switch ($value['enclosing']) { case 'parent': - //$pre = "("; - //$post = ")"; + //$pre = '('; + //$post = ')'; break; case 'forced_parent': - $pre = "("; - $post = ")"; + $pre = '('; + $post = ')'; break; case 'bracket': case 'forced_bracket': - $pre = "["; - $post = "]"; + $pre = '['; + $post = ']'; break; } } + $separator = $delim === '/' ? ' /' : $delim; + $prefix_value = ''; + if ($delim !== ' ') { $prefix_value = ' '; } $filtered = []; + $same_string_quote = null; foreach ($items as $item) { + if (\is_null($same_string_quote)) { + $same_string_quote = false; + if ($item[0] === Type::T_STRING) { + $same_string_quote = $item[1]; + foreach ($items as $ii) { + if ($ii[0] !== Type::T_STRING) { + $same_string_quote = false; + break; + } + } + } + } if ($item[0] === Type::T_NULL) { continue; } + if ($same_string_quote === '"' && $item[0] === Type::T_STRING && $item[1]) { + $item[1] = $same_string_quote; + } - $compiled = $this->compileValue($item); - if ($prefix_value && strlen($compiled)) { + $compiled = $this->compileValue($item, $quote); + + if ($prefix_value && \strlen($compiled)) { $compiled = $prefix_value . $compiled; } + $filtered[] = $compiled; } - return $pre . substr(implode("$delim", $filtered), strlen($prefix_value)) . $post; + return $pre . substr(implode($separator, $filtered), \strlen($prefix_value)) . $post; case Type::T_MAP: $keys = $value[1]; $values = $value[2]; $filtered = []; - for ($i = 0, $s = count($keys); $i < $s; $i++) { - $filtered[$this->compileValue($keys[$i])] = $this->compileValue($values[$i]); + for ($i = 0, $s = \count($keys); $i < $s; $i++) { + $filtered[$this->compileValue($keys[$i], $quote)] = $this->compileValue($values[$i], $quote); } array_walk($filtered, function (&$value, $key) { @@ -3761,8 +4690,9 @@ class Compiler $delim .= ' '; } - $left = count($left[2]) > 0 ? - $this->compileValue($left) . $delim . $whiteLeft: ''; + $left = \count($left[2]) > 0 + ? $this->compileValue($left, $quote) . $delim . $whiteLeft + : ''; $delim = $right[1]; @@ -3770,15 +4700,19 @@ class Compiler $delim .= ' '; } - $right = count($right[2]) > 0 ? - $whiteRight . $delim . $this->compileValue($right) : ''; + $right = \count($right[2]) > 0 ? + $whiteRight . $delim . $this->compileValue($right, $quote) : ''; - return $left . $this->compileValue($interpolate) . $right; + return $left . $this->compileValue($interpolate, $quote) . $right; case Type::T_INTERPOLATE: // strip quotes if it's a string $reduced = $this->reduce($value[1]); + if ($reduced instanceof Number) { + return $this->compileValue($reduced, $quote); + } + switch ($reduced[0]) { case Type::T_LIST: $reduced = $this->extractInterpolation($reduced); @@ -3800,14 +4734,12 @@ class Compiler continue; } - $temp = $this->compileValue([Type::T_KEYWORD, $item]); - - if ($temp[0] === Type::T_STRING) { - $filtered[] = $this->compileStringContent($temp); - } elseif ($temp[0] === Type::T_KEYWORD) { - $filtered[] = $temp[1]; + if ($item[0] === Type::T_STRING) { + $filtered[] = $this->compileStringContent($item, $quote); + } elseif ($item[0] === Type::T_KEYWORD) { + $filtered[] = $item[1]; } else { - $filtered[] = $this->compileValue($temp); + $filtered[] = $this->compileValue($item, $quote); } } @@ -3815,14 +4747,14 @@ class Compiler break; case Type::T_STRING: - $reduced = [Type::T_KEYWORD, $this->compileStringContent($reduced)]; + $reduced = [Type::T_STRING, '', [$this->compileStringContent($reduced)]]; break; case Type::T_NULL: $reduced = [Type::T_KEYWORD, '']; } - return $this->compileValue($reduced); + return $this->compileValue($reduced, $quote); case Type::T_NULL: return 'null'; @@ -3831,7 +4763,29 @@ class Compiler return $this->compileCommentValue($value); default: - $this->throwError("unknown value type: ".json_encode($value)); + throw $this->error('unknown value type: ' . json_encode($value)); + } + } + + /** + * @param array|Number $value + * + * @return string + */ + protected function compileDebugValue($value) + { + $value = $this->reduce($value, true); + + if ($value instanceof Number) { + return $this->compileValue($value); + } + + switch ($value[0]) { + case Type::T_STRING: + return $this->compileStringContent($value); + + default: + return $this->compileValue($value); } } @@ -3841,26 +4795,50 @@ class Compiler * @param array $list * * @return string + * + * @deprecated */ protected function flattenList($list) { + @trigger_error(sprintf('The "%s" method is deprecated.', __METHOD__), E_USER_DEPRECATED); + return $this->compileValue($list); } + /** + * Gets the text of a Sass string + * + * Calling this method on anything else than a SassString is unsupported. Use {@see assertString} first + * to ensure that the value is indeed a string. + * + * @param array $value + * + * @return string + */ + public function getStringText(array $value) + { + if ($value[0] !== Type::T_STRING) { + throw new \InvalidArgumentException('The argument is not a sass string. Did you forgot to use "assertString"?'); + } + + return $this->compileStringContent($value); + } + /** * Compile string content * * @param array $string + * @param bool $quote * * @return string */ - protected function compileStringContent($string) + protected function compileStringContent($string, $quote = true) { $parts = []; foreach ($string[2] as $part) { - if (is_array($part) || $part instanceof \ArrayAccess) { - $parts[] = $this->compileValue($part); + if (\is_array($part) || $part instanceof Number) { + $parts[] = $this->compileValue($part, $quote); } else { $parts[] = $part; } @@ -3882,8 +4860,8 @@ class Compiler foreach ($items as $i => $item) { if ($item[0] === Type::T_INTERPOLATE) { - $before = [Type::T_LIST, $list[1], array_slice($items, 0, $i)]; - $after = [Type::T_LIST, $list[1], array_slice($items, $i + 1)]; + $before = [Type::T_LIST, $list[1], \array_slice($items, 0, $i)]; + $after = [Type::T_LIST, $list[1], \array_slice($items, $i + 1)]; return [Type::T_INTERPOLATED, $item, $before, $after]; } @@ -3908,7 +4886,7 @@ class Compiler $selfParentSelectors = null; - if (! is_null($selfParent) && $selfParent->selectors) { + if (! \is_null($selfParent) && $selfParent->selectors) { $selfParentSelectors = $this->evalSelectors($selfParent->selectors); } @@ -3924,8 +4902,8 @@ class Compiler $prevSelectors = $selectors; $selectors = []; - foreach ($prevSelectors as $selector) { - foreach ($parentSelectors as $parent) { + foreach ($parentSelectors as $parent) { + foreach ($prevSelectors as $selector) { if ($selfParentSelectors) { foreach ($selfParentSelectors as $selfParent) { // if no '&' in the selector, each call will give same result, only add once @@ -3945,16 +4923,21 @@ class Compiler $selectors = array_values($selectors); + // case we are just starting a at-root : nothing to multiply but parentSelectors + if (! $selectors && $selfParentSelectors) { + $selectors = $selfParentSelectors; + } + return $selectors; } /** * Join selectors; looks for & to replace, or append parent before child * - * @param array $parent - * @param array $child - * @param boolean &$stillHasSelf - * @param array $selfParentSelectors + * @param array $parent + * @param array $child + * @param bool $stillHasSelf + * @param array $selfParentSelectors * @return array */ @@ -3975,7 +4958,7 @@ class Compiler if ($p === static::$selfSelector && ! $setSelf) { $setSelf = true; - if (is_null($selfParentSelectors)) { + if (\is_null($selfParentSelectors)) { $selfParentSelectors = $parent; } @@ -3986,7 +4969,7 @@ class Compiler } foreach ($parentPart as $pp) { - if (is_array($pp)) { + if (\is_array($pp)) { $flatten = []; array_walk_recursive($pp, function ($a) use (&$flatten) { @@ -4020,7 +5003,8 @@ class Compiler */ protected function multiplyMedia(Environment $env = null, $childQueries = null) { - if (! isset($env) || + if ( + ! isset($env) || ! empty($env->block->type) && $env->block->type !== Type::T_MEDIA ) { return $childQueries; @@ -4031,6 +5015,8 @@ class Compiler return $this->multiplyMedia($env->parent, $childQueries); } + assert($env->block instanceof MediaBlock); + $parentQueries = isset($env->block->queryList) ? $env->block->queryList : [[[Type::T_MEDIA_VALUE, $env->block->value]]]; @@ -4043,7 +5029,7 @@ class Compiler list($this->env, $this->storeEnv) = $store; - if (is_null($childQueries)) { + if (\is_null($childQueries)) { $childQueries = $parentQueries; } else { $originalQueries = $childQueries; @@ -4066,9 +5052,11 @@ class Compiler /** * Convert env linked list to stack * - * @param \ScssPhp\ScssPhp\Compiler\Environment $env + * @param Environment $env * - * @return array + * @return Environment[] + * + * @phpstan-return non-empty-array */ protected function compactEnv(Environment $env) { @@ -4082,9 +5070,11 @@ class Compiler /** * Convert env stack to singly linked list * - * @param array $envs + * @param Environment[] $envs * - * @return \ScssPhp\ScssPhp\Compiler\Environment + * @return Environment + * + * @phpstan-param non-empty-array $envs */ protected function extractEnv($envs) { @@ -4105,25 +5095,47 @@ class Compiler */ protected function pushEnv(Block $block = null) { - $env = new Environment; + $env = new Environment(); $env->parent = $this->env; + $env->parentStore = $this->storeEnv; $env->store = []; $env->block = $block; $env->depth = isset($this->env->depth) ? $this->env->depth + 1 : 0; $this->env = $env; + $this->storeEnv = null; return $env; } /** * Pop environment + * + * @return void */ protected function popEnv() { + $this->storeEnv = $this->env->parentStore; $this->env = $this->env->parent; } + /** + * Propagate vars from a just poped Env (used in @each and @for) + * + * @param array $store + * @param null|string[] $excludedVars + * + * @return void + */ + protected function backPropagateEnv($store, $excludedVars = null) + { + foreach ($store as $key => $value) { + if (empty($excludedVars) || ! \in_array($key, $excludedVars)) { + $this->set($key, $value, true); + } + } + } + /** * Get store environment * @@ -4139,9 +5151,11 @@ class Compiler * * @param string $name * @param mixed $value - * @param boolean $shadow + * @param bool $shadow * @param \ScssPhp\ScssPhp\Compiler\Environment $env * @param mixed $valueUnreduced + * + * @return void */ protected function set($name, $value, $shadow = false, Environment $env = null, $valueUnreduced = null) { @@ -4165,29 +5179,50 @@ class Compiler * @param mixed $value * @param \ScssPhp\ScssPhp\Compiler\Environment $env * @param mixed $valueUnreduced + * + * @return void */ protected function setExisting($name, $value, Environment $env, $valueUnreduced = null) { $storeEnv = $env; + $specialContentKey = static::$namespaces['special'] . 'content'; $hasNamespace = $name[0] === '^' || $name[0] === '@' || $name[0] === '%'; + $maxDepth = 10000; + for (;;) { - if (array_key_exists($name, $env->store)) { + if ($maxDepth-- <= 0) { + break; + } + + if (\array_key_exists($name, $env->store)) { break; } if (! $hasNamespace && isset($env->marker)) { + if (! empty($env->store[$specialContentKey])) { + $env = $env->store[$specialContentKey]->scope; + continue; + } + + if (! empty($env->declarationScopeParent)) { + $env = $env->declarationScopeParent; + continue; + } else { + $env = $storeEnv; + break; + } + } + + if (isset($env->parentStore)) { + $env = $env->parentStore; + } elseif (isset($env->parent)) { + $env = $env->parent; + } else { $env = $storeEnv; break; } - - if (! isset($env->parent)) { - $env = $storeEnv; - break; - } - - $env = $env->parent; } $env->store[$name] = $value; @@ -4204,6 +5239,8 @@ class Compiler * @param mixed $value * @param \ScssPhp\ScssPhp\Compiler\Environment $env * @param mixed $valueUnreduced + * + * @return void */ protected function setRaw($name, $value, Environment $env, $valueUnreduced = null) { @@ -4217,12 +5254,12 @@ class Compiler /** * Get variable * - * @api + * @internal * * @param string $name - * @param boolean $shouldThrow + * @param bool $shouldThrow * @param \ScssPhp\ScssPhp\Compiler\Environment $env - * @param boolean $unreduced + * @param bool $unreduced * * @return mixed|null */ @@ -4244,7 +5281,7 @@ class Compiler break; } - if (array_key_exists($normalizedName, $env->store)) { + if (\array_key_exists($normalizedName, $env->store)) { if ($unreduced && isset($env->storeUnreduced[$normalizedName])) { return $env->storeUnreduced[$normalizedName]; } @@ -4266,15 +5303,17 @@ class Compiler continue; } - if (! isset($env->parent)) { + if (isset($env->parentStore)) { + $env = $env->parentStore; + } elseif (isset($env->parent)) { + $env = $env->parent; + } else { break; } - - $env = $env->parent; } if ($shouldThrow) { - $this->throwError("Undefined variable \$$name" . ($maxDepth <= 0 ? " (infinite recursion)" : "")); + throw $this->error("Undefined variable \$$name" . ($maxDepth <= 0 ? ' (infinite recursion)' : '')); } // found nothing @@ -4287,17 +5326,19 @@ class Compiler * @param string $name * @param \ScssPhp\ScssPhp\Compiler\Environment $env * - * @return boolean + * @return bool */ protected function has($name, Environment $env = null) { - return ! is_null($this->get($name, false, $env)); + return ! \is_null($this->get($name, false, $env)); } /** * Inject variables * * @param array $args + * + * @return void */ protected function injectVariables(array $args) { @@ -4312,7 +5353,7 @@ class Compiler $name = substr($name, 1); } - if (! $parser->parseValue($strValue, $value)) { + if (!\is_string($strValue) || ! $parser->parseValue($strValue, $value)) { $value = $this->coerceValue($strValue); } @@ -4320,16 +5361,59 @@ class Compiler } } + /** + * Replaces variables. + * + * @param array $variables + * + * @return void + */ + public function replaceVariables(array $variables) + { + $this->registeredVars = []; + $this->addVariables($variables); + } + + /** + * Replaces variables. + * + * @param array $variables + * + * @return void + */ + public function addVariables(array $variables) + { + $triggerWarning = false; + + foreach ($variables as $name => $value) { + if (!$value instanceof Number && !\is_array($value)) { + $triggerWarning = true; + } + + $this->registeredVars[$name] = $value; + } + + if ($triggerWarning) { + @trigger_error('Passing raw values to as custom variables to the Compiler is deprecated. Use "\ScssPhp\ScssPhp\ValueConverter::parseValue" or "\ScssPhp\ScssPhp\ValueConverter::fromPhp" to convert them instead.', E_USER_DEPRECATED); + } + } + /** * Set variables * * @api * * @param array $variables + * + * @return void + * + * @deprecated Use "addVariables" or "replaceVariables" instead. */ public function setVariables(array $variables) { - $this->registeredVars = array_merge($this->registeredVars, $variables); + @trigger_error('The method "setVariables" of the Compiler is deprecated. Use the "addVariables" method for the equivalent behavior or "replaceVariables" if merging with previous variables was not desired.'); + + $this->addVariables($variables); } /** @@ -4338,6 +5422,8 @@ class Compiler * @api * * @param string $name + * + * @return void */ public function unsetVariable($name) { @@ -4359,13 +5445,15 @@ class Compiler /** * Adds to list of parsed files * - * @api + * @internal * - * @param string $path + * @param string|null $path + * + * @return void */ public function addParsedFile($path) { - if (isset($path) && is_file($path)) { + if (! \is_null($path) && is_file($path)) { $this->parsedFiles[realpath($path)] = filemtime($path); } } @@ -4373,12 +5461,12 @@ class Compiler /** * Returns list of parsed files * - * @api - * - * @return array + * @deprecated + * @return array */ public function getParsedFiles() { + @trigger_error('The method "getParsedFiles" of the Compiler is deprecated. Use the "getIncludedFiles" method on the CompilationResult instance returned by compileString() instead. Be careful that the signature of the method is different.', E_USER_DEPRECATED); return $this->parsedFiles; } @@ -4388,10 +5476,12 @@ class Compiler * @api * * @param string|callable $path + * + * @return void */ public function addImportPath($path) { - if (! in_array($path, $this->importPaths)) { + if (! \in_array($path, $this->importPaths)) { $this->importPaths[] = $path; } } @@ -4401,11 +5491,24 @@ class Compiler * * @api * - * @param string|array $path + * @param string|array $path + * + * @return void */ public function setImportPaths($path) { - $this->importPaths = (array) $path; + $paths = (array) $path; + $actualImportPaths = array_filter($paths, function ($path) { + return $path !== ''; + }); + + $this->legacyCwdImportPath = \count($actualImportPaths) !== \count($paths); + + if ($this->legacyCwdImportPath) { + @trigger_error('Passing an empty string in the import paths to refer to the current working directory is deprecated. If that\'s the intended behavior, the value of "getcwd()" should be used directly instead. If this was used for resolving relative imports of the input alongside "chdir" with the source directory, the path of the input file should be passed to "compileString()" instead.', E_USER_DEPRECATED); + } + + $this->importPaths = $actualImportPaths; } /** @@ -4413,11 +5516,43 @@ class Compiler * * @api * - * @param integer $numberPrecision + * @param int $numberPrecision + * + * @return void + * + * @deprecated The number precision is not configurable anymore. The default is enough for all browsers. */ public function setNumberPrecision($numberPrecision) { - Node\Number::$precision = $numberPrecision; + @trigger_error('The number precision is not configurable anymore. ' + . 'The default is enough for all browsers.', E_USER_DEPRECATED); + } + + /** + * Sets the output style. + * + * @api + * + * @param string $style One of the OutputStyle constants + * + * @return void + * + * @phpstan-param OutputStyle::* $style + */ + public function setOutputStyle($style) + { + switch ($style) { + case OutputStyle::EXPANDED: + $this->configuredFormatter = Expanded::class; + break; + + case OutputStyle::COMPRESSED: + $this->configuredFormatter = Compressed::class; + break; + + default: + throw new \InvalidArgumentException(sprintf('Invalid output style "%s".', $style)); + } } /** @@ -4426,10 +5561,21 @@ class Compiler * @api * * @param string $formatterName + * + * @return void + * + * @deprecated Use {@see setOutputStyle} instead. + * + * @phpstan-param class-string $formatterName */ public function setFormatter($formatterName) { - $this->formatter = $formatterName; + if (!\in_array($formatterName, [Expanded::class, Compressed::class], true)) { + @trigger_error('Formatters other than Expanded and Compressed are deprecated.', E_USER_DEPRECATED); + } + @trigger_error('The method "setFormatter" is deprecated. Use "setOutputStyle" instead.', E_USER_DEPRECATED); + + $this->configuredFormatter = $formatterName; } /** @@ -4438,10 +5584,34 @@ class Compiler * @api * * @param string $lineNumberStyle + * + * @return void + * + * @deprecated The line number output is not supported anymore. Use source maps instead. */ public function setLineNumberStyle($lineNumberStyle) { - $this->lineNumberStyle = $lineNumberStyle; + @trigger_error('The line number output is not supported anymore. ' + . 'Use source maps instead.', E_USER_DEPRECATED); + } + + /** + * Configures the handling of non-ASCII outputs. + * + * If $charset is `true`, this will include a `@charset` declaration or a + * UTF-8 [byte-order mark][] if the stylesheet contains any non-ASCII + * characters. Otherwise, it will never include a `@charset` declaration or a + * byte-order mark. + * + * [byte-order mark]: https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8 + * + * @param bool $charset + * + * @return void + */ + public function setCharset($charset) + { + $this->charset = $charset; } /** @@ -4449,7 +5619,11 @@ class Compiler * * @api * - * @param integer $sourceMap + * @param int $sourceMap + * + * @return void + * + * @phpstan-param self::SOURCE_MAP_* $sourceMap */ public function setSourceMap($sourceMap) { @@ -4462,6 +5636,10 @@ class Compiler * @api * * @param array $sourceMapOptions + * + * @phpstan-param array{sourceRoot?: string, sourceMapFilename?: string|null, sourceMapURL?: string|null, sourceMapWriteTo?: string|null, outputSourceFiles?: bool, sourceMapRootpath?: string, sourceMapBasepath?: string} $sourceMapOptions + * + * @return void */ public function setSourceMapOptions($sourceMapOptions) { @@ -4473,13 +5651,23 @@ class Compiler * * @api * - * @param string $name - * @param callable $func - * @param array $prototype + * @param string $name + * @param callable $callback + * @param string[]|null $argumentDeclaration + * + * @return void */ - public function registerFunction($name, $func, $prototype = null) + public function registerFunction($name, $callback, $argumentDeclaration = null) { - $this->userFunctions[$this->normalizeName($name)] = [$func, $prototype]; + if (self::isNativeFunction($name)) { + @trigger_error(sprintf('The "%s" function is a core sass function. Overriding it with a custom implementation through "%s" is deprecated and won\'t be supported in ScssPhp 2.0 anymore.', $name, __METHOD__), E_USER_DEPRECATED); + } + + if ($argumentDeclaration === null) { + @trigger_error('Omitting the argument declaration when registering custom function is deprecated and won\'t be supported in ScssPhp 2.0 anymore.', E_USER_DEPRECATED); + } + + $this->userFunctions[$this->normalizeName($name)] = [$callback, $argumentDeclaration]; } /** @@ -4488,6 +5676,8 @@ class Compiler * @api * * @param string $name + * + * @return void */ public function unregisterFunction($name) { @@ -4500,9 +5690,15 @@ class Compiler * @api * * @param string $name + * + * @return void + * + * @deprecated Registering additional features is deprecated. */ public function addFeature($name) { + @trigger_error('Registering additional features is deprecated.', E_USER_DEPRECATED); + $this->registeredFeatures[$name] = true; } @@ -4511,12 +5707,28 @@ class Compiler * * @param string $path * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $out + * + * @return void */ protected function importFile($path, OutputBlock $out) { + $this->pushCallStack('import ' . $this->getPrettyPath($path)); // see if tree is cached $realPath = realpath($path); + if ($realPath === false) { + $realPath = $path; + } + + if (substr($path, -5) === '.sass') { + $this->sourceIndex = \count($this->sourceNames); + $this->sourceNames[] = $path; + $this->sourceLine = 1; + $this->sourceColumn = 1; + + throw $this->error('The Sass indented syntax is not implemented.'); + } + if (isset($this->importCache[$realPath])) { $this->handleImportLoop($realPath); @@ -4529,62 +5741,261 @@ class Compiler $this->importCache[$realPath] = $tree; } - $pi = pathinfo($path); + $currentDirectory = $this->currentDirectory; + $this->currentDirectory = dirname($path); - array_unshift($this->importPaths, $pi['dirname']); $this->compileChildrenNoReturn($tree->children, $out); - array_shift($this->importPaths); + $this->currentDirectory = $currentDirectory; + $this->popCallStack(); + } + + /** + * Save the imported files with their resolving path context + * + * @param string|null $currentDirectory + * @param string $path + * @param string $filePath + * + * @return void + */ + private function registerImport($currentDirectory, $path, $filePath) + { + $this->resolvedImports[] = ['currentDir' => $currentDirectory, 'path' => $path, 'filePath' => $filePath]; + } + + /** + * Detects whether the import is a CSS import. + * + * For legacy reasons, custom importers are called for those, allowing them + * to replace them with an actual Sass import. However this behavior is + * deprecated. Custom importers are expected to return null when they receive + * a CSS import. + * + * @param string $url + * + * @return bool + */ + public static function isCssImport($url) + { + return 1 === preg_match('~\.css$|^https?://|^//~', $url); } /** * Return the file path for an import url if it exists * - * @api + * @internal * - * @param string $url + * @param string $url + * @param string|null $currentDir * * @return string|null */ - public function findImport($url) + public function findImport($url, $currentDir = null) { - $urls = []; + // Vanilla css and external requests. These are not meant to be Sass imports. + // Callback importers are still called for BC. + if (self::isCssImport($url)) { + foreach ($this->importPaths as $dir) { + if (\is_string($dir)) { + continue; + } - // for "normal" scss imports (ignore vanilla css and external requests) - if (! preg_match('~\.css$|^https?://~', $url)) { - // try both normal and the _partial filename - $urls = [$url, preg_replace('~[^/]+$~', '_\0', $url)]; - } + if (\is_callable($dir)) { + // check custom callback for import path + $file = \call_user_func($dir, $url); - $hasExtension = preg_match('/[.]s?css$/', $url); + if (! \is_null($file)) { + if (\is_array($dir)) { + $callableDescription = (\is_object($dir[0]) ? \get_class($dir[0]) : $dir[0]).'::'.$dir[1]; + } elseif ($dir instanceof \Closure) { + $r = new \ReflectionFunction($dir); + if (false !== strpos($r->name, '{closure}')) { + $callableDescription = sprintf('closure{%s:%s}', $r->getFileName(), $r->getStartLine()); + } elseif ($class = $r->getClosureScopeClass()) { + $callableDescription = $class->name.'::'.$r->name; + } else { + $callableDescription = $r->name; + } + } elseif (\is_object($dir)) { + $callableDescription = \get_class($dir) . '::__invoke'; + } else { + $callableDescription = 'callable'; // Fallback if we don't have a dedicated description + } + @trigger_error(sprintf('Returning a file to import for CSS or external references in custom importer callables is deprecated and will not be supported anymore in ScssPhp 2.0. This behavior is not compliant with the Sass specification. Update your "%s" importer.', $callableDescription), E_USER_DEPRECATED); - foreach ($this->importPaths as $dir) { - if (is_string($dir)) { - // check urls for normal import paths - foreach ($urls as $full) { - $separator = ( - ! empty($dir) && - substr($dir, -1) !== '/' && - substr($full, 0, 1) !== '/' - ) ? '/' : ''; - $full = $dir . $separator . $full; - - if (is_file($file = $full . '.scss') || - ($hasExtension && is_file($file = $full)) - ) { return $file; } } - } elseif (is_callable($dir)) { - // check custom callback for import path - $file = call_user_func($dir, $url); + } + return null; + } - if (! is_null($file)) { + if (!\is_null($currentDir)) { + $relativePath = $this->resolveImportPath($url, $currentDir); + + if (!\is_null($relativePath)) { + return $relativePath; + } + } + + foreach ($this->importPaths as $dir) { + if (\is_string($dir)) { + $path = $this->resolveImportPath($url, $dir); + + if (!\is_null($path)) { + return $path; + } + } elseif (\is_callable($dir)) { + // check custom callback for import path + $file = \call_user_func($dir, $url); + + if (! \is_null($file)) { return $file; } } } - return null; + if ($this->legacyCwdImportPath) { + $path = $this->resolveImportPath($url, getcwd()); + + if (!\is_null($path)) { + @trigger_error('Resolving imports relatively to the current working directory is deprecated. If that\'s the intended behavior, the value of "getcwd()" should be added as an import path explicitly instead. If this was used for resolving relative imports of the input alongside "chdir" with the source directory, the path of the input file should be passed to "compileString()" instead.', E_USER_DEPRECATED); + + return $path; + } + } + + throw $this->error("`$url` file not found for @import"); + } + + /** + * @param string $url + * @param string $baseDir + * + * @return string|null + */ + private function resolveImportPath($url, $baseDir) + { + $path = Path::join($baseDir, $url); + + $hasExtension = preg_match('/.s[ac]ss$/', $url); + + if ($hasExtension) { + return $this->checkImportPathConflicts($this->tryImportPath($path)); + } + + $result = $this->checkImportPathConflicts($this->tryImportPathWithExtensions($path)); + + if (!\is_null($result)) { + return $result; + } + + return $this->tryImportPathAsDirectory($path); + } + + /** + * @param string[] $paths + * + * @return string|null + */ + private function checkImportPathConflicts(array $paths) + { + if (\count($paths) === 0) { + return null; + } + + if (\count($paths) === 1) { + return $paths[0]; + } + + $formattedPrettyPaths = []; + + foreach ($paths as $path) { + $formattedPrettyPaths[] = ' ' . $this->getPrettyPath($path); + } + + throw $this->error("It's not clear which file to import. Found:\n" . implode("\n", $formattedPrettyPaths)); + } + + /** + * @param string $path + * + * @return string[] + */ + private function tryImportPathWithExtensions($path) + { + $result = array_merge( + $this->tryImportPath($path.'.sass'), + $this->tryImportPath($path.'.scss') + ); + + if ($result) { + return $result; + } + + return $this->tryImportPath($path.'.css'); + } + + /** + * @param string $path + * + * @return string[] + */ + private function tryImportPath($path) + { + $partial = dirname($path).'/_'.basename($path); + + $candidates = []; + + if (is_file($partial)) { + $candidates[] = $partial; + } + + if (is_file($path)) { + $candidates[] = $path; + } + + return $candidates; + } + + /** + * @param string $path + * + * @return string|null + */ + private function tryImportPathAsDirectory($path) + { + if (!is_dir($path)) { + return null; + } + + return $this->checkImportPathConflicts($this->tryImportPathWithExtensions($path.'/index')); + } + + /** + * @param string|null $path + * + * @return string + */ + private function getPrettyPath($path) + { + if ($path === null) { + return '(unknown file)'; + } + + $normalizedPath = $path; + $normalizedRootDirectory = $this->rootDirectory.'/'; + + if (\DIRECTORY_SEPARATOR === '\\') { + $normalizedRootDirectory = str_replace('\\', '/', $normalizedRootDirectory); + $normalizedPath = str_replace('\\', '/', $path); + } + + if (0 === strpos($normalizedPath, $normalizedRootDirectory)) { + return substr($path, \strlen($normalizedRootDirectory)); + } + + return $path; } /** @@ -4592,10 +6003,20 @@ class Compiler * * @api * - * @param string $encoding + * @param string|null $encoding + * + * @return void + * + * @deprecated Non-compliant support for other encodings than UTF-8 is deprecated. */ public function setEncoding($encoding) { + if (!$encoding || strtolower($encoding) === 'utf-8') { + @trigger_error(sprintf('The "%s" method is deprecated.', __METHOD__), E_USER_DEPRECATED); + } else { + @trigger_error(sprintf('The "%s" method is deprecated. Parsing will only support UTF-8 in ScssPhp 2.0. The non-UTF-8 parsing of ScssPhp 1.x is not spec compliant.', __METHOD__), E_USER_DEPRECATED); + } + $this->encoding = $encoding; } @@ -4604,17 +6025,37 @@ class Compiler * * @api * - * @param boolean $ignoreErrors + * @param bool $ignoreErrors * * @return \ScssPhp\ScssPhp\Compiler + * + * @deprecated Ignoring Sass errors is not longer supported. */ public function setIgnoreErrors($ignoreErrors) { - $this->ignoreErrors = $ignoreErrors; + @trigger_error('Ignoring Sass errors is not longer supported.', E_USER_DEPRECATED); return $this; } + /** + * Get source position + * + * @api + * + * @return array + * + * @deprecated + */ + public function getSourcePosition() + { + @trigger_error(sprintf('The "%s" method is deprecated.', __METHOD__), E_USER_DEPRECATED); + + $sourceFile = isset($this->sourceNames[$this->sourceIndex]) ? $this->sourceNames[$this->sourceIndex] : ''; + + return [$sourceFile, $this->sourceLine, $this->sourceColumn]; + } + /** * Throw error (exception) * @@ -4622,24 +6063,58 @@ class Compiler * * @param string $msg Message with optional sprintf()-style vararg parameters * + * @return never + * * @throws \ScssPhp\ScssPhp\Exception\CompilerException + * + * @deprecated use "error" and throw the exception in the caller instead. */ public function throwError($msg) { - if ($this->ignoreErrors) { - return; + @trigger_error( + 'The method "throwError" is deprecated. Use "error" and throw the exception in the caller instead', + E_USER_DEPRECATED + ); + + throw $this->error(...func_get_args()); + } + + /** + * Build an error (exception) + * + * @internal + * + * @param string $msg Message with optional sprintf()-style vararg parameters + * @param bool|float|int|string|null ...$args + * + * @return CompilerException + */ + public function error($msg, ...$args) + { + if ($args) { + $msg = sprintf($msg, ...$args); } + if (! $this->ignoreCallStackMessage) { + $msg = $this->addLocationToMessage($msg); + } + + return new CompilerException($msg); + } + + /** + * @param string $msg + * + * @return string + */ + private function addLocationToMessage($msg) + { $line = $this->sourceLine; $column = $this->sourceColumn; $loc = isset($this->sourceNames[$this->sourceIndex]) - ? $this->sourceNames[$this->sourceIndex] . " on line $line, at column $column" - : "line: $line, column: $column"; - - if (func_num_args() > 1) { - $msg = call_user_func_array('sprintf', func_get_args()); - } + ? $this->getPrettyPath($this->sourceNames[$this->sourceIndex]) . " on line $line, at column $column" + : "line: $line, column: $column"; $msg = "$msg: $loc"; @@ -4649,14 +6124,51 @@ class Compiler $msg .= "\nCall Stack:\n" . $callStackMsg; } - throw new CompilerException($msg); + return $msg; + } + + /** + * @param string $functionName + * @param array $ExpectedArgs + * @param int $nbActual + * @return CompilerException + * + * @deprecated + */ + public function errorArgsNumber($functionName, $ExpectedArgs, $nbActual) + { + @trigger_error(sprintf('The "%s" method is deprecated.', __METHOD__), E_USER_DEPRECATED); + + $nbExpected = \count($ExpectedArgs); + + if ($nbActual > $nbExpected) { + return $this->error( + 'Error: Only %d arguments allowed in %s(), but %d were passed.', + $nbExpected, + $functionName, + $nbActual + ); + } else { + $missing = []; + + while (count($ExpectedArgs) && count($ExpectedArgs) > $nbActual) { + array_unshift($missing, array_pop($ExpectedArgs)); + } + + return $this->error( + 'Error: %s() argument%s %s missing.', + $functionName, + count($missing) > 1 ? 's' : '', + implode(', ', $missing) + ); + } } /** * Beautify call stack for output * - * @param boolean $all - * @param null $limit + * @param bool $all + * @param int|null $limit * * @return string */ @@ -4668,15 +6180,15 @@ class Compiler if ($this->callStack) { foreach (array_reverse($this->callStack) as $call) { if ($all || (isset($call['n']) && $call['n'])) { - $msg = "#" . $ncall++ . " " . $call['n'] . " "; + $msg = '#' . $ncall++ . ' ' . $call['n'] . ' '; $msg .= (isset($this->sourceNames[$call[Parser::SOURCE_INDEX]]) - ? $this->sourceNames[$call[Parser::SOURCE_INDEX]] + ? $this->getPrettyPath($this->sourceNames[$call[Parser::SOURCE_INDEX]]) : '(unknown file)'); - $msg .= " on line " . $call[Parser::SOURCE_LINE]; + $msg .= ' on line ' . $call[Parser::SOURCE_LINE]; $callStackMsg[] = $msg; - if (! is_null($limit) && $ncall > $limit) { + if (! \is_null($limit) && $ncall > $limit) { break; } } @@ -4691,6 +6203,8 @@ class Compiler * * @param string $name * + * @return void + * * @throws \Exception */ protected function handleImportLoop($name) @@ -4702,9 +6216,12 @@ class Compiler $file = $this->sourceNames[$env->block->sourceIndex]; + if ($file === null) { + continue; + } + if (realpath($file) === $name) { - $this->throwError('An @import loop has been found: %s imports %s', $file, basename($file)); - break; + throw $this->error('An @import loop has been found: %s imports %s', $file, basename($file)); } } } @@ -4712,100 +6229,86 @@ class Compiler /** * Call SCSS @function * - * @param string $name - * @param array $argValues - * @param array $returnValue + * @param CallableBlock|null $func + * @param array $argValues * - * @return boolean Returns true if returnValue is set; otherwise, false + * @return array|Number */ - protected function callScssFunction($name, $argValues, &$returnValue) + protected function callScssFunction($func, $argValues) { - $func = $this->get(static::$namespaces['function'] . $name, false); - if (! $func) { - return false; + return static::$defaultValue; } + $name = $func->name; $this->pushEnv(); - $storeEnv = $this->storeEnv; - $this->storeEnv = $this->env; - // set the args if (isset($func->args)) { $this->applyArguments($func->args, $argValues); } // throw away lines and children - $tmp = new OutputBlock; + $tmp = new OutputBlock(); $tmp->lines = []; $tmp->children = []; $this->env->marker = 'function'; + if (! empty($func->parentEnv)) { $this->env->declarationScopeParent = $func->parentEnv; } else { - $this->throwError("@function $name() without parentEnv"); + throw $this->error("@function $name() without parentEnv"); } - $ret = $this->compileChildren($func->children, $tmp, $this->env->marker . " " . $name); - - $this->storeEnv = $storeEnv; + $ret = $this->compileChildren($func->children, $tmp, $this->env->marker . ' ' . $name); $this->popEnv(); - $returnValue = ! isset($ret) ? static::$defaultValue : $ret; - - return true; + return ! isset($ret) ? static::$defaultValue : $ret; } /** * Call built-in and registered (PHP) functions * * @param string $name + * @param callable $function + * @param array $prototype * @param array $args - * @param array $returnValue * - * @return boolean Returns true if returnValue is set; otherwise, false + * @return array|Number|null */ - protected function callNativeFunction($name, $args, &$returnValue) + protected function callNativeFunction($name, $function, $prototype, $args) { - // try a lib function - $name = $this->normalizeName($name); + $libName = (is_array($function) ? end($function) : null); + $sorted_kwargs = $this->sortNativeFunctionArgs($libName, $prototype, $args); - if (isset($this->userFunctions[$name])) { - // see if we can find a user function - list($f, $prototype) = $this->userFunctions[$name]; - } elseif (($f = $this->getBuiltinFunction($name)) && is_callable($f)) { - $libName = $f[1]; - $prototype = isset(static::$$libName) ? static::$$libName : null; - } else { - return false; + if (\is_null($sorted_kwargs)) { + return null; } + @list($sorted, $kwargs) = $sorted_kwargs; - @list($sorted, $kwargs) = $this->sortNativeFunctionArgs($libName, $prototype, $args); - - if ($name !== 'if' && $name !== 'call') { - $inExp = true; - - if ($name === 'join') { - $inExp = false; - } - + if ($name !== 'if') { foreach ($sorted as &$val) { - $val = $this->reduce($val, $inExp); + if ($val !== null) { + $val = $this->reduce($val, true); + } } } - $returnValue = call_user_func($f, $sorted, $kwargs); + $returnValue = \call_user_func($function, $sorted, $kwargs); if (! isset($returnValue)) { - return false; + return null; } - $returnValue = $this->coerceValue($returnValue); + if (\is_array($returnValue) || $returnValue instanceof Number) { + return $returnValue; + } - return true; + @trigger_error(sprintf('Returning a PHP value from the "%s" custom function is deprecated. A sass value must be returned instead.', $name), E_USER_DEPRECATED); + + return $this->coerceValue($returnValue); } /** @@ -4817,6 +6320,22 @@ class Compiler */ protected function getBuiltinFunction($name) { + $libName = self::normalizeNativeFunctionName($name); + return [$this, $libName]; + } + + /** + * Normalize native function name + * + * @internal + * + * @param string $name + * + * @return string + */ + public static function normalizeNativeFunctionName($name) + { + $name = str_replace("-", "_", $name); $libName = 'lib' . preg_replace_callback( '/_(.)/', function ($m) { @@ -4824,18 +6343,31 @@ class Compiler }, ucfirst($name) ); + return $libName; + } - return [$this, $libName]; + /** + * Check if a function is a native built-in scss function, for css parsing + * + * @internal + * + * @param string $name + * + * @return bool + */ + public static function isNativeFunction($name) + { + return method_exists(Compiler::class, self::normalizeNativeFunctionName($name)); } /** * Sorts keyword arguments * * @param string $functionName - * @param array $prototypes + * @param array|null $prototypes * @param array $args * - * @return array + * @return array|null */ protected function sortNativeFunctionArgs($functionName, $prototypes, $args) { @@ -4845,16 +6377,18 @@ class Compiler $keyArgs = []; $posArgs = []; + if (\is_array($args) && \count($args) && \end($args) === static::$null) { + array_pop($args); + } + // separate positional and keyword arguments foreach ($args as $arg) { list($key, $value) = $arg; - $key = $key[1]; - - if (empty($key)) { + if (empty($key) or empty($key[1])) { $posArgs[] = empty($arg[2]) ? $value : $arg; } else { - $keyArgs[$key] = $value; + $keyArgs[$key[1]] = $value; } } @@ -4862,209 +6396,359 @@ class Compiler } // specific cases ? - if (in_array($functionName, ['libRgb', 'libRgba', 'libHsl', 'libHsla'])) { + if (\in_array($functionName, ['libRgb', 'libRgba', 'libHsl', 'libHsla'])) { // notation 100 127 255 / 0 is in fact a simple list of 4 values foreach ($args as $k => $arg) { - if ($arg[1][0] === Type::T_LIST && count($arg[1][2]) === 3) { - $last = end($arg[1][2]); - - if ($last[0] === Type::T_EXPRESSION && $last[1] === '/') { - array_pop($arg[1][2]); - $arg[1][2][] = $last[2]; - $arg[1][2][] = $last[3]; - $args[$k] = $arg; - } + if (!isset($arg[1])) { + continue; // This happens when using a trailing comma + } + if ($arg[1][0] === Type::T_LIST && \count($arg[1][2]) === 3) { + $args[$k][1][2] = $this->extractSlashAlphaInColorFunction($arg[1][2]); } } } - $finalArgs = []; + list($positionalArgs, $namedArgs, $names, $separator, $hasSplat) = $this->evaluateArguments($args, false); - if (! is_array(reset($prototypes))) { + if (! \is_array(reset($prototypes))) { $prototypes = [$prototypes]; } + $parsedPrototypes = array_map([$this, 'parseFunctionPrototype'], $prototypes); + assert(!empty($parsedPrototypes)); + $matchedPrototype = $this->selectFunctionPrototype($parsedPrototypes, \count($positionalArgs), $names); + + $this->verifyPrototype($matchedPrototype, \count($positionalArgs), $names, $hasSplat); + + $vars = $this->applyArgumentsToDeclaration($matchedPrototype, $positionalArgs, $namedArgs, $separator); + + $finalArgs = []; $keyArgs = []; - // trying each prototypes - $prototypeHasMatch = false; - $exceptionMessage = ''; + foreach ($matchedPrototype['arguments'] as $argument) { + list($normalizedName, $originalName, $default) = $argument; - foreach ($prototypes as $prototype) { - $argDef = []; - - foreach ($prototype as $i => $p) { - $default = null; - $p = explode(':', $p, 2); - $name = array_shift($p); - - if (count($p)) { - $p = trim(reset($p)); - - if ($p === 'null') { - // differentiate this null from the static::$null - $default = [Type::T_KEYWORD, 'null']; - } else { - if (is_null($parser)) { - $parser = $this->parserFactory(__METHOD__); - } - - $parser->parseValue($p, $default); - } - } - - $isVariable = false; - - if (substr($name, -3) === '...') { - $isVariable = true; - $name = substr($name, 0, -3); - } - - $argDef[] = [$name, $default, $isVariable]; + if (isset($vars[$normalizedName])) { + $value = $vars[$normalizedName]; + } else { + $value = $default; } - try { - $vars = $this->applyArguments($argDef, $args, false, false); - - // ensure all args are populated - foreach ($prototype as $i => $p) { - $name = explode(':', $p)[0]; - - if (! isset($finalArgs[$i])) { - $finalArgs[$i] = null; - } - } - - // apply positional args - foreach (array_values($vars) as $i => $val) { - $finalArgs[$i] = $val; - } - - $keyArgs = array_merge($keyArgs, $vars); - $prototypeHasMatch = true; - - // overwrite positional args with keyword args - foreach ($prototype as $i => $p) { - $name = explode(':', $p)[0]; - - if (isset($keyArgs[$name])) { - $finalArgs[$i] = $keyArgs[$name]; - } - - // special null value as default: translate to real null here - if ($finalArgs[$i] === [Type::T_KEYWORD, 'null']) { - $finalArgs[$i] = null; - } - } - // should we break if this prototype seems fulfilled? - } catch (CompilerException $e) { - $exceptionMessage = $e->getMessage(); + // special null value as default: translate to real null here + if ($value === [Type::T_KEYWORD, 'null']) { + $value = null; } + + $finalArgs[] = $value; + $keyArgs[$originalName] = $value; } - if ($exceptionMessage && ! $prototypeHasMatch) { - $this->throwError($exceptionMessage); + if ($matchedPrototype['rest_argument'] !== null) { + $value = $vars[$matchedPrototype['rest_argument']]; + + $finalArgs[] = $value; + $keyArgs[$matchedPrototype['rest_argument']] = $value; } return [$finalArgs, $keyArgs]; } /** - * Apply argument values per definition + * Parses a function prototype to the internal representation of arguments. * - * @param array $argDef - * @param array $argValues - * @param boolean $storeInEnv - * @param boolean $reduce - * only used if $storeInEnv = false + * The input is an array of strings describing each argument, as supported + * in {@see registerFunction}. Argument names don't include the `$`. + * The output contains the list of positional argument, with their normalized + * name (underscores are replaced by dashes), their original name (to be used + * in case of error reporting) and their default value. The output also contains + * the normalized name of the rest argument, or null if the function prototype + * is not variadic. + * + * @param string[] $prototype + * + * @return array + * @phpstan-return array{arguments: list, rest_argument: string|null} + */ + private function parseFunctionPrototype(array $prototype) + { + static $parser = null; + + $arguments = []; + $restArgument = null; + + foreach ($prototype as $p) { + if (null !== $restArgument) { + throw new \InvalidArgumentException('The argument declaration is invalid. The rest argument must be the last one.'); + } + + $default = null; + $p = explode(':', $p, 2); + $name = str_replace('_', '-', $p[0]); + + if (isset($p[1])) { + $defaultSource = trim($p[1]); + + if ($defaultSource === 'null') { + // differentiate this null from the static::$null + $default = [Type::T_KEYWORD, 'null']; + } else { + if (\is_null($parser)) { + $parser = $this->parserFactory(__METHOD__); + } + + $parser->parseValue($defaultSource, $default); + } + } + + if (substr($name, -3) === '...') { + $restArgument = substr($name, 0, -3); + } else { + $arguments[] = [$name, $p[0], $default]; + } + } + + return [ + 'arguments' => $arguments, + 'rest_argument' => $restArgument, + ]; + } + + /** + * Returns the function prototype for the given positional and named arguments. + * + * If no exact match is found, finds the closest approximation. Note that this + * doesn't guarantee that $positional and $names are valid for the returned + * prototype. + * + * @param array[] $prototypes + * @param int $positional + * @param array $names A set of names, as both keys and values * * @return array * - * @throws \Exception + * @phpstan-param non-empty-list, rest_argument: string|null}> $prototypes + * @phpstan-return array{arguments: list, rest_argument: string|null} */ - protected function applyArguments($argDef, $argValues, $storeInEnv = true, $reduce = true) + private function selectFunctionPrototype(array $prototypes, $positional, array $names) { - $output = []; - if (is_array($argValues) && count($argValues) && end($argValues) === static::$null) { - array_pop($argValues); + $fuzzyMatch = null; + $minMismatchDistance = null; + + foreach ($prototypes as $prototype) { + // Ideally, find an exact match. + if ($this->checkPrototypeMatches($prototype, $positional, $names)) { + return $prototype; + } + + $mismatchDistance = \count($prototype['arguments']) - $positional; + + if ($minMismatchDistance !== null) { + if (abs($mismatchDistance) > abs($minMismatchDistance)) { + continue; + } + + // If two overloads have the same mismatch distance, favor the overload + // that has more arguments. + if (abs($mismatchDistance) === abs($minMismatchDistance) && $mismatchDistance < 0) { + continue; + } + } + + $minMismatchDistance = $mismatchDistance; + $fuzzyMatch = $prototype; } - if ($storeInEnv) { - $storeEnv = $this->getStoreEnv(); + return $fuzzyMatch; + } - $env = new Environment; - $env->store = $storeEnv->store; + /** + * Checks whether the argument invocation matches the callable prototype. + * + * The rules are similar to {@see verifyPrototype}. The boolean return value + * avoids the overhead of building and catching exceptions when the reason of + * not matching the prototype does not need to be known. + * + * @param array $prototype + * @param int $positional + * @param array $names + * + * @return bool + * + * @phpstan-param array{arguments: list, rest_argument: string|null} $prototype + */ + private function checkPrototypeMatches(array $prototype, $positional, array $names) + { + $nameUsed = 0; + + foreach ($prototype['arguments'] as $i => $argument) { + list ($name, $originalName, $default) = $argument; + + if ($i < $positional) { + if (isset($names[$name])) { + return false; + } + } elseif (isset($names[$name])) { + $nameUsed++; + } elseif ($default === null) { + return false; + } } - $hasVariable = false; - $args = []; - - foreach ($argDef as $i => $arg) { - list($name, $default, $isVariable) = $argDef[$i]; - - $args[$name] = [$i, $name, $default, $isVariable]; - $hasVariable |= $isVariable; + if ($prototype['rest_argument'] !== null) { + return true; } - $splatSeparator = null; - $keywordArgs = []; - $deferredKeywordArgs = []; - $remaining = []; - $hasKeywordArgument = false; + if ($positional > \count($prototype['arguments'])) { + return false; + } - // assign the keyword args - foreach ((array) $argValues as $arg) { - if (! empty($arg[0])) { + if ($nameUsed < \count($names)) { + return false; + } + + return true; + } + + /** + * Verifies that the argument invocation is valid for the callable prototype. + * + * @param array $prototype + * @param int $positional + * @param array $names + * @param bool $hasSplat + * + * @return void + * + * @throws SassScriptException + * + * @phpstan-param array{arguments: list, rest_argument: string|null} $prototype + */ + private function verifyPrototype(array $prototype, $positional, array $names, $hasSplat) + { + $nameUsed = 0; + + foreach ($prototype['arguments'] as $i => $argument) { + list ($name, $originalName, $default) = $argument; + + if ($i < $positional) { + if (isset($names[$name])) { + throw new SassScriptException(sprintf('Argument $%s was passed both by position and by name.', $originalName)); + } + } elseif (isset($names[$name])) { + $nameUsed++; + } elseif ($default === null) { + throw new SassScriptException(sprintf('Missing argument $%s', $originalName)); + } + } + + if ($prototype['rest_argument'] !== null) { + return; + } + + if ($positional > \count($prototype['arguments'])) { + $message = sprintf( + 'Only %d %sargument%s allowed, but %d %s passed.', + \count($prototype['arguments']), + empty($names) ? '' : 'positional ', + \count($prototype['arguments']) === 1 ? '' : 's', + $positional, + $positional === 1 ? 'was' : 'were' + ); + if (!$hasSplat) { + throw new SassScriptException($message); + } + + $message = $this->addLocationToMessage($message); + $message .= "\nThis will be an error in future versions of Sass."; + $this->logger->warn($message, true); + } + + if ($nameUsed < \count($names)) { + $unknownNames = array_values(array_diff($names, array_column($prototype['arguments'], 0))); + $lastName = array_pop($unknownNames); + $message = sprintf( + 'No argument%s named $%s%s.', + $unknownNames ? 's' : '', + $unknownNames ? implode(', $', $unknownNames) . ' or $' : '', + $lastName + ); + throw new SassScriptException($message); + } + } + + /** + * Evaluates the argument from the invocation. + * + * This returns several things about this invocation: + * - the list of positional arguments + * - the map of named arguments, indexed by normalized names + * - the set of names used in the arguments (that's an array using the normalized names as keys for O(1) access) + * - the separator used by the list using the splat operator, if any + * - a boolean indicator whether any splat argument (list or map) was used, to support the incomplete error reporting. + * + * @param array[] $args + * @param bool $reduce Whether arguments should be reduced to their value + * + * @return array + * + * @throws SassScriptException + * + * @phpstan-return array{0: list, 1: array, 2: array, 3: string|null, 4: bool} + */ + private function evaluateArguments(array $args, $reduce = true) + { + // this represents trailing commas + if (\count($args) && end($args) === static::$null) { + array_pop($args); + } + + $splatSeparator = null; + $keywordArgs = []; + $names = []; + $positionalArgs = []; + $hasKeywordArgument = false; + $hasSplat = false; + + foreach ($args as $arg) { + if (!empty($arg[0])) { $hasKeywordArgument = true; - $name = $arg[0][1]; - if (! isset($args[$name])) { - foreach (array_keys($args) as $an) { - if (str_replace("_", "-", $an) === str_replace("_", "-", $name)) { - $name = $an; - break; - } - } + assert(\is_string($arg[0][1])); + $name = str_replace('_', '-', $arg[0][1]); + + if (isset($keywordArgs[$name])) { + throw new SassScriptException(sprintf('Duplicate named argument $%s.', $arg[0][1])); } - if (! isset($args[$name]) || $args[$name][3]) { - if ($hasVariable) { - $deferredKeywordArgs[$name] = $arg[1]; - } else { - $this->throwError("Mixin or function doesn't have an argument named $%s.", $arg[0][1]); - break; - } - } elseif ($args[$name][0] < count($remaining)) { - $this->throwError("The argument $%s was passed both by position and by name.", $arg[0][1]); - break; - } else { - $keywordArgs[$name] = $arg[1]; - } - } elseif ($arg[2] === true) { + + $keywordArgs[$name] = $this->maybeReduce($reduce, $arg[1]); + $names[$name] = $name; + } elseif (! empty($arg[2])) { + // $arg[2] means a var followed by ... in the arg ($list... ) $val = $this->reduce($arg[1], true); + $hasSplat = true; if ($val[0] === Type::T_LIST) { - foreach ($val[2] as $name => $item) { - if (! is_numeric($name)) { - if (! isset($args[$name])) { - foreach (array_keys($args) as $an) { - if (str_replace("_", "-", $an) === str_replace("_", "-", $name)) { - $name = $an; - break; - } - } + foreach ($val[2] as $item) { + if (\is_null($splatSeparator)) { + $splatSeparator = $val[1]; + } + + $positionalArgs[] = $this->maybeReduce($reduce, $item); + } + + if (isset($val[3]) && \is_array($val[3])) { + foreach ($val[3] as $name => $item) { + assert(\is_string($name)); + + $normalizedName = str_replace('_', '-', $name); + + if (isset($keywordArgs[$normalizedName])) { + throw new SassScriptException(sprintf('Duplicate named argument $%s.', $name)); } - if ($hasVariable) { - $deferredKeywordArgs[$name] = $item; - } else { - $keywordArgs[$name] = $item; - } - } else { - if (is_null($splatSeparator)) { - $splatSeparator = $val[1]; - } - - $remaining[] = $item; + $keywordArgs[$normalizedName] = $this->maybeReduce($reduce, $item); + $names[$normalizedName] = $normalizedName; + $hasKeywordArgument = true; } } } elseif ($val[0] === Type::T_MAP) { @@ -5073,62 +6757,121 @@ class Compiler $item = $val[2][$i]; if (! is_numeric($name)) { - if (! isset($args[$name])) { - foreach (array_keys($args) as $an) { - if (str_replace("_", "-", $an) === str_replace("_", "-", $name)) { - $name = $an; - break; - } - } + $normalizedName = str_replace('_', '-', $name); + + if (isset($keywordArgs[$normalizedName])) { + throw new SassScriptException(sprintf('Duplicate named argument $%s.', $name)); } - if ($hasVariable) { - $deferredKeywordArgs[$name] = $item; - } else { - $keywordArgs[$name] = $item; - } + $keywordArgs[$normalizedName] = $this->maybeReduce($reduce, $item); + $names[$normalizedName] = $normalizedName; + $hasKeywordArgument = true; } else { - if (is_null($splatSeparator)) { + if (\is_null($splatSeparator)) { $splatSeparator = $val[1]; } - $remaining[] = $item; + $positionalArgs[] = $this->maybeReduce($reduce, $item); } } - } else { - $remaining[] = $val; + } elseif ($val[0] !== Type::T_NULL) { // values other than null are treated a single-element lists, while null is the empty list + $positionalArgs[] = $this->maybeReduce($reduce, $val); } } elseif ($hasKeywordArgument) { - $this->throwError('Positional arguments must come before keyword arguments.'); - break; + throw new SassScriptException('Positional arguments must come before keyword arguments.'); } else { - $remaining[] = $arg[1]; + $positionalArgs[] = $this->maybeReduce($reduce, $arg[1]); } } - foreach ($args as $arg) { - list($i, $name, $default, $isVariable) = $arg; + return [$positionalArgs, $keywordArgs, $names, $splatSeparator, $hasSplat]; + } + + /** + * @param bool $reduce + * @param array|Number $value + * + * @return array|Number + */ + private function maybeReduce($reduce, $value) + { + if ($reduce) { + return $this->reduce($value, true); + } + + return $value; + } + + /** + * Apply argument values per definition + * + * @param array[] $argDef + * @param array|null $argValues + * @param bool $storeInEnv + * @param bool $reduce only used if $storeInEnv = false + * + * @return array + * + * @phpstan-param list $argDef + * + * @throws \Exception + */ + protected function applyArguments($argDef, $argValues, $storeInEnv = true, $reduce = true) + { + $output = []; + + if (\is_null($argValues)) { + $argValues = []; + } + + if ($storeInEnv) { + $storeEnv = $this->getStoreEnv(); + + $env = new Environment(); + $env->store = $storeEnv->store; + } + + $prototype = ['arguments' => [], 'rest_argument' => null]; + $originalRestArgumentName = null; + + foreach ($argDef as $arg) { + list($name, $default, $isVariable) = $arg; + $normalizedName = str_replace('_', '-', $name); if ($isVariable) { - $val = [Type::T_LIST, is_null($splatSeparator) ? ',' : $splatSeparator , [], $isVariable]; - - for ($count = count($remaining); $i < $count; $i++) { - $val[2][] = $remaining[$i]; - } - - foreach ($deferredKeywordArgs as $itemName => $item) { - $val[2][$itemName] = $item; - } - } elseif (isset($remaining[$i])) { - $val = $remaining[$i]; - } elseif (isset($keywordArgs[$name])) { - $val = $keywordArgs[$name]; - } elseif (! empty($default)) { - continue; + $originalRestArgumentName = $name; + $prototype['rest_argument'] = $normalizedName; } else { - $this->throwError("Missing argument $name"); - break; + $prototype['arguments'][] = [$normalizedName, $name, !empty($default) ? $default : null]; } + } + + list($positionalArgs, $namedArgs, $names, $splatSeparator, $hasSplat) = $this->evaluateArguments($argValues, $reduce); + + $this->verifyPrototype($prototype, \count($positionalArgs), $names, $hasSplat); + + $vars = $this->applyArgumentsToDeclaration($prototype, $positionalArgs, $namedArgs, $splatSeparator); + + foreach ($prototype['arguments'] as $argument) { + list($normalizedName, $name) = $argument; + + if (!isset($vars[$normalizedName])) { + continue; + } + + $val = $vars[$normalizedName]; + + if ($storeInEnv) { + $this->set($name, $this->reduce($val, true), true, $env); + } else { + $output[$name] = ($reduce ? $this->reduce($val, true) : $val); + } + } + + if ($prototype['rest_argument'] !== null) { + assert($originalRestArgumentName !== null); + $name = $originalRestArgumentName; + $val = $vars[$prototype['rest_argument']]; if ($storeInEnv) { $this->set($name, $this->reduce($val, true), true, $env); @@ -5141,12 +6884,13 @@ class Compiler $storeEnv->store = $env->store; } - foreach ($args as $arg) { - list($i, $name, $default, $isVariable) = $arg; + foreach ($prototype['arguments'] as $argument) { + list($normalizedName, $name, $default) = $argument; - if ($isVariable || isset($remaining[$i]) || isset($keywordArgs[$name]) || empty($default)) { + if (isset($vars[$normalizedName])) { continue; } + assert($default !== null); if ($storeInEnv) { $this->set($name, $this->reduce($default, true), true); @@ -5158,29 +6902,90 @@ class Compiler return $output; } + /** + * Apply argument values per definition. + * + * This method assumes that the arguments are valid for the provided prototype. + * The validation with {@see verifyPrototype} must have been run before calling + * it. + * Arguments are returned as a map from the normalized argument names to the + * value. Additional arguments are collected in a sass argument list available + * under the name of the rest argument in the result. + * + * Defaults are not applied as they are resolved in a different environment. + * + * @param array $prototype + * @param array $positionalArgs + * @param array $namedArgs + * @param string|null $splatSeparator + * + * @return array + * + * @phpstan-param array{arguments: list, rest_argument: string|null} $prototype + */ + private function applyArgumentsToDeclaration(array $prototype, array $positionalArgs, array $namedArgs, $splatSeparator) + { + $output = []; + $minLength = min(\count($positionalArgs), \count($prototype['arguments'])); + + for ($i = 0; $i < $minLength; $i++) { + list($name) = $prototype['arguments'][$i]; + $val = $positionalArgs[$i]; + + $output[$name] = $val; + } + + $restNamed = $namedArgs; + + for ($i = \count($positionalArgs); $i < \count($prototype['arguments']); $i++) { + $argument = $prototype['arguments'][$i]; + list($name) = $argument; + + if (isset($namedArgs[$name])) { + $val = $namedArgs[$name]; + unset($restNamed[$name]); + } else { + continue; + } + + $output[$name] = $val; + } + + if ($prototype['rest_argument'] !== null) { + $name = $prototype['rest_argument']; + $rest = array_values(array_slice($positionalArgs, \count($prototype['arguments']))); + + $val = [Type::T_LIST, \is_null($splatSeparator) ? ',' : $splatSeparator , $rest, $restNamed]; + + $output[$name] = $val; + } + + return $output; + } + /** * Coerce a php value into a scss one * * @param mixed $value * - * @return array|\ScssPhp\ScssPhp\Node\Number + * @return array|Number */ protected function coerceValue($value) { - if (is_array($value) || $value instanceof \ArrayAccess) { + if (\is_array($value) || $value instanceof Number) { return $value; } - if (is_bool($value)) { + if (\is_bool($value)) { return $this->toBool($value); } - if (is_null($value)) { + if (\is_null($value)) { return static::$null; } if (is_numeric($value)) { - return new Node\Number($value, ''); + return new Number($value, ''); } if ($value === '') { @@ -5198,80 +7003,102 @@ class Compiler } /** - * Coerce something to map + * Tries to convert an item to a Sass map * - * @param array $item + * @param Number|array $item * - * @return array + * @return array|null */ - protected function coerceMap($item) + private function tryMap($item) { + if ($item instanceof Number) { + return null; + } + if ($item[0] === Type::T_MAP) { return $item; } - if ($item[0] === static::$emptyList[0] - && $item[1] === static::$emptyList[1] - && $item[2] === static::$emptyList[2]) { + if ( + $item[0] === Type::T_LIST && + $item[2] === [] + ) { return static::$emptyMap; } - return [Type::T_MAP, [$item], [static::$null]]; + return null; + } + + /** + * Coerce something to map + * + * @param array|Number $item + * + * @return array|Number + */ + protected function coerceMap($item) + { + $map = $this->tryMap($item); + + if ($map !== null) { + return $map; + } + + return $item; } /** * Coerce something to list * - * @param array $item - * @param string $delim + * @param array|Number $item + * @param string $delim + * @param bool $removeTrailingNull * * @return array */ - protected function coerceList($item, $delim = ',') + protected function coerceList($item, $delim = ',', $removeTrailingNull = false) { - if (isset($item) && $item[0] === Type::T_LIST) { + if ($item instanceof Number) { + return [Type::T_LIST, '', [$item]]; + } + + if ($item[0] === Type::T_LIST) { + // remove trailing null from the list + if ($removeTrailingNull && end($item[2]) === static::$null) { + array_pop($item[2]); + } + return $item; } - if (isset($item) && $item[0] === Type::T_MAP) { + if ($item[0] === Type::T_MAP) { $keys = $item[1]; $values = $item[2]; $list = []; - for ($i = 0, $s = count($keys); $i < $s; $i++) { + for ($i = 0, $s = \count($keys); $i < $s; $i++) { $key = $keys[$i]; $value = $values[$i]; - switch ($key[0]) { - case Type::T_LIST: - case Type::T_MAP: - case Type::T_STRING: - break; - - default: - $key = [Type::T_KEYWORD, $this->compileStringContent($this->coerceString($key))]; - break; - } - $list[] = [ Type::T_LIST, - '', + ' ', [$key, $value] ]; } - return [Type::T_LIST, ',', $list]; + return [Type::T_LIST, $list ? ',' : '', $list]; } - return [Type::T_LIST, $delim, ! isset($item) ? []: [$item]]; + return [Type::T_LIST, '', [$item]]; } /** * Coerce color for expression * - * @param array $value + * @param array|Number $value * - * @return array|null + * @return array|Number */ protected function coerceForExpression($value) { @@ -5285,12 +7112,17 @@ class Compiler /** * Coerce value to color * - * @param array $value + * @param array|Number $value + * @param bool $inRGBFunction * * @return array|null */ protected function coerceColor($value, $inRGBFunction = false) { + if ($value instanceof Number) { + return null; + } + switch ($value[0]) { case Type::T_COLOR: for ($i = 1; $i <= 3; $i++) { @@ -5321,7 +7153,7 @@ class Compiler case Type::T_LIST: if ($inRGBFunction) { - if (count($value[2]) == 3 || count($value[2]) == 4) { + if (\count($value[2]) == 3 || \count($value[2]) == 4) { $color = $value[2]; array_unshift($color, Type::T_COLOR); @@ -5332,16 +7164,17 @@ class Compiler return null; case Type::T_KEYWORD: - if (! is_string($value[1])) { + if (! \is_string($value[1])) { return null; } $name = strtolower($value[1]); + // hexa color? if (preg_match('/^#([0-9a-f]+)$/i', $name, $m)) { - $nofValues = strlen($m[1]); + $nofValues = \strlen($m[1]); - if (in_array($nofValues, [3, 4, 6, 8])) { + if (\in_array($nofValues, [3, 4, 6, 8])) { $nbChannels = 3; $color = []; $num = hexdec($m[1]); @@ -5375,7 +7208,7 @@ class Compiler if ($color[3] === 255) { $color[3] = 1; // fully opaque } else { - $color[3] = round($color[3] / 255, 3); + $color[3] = round($color[3] / 255, Number::PRECISION); } } @@ -5398,10 +7231,10 @@ class Compiler } /** - * @param integer|\ScssPhp\ScssPhp\Node\Number $value - * @param boolean $isAlpha + * @param int|Number $value + * @param bool $isAlpha * - * @return integer|mixed + * @return int|mixed */ protected function compileRGBAValue($value, $isAlpha = false) { @@ -5413,44 +7246,35 @@ class Compiler } /** - * @param mixed $value - * @param integer|float $min - * @param integer|float $max - * @param boolean $isInt - * @param boolean $clamp - * @param boolean $modulo + * @param mixed $value + * @param int|float $min + * @param int|float $max + * @param bool $isInt * - * @return integer|mixed + * @return int|mixed */ - protected function compileColorPartValue($value, $min, $max, $isInt = true, $clamp = true, $modulo = false) + protected function compileColorPartValue($value, $min, $max, $isInt = true) { if (! is_numeric($value)) { - if (is_array($value)) { + if (\is_array($value)) { $reduced = $this->reduce($value); - if (is_object($reduced) && $value->type === Type::T_NUMBER) { + if ($reduced instanceof Number) { $value = $reduced; } } - if (is_object($value) && $value->type === Type::T_NUMBER) { - $num = $value->dimension; - - if (count($value->units)) { - $unit = array_keys($value->units); - $unit = reset($unit); - - switch ($unit) { - case '%': - $num *= $max / 100; - break; - default: - break; - } + if ($value instanceof Number) { + if ($value->unitless()) { + $num = $value->getDimension(); + } elseif ($value->hasUnit('%')) { + $num = $max * $value->getDimension() / 100; + } else { + throw $this->error('Expected %s to have no units or "%%".', $value); } $value = $num; - } elseif (is_array($value)) { + } elseif (\is_array($value)) { $value = $this->compileValue($value); } } @@ -5460,18 +7284,7 @@ class Compiler $value = round($value); } - if ($clamp) { - $value = min($max, max($min, $value)); - } - - if ($modulo) { - $value = $value % $max; - - // still negative? - while ($value < $min) { - $value += $max; - } - } + $value = min($max, max($min, $value)); return $value; } @@ -5482,34 +7295,72 @@ class Compiler /** * Coerce value to string * - * @param array $value + * @param array|Number $value * - * @return array|null + * @return array */ protected function coerceString($value) { if ($value[0] === Type::T_STRING) { + assert(\is_array($value)); + return $value; } return [Type::T_STRING, '', [$this->compileValue($value)]]; } + /** + * Assert value is a string + * + * This method deals with internal implementation details of the value + * representation where unquoted strings can sometimes be stored under + * other types. + * The returned value is always using the T_STRING type. + * + * @api + * + * @param array|Number $value + * @param string|null $varName + * + * @return array + * + * @throws SassScriptException + */ + public function assertString($value, $varName = null) + { + // case of url(...) parsed a a function + if ($value[0] === Type::T_FUNCTION) { + $value = $this->coerceString($value); + } + + if (! \in_array($value[0], [Type::T_STRING, Type::T_KEYWORD])) { + $value = $this->compileValue($value); + throw SassScriptException::forArgument("$value is not a string.", $varName); + } + + return $this->coerceString($value); + } + /** * Coerce value to a percentage * - * @param array $value + * @param array|Number $value * - * @return integer|float + * @return int|float + * + * @deprecated */ protected function coercePercent($value) { - if ($value[0] === Type::T_NUMBER) { - if (! empty($value[2]['%'])) { - return $value[1] / 100; + @trigger_error(sprintf('"%s" is deprecated since 1.7.0.', __METHOD__), E_USER_DEPRECATED); + + if ($value instanceof Number) { + if ($value->hasUnit('%')) { + return $value->getDimension() / 100; } - return $value[1]; + return $value->getDimension(); } return 0; @@ -5520,21 +7371,24 @@ class Compiler * * @api * - * @param array $value + * @param array|Number $value + * @param string|null $varName * * @return array * - * @throws \Exception + * @throws SassScriptException */ - public function assertMap($value) + public function assertMap($value, $varName = null) { - $value = $this->coerceMap($value); + $map = $this->tryMap($value); - if ($value[0] !== Type::T_MAP) { - $this->throwError('expecting map, %s received', $value[0]); + if ($map === null) { + $value = $this->compileValue($value); + + throw SassScriptException::forArgument("$value is not a map.", $varName); } - return $value; + return $map; } /** @@ -5542,7 +7396,7 @@ class Compiler * * @api * - * @param array $value + * @param array|Number $value * * @return array * @@ -5551,30 +7405,55 @@ class Compiler public function assertList($value) { if ($value[0] !== Type::T_LIST) { - $this->throwError('expecting list, %s received', $value[0]); + throw $this->error('expecting list, %s received', $value[0]); } + assert(\is_array($value)); return $value; } + /** + * Gets the keywords of an argument list. + * + * Keys in the returned array are normalized names (underscores are replaced with dashes) + * without the leading `$`. + * Calling this helper with anything that an argument list received for a rest argument + * of the function argument declaration is not supported. + * + * @param array|Number $value + * + * @return array + */ + public function getArgumentListKeywords($value) + { + if ($value[0] !== Type::T_LIST || !isset($value[3]) || !\is_array($value[3])) { + throw new \InvalidArgumentException('The argument is not a sass argument list.'); + } + + return $value[3]; + } + /** * Assert value is a color * * @api * - * @param array $value + * @param array|Number $value + * @param string|null $varName * * @return array * - * @throws \Exception + * @throws SassScriptException */ - public function assertColor($value) + public function assertColor($value, $varName = null) { if ($color = $this->coerceColor($value)) { return $color; } - $this->throwError('expecting color, %s received', $value[0]); + $value = $this->compileValue($value); + + throw SassScriptException::forArgument("$value is not a color.", $varName); } /** @@ -5582,21 +7461,64 @@ class Compiler * * @api * - * @param array $value + * @param array|Number $value + * @param string|null $varName * - * @return integer|float + * @return Number * - * @throws \Exception + * @throws SassScriptException */ - public function assertNumber($value) + public function assertNumber($value, $varName = null) { - if ($value[0] !== Type::T_NUMBER) { - $this->throwError('expecting number, %s received', $value[0]); + if (!$value instanceof Number) { + $value = $this->compileValue($value); + throw SassScriptException::forArgument("$value is not a number.", $varName); } - return $value[1]; + return $value; } + /** + * Assert value is a integer + * + * @api + * + * @param array|Number $value + * @param string|null $varName + * + * @return int + * + * @throws SassScriptException + */ + public function assertInteger($value, $varName = null) + { + $value = $this->assertNumber($value, $varName)->getDimension(); + if (round($value - \intval($value), Number::PRECISION) > 0) { + throw SassScriptException::forArgument("$value is not an integer.", $varName); + } + + return intval($value); + } + + /** + * Extract the ... / alpha on the last argument of channel arg + * in color functions + * + * @param array $args + * @return array + */ + private function extractSlashAlphaInColorFunction($args) + { + $last = end($args); + if (\count($args) === 3 && $last[0] === Type::T_EXPRESSION && $last[1] === '/') { + array_pop($args); + $args[] = $last[2]; + $args[] = $last[3]; + } + return $args; + } + + /** * Make sure a color's components don't go out of bounds * @@ -5614,6 +7536,10 @@ class Compiler if ($c[$i] > 255) { $c[$i] = 255; } + + if (!\is_int($c[$i])) { + $c[$i] = round($c[$i]); + } } return $c; @@ -5622,11 +7548,11 @@ class Compiler /** * Convert RGB to HSL * - * @api + * @internal * - * @param integer $red - * @param integer $green - * @param integer $blue + * @param int $red + * @param int $green + * @param int $blue * * @return array */ @@ -5651,12 +7577,12 @@ class Compiler $h = 60 * ($green - $blue) / $d; } elseif ($green == $max) { $h = 60 * ($blue - $red) / $d + 120; - } elseif ($blue == $max) { + } else { $h = 60 * ($red - $green) / $d + 240; } } - return [Type::T_HSL, fmod($h, 360), $s * 100, $l / 5.1]; + return [Type::T_HSL, fmod($h + 360, 360), $s * 100, $l / 5.1]; } /** @@ -5685,7 +7611,7 @@ class Compiler } if ($h * 3 < 2) { - return $m1 + ($m2 - $m1) * (2/3 - $h) * 6; + return $m1 + ($m2 - $m1) * (2 / 3 - $h) * 6; } return $m1; @@ -5694,11 +7620,11 @@ class Compiler /** * Convert HSL to RGB * - * @api + * @internal * - * @param integer $hue H from 0 to 360 - * @param integer $saturation S from 0 to 100 - * @param integer $lightness L from 0 to 100 + * @param int|float $hue H from 0 to 360 + * @param int|float $saturation S from 0 to 100 + * @param int|float $lightness L from 0 to 100 * * @return array */ @@ -5715,35 +7641,133 @@ class Compiler $m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s; $m1 = $l * 2 - $m2; - $r = $this->hueToRGB($m1, $m2, $h + 1/3) * 255; + $r = $this->hueToRGB($m1, $m2, $h + 1 / 3) * 255; $g = $this->hueToRGB($m1, $m2, $h) * 255; - $b = $this->hueToRGB($m1, $m2, $h - 1/3) * 255; + $b = $this->hueToRGB($m1, $m2, $h - 1 / 3) * 255; $out = [Type::T_COLOR, $r, $g, $b]; return $out; } - // Built in functions - - protected static $libCall = ['name', 'args...']; - protected function libCall($args, $kwargs) + /** + * Convert HWB to RGB + * https://www.w3.org/TR/css-color-4/#hwb-to-rgb + * + * @api + * + * @param int|float $hue H from 0 to 360 + * @param int|float $whiteness W from 0 to 100 + * @param int|float $blackness B from 0 to 100 + * + * @return array + */ + private function HWBtoRGB($hue, $whiteness, $blackness) { - $name = $this->compileStringContent($this->coerceString($this->reduce(array_shift($args), true))); - $callArgs = []; + $w = min(100, max(0, $whiteness)) / 100; + $b = min(100, max(0, $blackness)) / 100; - // $kwargs['args'] is [Type::T_LIST, ',', [..]] - foreach ($kwargs['args'][2] as $varname => $arg) { - if (is_numeric($varname)) { - $varname = null; - } else { - $varname = [ 'var', $varname]; - } + $sum = $w + $b; + if ($sum > 1.0) { + $w = $w / $sum; + $b = $b / $sum; + } + $b = min(1.0 - $w, $b); - $callArgs[] = [$varname, $arg, false]; + $rgb = $this->toRGB($hue, 100, 50); + for($i = 1; $i < 4; $i++) { + $rgb[$i] *= (1.0 - $w - $b); + $rgb[$i] = round($rgb[$i] + 255 * $w + 0.0001); } - return $this->reduce([Type::T_FUNCTION_CALL, $name, $callArgs]); + return $rgb; + } + + /** + * Convert RGB to HWB + * + * @api + * + * @param int $red + * @param int $green + * @param int $blue + * + * @return array + */ + private function RGBtoHWB($red, $green, $blue) + { + $min = min($red, $green, $blue); + $max = max($red, $green, $blue); + + $d = $max - $min; + + if ((int) $d === 0) { + $h = 0; + } else { + + if ($red == $max) { + $h = 60 * ($green - $blue) / $d; + } elseif ($green == $max) { + $h = 60 * ($blue - $red) / $d + 120; + } else { + $h = 60 * ($red - $green) / $d + 240; + } + } + + return [Type::T_HWB, fmod($h, 360), $min / 255 * 100, 100 - $max / 255 *100]; + } + + + // Built in functions + + protected static $libCall = ['function', 'args...']; + protected function libCall($args) + { + $functionReference = $args[0]; + + if (in_array($functionReference[0], [Type::T_STRING, Type::T_KEYWORD])) { + $name = $this->compileStringContent($this->coerceString($functionReference)); + $warning = "Passing a string to call() is deprecated and will be illegal\n" + . "in Sass 4.0. Use call(function-reference($name)) instead."; + Warn::deprecation($warning); + $functionReference = $this->libGetFunction([$this->assertString($functionReference, 'function')]); + } + + if ($functionReference === static::$null) { + return static::$null; + } + + if (! in_array($functionReference[0], [Type::T_FUNCTION_REFERENCE, Type::T_FUNCTION])) { + throw $this->error('Function reference expected, got ' . $functionReference[0]); + } + + $callArgs = [ + [null, $args[1], true] + ]; + + return $this->reduce([Type::T_FUNCTION_CALL, $functionReference, $callArgs]); + } + + + protected static $libGetFunction = [ + ['name'], + ['name', 'css'] + ]; + protected function libGetFunction($args) + { + $name = $this->compileStringContent($this->assertString(array_shift($args), 'name')); + $isCss = false; + + if (count($args)) { + $isCss = array_shift($args); + $isCss = (($isCss === static::$true) ? true : false); + } + + if ($isCss) { + return [Type::T_FUNCTION, $name, [Type::T_LIST, ',', []]]; + } + + return $this->getFunctionReference($name, true); } protected static $libIf = ['condition', 'if-true', 'if-false:']; @@ -5763,11 +7787,8 @@ class Compiler { list($list, $value) = $args; - if ($value[0] === Type::T_MAP) { - return static::$null; - } - - if ($list[0] === Type::T_MAP || + if ( + $list[0] === Type::T_MAP || $list[0] === Type::T_STRING || $list[0] === Type::T_KEYWORD || $list[0] === Type::T_INTERPOLATE @@ -5779,6 +7800,22 @@ class Compiler return static::$null; } + // Numbers are represented with value objects, for which the PHP equality operator does not + // match the Sass rules (and we cannot overload it). As they are the only type of values + // represented with a value object for now, they require a special case. + if ($value instanceof Number) { + $key = 0; + foreach ($list[2] as $item) { + $key++; + $itemValue = $this->normalizeValue($item); + + if ($itemValue instanceof Number && $value->equals($itemValue)) { + return new Number($key, ''); + } + } + return static::$null; + } + $values = []; foreach ($list[2] as $item) { @@ -5787,7 +7824,7 @@ class Compiler $key = array_search($this->normalizeValue($value), $values); - return false === $key ? static::$null : $key + 1; + return false === $key ? static::$null : new Number($key + 1, ''); } protected static $libRgb = [ @@ -5796,9 +7833,17 @@ class Compiler ['channels'], ['red', 'green', 'blue'], ['red', 'green', 'blue', 'alpha'] ]; + + /** + * @param array $args + * @param array $kwargs + * @param string $funcName + * + * @return array + */ protected function libRgb($args, $kwargs, $funcName = 'rgb') { - switch (count($args)) { + switch (\count($args)) { case 1: if (! $color = $this->coerceColor($args[0], true)) { $color = [Type::T_STRING, '', [$funcName . '(', $args[0], ')']]; @@ -5809,7 +7854,7 @@ class Compiler $color = [Type::T_COLOR, $args[0], $args[1], $args[2]]; if (! $color = $this->coerceColor($color)) { - $color = [Type::T_STRING, '', [$funcName .'(', $args[0], ', ', $args[1], ', ', $args[2], ')']]; + $color = [Type::T_STRING, '', [$funcName . '(', $args[0], ', ', $args[1], ', ', $args[2], ')']]; } return $color; @@ -5825,7 +7870,7 @@ class Compiler [$funcName . '(', $color[1], ', ', $color[2], ', ', $color[3], ', ', $alpha, ')']]; } } else { - $color = [Type::T_STRING, '', [$funcName . '(', $args[0], ')']]; + $color = [Type::T_STRING, '', [$funcName . '(', $args[0], ', ', $args[1], ')']]; } break; @@ -5854,33 +7899,126 @@ class Compiler return $this->libRgb($args, $kwargs, 'rgba'); } - // helper function for adjust_color, change_color, and scale_color - protected function alterColor($args, $fn) + /** + * Helper function for adjust_color, change_color, and scale_color + * + * @param array $args + * @param string $operation + * @param callable $fn + * + * @return array + * + * @phpstan-param callable(float|int, float|int|null, float|int): (float|int) $fn + */ + protected function alterColor(array $args, $operation, $fn) { - $color = $this->assertColor($args[0]); + $color = $this->assertColor($args[0], 'color'); - foreach ([1 => 1, 2 => 2, 3 => 3, 7 => 4] as $iarg => $irgba) { - if (isset($args[$iarg])) { - $val = $this->assertNumber($args[$iarg]); - - if (! isset($color[$irgba])) { - $color[$irgba] = (($irgba < 4) ? 0 : 1); - } - - $color[$irgba] = call_user_func($fn, $color[$irgba], $val, $iarg); - } + if ($args[1][2]) { + throw new SassScriptException('Only one positional argument is allowed. All other arguments must be passed by name.'); } - if (! empty($args[4]) || ! empty($args[5]) || ! empty($args[6])) { - $hsl = $this->toHSL($color[1], $color[2], $color[3]); + $kwargs = $this->getArgumentListKeywords($args[1]); - foreach ([4 => 1, 5 => 2, 6 => 3] as $iarg => $ihsl) { - if (! empty($args[$iarg])) { - $val = $this->assertNumber($args[$iarg]); - $hsl[$ihsl] = call_user_func($fn, $hsl[$ihsl], $val, $iarg); + $scale = $operation === 'scale'; + $change = $operation === 'change'; + + /** @phpstan-var callable(string, float|int, bool=, bool=): (float|int|null) $getParam */ + $getParam = function ($name, $max, $checkPercent = false, $assertPercent = false) use (&$kwargs, $scale, $change) { + if (!isset($kwargs[$name])) { + return null; + } + + $number = $this->assertNumber($kwargs[$name], $name); + unset($kwargs[$name]); + + if (!$scale && $checkPercent) { + if (!$number->hasUnit('%')) { + $warning = $this->error("{$name} Passing a number `$number` without unit % is deprecated."); + $this->logger->warn($warning->getMessage(), true); } } + if ($scale || $assertPercent) { + $number->assertUnit('%', $name); + } + + if ($scale) { + $max = 100; + } + + return $number->valueInRange($change ? 0 : -$max, $max, $name); + }; + + $alpha = $getParam('alpha', 1); + $red = $getParam('red', 255); + $green = $getParam('green', 255); + $blue = $getParam('blue', 255); + + if ($scale || !isset($kwargs['hue'])) { + $hue = null; + } else { + $hueNumber = $this->assertNumber($kwargs['hue'], 'hue'); + unset($kwargs['hue']); + $hue = $hueNumber->getDimension(); + } + $saturation = $getParam('saturation', 100, true); + $lightness = $getParam('lightness', 100, true); + $whiteness = $getParam('whiteness', 100, false, true); + $blackness = $getParam('blackness', 100, false, true); + + if (!empty($kwargs)) { + $unknownNames = array_keys($kwargs); + $lastName = array_pop($unknownNames); + $message = sprintf( + 'No argument%s named $%s%s.', + $unknownNames ? 's' : '', + $unknownNames ? implode(', $', $unknownNames) . ' or $' : '', + $lastName + ); + throw new SassScriptException($message); + } + + $hasRgb = $red !== null || $green !== null || $blue !== null; + $hasSL = $saturation !== null || $lightness !== null; + $hasWB = $whiteness !== null || $blackness !== null; + + if ($hasRgb && ($hasSL || $hasWB || $hue !== null)) { + throw new SassScriptException(sprintf('RGB parameters may not be passed along with %s parameters.', $hasWB ? 'HWB' : 'HSL')); + } + + if ($hasWB && $hasSL) { + throw new SassScriptException('HSL parameters may not be passed along with HWB parameters.'); + } + + if ($hasRgb) { + $color[1] = round($fn($color[1], $red, 255)); + $color[2] = round($fn($color[2], $green, 255)); + $color[3] = round($fn($color[3], $blue, 255)); + } elseif ($hasWB) { + $hwb = $this->RGBtoHWB($color[1], $color[2], $color[3]); + if ($hue !== null) { + $hwb[1] = $change ? $hue : $hwb[1] + $hue; + } + $hwb[2] = $fn($hwb[2], $whiteness, 100); + $hwb[3] = $fn($hwb[3], $blackness, 100); + + $rgb = $this->HWBtoRGB($hwb[1], $hwb[2], $hwb[3]); + + if (isset($color[4])) { + $rgb[4] = $color[4]; + } + + $color = $rgb; + } elseif ($hue !== null || $hasSL) { + $hsl = $this->toHSL($color[1], $color[2], $color[3]); + + if ($hue !== null) { + $hsl[1] = $change ? $hue : $hsl[1] + $hue; + } + $hsl[2] = $fn($hsl[2], $saturation, 100); + $hsl[3] = $fn($hsl[3], $lightness, 100); + $rgb = $this->toRGB($hsl[1], $hsl[2], $hsl[3]); if (isset($color[4])) { @@ -5890,58 +8028,54 @@ class Compiler $color = $rgb; } + if ($alpha !== null) { + $existingAlpha = isset($color[4]) ? $color[4] : 1; + $color[4] = $fn($existingAlpha, $alpha, 1); + } + return $color; } - protected static $libAdjustColor = [ - 'color', 'red:null', 'green:null', 'blue:null', - 'hue:null', 'saturation:null', 'lightness:null', 'alpha:null' - ]; + protected static $libAdjustColor = ['color', 'kwargs...']; protected function libAdjustColor($args) { - return $this->alterColor($args, function ($base, $alter, $i) { - return $base + $alter; + return $this->alterColor($args, 'adjust', function ($base, $alter, $max) { + if ($alter === null) { + return $base; + } + + $new = $base + $alter; + + if ($new < 0) { + return 0; + } + + if ($new > $max) { + return $max; + } + + return $new; }); } - protected static $libChangeColor = [ - 'color', 'red:null', 'green:null', 'blue:null', - 'hue:null', 'saturation:null', 'lightness:null', 'alpha:null' - ]; + protected static $libChangeColor = ['color', 'kwargs...']; protected function libChangeColor($args) { - return $this->alterColor($args, function ($base, $alter, $i) { + return $this->alterColor($args,'change', function ($base, $alter, $max) { + if ($alter === null) { + return $base; + } + return $alter; }); } - protected static $libScaleColor = [ - 'color', 'red:null', 'green:null', 'blue:null', - 'hue:null', 'saturation:null', 'lightness:null', 'alpha:null' - ]; + protected static $libScaleColor = ['color', 'kwargs...']; protected function libScaleColor($args) { - return $this->alterColor($args, function ($base, $scale, $i) { - // 1, 2, 3 - rgb - // 4, 5, 6 - hsl - // 7 - a - switch ($i) { - case 1: - case 2: - case 3: - $max = 255; - break; - - case 4: - $max = 360; - break; - - case 7: - $max = 1; - break; - - default: - $max = 100; + return $this->alterColor($args, 'scale', function ($base, $scale, $max) { + if ($scale === null) { + return $base; } $scale = $scale / 100; @@ -5958,6 +8092,11 @@ class Compiler protected function libIeHexStr($args) { $color = $this->coerceColor($args[0]); + + if (\is_null($color)) { + throw $this->error('Error: argument `$color` of `ie-hex-str($color)` must be a color'); + } + $color[4] = isset($color[4]) ? round(255 * $color[4]) : 255; return [Type::T_STRING, '', [sprintf('#%02X%02X%02X%02X', $color[4], $color[1], $color[2], $color[3])]]; @@ -5968,7 +8107,11 @@ class Compiler { $color = $this->coerceColor($args[0]); - return $color[1]; + if (\is_null($color)) { + throw $this->error('Error: argument `$color` of `red($color)` must be a color'); + } + + return new Number((int) $color[1], ''); } protected static $libGreen = ['color']; @@ -5976,7 +8119,11 @@ class Compiler { $color = $this->coerceColor($args[0]); - return $color[2]; + if (\is_null($color)) { + throw $this->error('Error: argument `$color` of `green($color)` must be a color'); + } + + return new Number((int) $color[2], ''); } protected static $libBlue = ['color']; @@ -5984,14 +8131,18 @@ class Compiler { $color = $this->coerceColor($args[0]); - return $color[3]; + if (\is_null($color)) { + throw $this->error('Error: argument `$color` of `blue($color)` must be a color'); + } + + return new Number((int) $color[3], ''); } protected static $libAlpha = ['color']; protected function libAlpha($args) { if ($color = $this->coerceColor($args[0])) { - return isset($color[4]) ? $color[4] : 1; + return new Number(isset($color[4]) ? $color[4] : 1, ''); } // this might be the IE function, so return value unchanged @@ -6003,7 +8154,7 @@ class Compiler { $value = $args[0]; - if ($value[0] === Type::T_NUMBER) { + if ($value instanceof Number) { return null; } @@ -6011,78 +8162,129 @@ class Compiler } // mix two colors - protected static $libMix = ['color-1', 'color-2', 'weight:0.5']; + protected static $libMix = [ + ['color1', 'color2', 'weight:50%'], + ['color-1', 'color-2', 'weight:50%'] + ]; protected function libMix($args) { list($first, $second, $weight) = $args; - $first = $this->assertColor($first); - $second = $this->assertColor($second); - - if (! isset($weight)) { - $weight = 0.5; - } else { - $weight = $this->coercePercent($weight); - } + $first = $this->assertColor($first, 'color1'); + $second = $this->assertColor($second, 'color2'); + $weightScale = $this->assertNumber($weight, 'weight')->valueInRange(0, 100, 'weight') / 100; $firstAlpha = isset($first[4]) ? $first[4] : 1; $secondAlpha = isset($second[4]) ? $second[4] : 1; - $w = $weight * 2 - 1; - $a = $firstAlpha - $secondAlpha; + $normalizedWeight = $weightScale * 2 - 1; + $alphaDistance = $firstAlpha - $secondAlpha; - $w1 = (($w * $a === -1 ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2.0; - $w2 = 1.0 - $w1; + $combinedWeight = $normalizedWeight * $alphaDistance == -1 ? $normalizedWeight : ($normalizedWeight + $alphaDistance) / (1 + $normalizedWeight * $alphaDistance); + $weight1 = ($combinedWeight + 1) / 2.0; + $weight2 = 1.0 - $weight1; $new = [Type::T_COLOR, - $w1 * $first[1] + $w2 * $second[1], - $w1 * $first[2] + $w2 * $second[2], - $w1 * $first[3] + $w2 * $second[3], + $weight1 * $first[1] + $weight2 * $second[1], + $weight1 * $first[2] + $weight2 * $second[2], + $weight1 * $first[3] + $weight2 * $second[3], ]; if ($firstAlpha != 1.0 || $secondAlpha != 1.0) { - $new[] = $firstAlpha * $weight + $secondAlpha * (1 - $weight); + $new[] = $firstAlpha * $weightScale + $secondAlpha * (1 - $weightScale); } return $this->fixColor($new); } - protected static $libHsl =[ + protected static $libHsl = [ ['channels'], + ['hue', 'saturation'], ['hue', 'saturation', 'lightness'], ['hue', 'saturation', 'lightness', 'alpha'] ]; + + /** + * @param array $args + * @param array $kwargs + * @param string $funcName + * + * @return array|null + */ protected function libHsl($args, $kwargs, $funcName = 'hsl') { - if (count($args) == 1) { - if ($args[0][0] !== Type::T_LIST || count($args[0][2]) < 3 || count($args[0][2]) > 4) { + $args_to_check = $args; + + if (\count($args) == 1) { + if ($args[0][0] !== Type::T_LIST || \count($args[0][2]) < 3 || \count($args[0][2]) > 4) { return [Type::T_STRING, '', [$funcName . '(', $args[0], ')']]; } $args = $args[0][2]; + $args_to_check = $kwargs['channels'][2]; } - $hue = $this->compileColorPartValue($args[0], 0, 360, false, false, true); - $saturation = $this->compileColorPartValue($args[1], 0, 100, false); - $lightness = $this->compileColorPartValue($args[2], 0, 100, false); + if (\count($args) === 2) { + // if var() is used as an argument, return as a css function + foreach ($args as $arg) { + if ($arg[0] === Type::T_FUNCTION && in_array($arg[1], ['var'])) { + return null; + } + } + throw new SassScriptException('Missing argument $lightness.'); + } + + foreach ($kwargs as $arg) { + if (in_array($arg[0], [Type::T_FUNCTION_CALL, Type::T_FUNCTION]) && in_array($arg[1], ['min', 'max'])) { + return null; + } + } + + foreach ($args_to_check as $k => $arg) { + if (in_array($arg[0], [Type::T_FUNCTION_CALL, Type::T_FUNCTION]) && in_array($arg[1], ['min', 'max'])) { + if (count($kwargs) > 1 || ($k >= 2 && count($args) === 4)) { + return null; + } + + $args[$k] = $this->stringifyFncallArgs($arg); + } + + if ( + $k >= 2 && count($args) === 4 && + in_array($arg[0], [Type::T_FUNCTION_CALL, Type::T_FUNCTION]) && + in_array($arg[1], ['calc','env']) + ) { + return null; + } + } + + $hue = $this->reduce($args[0]); + $saturation = $this->reduce($args[1]); + $lightness = $this->reduce($args[2]); $alpha = null; - if (count($args) === 4) { + if (\count($args) === 4) { $alpha = $this->compileColorPartValue($args[3], 0, 100, false); - if (! is_numeric($hue) || ! is_numeric($saturation) || ! is_numeric($lightness) || ! is_numeric($alpha)) { + if (!$hue instanceof Number || !$saturation instanceof Number || ! $lightness instanceof Number || ! is_numeric($alpha)) { return [Type::T_STRING, '', [$funcName . '(', $args[0], ', ', $args[1], ', ', $args[2], ', ', $args[3], ')']]; } } else { - if (! is_numeric($hue) || ! is_numeric($saturation) || ! is_numeric($lightness)) { + if (!$hue instanceof Number || !$saturation instanceof Number || ! $lightness instanceof Number) { return [Type::T_STRING, '', [$funcName . '(', $args[0], ', ', $args[1], ', ', $args[2], ')']]; } } - $color = $this->toRGB($hue, $saturation, $lightness); + $hueValue = fmod($hue->getDimension(), 360); - if (! is_null($alpha)) { + while ($hueValue < 0) { + $hueValue += 360; + } + + $color = $this->toRGB($hueValue, max(0, min($saturation->getDimension(), 100)), max(0, min($lightness->getDimension(), 100))); + + if (! \is_null($alpha)) { $color[4] = $alpha; } @@ -6091,7 +8293,9 @@ class Compiler protected static $libHsla = [ ['channels'], - ['hue', 'saturation', 'lightness', 'alpha:1'] ]; + ['hue', 'saturation'], + ['hue', 'saturation', 'lightness'], + ['hue', 'saturation', 'lightness', 'alpha']]; protected function libHsla($args, $kwargs) { return $this->libHsl($args, $kwargs, 'hsla'); @@ -6100,34 +8304,173 @@ class Compiler protected static $libHue = ['color']; protected function libHue($args) { - $color = $this->assertColor($args[0]); + $color = $this->assertColor($args[0], 'color'); $hsl = $this->toHSL($color[1], $color[2], $color[3]); - return new Node\Number($hsl[1], 'deg'); + return new Number($hsl[1], 'deg'); } protected static $libSaturation = ['color']; protected function libSaturation($args) { - $color = $this->assertColor($args[0]); + $color = $this->assertColor($args[0], 'color'); $hsl = $this->toHSL($color[1], $color[2], $color[3]); - return new Node\Number($hsl[2], '%'); + return new Number($hsl[2], '%'); } protected static $libLightness = ['color']; protected function libLightness($args) { - $color = $this->assertColor($args[0]); + $color = $this->assertColor($args[0], 'color'); $hsl = $this->toHSL($color[1], $color[2], $color[3]); - return new Node\Number($hsl[3], '%'); + return new Number($hsl[3], '%'); } + /* + * Todo : a integrer dans le futur module color + protected static $libHwb = [ + ['channels'], + ['hue', 'whiteness', 'blackness'], + ['hue', 'whiteness', 'blackness', 'alpha'] ]; + protected function libHwb($args, $kwargs, $funcName = 'hwb') + { + $args_to_check = $args; + + if (\count($args) == 1) { + if ($args[0][0] !== Type::T_LIST) { + throw $this->error("Missing elements \$whiteness and \$blackness"); + } + + if (\trim($args[0][1])) { + throw $this->error("\$channels must be a space-separated list."); + } + + if (! empty($args[0]['enclosing'])) { + throw $this->error("\$channels must be an unbracketed list."); + } + + $args = $args[0][2]; + if (\count($args) > 3) { + throw $this->error("hwb() : Only 3 elements are allowed but ". \count($args) . "were passed"); + } + + $args_to_check = $this->extractSlashAlphaInColorFunction($kwargs['channels'][2]); + if (\count($args_to_check) !== \count($kwargs['channels'][2])) { + $args = $args_to_check; + } + } + + if (\count($args_to_check) < 2) { + throw $this->error("Missing elements \$whiteness and \$blackness"); + } + if (\count($args_to_check) < 3) { + throw $this->error("Missing element \$blackness"); + } + if (\count($args_to_check) > 4) { + throw $this->error("hwb() : Only 4 elements are allowed but ". \count($args) . "were passed"); + } + + foreach ($kwargs as $k => $arg) { + if (in_array($arg[0], [Type::T_FUNCTION_CALL]) && in_array($arg[1], ['min', 'max'])) { + return null; + } + } + + foreach ($args_to_check as $k => $arg) { + if (in_array($arg[0], [Type::T_FUNCTION_CALL]) && in_array($arg[1], ['min', 'max'])) { + if (count($kwargs) > 1 || ($k >= 2 && count($args) === 4)) { + return null; + } + + $args[$k] = $this->stringifyFncallArgs($arg); + } + + if ( + $k >= 2 && count($args) === 4 && + in_array($arg[0], [Type::T_FUNCTION_CALL, Type::T_FUNCTION]) && + in_array($arg[1], ['calc','env']) + ) { + return null; + } + } + + $hue = $this->reduce($args[0]); + $whiteness = $this->reduce($args[1]); + $blackness = $this->reduce($args[2]); + $alpha = null; + + if (\count($args) === 4) { + $alpha = $this->compileColorPartValue($args[3], 0, 1, false); + + if (! \is_numeric($alpha)) { + $val = $this->compileValue($args[3]); + throw $this->error("\$alpha: $val is not a number"); + } + } + + $this->assertNumber($hue, 'hue'); + $this->assertUnit($whiteness, ['%'], 'whiteness'); + $this->assertUnit($blackness, ['%'], 'blackness'); + + $this->assertRange($whiteness, 0, 100, "0% and 100%", "whiteness"); + $this->assertRange($blackness, 0, 100, "0% and 100%", "blackness"); + + $w = $whiteness->getDimension(); + $b = $blackness->getDimension(); + + $hueValue = $hue->getDimension() % 360; + + while ($hueValue < 0) { + $hueValue += 360; + } + + $color = $this->HWBtoRGB($hueValue, $w, $b); + + if (! \is_null($alpha)) { + $color[4] = $alpha; + } + + return $color; + } + + protected static $libWhiteness = ['color']; + protected function libWhiteness($args, $kwargs, $funcName = 'whiteness') { + + $color = $this->assertColor($args[0]); + $hwb = $this->RGBtoHWB($color[1], $color[2], $color[3]); + + return new Number($hwb[2], '%'); + } + + protected static $libBlackness = ['color']; + protected function libBlackness($args, $kwargs, $funcName = 'blackness') { + + $color = $this->assertColor($args[0]); + $hwb = $this->RGBtoHWB($color[1], $color[2], $color[3]); + + return new Number($hwb[3], '%'); + } + */ + + /** + * @param array $color + * @param int $idx + * @param int|float $amount + * + * @return array + */ protected function adjustHsl($color, $idx, $amount) { $hsl = $this->toHSL($color[1], $color[2], $color[3]); $hsl[$idx] += $amount; + + if ($idx !== 1) { + // Clamp the saturation and lightness + $hsl[$idx] = min(max(0, $hsl[$idx]), 100); + } + $out = $this->toRGB($hsl[1], $hsl[2], $hsl[3]); if (isset($color[4])) { @@ -6140,8 +8483,8 @@ class Compiler protected static $libAdjustHue = ['color', 'degrees']; protected function libAdjustHue($args) { - $color = $this->assertColor($args[0]); - $degrees = $this->assertNumber($args[1]); + $color = $this->assertColor($args[0], 'color'); + $degrees = $this->assertNumber($args[1], 'degrees')->getDimension(); return $this->adjustHsl($color, 1, $degrees); } @@ -6149,7 +8492,7 @@ class Compiler protected static $libLighten = ['color', 'amount']; protected function libLighten($args) { - $color = $this->assertColor($args[0]); + $color = $this->assertColor($args[0], 'color'); $amount = Util::checkRange('amount', new Range(0, 100), $args[1], '%'); return $this->adjustHsl($color, 3, $amount); @@ -6158,34 +8501,36 @@ class Compiler protected static $libDarken = ['color', 'amount']; protected function libDarken($args) { - $color = $this->assertColor($args[0]); + $color = $this->assertColor($args[0], 'color'); $amount = Util::checkRange('amount', new Range(0, 100), $args[1], '%'); return $this->adjustHsl($color, 3, -$amount); } - protected static $libSaturate = [['color', 'amount'], ['number']]; + protected static $libSaturate = [['color', 'amount'], ['amount']]; protected function libSaturate($args) { $value = $args[0]; - if ($value[0] === Type::T_NUMBER) { + if (count($args) === 1) { + $this->assertNumber($args[0], 'amount'); + return null; } - $color = $this->assertColor($value); - $amount = 100 * $this->coercePercent($args[1]); + $color = $this->assertColor($args[0], 'color'); + $amount = $this->assertNumber($args[1], 'amount'); - return $this->adjustHsl($color, 2, $amount); + return $this->adjustHsl($color, 2, $amount->valueInRange(0, 100, 'amount')); } protected static $libDesaturate = ['color', 'amount']; protected function libDesaturate($args) { - $color = $this->assertColor($args[0]); - $amount = 100 * $this->coercePercent($args[1]); + $color = $this->assertColor($args[0], 'color'); + $amount = $this->assertNumber($args[1], 'amount'); - return $this->adjustHsl($color, 2, -$amount); + return $this->adjustHsl($color, 2, -$amount->valueInRange(0, 100, 'amount')); } protected static $libGrayscale = ['color']; @@ -6193,55 +8538,51 @@ class Compiler { $value = $args[0]; - if ($value[0] === Type::T_NUMBER) { + if ($value instanceof Number) { return null; } - return $this->adjustHsl($this->assertColor($value), 2, -100); + return $this->adjustHsl($this->assertColor($value, 'color'), 2, -100); } protected static $libComplement = ['color']; protected function libComplement($args) { - return $this->adjustHsl($this->assertColor($args[0]), 1, 180); + return $this->adjustHsl($this->assertColor($args[0], 'color'), 1, 180); } - protected static $libInvert = ['color', 'weight:1']; + protected static $libInvert = ['color', 'weight:100%']; protected function libInvert($args) { - list($value, $weight) = $args; + $value = $args[0]; - if (! isset($weight)) { - $weight = 1; - } else { - $weight = $this->coercePercent($weight); - } + $weight = $this->assertNumber($args[1], 'weight'); + + if ($value instanceof Number) { + if ($weight->getDimension() != 100 || !$weight->hasUnit('%')) { + throw new SassScriptException('Only one argument may be passed to the plain-CSS invert() function.'); + } - if ($value[0] === Type::T_NUMBER) { return null; } - $color = $this->assertColor($value); + $color = $this->assertColor($value, 'color'); $inverted = $color; $inverted[1] = 255 - $inverted[1]; $inverted[2] = 255 - $inverted[2]; $inverted[3] = 255 - $inverted[3]; - if ($weight < 1) { - return $this->libMix([$inverted, $color, [Type::T_NUMBER, $weight]]); - } - - return $inverted; + return $this->libMix([$inverted, $color, $weight]); } // increases opacity by amount protected static $libOpacify = ['color', 'amount']; protected function libOpacify($args) { - $color = $this->assertColor($args[0]); - $amount = $this->coercePercent($args[1]); + $color = $this->assertColor($args[0], 'color'); + $amount = $this->assertNumber($args[1], 'amount'); - $color[4] = (isset($color[4]) ? $color[4] : 1) + $amount; + $color[4] = (isset($color[4]) ? $color[4] : 1) + $amount->valueInRange(0, 1, 'amount'); $color[4] = min(1, max(0, $color[4])); return $color; @@ -6257,10 +8598,10 @@ class Compiler protected static $libTransparentize = ['color', 'amount']; protected function libTransparentize($args) { - $color = $this->assertColor($args[0]); - $amount = $this->coercePercent($args[1]); + $color = $this->assertColor($args[0], 'color'); + $amount = $this->assertNumber($args[1], 'amount'); - $color[4] = (isset($color[4]) ? $color[4] : 1) - $amount; + $color[4] = (isset($color[4]) ? $color[4] : 1) - $amount->valueInRange(0, 1, 'amount'); $color[4] = min(1, max(0, $color[4])); return $color; @@ -6275,166 +8616,165 @@ class Compiler protected static $libUnquote = ['string']; protected function libUnquote($args) { - $str = $args[0]; + try { + $str = $this->assertString($args[0], 'string'); + } catch (SassScriptException $e) { + $value = $this->compileValue($args[0]); + $fname = $this->getPrettyPath($this->sourceNames[$this->sourceIndex]); + $line = $this->sourceLine; - if ($str[0] === Type::T_STRING) { - $str[1] = ''; + $message = "Passing $value, a non-string value, to unquote() +will be an error in future versions of Sass.\n on line $line of $fname"; + + $this->logger->warn($message, true); + + return $args[0]; } + $str[1] = ''; + return $str; } protected static $libQuote = ['string']; protected function libQuote($args) { - $value = $args[0]; + $value = $this->assertString($args[0], 'string'); - if ($value[0] === Type::T_STRING && ! empty($value[1])) { - return $value; - } + $value[1] = '"'; - return [Type::T_STRING, '"', [$value]]; + return $value; } - protected static $libPercentage = ['value']; + protected static $libPercentage = ['number']; protected function libPercentage($args) { - return new Node\Number($this->coercePercent($args[0]) * 100, '%'); + $num = $this->assertNumber($args[0], 'number'); + $num->assertNoUnits('number'); + + return new Number($num->getDimension() * 100, '%'); } - protected static $libRound = ['value']; + protected static $libRound = ['number']; protected function libRound($args) { - $num = $args[0]; + $num = $this->assertNumber($args[0], 'number'); - return new Node\Number(round($num[1]), $num[2]); + return new Number(round($num->getDimension()), $num->getNumeratorUnits(), $num->getDenominatorUnits()); } - protected static $libFloor = ['value']; + protected static $libFloor = ['number']; protected function libFloor($args) { - $num = $args[0]; + $num = $this->assertNumber($args[0], 'number'); - return new Node\Number(floor($num[1]), $num[2]); + return new Number(floor($num->getDimension()), $num->getNumeratorUnits(), $num->getDenominatorUnits()); } - protected static $libCeil = ['value']; + protected static $libCeil = ['number']; protected function libCeil($args) { - $num = $args[0]; + $num = $this->assertNumber($args[0], 'number'); - return new Node\Number(ceil($num[1]), $num[2]); + return new Number(ceil($num->getDimension()), $num->getNumeratorUnits(), $num->getDenominatorUnits()); } - protected static $libAbs = ['value']; + protected static $libAbs = ['number']; protected function libAbs($args) { - $num = $args[0]; + $num = $this->assertNumber($args[0], 'number'); - return new Node\Number(abs($num[1]), $num[2]); + return new Number(abs($num->getDimension()), $num->getNumeratorUnits(), $num->getDenominatorUnits()); } + protected static $libMin = ['numbers...']; protected function libMin($args) { - $numbers = $this->getNormalizedNumbers($args); + /** + * @var Number|null + */ $min = null; - foreach ($numbers as $key => $number) { - if (is_null($min) || $number[1] <= $min[1]) { - $min = [$key, $number[1]]; + foreach ($args[0][2] as $arg) { + $number = $this->assertNumber($arg); + + if (\is_null($min) || $min->greaterThan($number)) { + $min = $number; } } - return $args[$min[0]]; + if (!\is_null($min)) { + return $min; + } + + throw $this->error('At least one argument must be passed.'); } + protected static $libMax = ['numbers...']; protected function libMax($args) { - $numbers = $this->getNormalizedNumbers($args); + /** + * @var Number|null + */ $max = null; - foreach ($numbers as $key => $number) { - if (is_null($max) || $number[1] >= $max[1]) { - $max = [$key, $number[1]]; + foreach ($args[0][2] as $arg) { + $number = $this->assertNumber($arg); + + if (\is_null($max) || $max->lessThan($number)) { + $max = $number; } } - return $args[$max[0]]; - } - - /** - * Helper to normalize args containing numbers - * - * @param array $args - * - * @return array - */ - protected function getNormalizedNumbers($args) - { - $unit = null; - $originalUnit = null; - $numbers = []; - - foreach ($args as $key => $item) { - if ($item[0] !== Type::T_NUMBER) { - $this->throwError('%s is not a number', $item[0]); - break; - } - - $number = $item->normalize(); - - if (is_null($unit)) { - $unit = $number[2]; - $originalUnit = $item->unitStr(); - } elseif ($number[1] && $unit !== $number[2]) { - $this->throwError('Incompatible units: "%s" and "%s".', $originalUnit, $item->unitStr()); - break; - } - - $numbers[$key] = $number; + if (!\is_null($max)) { + return $max; } - return $numbers; + throw $this->error('At least one argument must be passed.'); } protected static $libLength = ['list']; protected function libLength($args) { - $list = $this->coerceList($args[0]); + $list = $this->coerceList($args[0], ',', true); - return count($list[2]); + return new Number(\count($list[2]), ''); } - //protected static $libListSeparator = ['list...']; + protected static $libListSeparator = ['list']; protected function libListSeparator($args) { - if (count($args) > 1) { - return 'comma'; + if (! \in_array($args[0][0], [Type::T_LIST, Type::T_MAP])) { + return [Type::T_KEYWORD, 'space']; } $list = $this->coerceList($args[0]); - if (count($list[2]) <= 1) { - return 'space'; + if ($list[1] === '' && \count($list[2]) <= 1 && empty($list['enclosing'])) { + return [Type::T_KEYWORD, 'space']; } if ($list[1] === ',') { - return 'comma'; + return [Type::T_KEYWORD, 'comma']; } - return 'space'; + if ($list[1] === '/') { + return [Type::T_KEYWORD, 'slash']; + } + + return [Type::T_KEYWORD, 'space']; } protected static $libNth = ['list', 'n']; protected function libNth($args) { - $list = $this->coerceList($args[0]); - $n = $this->assertNumber($args[1]); + $list = $this->coerceList($args[0], ',', false); + $n = $this->assertNumber($args[1])->getDimension(); if ($n > 0) { $n--; } elseif ($n < 0) { - $n += count($list[2]); + $n += \count($list[2]); } return isset($list[2][$n]) ? $list[2][$n] : static::$defaultValue; @@ -6444,18 +8784,16 @@ class Compiler protected function libSetNth($args) { $list = $this->coerceList($args[0]); - $n = $this->assertNumber($args[1]); + $n = $this->assertNumber($args[1])->getDimension(); if ($n > 0) { $n--; } elseif ($n < 0) { - $n += count($list[2]); + $n += \count($list[2]); } if (! isset($list[2][$n])) { - $this->throwError('Invalid argument for "n"'); - - return null; + throw $this->error('Invalid argument for "n"'); } $list[2][$n] = $args[2]; @@ -6463,29 +8801,78 @@ class Compiler return $list; } - protected static $libMapGet = ['map', 'key']; + protected static $libMapGet = ['map', 'key', 'keys...']; protected function libMapGet($args) { - $map = $this->assertMap($args[0]); - $key = $args[1]; + $map = $this->assertMap($args[0], 'map'); + if (!isset($args[2])) { + // BC layer for usages of the function from PHP code rather than from the Sass function + $args[2] = self::$emptyArgumentList; + } + $keys = array_merge([$args[1]], $args[2][2]); + $value = static::$null; - if (! is_null($key)) { - $key = $this->compileStringContent($this->coerceString($key)); + foreach ($keys as $key) { + if (!\is_array($map) || $map[0] !== Type::T_MAP) { + return static::$null; + } - for ($i = count($map[1]) - 1; $i >= 0; $i--) { - if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) { - return $map[2][$i]; - } + $map = $this->mapGet($map, $key); + + if ($map === null) { + return static::$null; + } + + $value = $map; + } + + return $value; + } + + /** + * Gets the value corresponding to that key in the map + * + * @param array $map + * @param Number|array $key + * + * @return Number|array|null + */ + private function mapGet(array $map, $key) + { + $index = $this->mapGetEntryIndex($map, $key); + + if ($index !== null) { + return $map[2][$index]; + } + + return null; + } + + /** + * Gets the index corresponding to that key in the map entries + * + * @param array $map + * @param Number|array $key + * + * @return int|null + */ + private function mapGetEntryIndex(array $map, $key) + { + $key = $this->compileStringContent($this->coerceString($key)); + + for ($i = \count($map[1]) - 1; $i >= 0; $i--) { + if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) { + return $i; } } - return static::$null; + return null; } protected static $libMapKeys = ['map']; protected function libMapKeys($args) { - $map = $this->assertMap($args[0]); + $map = $this->assertMap($args[0], 'map'); $keys = $map[1]; return [Type::T_LIST, ',', $keys]; @@ -6494,20 +8881,33 @@ class Compiler protected static $libMapValues = ['map']; protected function libMapValues($args) { - $map = $this->assertMap($args[0]); + $map = $this->assertMap($args[0], 'map'); $values = $map[2]; return [Type::T_LIST, ',', $values]; } - protected static $libMapRemove = ['map', 'key']; + protected static $libMapRemove = [ + ['map'], + ['map', 'key', 'keys...'], + ]; protected function libMapRemove($args) { - $map = $this->assertMap($args[0]); - $key = $this->compileStringContent($this->coerceString($args[1])); + $map = $this->assertMap($args[0], 'map'); - for ($i = count($map[1]) - 1; $i >= 0; $i--) { - if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) { + if (\count($args) === 1) { + return $map; + } + + $keys = []; + $keys[] = $this->compileStringContent($this->coerceString($args[1])); + + foreach ($args[2][2] as $key) { + $keys[] = $this->compileStringContent($this->coerceString($key)); + } + + for ($i = \count($map[1]) - 1; $i >= 0; $i--) { + if (in_array($this->compileStringContent($this->coerceString($map[1][$i])), $keys)) { array_splice($map[1], $i, 1); array_splice($map[2], $i, 1); } @@ -6516,13 +8916,40 @@ class Compiler return $map; } - protected static $libMapHasKey = ['map', 'key']; + protected static $libMapHasKey = ['map', 'key', 'keys...']; protected function libMapHasKey($args) { - $map = $this->assertMap($args[0]); - $key = $this->compileStringContent($this->coerceString($args[1])); + $map = $this->assertMap($args[0], 'map'); + if (!isset($args[2])) { + // BC layer for usages of the function from PHP code rather than from the Sass function + $args[2] = self::$emptyArgumentList; + } + $keys = array_merge([$args[1]], $args[2][2]); + $lastKey = array_pop($keys); - for ($i = count($map[1]) - 1; $i >= 0; $i--) { + foreach ($keys as $key) { + $value = $this->mapGet($map, $key); + + if ($value === null || $value instanceof Number || $value[0] !== Type::T_MAP) { + return self::$false; + } + + $map = $value; + } + + return $this->toBool($this->mapHasKey($map, $lastKey)); + } + + /** + * @param array|Number $keyValue + * + * @return bool + */ + private function mapHasKey(array $map, $keyValue) + { + $key = $this->compileStringContent($this->coerceString($keyValue)); + + for ($i = \count($map[1]) - 1; $i >= 0; $i--) { if ($key === $this->compileStringContent($this->coerceString($map[1][$i]))) { return true; } @@ -6531,23 +8958,129 @@ class Compiler return false; } - protected static $libMapMerge = ['map-1', 'map-2']; + protected static $libMapMerge = [ + ['map1', 'map2'], + ['map-1', 'map-2'], + ['map1', 'args...'] + ]; protected function libMapMerge($args) { - $map1 = $this->assertMap($args[0]); - $map2 = $this->assertMap($args[1]); + $map1 = $this->assertMap($args[0], 'map1'); + $map2 = $args[1]; + $keys = []; + if ($map2[0] === Type::T_LIST && isset($map2[3]) && \is_array($map2[3])) { + // This is an argument list for the variadic signature + if (\count($map2[2]) === 0) { + throw new SassScriptException('Expected $args to contain a key.'); + } + if (\count($map2[2]) === 1) { + throw new SassScriptException('Expected $args to contain a value.'); + } + $keys = $map2[2]; + $map2 = array_pop($keys); + } + $map2 = $this->assertMap($map2, 'map2'); - foreach ($map2[1] as $i2 => $key2) { - $key = $this->compileStringContent($this->coerceString($key2)); + return $this->modifyMap($map1, $keys, function ($oldValue) use ($map2) { + $nestedMap = $this->tryMap($oldValue); - foreach ($map1[1] as $i1 => $key1) { - if ($key === $this->compileStringContent($this->coerceString($key1))) { - $map1[2][$i1] = $map2[2][$i2]; - continue 2; - } + if ($nestedMap === null) { + return $map2; } - $map1[1][] = $map2[1][$i2]; + return $this->mergeMaps($nestedMap, $map2); + }); + } + + /** + * @param array $map + * @param array $keys + * @param callable $modify + * @param bool $addNesting + * + * @return Number|array + * + * @phpstan-param array $keys + * @phpstan-param callable(Number|array): (Number|array) $modify + */ + private function modifyMap(array $map, array $keys, callable $modify, $addNesting = true) + { + if ($keys === []) { + return $modify($map); + } + + return $this->modifyNestedMap($map, $keys, $modify, $addNesting); + } + + /** + * @param array $map + * @param array $keys + * @param callable $modify + * @param bool $addNesting + * + * @return array + * + * @phpstan-param non-empty-array $keys + * @phpstan-param callable(Number|array): (Number|array) $modify + */ + private function modifyNestedMap(array $map, array $keys, callable $modify, $addNesting) + { + $key = array_shift($keys); + + $nestedValueIndex = $this->mapGetEntryIndex($map, $key); + + if ($keys === []) { + if ($nestedValueIndex !== null) { + $map[2][$nestedValueIndex] = $modify($map[2][$nestedValueIndex]); + } else { + $map[1][] = $key; + $map[2][] = $modify(self::$null); + } + + return $map; + } + + $nestedMap = $nestedValueIndex !== null ? $this->tryMap($map[2][$nestedValueIndex]) : null; + + if ($nestedMap === null && !$addNesting) { + return $map; + } + + if ($nestedMap === null) { + $nestedMap = self::$emptyMap; + } + + $newNestedMap = $this->modifyNestedMap($nestedMap, $keys, $modify, $addNesting); + + if ($nestedValueIndex !== null) { + $map[2][$nestedValueIndex] = $newNestedMap; + } else { + $map[1][] = $key; + $map[2][] = $newNestedMap; + } + + return $map; + } + + /** + * Merges 2 Sass maps together + * + * @param array $map1 + * @param array $map2 + * + * @return array + */ + private function mergeMaps(array $map1, array $map2) + { + foreach ($map2[1] as $i2 => $key2) { + $map1EntryIndex = $this->mapGetEntryIndex($map1, $key2); + + if ($map1EntryIndex !== null) { + $map1[2][$map1EntryIndex] = $map2[2][$i2]; + continue; + } + + $map1[1][] = $key2; $map1[2][] = $map2[2][$i2]; } @@ -6557,12 +9090,18 @@ class Compiler protected static $libKeywords = ['args']; protected function libKeywords($args) { - $this->assertList($args[0]); + $value = $args[0]; + + if ($value[0] !== Type::T_LIST || !isset($value[3]) || !\is_array($value[3])) { + $compiledValue = $this->compileValue($value); + + throw SassScriptException::forArgument($compiledValue . ' is not an argument list.', 'args'); + } $keys = []; $values = []; - foreach ($args[0][2] as $name => $arg) { + foreach ($this->getArgumentListKeywords($value) as $name => $arg) { $keys[] = [Type::T_KEYWORD, $name]; $values[] = $arg; } @@ -6575,15 +9114,27 @@ class Compiler { $list = $args[0]; $this->coerceList($list, ' '); + if (! empty($list['enclosing']) && $list['enclosing'] === 'bracket') { - return true; + return self::$true; } - return false; + + return self::$false; } - + /** + * @param array $list1 + * @param array|Number|null $sep + * + * @return string + * @throws CompilerException + * + * @deprecated + */ protected function listSeparatorForJoin($list1, $sep) { + @trigger_error(sprintf('The "%s" method is deprecated.', __METHOD__), E_USER_DEPRECATED); + if (! isset($sep)) { return $list1[1]; } @@ -6600,14 +9151,40 @@ class Compiler } } - protected static $libJoin = ['list1', 'list2', 'separator:null', 'bracketed:auto']; + protected static $libJoin = ['list1', 'list2', 'separator:auto', 'bracketed:auto']; protected function libJoin($args) { list($list1, $list2, $sep, $bracketed) = $args; - $list1 = $this->coerceList($list1, ' '); - $list2 = $this->coerceList($list2, ' '); - $sep = $this->listSeparatorForJoin($list1, $sep); + $list1 = $this->coerceList($list1, ' ', true); + $list2 = $this->coerceList($list2, ' ', true); + + switch ($this->compileStringContent($this->assertString($sep, 'separator'))) { + case 'comma': + $separator = ','; + break; + + case 'space': + $separator = ' '; + break; + + case 'slash': + $separator = '/'; + break; + + case 'auto': + if ($list1[1] !== '' || count($list1[2]) > 1 || !empty($list1['enclosing']) && $list1['enclosing'] !== 'parent') { + $separator = $list1[1] ?: ' '; + } elseif ($list2[1] !== '' || count($list2[2]) > 1 || !empty($list2['enclosing']) && $list2['enclosing'] !== 'parent') { + $separator = $list2[1] ?: ' '; + } else { + $separator = ' '; + } + break; + + default: + throw SassScriptException::forArgument('Must be "space", "comma", "slash", or "auto".', 'separator'); + } if ($bracketed === static::$true) { $bracketed = true; @@ -6620,6 +9197,7 @@ class Compiler } else { $bracketed = $this->compileValue($bracketed); $bracketed = ! ! $bracketed; + if ($bracketed === true) { $bracketed = true; } @@ -6627,60 +9205,91 @@ class Compiler if ($bracketed === 'auto') { $bracketed = false; + if (! empty($list1['enclosing']) && $list1['enclosing'] === 'bracket') { $bracketed = true; } } - $res = [Type::T_LIST, $sep, array_merge($list1[2], $list2[2])]; - if (isset($list1['enclosing'])) { - $res['enlcosing'] = $list1['enclosing']; - } + $res = [Type::T_LIST, $separator, array_merge($list1[2], $list2[2])]; + if ($bracketed) { $res['enclosing'] = 'bracket'; } + return $res; } - protected static $libAppend = ['list', 'val', 'separator:null']; + protected static $libAppend = ['list', 'val', 'separator:auto']; protected function libAppend($args) { list($list1, $value, $sep) = $args; - $list1 = $this->coerceList($list1, ' '); - $sep = $this->listSeparatorForJoin($list1, $sep); + $list1 = $this->coerceList($list1, ' ', true); + + switch ($this->compileStringContent($this->assertString($sep, 'separator'))) { + case 'comma': + $separator = ','; + break; + + case 'space': + $separator = ' '; + break; + + case 'slash': + $separator = '/'; + break; + + case 'auto': + $separator = $list1[1] === '' && \count($list1[2]) <= 1 && (empty($list1['enclosing']) || $list1['enclosing'] === 'parent') ? ' ' : $list1[1]; + break; + + default: + throw SassScriptException::forArgument('Must be "space", "comma", "slash", or "auto".', 'separator'); + } + + $res = [Type::T_LIST, $separator, array_merge($list1[2], [$value])]; - $res = [Type::T_LIST, $sep, array_merge($list1[2], [$value])]; if (isset($list1['enclosing'])) { $res['enclosing'] = $list1['enclosing']; } + return $res; } + protected static $libZip = ['lists...']; protected function libZip($args) { - foreach ($args as $key => $arg) { - $args[$key] = $this->coerceList($arg); + $argLists = []; + foreach ($args[0][2] as $arg) { + $argLists[] = $this->coerceList($arg); } $lists = []; - $firstList = array_shift($args); + $firstList = array_shift($argLists); - foreach ($firstList[2] as $key => $item) { - $list = [Type::T_LIST, '', [$item]]; + $result = [Type::T_LIST, ',', $lists]; + if (! \is_null($firstList)) { + foreach ($firstList[2] as $key => $item) { + $list = [Type::T_LIST, ' ', [$item]]; - foreach ($args as $arg) { - if (isset($arg[2][$key])) { - $list[2][] = $arg[2][$key]; - } else { - break 2; + foreach ($argLists as $arg) { + if (isset($arg[2][$key])) { + $list[2][] = $arg[2][$key]; + } else { + break 2; + } } + + $lists[] = $list; } - $lists[] = $list; + $result[2] = $lists; + } else { + $result['enclosing'] = 'parent'; } - return [Type::T_LIST, ',', $lists]; + return $result; } protected static $libTypeOf = ['value']; @@ -6688,6 +9297,16 @@ class Compiler { $value = $args[0]; + return [Type::T_KEYWORD, $this->getTypeOf($value)]; + } + + /** + * @param array|Number $value + * + * @return string + */ + private function getTypeOf($value) + { switch ($value[0]) { case Type::T_KEYWORD: if ($value === static::$true || $value === static::$false) { @@ -6702,8 +9321,11 @@ class Compiler case Type::T_FUNCTION: return 'string'; + case Type::T_FUNCTION_REFERENCE: + return 'function'; + case Type::T_LIST: - if (isset($value[3]) && $value[3]) { + if (isset($value[3]) && \is_array($value[3])) { return 'arglist'; } @@ -6716,68 +9338,77 @@ class Compiler protected static $libUnit = ['number']; protected function libUnit($args) { - $num = $args[0]; + $num = $this->assertNumber($args[0], 'number'); - if ($num[0] === Type::T_NUMBER) { - return [Type::T_STRING, '"', [$num->unitStr()]]; - } - - return ''; + return [Type::T_STRING, '"', [$num->unitStr()]]; } protected static $libUnitless = ['number']; protected function libUnitless($args) { - $value = $args[0]; + $value = $this->assertNumber($args[0], 'number'); - return $value[0] === Type::T_NUMBER && $value->unitless(); + return $this->toBool($value->unitless()); } - protected static $libComparable = ['number-1', 'number-2']; + protected static $libComparable = [ + ['number1', 'number2'], + ['number-1', 'number-2'] + ]; protected function libComparable($args) { list($number1, $number2) = $args; - if (! isset($number1[0]) || $number1[0] !== Type::T_NUMBER || - ! isset($number2[0]) || $number2[0] !== Type::T_NUMBER + if ( + ! $number1 instanceof Number || + ! $number2 instanceof Number ) { - $this->throwError('Invalid argument(s) for "comparable"'); - - return null; + throw $this->error('Invalid argument(s) for "comparable"'); } - $number1 = $number1->normalize(); - $number2 = $number2->normalize(); - - return $number1[2] === $number2[2] || $number1->unitless() || $number2->unitless(); + return $this->toBool($number1->isComparableTo($number2)); } protected static $libStrIndex = ['string', 'substring']; protected function libStrIndex($args) { - $string = $this->coerceString($args[0]); + $string = $this->assertString($args[0], 'string'); $stringContent = $this->compileStringContent($string); - $substring = $this->coerceString($args[1]); + $substring = $this->assertString($args[1], 'substring'); $substringContent = $this->compileStringContent($substring); - $result = strpos($stringContent, $substringContent); + if (! \strlen($substringContent)) { + $result = 0; + } else { + $result = Util::mbStrpos($stringContent, $substringContent); + } - return $result === false ? static::$null : new Node\Number($result + 1, ''); + return $result === false ? static::$null : new Number($result + 1, ''); } protected static $libStrInsert = ['string', 'insert', 'index']; protected function libStrInsert($args) { - $string = $this->coerceString($args[0]); + $string = $this->assertString($args[0], 'string'); $stringContent = $this->compileStringContent($string); - $insert = $this->coerceString($args[1]); + $insert = $this->assertString($args[1], 'insert'); $insertContent = $this->compileStringContent($insert); - list(, $index) = $args[2]; + $index = $this->assertInteger($args[2], 'index'); + if ($index > 0) { + $index = $index - 1; + } + if ($index < 0) { + $index = max(Util::mbStrlen($stringContent) + 1 + $index, 0); + } - $string[2] = [substr_replace($stringContent, $insertContent, $index - 1, 0)]; + $string[2] = [ + Util::mbSubstr($stringContent, 0, $index), + $insertContent, + Util::mbSubstr($stringContent, $index) + ]; return $string; } @@ -6785,34 +9416,46 @@ class Compiler protected static $libStrLength = ['string']; protected function libStrLength($args) { - $string = $this->coerceString($args[0]); + $string = $this->assertString($args[0], 'string'); $stringContent = $this->compileStringContent($string); - return new Node\Number(strlen($stringContent), ''); + return new Number(Util::mbStrlen($stringContent), ''); } protected static $libStrSlice = ['string', 'start-at', 'end-at:-1']; protected function libStrSlice($args) { - if (isset($args[2]) && ! $args[2][1]) { - return static::$nullString; - } - - $string = $this->coerceString($args[0]); + $string = $this->assertString($args[0], 'string'); $stringContent = $this->compileStringContent($string); - $start = (int) $args[1][1]; + $start = $this->assertNumber($args[1], 'start-at'); + $start->assertNoUnits('start-at'); + $startInt = $this->assertInteger($start, 'start-at'); + $end = $this->assertNumber($args[2], 'end-at'); + $end->assertNoUnits('end-at'); + $endInt = $this->assertInteger($end, 'end-at'); - if ($start > 0) { - $start--; + if ($endInt === 0) { + return [Type::T_STRING, $string[1], []]; } - $end = isset($args[2]) ? (int) $args[2][1] : -1; - $length = $end < 0 ? $end + 1 : ($end > 0 ? $end - $start : $end); + if ($startInt > 0) { + $startInt--; + } - $string[2] = $length - ? [substr($stringContent, $start, $length)] - : [substr($stringContent, $start)]; + if ($endInt < 0) { + $endInt = Util::mbStrlen($stringContent) + $endInt; + } else { + $endInt--; + } + + if ($endInt < $startInt) { + return [Type::T_STRING, $string[1], []]; + } + + $length = $endInt - $startInt + 1; // The end of the slice is inclusive + + $string[2] = [Util::mbSubstr($stringContent, $startInt, $length)]; return $string; } @@ -6820,10 +9463,10 @@ class Compiler protected static $libToLowerCase = ['string']; protected function libToLowerCase($args) { - $string = $this->coerceString($args[0]); + $string = $this->assertString($args[0], 'string'); $stringContent = $this->compileStringContent($string); - $string[2] = [function_exists('mb_strtolower') ? mb_strtolower($stringContent) : strtolower($stringContent)]; + $string[2] = [$this->stringTransformAsciiOnly($stringContent, 'strtolower')]; return $string; } @@ -6831,75 +9474,103 @@ class Compiler protected static $libToUpperCase = ['string']; protected function libToUpperCase($args) { - $string = $this->coerceString($args[0]); + $string = $this->assertString($args[0], 'string'); $stringContent = $this->compileStringContent($string); - $string[2] = [function_exists('mb_strtoupper') ? mb_strtoupper($stringContent) : strtoupper($stringContent)]; + $string[2] = [$this->stringTransformAsciiOnly($stringContent, 'strtoupper')]; return $string; } + /** + * Apply a filter on a string content, only on ascii chars + * let extended chars untouched + * + * @param string $stringContent + * @param callable $filter + * @return string + */ + protected function stringTransformAsciiOnly($stringContent, $filter) + { + $mblength = Util::mbStrlen($stringContent); + if ($mblength === strlen($stringContent)) { + return $filter($stringContent); + } + $filteredString = ""; + for ($i = 0; $i < $mblength; $i++) { + $char = Util::mbSubstr($stringContent, $i, 1); + if (strlen($char) > 1) { + $filteredString .= $char; + } else { + $filteredString .= $filter($char); + } + } + + return $filteredString; + } + protected static $libFeatureExists = ['feature']; protected function libFeatureExists($args) { - $string = $this->coerceString($args[0]); + $string = $this->assertString($args[0], 'feature'); $name = $this->compileStringContent($string); return $this->toBool( - array_key_exists($name, $this->registeredFeatures) ? $this->registeredFeatures[$name] : false + \array_key_exists($name, $this->registeredFeatures) ? $this->registeredFeatures[$name] : false ); } protected static $libFunctionExists = ['name']; protected function libFunctionExists($args) { - $string = $this->coerceString($args[0]); + $string = $this->assertString($args[0], 'name'); $name = $this->compileStringContent($string); // user defined functions if ($this->has(static::$namespaces['function'] . $name)) { - return true; + return self::$true; } $name = $this->normalizeName($name); if (isset($this->userFunctions[$name])) { - return true; + return self::$true; } // built-in functions $f = $this->getBuiltinFunction($name); - return $this->toBool(is_callable($f)); + return $this->toBool(\is_callable($f)); } protected static $libGlobalVariableExists = ['name']; protected function libGlobalVariableExists($args) { - $string = $this->coerceString($args[0]); + $string = $this->assertString($args[0], 'name'); $name = $this->compileStringContent($string); - return $this->has($name, $this->rootEnv); + return $this->toBool($this->has($name, $this->rootEnv)); } protected static $libMixinExists = ['name']; protected function libMixinExists($args) { - $string = $this->coerceString($args[0]); + $string = $this->assertString($args[0], 'name'); $name = $this->compileStringContent($string); - return $this->has(static::$namespaces['mixin'] . $name); + return $this->toBool($this->has(static::$namespaces['mixin'] . $name)); } protected static $libVariableExists = ['name']; protected function libVariableExists($args) { - $string = $this->coerceString($args[0]); + $string = $this->assertString($args[0], 'name'); $name = $this->compileStringContent($string); - return $this->has($name); + return $this->toBool($this->has($name)); } + protected static $libCounter = ['args...']; /** * Workaround IE7's content counter bug. * @@ -6909,35 +9580,37 @@ class Compiler */ protected function libCounter($args) { - $list = array_map([$this, 'compileValue'], $args); + $list = array_map([$this, 'compileValue'], $args[0][2]); return [Type::T_STRING, '', ['counter(' . implode(',', $list) . ')']]; } - protected static $libRandom = ['limit']; + protected static $libRandom = ['limit:null']; protected function libRandom($args) { - if (isset($args[0])) { - $n = $this->assertNumber($args[0]); + if (isset($args[0]) && $args[0] !== static::$null) { + $n = $this->assertInteger($args[0], 'limit'); if ($n < 1) { - $this->throwError("limit must be greater than or equal to 1"); - - return null; + throw new SassScriptException("\$limit: Must be greater than 0, was $n."); } - return new Node\Number(mt_rand(1, $n), ''); + return new Number(mt_rand(1, $n), ''); } - return new Node\Number(mt_rand(1, mt_getrandmax()), ''); + $max = mt_getrandmax(); + return new Number(mt_rand(0, $max - 1) / $max, ''); } + protected static $libUniqueId = []; protected function libUniqueId() { static $id; if (! isset($id)) { - $id = mt_rand(0, pow(36, 8)); + $id = PHP_INT_SIZE === 4 + ? mt_rand(0, pow(36, 5)) . str_pad(mt_rand(0, pow(36, 5)) % 10000000, 7, '0', STR_PAD_LEFT) + : mt_rand(0, pow(36, 8)); } $id += mt_rand(0, 10) + 1; @@ -6945,29 +9618,47 @@ class Compiler return [Type::T_STRING, '', ['u' . str_pad(base_convert($id, 10, 36), 8, '0', STR_PAD_LEFT)]]; } + /** + * @param array|Number $value + * @param bool $force_enclosing_display + * + * @return array + */ protected function inspectFormatValue($value, $force_enclosing_display = false) { if ($value === static::$null) { $value = [Type::T_KEYWORD, 'null']; } + $stringValue = [$value]; + + if ($value instanceof Number) { + return [Type::T_STRING, '', $stringValue]; + } + if ($value[0] === Type::T_LIST) { if (end($value[2]) === static::$null) { array_pop($value[2]); $value[2][] = [Type::T_STRING, '', ['']]; $force_enclosing_display = true; } - if (! empty($value['enclosing'])) { - if ($force_enclosing_display - || ($value['enclosing'] === 'bracket' ) - || !count($value[2])) { - $value['enclosing'] = 'forced_'.$value['enclosing']; - $force_enclosing_display = true; - } + + if ( + ! empty($value['enclosing']) && + ($force_enclosing_display || + ($value['enclosing'] === 'bracket') || + ! \count($value[2])) + ) { + $value['enclosing'] = 'forced_' . $value['enclosing']; + $force_enclosing_display = true; + } elseif (! \count($value[2])) { + $value['enclosing'] = 'forced_parent'; } + foreach ($value[2] as $k => $listelement) { $value[2][$k] = $this->inspectFormatValue($listelement, $force_enclosing_display); } + $stringValue = [$value]; } @@ -6978,37 +9669,80 @@ class Compiler protected function libInspect($args) { $value = $args[0]; + return $this->inspectFormatValue($value); } /** * Preprocess selector args * - * @param array $arg + * @param array $arg + * @param string|null $varname + * @param bool $allowParent * - * @return array|boolean + * @return array */ - protected function getSelectorArg($arg) + protected function getSelectorArg($arg, $varname = null, $allowParent = false) { static $parser = null; - if (is_null($parser)) { + if (\is_null($parser)) { $parser = $this->parserFactory(__METHOD__); } - $arg = $this->libUnquote([$arg]); + if (! $this->checkSelectorArgType($arg)) { + $var_value = $this->compileValue($arg); + throw SassScriptException::forArgument("$var_value is not a valid selector: it must be a string, a list of strings, or a list of lists of strings", $varname); + } + + + if ($arg[0] === Type::T_STRING) { + $arg[1] = ''; + } $arg = $this->compileValue($arg); $parsedSelector = []; - if ($parser->parseSelector($arg, $parsedSelector)) { + if ($parser->parseSelector($arg, $parsedSelector, true)) { $selector = $this->evalSelectors($parsedSelector); $gluedSelector = $this->glueFunctionSelectors($selector); + if (! $allowParent) { + foreach ($gluedSelector as $selector) { + foreach ($selector as $s) { + if (in_array(static::$selfSelector, $s)) { + throw SassScriptException::forArgument("Parent selectors aren't allowed here.", $varname); + } + } + } + } + return $gluedSelector; } - return false; + throw SassScriptException::forArgument("expected more input, invalid selector.", $varname); + } + + /** + * Check variable type for getSelectorArg() function + * @param array $arg + * @param int $maxDepth + * @return bool + */ + protected function checkSelectorArgType($arg, $maxDepth = 2) + { + if ($arg[0] === Type::T_LIST && $maxDepth > 0) { + foreach ($arg[2] as $elt) { + if (! $this->checkSelectorArgType($elt, $maxDepth - 1)) { + return false; + } + } + return true; + } + if (!in_array($arg[0], [Type::T_STRING, Type::T_KEYWORD])) { + return false; + } + return true; } /** @@ -7016,11 +9750,11 @@ class Compiler * * @param array $selectors * - * @return string + * @return array */ protected function formatOutputSelector($selectors) { - $selectors = $this->collapseSelectors($selectors, true); + $selectors = $this->collapseSelectorsAsList($selectors); return $selectors; } @@ -7030,10 +9764,10 @@ class Compiler { list($super, $sub) = $args; - $super = $this->getSelectorArg($super); - $sub = $this->getSelectorArg($sub); + $super = $this->getSelectorArg($super, 'super'); + $sub = $this->getSelectorArg($sub, 'sub'); - return $this->isSuperSelector($super, $sub); + return $this->toBool($this->isSuperSelector($super, $sub)); } /** @@ -7042,17 +9776,35 @@ class Compiler * @param array $super * @param array $sub * - * @return boolean + * @return bool */ protected function isSuperSelector($super, $sub) { // one and only one selector for each arg - if (! $super || count($super) !== 1) { - $this->throwError("Invalid super selector for isSuperSelector()"); + if (! $super) { + throw $this->error('Invalid super selector for isSuperSelector()'); } - if (! $sub || count($sub) !== 1) { - $this->throwError("Invalid sub selector for isSuperSelector()"); + if (! $sub) { + throw $this->error('Invalid sub selector for isSuperSelector()'); + } + + if (count($sub) > 1) { + foreach ($sub as $s) { + if (! $this->isSuperSelector($super, [$s])) { + return false; + } + } + return true; + } + + if (count($super) > 1) { + foreach ($super as $s) { + if ($this->isSuperSelector([$s], $sub)) { + return true; + } + } + return false; } $super = reset($super); @@ -7079,7 +9831,7 @@ class Compiler $nextMustMatch = true; $i++; } else { - while ($i < count($sub) && ! $this->isSuperPart($node, $sub[$i])) { + while ($i < \count($sub) && ! $this->isSuperPart($node, $sub[$i])) { if ($nextMustMatch) { return false; } @@ -7087,7 +9839,7 @@ class Compiler $i++; } - if ($i >= count($sub)) { + if ($i >= \count($sub)) { return false; } @@ -7105,18 +9857,18 @@ class Compiler * @param array $superParts * @param array $subParts * - * @return boolean + * @return bool */ protected function isSuperPart($superParts, $subParts) { $i = 0; foreach ($superParts as $superPart) { - while ($i < count($subParts) && $subParts[$i] !== $superPart) { + while ($i < \count($subParts) && $subParts[$i] !== $superPart) { $i++; } - if ($i >= count($subParts)) { + if ($i >= \count($subParts)) { return false; } @@ -7133,11 +9885,14 @@ class Compiler $args = reset($args); $args = $args[2]; - if (count($args) < 1) { - $this->throwError("selector-append() needs at least 1 argument"); + if (\count($args) < 1) { + throw $this->error('selector-append() needs at least 1 argument'); } - $selectors = array_map([$this, 'getSelectorArg'], $args); + $selectors = []; + foreach ($args as $arg) { + $selectors[] = $this->getSelectorArg($arg, 'selector'); + } return $this->formatOutputSelector($this->selectorAppend($selectors)); } @@ -7156,34 +9911,31 @@ class Compiler $lastSelectors = array_pop($selectors); if (! $lastSelectors) { - $this->throwError("Invalid selector list in selector-append()"); + throw $this->error('Invalid selector list in selector-append()'); } - while (count($selectors)) { + while (\count($selectors)) { $previousSelectors = array_pop($selectors); if (! $previousSelectors) { - $this->throwError("Invalid selector list in selector-append()"); + throw $this->error('Invalid selector list in selector-append()'); } // do the trick, happening $lastSelector to $previousSelector $appended = []; - foreach ($lastSelectors as $lastSelector) { - $previous = $previousSelectors; - - foreach ($lastSelector as $lastSelectorParts) { - foreach ($lastSelectorParts as $lastSelectorPart) { - foreach ($previous as $i => $previousSelector) { - foreach ($previousSelector as $j => $previousSelectorParts) { - $previous[$i][$j][] = $lastSelectorPart; + foreach ($previousSelectors as $previousSelector) { + foreach ($lastSelectors as $lastSelector) { + $previous = $previousSelector; + foreach ($previousSelector as $j => $previousSelectorParts) { + foreach ($lastSelector as $lastSelectorParts) { + foreach ($lastSelectorParts as $lastSelectorPart) { + $previous[$j][] = $lastSelectorPart; } } } - } - foreach ($previous as $ps) { - $appended[] = $ps; + $appended[] = $previous; } } @@ -7193,17 +9945,20 @@ class Compiler return $lastSelectors; } - protected static $libSelectorExtend = ['selectors', 'extendee', 'extender']; + protected static $libSelectorExtend = [ + ['selector', 'extendee', 'extender'], + ['selectors', 'extendee', 'extender'] + ]; protected function libSelectorExtend($args) { list($selectors, $extendee, $extender) = $args; - $selectors = $this->getSelectorArg($selectors); - $extendee = $this->getSelectorArg($extendee); - $extender = $this->getSelectorArg($extender); + $selectors = $this->getSelectorArg($selectors, 'selector'); + $extendee = $this->getSelectorArg($extendee, 'extendee'); + $extender = $this->getSelectorArg($extender, 'extender'); if (! $selectors || ! $extendee || ! $extender) { - $this->throwError("selector-extend() invalid arguments"); + throw $this->error('selector-extend() invalid arguments'); } $extended = $this->extendOrReplaceSelectors($selectors, $extendee, $extender); @@ -7211,17 +9966,20 @@ class Compiler return $this->formatOutputSelector($extended); } - protected static $libSelectorReplace = ['selectors', 'original', 'replacement']; + protected static $libSelectorReplace = [ + ['selector', 'original', 'replacement'], + ['selectors', 'original', 'replacement'] + ]; protected function libSelectorReplace($args) { list($selectors, $original, $replacement) = $args; - $selectors = $this->getSelectorArg($selectors); - $original = $this->getSelectorArg($original); - $replacement = $this->getSelectorArg($replacement); + $selectors = $this->getSelectorArg($selectors, 'selector'); + $original = $this->getSelectorArg($original, 'original'); + $replacement = $this->getSelectorArg($replacement, 'replacement'); if (! $selectors || ! $original || ! $replacement) { - $this->throwError("selector-replace() invalid arguments"); + throw $this->error('selector-replace() invalid arguments'); } $replaced = $this->extendOrReplaceSelectors($selectors, $original, $replacement, true); @@ -7233,10 +9991,10 @@ class Compiler * Extend/replace in selectors * used by selector-extend and selector-replace that use the same logic * - * @param array $selectors - * @param array $extendee - * @param array $extender - * @param boolean $replace + * @param array $selectors + * @param array $extendee + * @param array $extender + * @param bool $replace * * @return array */ @@ -7249,6 +10007,10 @@ class Compiler $this->extendsMap = []; foreach ($extendee as $es) { + if (\count($es) !== 1) { + throw $this->error('Can\'t extend complex selector.'); + } + // only use the first one $this->pushExtends(reset($es), $extender, null); } @@ -7260,12 +10022,12 @@ class Compiler $extended[] = $selector; } - $n = count($extended); + $n = \count($extended); $this->matchExtends($selector, $extended); // if didnt match, keep the original selector if we are in a replace operation - if ($replace and count($extended) === $n) { + if ($replace && \count($extended) === $n) { $extended[] = $selector; } } @@ -7283,11 +10045,17 @@ class Compiler $args = reset($args); $args = $args[2]; - if (count($args) < 1) { - $this->throwError("selector-nest() needs at least 1 argument"); + if (\count($args) < 1) { + throw $this->error('selector-nest() needs at least 1 argument'); } - $selectorsMap = array_map([$this, 'getSelectorArg'], $args); + $selectorsMap = []; + foreach ($args as $arg) { + $selectorsMap[] = $this->getSelectorArg($arg, 'selector', true); + } + + assert(!empty($selectorsMap)); + $envs = []; foreach ($selectorsMap as $selectors) { @@ -7304,11 +10072,14 @@ class Compiler return $this->formatOutputSelector($outputSelectors); } - protected static $libSelectorParse = ['selectors']; + protected static $libSelectorParse = [ + ['selector'], + ['selectors'] + ]; protected function libSelectorParse($args) { $selectors = reset($args); - $selectors = $this->getSelectorArg($selectors); + $selectors = $this->getSelectorArg($selectors, 'selector'); return $this->formatOutputSelector($selectors); } @@ -7318,11 +10089,11 @@ class Compiler { list($selectors1, $selectors2) = $args; - $selectors1 = $this->getSelectorArg($selectors1); - $selectors2 = $this->getSelectorArg($selectors2); + $selectors1 = $this->getSelectorArg($selectors1, 'selectors1'); + $selectors2 = $this->getSelectorArg($selectors2, 'selectors2'); if (! $selectors1 || ! $selectors2) { - $this->throwError("selector-unify() invalid arguments"); + throw $this->error('selector-unify() invalid arguments'); } // only consider the first compound of each @@ -7342,15 +10113,15 @@ class Compiler * @param array $compound1 * @param array $compound2 * - * @return array|mixed + * @return array */ protected function unifyCompoundSelectors($compound1, $compound2) { - if (! count($compound1)) { + if (! \count($compound1)) { return $compound2; } - if (! count($compound2)) { + if (! \count($compound2)) { return $compound1; } @@ -7367,7 +10138,7 @@ class Compiler $unifiedSelectors = [$unifiedCompound]; // do the rest - while (count($compound1) || count($compound2)) { + while (\count($compound1) || \count($compound2)) { $part1 = end($compound1); $part2 = end($compound2); @@ -7458,7 +10229,7 @@ class Compiler * @param array $part * @param array $compound * - * @return array|boolean + * @return array|false */ protected function matchPartInCompound($part, $compound) { @@ -7467,7 +10238,7 @@ class Compiler $after = []; // try to find a match by tag name first - while (count($before)) { + while (\count($before)) { $p = array_pop($before); if ($partTag && $partTag !== '*' && $partTag == $this->findTagName($p)) { @@ -7481,11 +10252,11 @@ class Compiler $before = $compound; $after = []; - while (count($before)) { + while (\count($before)) { $p = array_pop($before); if ($this->checkCompatibleTags($partTag, $this->findTagName($p))) { - if (count(array_intersect($part, $p))) { + if (\count(array_intersect($part, $p))) { return [$before, $p, $after]; } } @@ -7553,7 +10324,7 @@ class Compiler * @param string $tag1 * @param string $tag2 * - * @return array|boolean + * @return array|false */ protected function checkCompatibleTags($tag1, $tag2) { @@ -7561,12 +10332,12 @@ class Compiler $tags = array_unique($tags); $tags = array_filter($tags); - if (count($tags) > 1) { + if (\count($tags) > 1) { $tags = array_diff($tags, ['*']); } // not compatible nodes - if (count($tags) > 1) { + if (\count($tags) > 1) { return false; } @@ -7576,9 +10347,9 @@ class Compiler /** * Find the html tag name in a selector parts list * - * @param array $parts + * @param string[] $parts * - * @return mixed|string + * @return string */ protected function findTagName($parts) { @@ -7595,7 +10366,7 @@ class Compiler protected function libSimpleSelectors($args) { $selector = reset($args); - $selector = $this->getSelectorArg($selector); + $selector = $this->getSelectorArg($selector, 'selector'); // remove selectors list layer, keeping the first one $selector = reset($selector); @@ -7611,4 +10382,27 @@ class Compiler return [Type::T_LIST, ',', $listParts]; } + + protected static $libScssphpGlob = ['pattern']; + protected function libScssphpGlob($args) + { + @trigger_error(sprintf('The "scssphp-glob" function is deprecated an will be removed in ScssPhp 2.0. Register your own alternative through "%s::registerFunction', __CLASS__), E_USER_DEPRECATED); + + $this->logger->warn('The "scssphp-glob" function is deprecated an will be removed in ScssPhp 2.0.', true); + + $string = $this->assertString($args[0], 'pattern'); + $pattern = $this->compileStringContent($string); + $matches = glob($pattern); + $listParts = []; + + foreach ($matches as $match) { + if (! is_file($match)) { + continue; + } + + $listParts[] = [Type::T_STRING, '"', [$match]]; + } + + return [Type::T_LIST, ',', $listParts]; + } } diff --git a/lib/scssphp/scssphp/src/Compiler/Environment.php b/lib/scssphp/scssphp/src/Compiler/Environment.php index 03eb86a5d..b205a077f 100644 --- a/lib/scssphp/scssphp/src/Compiler/Environment.php +++ b/lib/scssphp/scssphp/src/Compiler/Environment.php @@ -1,8 +1,9 @@ + * + * @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; } diff --git a/lib/scssphp/scssphp/src/Exception/CompilerException.php b/lib/scssphp/scssphp/src/Exception/CompilerException.php index a9d134fca..0b00cf525 100644 --- a/lib/scssphp/scssphp/src/Exception/CompilerException.php +++ b/lib/scssphp/scssphp/src/Exception/CompilerException.php @@ -1,8 +1,9 @@ + * + * @internal */ -class CompilerException extends \Exception +class CompilerException extends \Exception implements SassException { } diff --git a/lib/scssphp/scssphp/src/Exception/ParserException.php b/lib/scssphp/scssphp/src/Exception/ParserException.php index 2fa12dd7a..f0726698f 100644 --- a/lib/scssphp/scssphp/src/Exception/ParserException.php +++ b/lib/scssphp/scssphp/src/Exception/ParserException.php @@ -1,8 +1,9 @@ + * + * @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; + } } diff --git a/lib/scssphp/scssphp/src/Exception/RangeException.php b/lib/scssphp/scssphp/src/Exception/RangeException.php index ee36c97e1..4be4dee70 100644 --- a/lib/scssphp/scssphp/src/Exception/RangeException.php +++ b/lib/scssphp/scssphp/src/Exception/RangeException.php @@ -1,8 +1,9 @@ + * + * @internal */ -class RangeException extends \Exception +class RangeException extends \Exception implements SassException { } diff --git a/lib/scssphp/scssphp/src/Exception/ServerException.php b/lib/scssphp/scssphp/src/Exception/ServerException.php index d4a207aff..e593c4014 100644 --- a/lib/scssphp/scssphp/src/Exception/ServerException.php +++ b/lib/scssphp/scssphp/src/Exception/ServerException.php @@ -1,8 +1,9 @@ + * + * @deprecated The Scssphp server should define its own exception instead. */ -class ServerException extends \Exception +class ServerException extends \Exception implements SassException { } diff --git a/lib/scssphp/scssphp/src/Formatter.php b/lib/scssphp/scssphp/src/Formatter.php index e17770458..6137dc650 100644 --- a/lib/scssphp/scssphp/src/Formatter.php +++ b/lib/scssphp/scssphp/src/Formatter.php @@ -1,8 +1,9 @@ + * + * @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; diff --git a/lib/scssphp/scssphp/src/Formatter/Compact.php b/lib/scssphp/scssphp/src/Formatter/Compact.php index 591f0c92e..22f226889 100644 --- a/lib/scssphp/scssphp/src/Formatter/Compact.php +++ b/lib/scssphp/scssphp/src/Formatter/Compact.php @@ -1,8 +1,9 @@ + * + * @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 = ''; diff --git a/lib/scssphp/scssphp/src/Formatter/Compressed.php b/lib/scssphp/scssphp/src/Formatter/Compressed.php index ec4722eaf..58ebe3f11 100644 --- a/lib/scssphp/scssphp/src/Formatter/Compressed.php +++ b/lib/scssphp/scssphp/src/Formatter/Compressed.php @@ -1,8 +1,9 @@ + * + * @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( diff --git a/lib/scssphp/scssphp/src/Formatter/Crunched.php b/lib/scssphp/scssphp/src/Formatter/Crunched.php index 51ccb516d..2bc1e9299 100644 --- a/lib/scssphp/scssphp/src/Formatter/Crunched.php +++ b/lib/scssphp/scssphp/src/Formatter/Crunched.php @@ -1,8 +1,9 @@ + * + * @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( diff --git a/lib/scssphp/scssphp/src/Formatter/Debug.php b/lib/scssphp/scssphp/src/Formatter/Debug.php index 94e70c815..b3f442253 100644 --- a/lib/scssphp/scssphp/src/Formatter/Debug.php +++ b/lib/scssphp/scssphp/src/Formatter/Debug.php @@ -1,8 +1,9 @@ + * + * @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"; diff --git a/lib/scssphp/scssphp/src/Formatter/Expanded.php b/lib/scssphp/scssphp/src/Formatter/Expanded.php index 9549c6cfa..6eb4a0cb7 100644 --- a/lib/scssphp/scssphp/src/Formatter/Expanded.php +++ b/lib/scssphp/scssphp/src/Formatter/Expanded.php @@ -1,8 +1,9 @@ + * + * @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; } } diff --git a/lib/scssphp/scssphp/src/Formatter/Nested.php b/lib/scssphp/scssphp/src/Formatter/Nested.php index f9e7f037f..d5ed85cc2 100644 --- a/lib/scssphp/scssphp/src/Formatter/Nested.php +++ b/lib/scssphp/scssphp/src/Formatter/Nested.php @@ -1,8 +1,9 @@ + * + * @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) { diff --git a/lib/scssphp/scssphp/src/Formatter/OutputBlock.php b/lib/scssphp/scssphp/src/Formatter/OutputBlock.php index 3e6fd9289..2799656a4 100644 --- a/lib/scssphp/scssphp/src/Formatter/OutputBlock.php +++ b/lib/scssphp/scssphp/src/Formatter/OutputBlock.php @@ -1,8 +1,9 @@ + * + * @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; } diff --git a/lib/scssphp/scssphp/src/Node.php b/lib/scssphp/scssphp/src/Node.php index dab565a63..fcaf8a95f 100644 --- a/lib/scssphp/scssphp/src/Node.php +++ b/lib/scssphp/scssphp/src/Node.php @@ -1,8 +1,9 @@ + * + * @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; } diff --git a/lib/scssphp/scssphp/src/Node/Number.php b/lib/scssphp/scssphp/src/Node/Number.php index acbabff7f..ca9b5b652 100644 --- a/lib/scssphp/scssphp/src/Node/Number.php +++ b/lib/scssphp/scssphp/src/Node/Number.php @@ -1,8 +1,9 @@ + * + * @template-implements \ArrayAccess */ 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> */ - 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 */ - public $units; + private $numeratorUnits; + + /** + * @var string[] + * @phpstan-var list + */ + 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 $numeratorUnits + * @phpstan-param list $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 $newNumeratorUnits + * @phpstan-param list $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 $numeratorUnits + * @phpstan-param list $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 $numerators1 + * @phpstan-param list $denominators1 + * @phpstan-param list $numerators2 + * @phpstan-param list $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 $numerators + * @phpstan-param list $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) : ''); } } diff --git a/lib/scssphp/scssphp/src/Parser.php b/lib/scssphp/scssphp/src/Parser.php index 6a30af129..e9cbaf375 100644 --- a/lib/scssphp/scssphp/src/Parser.php +++ b/lib/scssphp/scssphp/src/Parser.php @@ -1,8 +1,9 @@ + * + * @internal */ class Parser { @@ -30,7 +43,7 @@ class Parser const SOURCE_COLUMN = -3; /** - * @var array + * @var array */ protected static $precedence = [ '=' => 0, @@ -38,7 +51,6 @@ class Parser 'and' => 2, '==' => 3, '!=' => 3, - '<=>' => 3, '<=' => 4, '>=' => 4, '<' => 4, @@ -50,49 +62,97 @@ class Parser '%' => 6, ]; + /** + * @var string + */ protected static $commentPattern; + /** + * @var string + */ protected static $operatorPattern; + /** + * @var string + */ protected static $whitePattern; + /** + * @var Cache|null + */ protected $cache; private $sourceName; private $sourceIndex; + /** + * @var array + */ private $sourcePositions; - private $charset; + /** + * The current offset in the buffer + * + * @var int + */ private $count; + /** + * @var Block|null + */ private $env; + /** + * @var bool + */ private $inParens; + /** + * @var bool + */ private $eatWhiteDefault; + /** + * @var bool + */ private $discardComments; + private $allowVars; + /** + * @var string + */ private $buffer; private $utf8; + /** + * @var string|null + */ private $encoding; private $patternModifiers; private $commentsSeen; + private $cssOnly; + + /** + * @var LoggerInterface + */ + private $logger; + /** * Constructor * * @api * - * @param string $sourceName - * @param integer $sourceIndex - * @param string $encoding - * @param \ScssPhp\ScssPhp\Cache $cache + * @param string|null $sourceName + * @param int $sourceIndex + * @param string|null $encoding + * @param Cache|null $cache + * @param bool $cssOnly + * @param LoggerInterface|null $logger */ - public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8', $cache = null) + public function __construct($sourceName, $sourceIndex = 0, $encoding = 'utf-8', Cache $cache = null, $cssOnly = false, LoggerInterface $logger = null) { $this->sourceName = $sourceName ?: '(stdin)'; $this->sourceIndex = $sourceIndex; - $this->charset = null; $this->utf8 = ! $encoding || strtolower($encoding) === 'utf-8'; $this->patternModifiers = $this->utf8 ? 'Aisu' : 'Ais'; $this->commentsSeen = []; - $this->discardComments = false; + $this->allowVars = true; + $this->cssOnly = $cssOnly; + $this->logger = $logger ?: new QuietLogger(); if (empty(static::$operatorPattern)) { - static::$operatorPattern = '([*\/%+-]|[!=]\=|\>\=?|\<\=\>|\<\=?|and|or)'; + static::$operatorPattern = '([*\/%+-]|[!=]\=|\>\=?|\<\=?|and|or)'; $commentSingle = '\/\/'; $commentMultiLeft = '\/\*'; @@ -104,9 +164,7 @@ class Parser : '/' . $commentSingle . '[^\n]*\s*|(' . static::$commentPattern . ')\s*|\s+/AisS'; } - if ($cache) { - $this->cache = $cache; - } + $this->cache = $cache; } /** @@ -128,9 +186,32 @@ class Parser * * @param string $msg * - * @throws \ScssPhp\ScssPhp\Exception\ParserException + * @phpstan-return never-return + * + * @throws ParserException + * + * @deprecated use "parseError" and throw the exception in the caller instead. */ public function throwParseError($msg = 'parse error') + { + @trigger_error( + 'The method "throwParseError" is deprecated. Use "parseError" and throw the exception in the caller instead', + E_USER_DEPRECATED + ); + + throw $this->parseError($msg); + } + + /** + * Creates a parser error + * + * @api + * + * @param string $msg + * + * @return ParserException + */ + public function parseError($msg = 'parse error') { list($line, $column) = $this->getSourcePosition($this->count); @@ -138,11 +219,21 @@ class Parser ? "line: $line, column: $column" : "$this->sourceName on line $line, at column $column"; - if ($this->peek("(.*?)(\n|$)", $m, $this->count)) { - throw new ParserException("$msg: failed at `$m[1]` $loc"); + if ($this->peek('(.*?)(\n|$)', $m, $this->count)) { + $this->restoreEncoding(); + + $e = new ParserException("$msg: failed at `$m[1]` $loc"); + $e->setSourcePosition([$this->sourceName, $line, $column]); + + return $e; } - throw new ParserException("$msg: $loc"); + $this->restoreEncoding(); + + $e = new ParserException("$msg: $loc"); + $e->setSourcePosition([$this->sourceName, $line, $column]); + + return $e; } /** @@ -152,19 +243,18 @@ class Parser * * @param string $buffer * - * @return \ScssPhp\ScssPhp\Block + * @return Block */ public function parse($buffer) { if ($this->cache) { - $cacheKey = $this->sourceName . ":" . md5($buffer); + $cacheKey = $this->sourceName . ':' . md5($buffer); $parseOptions = [ - 'charset' => $this->charset, 'utf8' => $this->utf8, ]; - $v = $this->cache->getCache("parse", $cacheKey, $parseOptions); + $v = $this->cache->getCache('parse', $cacheKey, $parseOptions); - if (! is_null($v)) { + if (! \is_null($v)) { return $v; } } @@ -192,22 +282,19 @@ class Parser ; } - if ($this->count !== strlen($this->buffer)) { - $this->throwParseError(); + if ($this->count !== \strlen($this->buffer)) { + throw $this->parseError(); } if (! empty($this->env->parent)) { - $this->throwParseError('unclosed block'); - } - - if ($this->charset) { - array_unshift($this->env->children, $this->charset); + throw $this->parseError('unclosed block'); } $this->restoreEncoding(); + assert($this->env !== null); if ($this->cache) { - $this->cache->setCache("parse", $cacheKey, $this->env, $parseOptions); + $this->cache->setCache('parse', $cacheKey, $this->env, $parseOptions); } return $this->env; @@ -221,7 +308,7 @@ class Parser * @param string $buffer * @param string|array $out * - * @return boolean + * @return bool */ public function parseValue($buffer, &$out) { @@ -232,6 +319,7 @@ class Parser $this->buffer = (string) $buffer; $this->saveEncoding(); + $this->extractLineNumbers($this->buffer); $list = $this->valueList($out); @@ -247,10 +335,11 @@ class Parser * * @param string $buffer * @param string|array $out + * @param bool $shouldValidate * - * @return boolean + * @return bool */ - public function parseSelector($buffer, &$out) + public function parseSelector($buffer, &$out, $shouldValidate = true) { $this->count = 0; $this->env = null; @@ -259,11 +348,21 @@ class Parser $this->buffer = (string) $buffer; $this->saveEncoding(); + $this->extractLineNumbers($this->buffer); + + // discard space/comments at the start + $this->discardComments = true; + $this->whitespace(); + $this->discardComments = false; $selector = $this->selectors($out); $this->restoreEncoding(); + if ($shouldValidate && $this->count !== strlen($buffer)) { + throw $this->parseError("`" . substr($buffer, $this->count) . "` is not a valid Selector in `$buffer`"); + } + return $selector; } @@ -272,10 +371,10 @@ class Parser * * @api * - * @param string $buffer - * @param string|array $out + * @param string $buffer + * @param array $out * - * @return boolean + * @return bool */ public function parseMediaQueryList($buffer, &$out) { @@ -286,6 +385,7 @@ class Parser $this->buffer = (string) $buffer; $this->saveEncoding(); + $this->extractLineNumbers($this->buffer); $isMediaQuery = $this->mediaQueryList($out); @@ -331,7 +431,7 @@ class Parser * position into $s. Then if a chain fails, use $this->seek($s) to * go back where we started. * - * @return boolean + * @return bool */ protected function parseChunk() { @@ -339,15 +439,19 @@ class Parser // the directives if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] === '@') { - if ($this->literal('@at-root', 8) && + if ( + $this->literal('@at-root', 8) && ($this->selectors($selector) || true) && ($this->map($with) || true) && - (($this->matchChar('(') - && $this->interpolation($with) - && $this->matchChar(')')) || true) && + (($this->matchChar('(') && + $this->interpolation($with) && + $this->matchChar(')')) || true) && $this->matchChar('{', false) ) { - $atRoot = $this->pushSpecialBlock(Type::T_AT_ROOT, $s); + ! $this->cssOnly || $this->assertPlainCssValid(false, $s); + + $atRoot = new AtRootBlock(); + $this->registerPushedBlock($atRoot, $s); $atRoot->selector = $selector; $atRoot->with = $with; @@ -356,8 +460,13 @@ class Parser $this->seek($s); - if ($this->literal('@media', 6) && $this->mediaQueryList($mediaQueryList) && $this->matchChar('{', false)) { - $media = $this->pushSpecialBlock(Type::T_MEDIA, $s); + if ( + $this->literal('@media', 6) && + $this->mediaQueryList($mediaQueryList) && + $this->matchChar('{', false) + ) { + $media = new MediaBlock(); + $this->registerPushedBlock($media, $s); $media->queryList = $mediaQueryList[2]; return true; @@ -365,12 +474,16 @@ class Parser $this->seek($s); - if ($this->literal('@mixin', 6) && + if ( + $this->literal('@mixin', 6) && $this->keyword($mixinName) && ($this->argumentDef($args) || true) && $this->matchChar('{', false) ) { - $mixin = $this->pushSpecialBlock(Type::T_MIXIN, $s); + ! $this->cssOnly || $this->assertPlainCssValid(false, $s); + + $mixin = new CallableBlock(Type::T_MIXIN); + $this->registerPushedBlock($mixin, $s); $mixin->name = $mixinName; $mixin->args = $args; @@ -379,17 +492,20 @@ class Parser $this->seek($s); - if ($this->literal('@include', 8) && - $this->keyword($mixinName) && - ($this->matchChar('(') && + if ( + ($this->literal('@include', 8) && + $this->keyword($mixinName) && + ($this->matchChar('(') && ($this->argValues($argValues) || true) && $this->matchChar(')') || true) && - ($this->end() || - ($this->literal('using', 5) && - $this->argumentDef($argUsing) && - ($this->end() || $this->matchChar('{') && $hasBlock = true)) || - $this->matchChar('{') && $hasBlock = true) + ($this->end()) || + ($this->literal('using', 5) && + $this->argumentDef($argUsing) && + ($this->end() || $this->matchChar('{') && $hasBlock = true)) || + $this->matchChar('{') && $hasBlock = true) ) { + ! $this->cssOnly || $this->assertPlainCssValid(false, $s); + $child = [ Type::T_INCLUDE, $mixinName, @@ -399,7 +515,8 @@ class Parser ]; if (! empty($hasBlock)) { - $include = $this->pushSpecialBlock(Type::T_INCLUDE, $s); + $include = new ContentBlock(); + $this->registerPushedBlock($include, $s); $include->child = $child; } else { $this->append($child, $s); @@ -410,10 +527,17 @@ class Parser $this->seek($s); - if ($this->literal('@scssphp-import-once', 20) && + if ( + $this->literal('@scssphp-import-once', 20) && $this->valueList($importPath) && $this->end() ) { + ! $this->cssOnly || $this->assertPlainCssValid(false, $s); + + list($line, $column) = $this->getSourcePosition($s); + $file = $this->sourceName; + $this->logger->warn("The \"@scssphp-import-once\" directive is deprecated and will be removed in ScssPhp 2.0, in \"$file\", line $line, column $column.", true); + $this->append([Type::T_SCSSPHP_IMPORT_ONCE, $importPath], $s); return true; @@ -421,10 +545,18 @@ class Parser $this->seek($s); - if ($this->literal('@import', 7) && + if ( + $this->literal('@import', 7) && $this->valueList($importPath) && + $importPath[0] !== Type::T_FUNCTION_CALL && $this->end() ) { + if ($this->cssOnly) { + $this->assertPlainCssValid([Type::T_IMPORT, $importPath], $s); + $this->append([Type::T_COMMENT, rtrim(substr($this->buffer, $s, $this->count - $s))]); + return true; + } + $this->append([Type::T_IMPORT, $importPath], $s); return true; @@ -432,10 +564,17 @@ class Parser $this->seek($s); - if ($this->literal('@import', 7) && + if ( + $this->literal('@import', 7) && $this->url($importPath) && $this->end() ) { + if ($this->cssOnly) { + $this->assertPlainCssValid([Type::T_IMPORT, $importPath], $s); + $this->append([Type::T_COMMENT, rtrim(substr($this->buffer, $s, $this->count - $s))]); + return true; + } + $this->append([Type::T_IMPORT, $importPath], $s); return true; @@ -443,10 +582,13 @@ class Parser $this->seek($s); - if ($this->literal('@extend', 7) && + if ( + $this->literal('@extend', 7) && $this->selectors($selectors) && $this->end() ) { + ! $this->cssOnly || $this->assertPlainCssValid(false, $s); + // check for '!flag' $optional = $this->stripOptionalFlag($selectors); $this->append([Type::T_EXTEND, $selectors, $optional], $s); @@ -456,12 +598,16 @@ class Parser $this->seek($s); - if ($this->literal('@function', 9) && + if ( + $this->literal('@function', 9) && $this->keyword($fnName) && $this->argumentDef($args) && $this->matchChar('{', false) ) { - $func = $this->pushSpecialBlock(Type::T_FUNCTION, $s); + ! $this->cssOnly || $this->assertPlainCssValid(false, $s); + + $func = new CallableBlock(Type::T_FUNCTION); + $this->registerPushedBlock($func, $s); $func->name = $fnName; $func->args = $args; @@ -470,23 +616,13 @@ class Parser $this->seek($s); - if ($this->literal('@break', 6) && $this->end()) { - $this->append([Type::T_BREAK], $s); + if ( + $this->literal('@return', 7) && + ($this->valueList($retVal) || true) && + $this->end() + ) { + ! $this->cssOnly || $this->assertPlainCssValid(false, $s); - return true; - } - - $this->seek($s); - - if ($this->literal('@continue', 9) && $this->end()) { - $this->append([Type::T_CONTINUE], $s); - - return true; - } - - $this->seek($s); - - if ($this->literal('@return', 7) && ($this->valueList($retVal) || true) && $this->end()) { $this->append([Type::T_RETURN, isset($retVal) ? $retVal : [Type::T_NULL]], $s); return true; @@ -494,13 +630,17 @@ class Parser $this->seek($s); - if ($this->literal('@each', 5) && + if ( + $this->literal('@each', 5) && $this->genericList($varNames, 'variable', ',', false) && $this->literal('in', 2) && $this->valueList($list) && $this->matchChar('{', false) ) { - $each = $this->pushSpecialBlock(Type::T_EACH, $s); + ! $this->cssOnly || $this->assertPlainCssValid(false, $s); + + $each = new EachBlock(); + $this->registerPushedBlock($each, $s); foreach ($varNames[2] as $varName) { $each->vars[] = $varName[1]; @@ -513,11 +653,24 @@ class Parser $this->seek($s); - if ($this->literal('@while', 6) && + if ( + $this->literal('@while', 6) && $this->expression($cond) && $this->matchChar('{', false) ) { - $while = $this->pushSpecialBlock(Type::T_WHILE, $s); + ! $this->cssOnly || $this->assertPlainCssValid(false, $s); + + while ( + $cond[0] === Type::T_LIST && + ! empty($cond['enclosing']) && + $cond['enclosing'] === 'parent' && + \count($cond[2]) == 1 + ) { + $cond = reset($cond[2]); + } + + $while = new WhileBlock(); + $this->registerPushedBlock($while, $s); $while->cond = $cond; return true; @@ -525,7 +678,8 @@ class Parser $this->seek($s); - if ($this->literal('@for', 4) && + if ( + $this->literal('@for', 4) && $this->variable($varName) && $this->literal('from', 4) && $this->expression($start) && @@ -534,7 +688,10 @@ class Parser $this->expression($end) && $this->matchChar('{', false) ) { - $for = $this->pushSpecialBlock(Type::T_FOR, $s); + ! $this->cssOnly || $this->assertPlainCssValid(false, $s); + + $for = new ForBlock(); + $this->registerPushedBlock($for, $s); $for->var = $varName[1]; $for->start = $start; $for->end = $end; @@ -545,14 +702,24 @@ class Parser $this->seek($s); - if ($this->literal('@if', 3) && $this->valueList($cond) && $this->matchChar('{', false)) { - $if = $this->pushSpecialBlock(Type::T_IF, $s); - while ($cond[0] === Type::T_LIST - && !empty($cond['enclosing']) - && $cond['enclosing'] === 'parent' - && count($cond[2]) == 1) { + if ( + $this->literal('@if', 3) && + $this->functionCallArgumentsList($cond, false, '{', false) + ) { + ! $this->cssOnly || $this->assertPlainCssValid(false, $s); + + $if = new IfBlock(); + $this->registerPushedBlock($if, $s); + + while ( + $cond[0] === Type::T_LIST && + ! empty($cond['enclosing']) && + $cond['enclosing'] === 'parent' && + \count($cond[2]) == 1 + ) { $cond = reset($cond[2]); } + $if->cond = $cond; $if->cases = []; @@ -561,10 +728,12 @@ class Parser $this->seek($s); - if ($this->literal('@debug', 6) && - $this->valueList($value) && - $this->end() + if ( + $this->literal('@debug', 6) && + $this->functionCallArgumentsList($value, false) ) { + ! $this->cssOnly || $this->assertPlainCssValid(false, $s); + $this->append([Type::T_DEBUG, $value], $s); return true; @@ -572,10 +741,12 @@ class Parser $this->seek($s); - if ($this->literal('@warn', 5) && - $this->valueList($value) && - $this->end() + if ( + $this->literal('@warn', 5) && + $this->functionCallArgumentsList($value, false) ) { + ! $this->cssOnly || $this->assertPlainCssValid(false, $s); + $this->append([Type::T_WARN, $value], $s); return true; @@ -583,10 +754,12 @@ class Parser $this->seek($s); - if ($this->literal('@error', 6) && - $this->valueList($value) && - $this->end() + if ( + $this->literal('@error', 6) && + $this->functionCallArgumentsList($value, false) ) { + ! $this->cssOnly || $this->assertPlainCssValid(false, $s); + $this->append([Type::T_ERROR, $value], $s); return true; @@ -594,14 +767,16 @@ class Parser $this->seek($s); - #if ($this->literal('@content', 8)) - - if ($this->literal('@content', 8) && + if ( + $this->literal('@content', 8) && ($this->end() || $this->matchChar('(') && - $this->argValues($argContent) && - $this->matchChar(')') && - $this->end())) { + $this->argValues($argContent) && + $this->matchChar(')') && + $this->end()) + ) { + ! $this->cssOnly || $this->assertPlainCssValid(false, $s); + $this->append([Type::T_MIXIN_CONTENT, isset($argContent) ? $argContent : null], $s); return true; @@ -613,17 +788,21 @@ class Parser if (isset($last) && $last[0] === Type::T_IF) { list(, $if) = $last; + assert($if instanceof IfBlock); if ($this->literal('@else', 5)) { if ($this->matchChar('{', false)) { - $else = $this->pushSpecialBlock(Type::T_ELSE, $s); - } elseif ($this->literal('if', 2) && $this->valueList($cond) && $this->matchChar('{', false)) { - $else = $this->pushSpecialBlock(Type::T_ELSEIF, $s); + $else = new ElseBlock(); + } elseif ( + $this->literal('if', 2) && + $this->functionCallArgumentsList($cond, false, '{', false) + ) { + $else = new ElseifBlock(); $else->cond = $cond; } if (isset($else)) { - $else->dontAppend = true; + $this->registerPushedBlock($else, $s); $if->cases[] = $else; return true; @@ -634,32 +813,23 @@ class Parser } // only retain the first @charset directive encountered - if ($this->literal('@charset', 8) && + if ( + $this->literal('@charset', 8) && $this->valueList($charset) && $this->end() ) { - if (! isset($this->charset)) { - $statement = [Type::T_CHARSET, $charset]; - - list($line, $column) = $this->getSourcePosition($s); - - $statement[static::SOURCE_LINE] = $line; - $statement[static::SOURCE_COLUMN] = $column; - $statement[static::SOURCE_INDEX] = $this->sourceIndex; - - $this->charset = $statement; - } - return true; } $this->seek($s); - if ($this->literal('@supports', 9) && - ($t1=$this->supportsQuery($supportQuery)) && - ($t2=$this->matchChar('{', false)) + if ( + $this->literal('@supports', 9) && + ($t1 = $this->supportsQuery($supportQuery)) && + ($t2 = $this->matchChar('{', false)) ) { - $directive = $this->pushSpecialBlock(Type::T_DIRECTIVE, $s); + $directive = new DirectiveBlock(); + $this->registerPushedBlock($directive, $s); $directive->name = 'supports'; $directive->value = $supportQuery; @@ -669,19 +839,26 @@ class Parser $this->seek($s); // doesn't match built in directive, do generic one - if ($this->matchChar('@', false) && - $this->keyword($dirName) && - ($this->variable($dirValue) || $this->openString('{', $dirValue) || true) && - $this->matchChar('{', false) + if ( + $this->matchChar('@', false) && + $this->mixedKeyword($dirName) && + $this->directiveValue($dirValue, '{') ) { - if ($dirName === 'media') { - $directive = $this->pushSpecialBlock(Type::T_MEDIA, $s); + if (count($dirName) === 1 && is_string(reset($dirName))) { + $dirName = reset($dirName); } else { - $directive = $this->pushSpecialBlock(Type::T_DIRECTIVE, $s); + $dirName = [Type::T_STRING, '', $dirName]; + } + if ($dirName === 'media') { + $directive = new MediaBlock(); + } else { + $directive = new DirectiveBlock(); $directive->name = $dirName; } + $this->registerPushedBlock($directive, $s); if (isset($dirValue)) { + ! $this->cssOnly || ($dirValue = $this->assertPlainCssValid($dirValue)); $directive->value = $dirValue; } @@ -691,12 +868,38 @@ class Parser $this->seek($s); // maybe it's a generic blockless directive - if ($this->matchChar('@', false) && - $this->keyword($dirName) && - $this->valueList($dirValue) && - $this->end() + if ( + $this->matchChar('@', false) && + $this->mixedKeyword($dirName) && + ! $this->isKnownGenericDirective($dirName) && + ($this->end(false) || ($this->directiveValue($dirValue, '') && $this->end(false))) ) { - $this->append([Type::T_DIRECTIVE, [$dirName, $dirValue]], $s); + if (\count($dirName) === 1 && \is_string(\reset($dirName))) { + $dirName = \reset($dirName); + } else { + $dirName = [Type::T_STRING, '', $dirName]; + } + if ( + ! empty($this->env->parent) && + $this->env->type && + ! \in_array($this->env->type, [Type::T_DIRECTIVE, Type::T_MEDIA]) + ) { + $plain = \trim(\substr($this->buffer, $s, $this->count - $s)); + throw $this->parseError( + "Unknown directive `{$plain}` not allowed in `" . $this->env->type . "` block" + ); + } + // blockless directives with a blank line after keeps their blank lines after + // sass-spec compliance purpose + $s = $this->count; + $hasBlankLine = false; + if ($this->match('\s*?\n\s*\n', $out, false)) { + $hasBlankLine = true; + $this->seek($s); + } + $isNotRoot = ! empty($this->env->parent); + $this->append([Type::T_DIRECTIVE, [$dirName, $dirValue, $hasBlankLine, $isNotRoot]], $s); + $this->whitespace(); return true; } @@ -706,9 +909,60 @@ class Parser return false; } + $inCssSelector = null; + if ($this->cssOnly) { + $inCssSelector = (! empty($this->env->parent) && + ! in_array($this->env->type, [Type::T_DIRECTIVE, Type::T_MEDIA])); + } + // custom properties : right part is static + if (($this->customProperty($name) ) && $this->matchChar(':', false)) { + $start = $this->count; + + // but can be complex and finish with ; or } + foreach ([';','}'] as $ending) { + if ( + $this->openString($ending, $stringValue, '(', ')', false) && + $this->end() + ) { + $end = $this->count; + $value = $stringValue; + + // check if we have only a partial value due to nested [] or { } to take in account + $nestingPairs = [['[', ']'], ['{', '}']]; + + foreach ($nestingPairs as $nestingPair) { + $p = strpos($this->buffer, $nestingPair[0], $start); + + if ($p && $p < $end) { + $this->seek($start); + + if ( + $this->openString($ending, $stringValue, $nestingPair[0], $nestingPair[1], false) && + $this->end() && + $this->count > $end + ) { + $end = $this->count; + $value = $stringValue; + } + } + } + + $this->seek($end); + $this->append([Type::T_CUSTOM_PROPERTY, $name, $value], $s); + + return true; + } + } + + // TODO: output an error here if nothing found according to sass spec + } + + $this->seek($s); + // property shortcut // captures most properties before having to parse a selector - if ($this->keyword($name, false) && + if ( + $this->keyword($name, false) && $this->literal(': ', 2) && $this->valueList($value) && $this->end() @@ -722,11 +976,14 @@ class Parser $this->seek($s); // variable assigns - if ($this->variable($name) && + if ( + $this->variable($name) && $this->matchChar(':') && $this->valueList($value) && $this->end() ) { + ! $this->cssOnly || $this->assertPlainCssValid(false, $s); + // check for '!flag' $assignmentFlags = $this->stripAssignmentFlags($value); $this->append([Type::T_ASSIGN, $name, $value, $assignmentFlags], $s); @@ -736,13 +993,13 @@ class Parser $this->seek($s); - // misc - if ($this->literal('-->', 3)) { - return true; - } - // opening css block - if ($this->selectors($selectors) && $this->matchChar('{', false)) { + if ( + $this->selectors($selectors) && + $this->matchChar('{', false) + ) { + ! $this->cssOnly || ! $inCssSelector || $this->assertPlainCssValid(false); + $this->pushBlock($selectors, $s); if ($this->eatWhiteDefault) { @@ -756,12 +1013,15 @@ class Parser $this->seek($s); // property assign, or nested assign - if ($this->propertyName($name) && $this->matchChar(':')) { + if ( + $this->propertyName($name) && + $this->matchChar(':') + ) { $foundSomething = false; if ($this->valueList($value)) { if (empty($this->env->parent)) { - $this->throwParseError('expected "{"'); + throw $this->parseError('expected "{"'); } $this->append([Type::T_ASSIGN, $name, $value], $s); @@ -769,7 +1029,10 @@ class Parser } if ($this->matchChar('{', false)) { - $propBlock = $this->pushSpecialBlock(Type::T_NESTED_PROPERTY, $s); + ! $this->cssOnly || $this->assertPlainCssValid(false); + + $propBlock = new NestedPropertyBlock(); + $this->registerPushedBlock($propBlock, $s); $propBlock->prefix = $name; $propBlock->hasValue = $foundSomething; @@ -790,17 +1053,20 @@ class Parser $block = $this->popBlock(); if (! isset($block->type) || $block->type !== Type::T_IF) { + assert($this->env !== null); + if ($this->env->parent) { $this->append(null); // collect comments before next statement if needed } } - if (isset($block->type) && $block->type === Type::T_INCLUDE) { + if ($block instanceof ContentBlock) { $include = $block->child; + assert(\is_array($include)); unset($block->child); $include[3] = $block; $this->append($include, $s); - } elseif (empty($block->dontAppend)) { + } elseif (!$block instanceof ElseBlock && !$block instanceof ElseifBlock) { $type = isset($block->type) ? $block->type : Type::T_BLOCK; $this->append([$type, $block], $s); } @@ -808,6 +1074,7 @@ class Parser // collect comments just after the block closing if needed if ($this->eatWhiteDefault) { $this->whitespace(); + assert($this->env !== null); if ($this->env->comments) { $this->append(null); @@ -818,9 +1085,7 @@ class Parser } // extra stuff - if ($this->matchChar(';') || - $this->literal('