Upgrade scssphp/scssphp to v1.3
authorAlexander Ebert <ebert@woltlab.com>
Tue, 3 Nov 2020 18:45:45 +0000 (19:45 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Tue, 3 Nov 2020 18:45:45 +0000 (19:45 +0100)
20 files changed:
wcfsetup/install/files/lib/system/api/bin/pscss
wcfsetup/install/files/lib/system/api/bin/pscss.bat [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/composer.lock
wcfsetup/install/files/lib/system/api/composer/ClassLoader.php
wcfsetup/install/files/lib/system/api/composer/InstalledVersions.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/composer/autoload_classmap.php
wcfsetup/install/files/lib/system/api/composer/autoload_real.php
wcfsetup/install/files/lib/system/api/composer/autoload_static.php
wcfsetup/install/files/lib/system/api/composer/installed.json
wcfsetup/install/files/lib/system/api/composer/installed.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/composer/platform_check.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/scssphp/scssphp/README.md
wcfsetup/install/files/lib/system/api/scssphp/scssphp/bin/pscss
wcfsetup/install/files/lib/system/api/scssphp/scssphp/composer.json
wcfsetup/install/files/lib/system/api/scssphp/scssphp/scss.inc.php
wcfsetup/install/files/lib/system/api/scssphp/scssphp/src/Compiler.php
wcfsetup/install/files/lib/system/api/scssphp/scssphp/src/Node/Number.php
wcfsetup/install/files/lib/system/api/scssphp/scssphp/src/Parser.php
wcfsetup/install/files/lib/system/api/scssphp/scssphp/src/Util.php
wcfsetup/install/files/lib/system/api/scssphp/scssphp/src/Version.php

index d08fca2ff6d8193e66fe6d02d1b688708f589341..2879dcb97719d27a1f064106f3fa9b871c78a8a8 120000 (symlink)
@@ -1 +1,14 @@
-../scssphp/scssphp/bin/pscss
\ No newline at end of file
+#!/usr/bin/env sh
+
+dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../scssphp/scssphp/bin" && pwd)
+
+if [ -d /proc/cygdrive ]; then
+    case $(which php) in
+        $(readlink -n /proc/cygdrive)/*)
+            # We are in Cygwin using Windows php, so the path must be translated
+            dir=$(cygpath -m "$dir");
+            ;;
+    esac
+fi
+
+"${dir}/pscss" "$@"
diff --git a/wcfsetup/install/files/lib/system/api/bin/pscss.bat b/wcfsetup/install/files/lib/system/api/bin/pscss.bat
new file mode 100644 (file)
index 0000000..1e368dc
--- /dev/null
@@ -0,0 +1,4 @@
+@ECHO OFF
+setlocal DISABLEDELAYEDEXPANSION
+SET BIN_TARGET=%~dp0/../scssphp/scssphp/bin/pscss
+php "%BIN_TARGET%" %*
index d5f14700d71a6fb1a496396432b8f3899a890b25..d82288406116eb6cf84744021cf324b6a0a55375 100644 (file)
         },
         {
             "name": "scssphp/scssphp",
-            "version": "1.2.1",
+            "version": "1.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/scssphp/scssphp.git",
-                "reference": "a05ea68160b7286ebbfd6e5fd7ae9e1a946ad6e1"
+                "reference": "261cd018025d5790e135a1e5b694d6af186e6bca"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/scssphp/scssphp/zipball/a05ea68160b7286ebbfd6e5fd7ae9e1a946ad6e1",
-                "reference": "a05ea68160b7286ebbfd6e5fd7ae9e1a946ad6e1",
+                "url": "https://api.github.com/repos/scssphp/scssphp/zipball/261cd018025d5790e135a1e5b694d6af186e6bca",
+                "reference": "261cd018025d5790e135a1e5b694d6af186e6bca",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.6.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.3",
+                "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.3 || ^9.4",
                 "sass/sass-spec": "2020.08.10",
                 "squizlabs/php_codesniffer": "~3.5",
                 "twbs/bootstrap": "~4.3",
                 "scss",
                 "stylesheet"
             ],
-            "time": "2020-09-07T21:15:42+00:00"
+            "support": {
+                "issues": "https://github.com/scssphp/scssphp/issues",
+                "source": "https://github.com/scssphp/scssphp/tree/1.3"
+            },
+            "time": "2020-10-29T11:09:57+00:00"
         },
         {
             "name": "symfony/css-selector",
         "php": "7.2.24",
         "ext-gd": "0"
     },
-    "plugin-api-version": "1.1.0"
+    "plugin-api-version": "2.0.0"
 }
index 03b9bb9c40cb86c2c2bbec2ce6ff0ddce9ad586c..1a58957d25d4a5fd00be8d9f086a4ece3bf8371e 100644 (file)
@@ -37,8 +37,8 @@ namespace Composer\Autoload;
  *
  * @author Fabien Potencier <fabien@symfony.com>
  * @author Jordi Boggiano <j.boggiano@seld.be>
- * @see    http://www.php-fig.org/psr/psr-0/
- * @see    http://www.php-fig.org/psr/psr-4/
+ * @see    https://www.php-fig.org/psr/psr-0/
+ * @see    https://www.php-fig.org/psr/psr-4/
  */
 class ClassLoader
 {
diff --git a/wcfsetup/install/files/lib/system/api/composer/InstalledVersions.php b/wcfsetup/install/files/lib/system/api/composer/InstalledVersions.php
new file mode 100644 (file)
index 0000000..03cf0ae
--- /dev/null
@@ -0,0 +1,367 @@
+<?php
+
+namespace Composer;
+
+use Composer\Semver\VersionParser;
+
+
+
+
+
+
+class InstalledVersions
+{
+private static $installed = array (
+  'root' => 
+  array (
+    'pretty_version' => 'dev-master',
+    'version' => 'dev-master',
+    'aliases' => 
+    array (
+    ),
+    'reference' => 'f448efb8521534c520f84e89a1a38296d4c0e5fd',
+    'name' => '__root__',
+  ),
+  'versions' => 
+  array (
+    '__root__' => 
+    array (
+      'pretty_version' => 'dev-master',
+      'version' => 'dev-master',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'f448efb8521534c520f84e89a1a38296d4c0e5fd',
+    ),
+    'chrisjean/php-ico' => 
+    array (
+      'pretty_version' => '1.0.4',
+      'version' => '1.0.4.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'ccd5c0d56554f3ddcd7a823e695be83e0d1e43b6',
+    ),
+    'erusev/parsedown' => 
+    array (
+      'pretty_version' => '1.7.4',
+      'version' => '1.7.4.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'cb17b6477dfff935958ba01325f2e8a2bfa6dab3',
+    ),
+    'ezyang/htmlpurifier' => 
+    array (
+      'pretty_version' => 'v4.13.0',
+      'version' => '4.13.0.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '08e27c97e4c6ed02f37c5b2b20488046c8d90d75',
+    ),
+    'guzzlehttp/guzzle' => 
+    array (
+      'pretty_version' => '7.2.0',
+      'version' => '7.2.0.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '0aa74dfb41ae110835923ef10a9d803a22d50e79',
+    ),
+    'guzzlehttp/promises' => 
+    array (
+      'pretty_version' => '1.4.0',
+      'version' => '1.4.0.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '60d379c243457e073cff02bc323a2a86cb355631',
+    ),
+    'guzzlehttp/psr7' => 
+    array (
+      'pretty_version' => '1.7.0',
+      'version' => '1.7.0.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '53330f47520498c0ae1f61f7e2c90f55690c06a3',
+    ),
+    'pear/net_idna2' => 
+    array (
+      'pretty_version' => 'v0.2.0',
+      'version' => '0.2.0.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '51734eaf8be2df58e8aad5835b9966459b2fb37c',
+    ),
+    'pear/pear_exception' => 
+    array (
+      'pretty_version' => 'v1.0.1',
+      'version' => '1.0.1.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7',
+    ),
+    'pelago/emogrifier' => 
+    array (
+      'pretty_version' => 'v2.1.1',
+      'version' => '2.1.1.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '8ee7fb5ad772915451ed3415c1992bd3697d4983',
+    ),
+    'psr/http-client' => 
+    array (
+      'pretty_version' => '1.0.1',
+      'version' => '1.0.1.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '2dfb5f6c5eff0e91e20e913f8c5452ed95b86621',
+    ),
+    'psr/http-client-implementation' => 
+    array (
+      'provided' => 
+      array (
+        0 => '1.0',
+      ),
+    ),
+    'psr/http-message' => 
+    array (
+      'pretty_version' => '1.0.1',
+      'version' => '1.0.1.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363',
+    ),
+    'psr/http-message-implementation' => 
+    array (
+      'provided' => 
+      array (
+        0 => '1.0',
+      ),
+    ),
+    'ralouphie/getallheaders' => 
+    array (
+      'pretty_version' => '3.0.3',
+      'version' => '3.0.3.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '120b605dfeb996808c31b6477290a714d356e822',
+    ),
+    'scssphp/scssphp' => 
+    array (
+      'pretty_version' => '1.3',
+      'version' => '1.3.0.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '261cd018025d5790e135a1e5b694d6af186e6bca',
+    ),
+    'symfony/css-selector' => 
+    array (
+      'pretty_version' => 'v4.4.15',
+      'version' => '4.4.15.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'bf17dc9f6ce144e41f786c32435feea4d8e11dcc',
+    ),
+    'symfony/polyfill-mbstring' => 
+    array (
+      'pretty_version' => 'v1.18.1',
+      'version' => '1.18.1.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'a6977d63bf9a0ad4c65cd352709e230876f9904a',
+    ),
+    'true/punycode' => 
+    array (
+      'pretty_version' => 'v2.1.1',
+      'version' => '2.1.1.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'a4d0c11a36dd7f4e7cd7096076cab6d3378a071e',
+    ),
+  ),
+);
+
+
+
+
+
+
+
+public static function getInstalledPackages()
+{
+return array_keys(self::$installed['versions']);
+}
+
+
+
+
+
+
+
+
+
+public static function isInstalled($packageName)
+{
+return isset(self::$installed['versions'][$packageName]);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+public static function satisfies(VersionParser $parser, $packageName, $constraint)
+{
+$constraint = $parser->parseConstraints($constraint);
+$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
+
+return $provided->matches($constraint);
+}
+
+
+
+
+
+
+
+
+
+
+public static function getVersionRanges($packageName)
+{
+if (!isset(self::$installed['versions'][$packageName])) {
+throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+}
+
+$ranges = array();
+if (isset(self::$installed['versions'][$packageName]['pretty_version'])) {
+$ranges[] = self::$installed['versions'][$packageName]['pretty_version'];
+}
+if (array_key_exists('aliases', self::$installed['versions'][$packageName])) {
+$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['aliases']);
+}
+if (array_key_exists('replaced', self::$installed['versions'][$packageName])) {
+$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['replaced']);
+}
+if (array_key_exists('provided', self::$installed['versions'][$packageName])) {
+$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['provided']);
+}
+
+return implode(' || ', $ranges);
+}
+
+
+
+
+
+public static function getVersion($packageName)
+{
+if (!isset(self::$installed['versions'][$packageName])) {
+throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+}
+
+if (!isset(self::$installed['versions'][$packageName]['version'])) {
+return null;
+}
+
+return self::$installed['versions'][$packageName]['version'];
+}
+
+
+
+
+
+public static function getPrettyVersion($packageName)
+{
+if (!isset(self::$installed['versions'][$packageName])) {
+throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+}
+
+if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) {
+return null;
+}
+
+return self::$installed['versions'][$packageName]['pretty_version'];
+}
+
+
+
+
+
+public static function getReference($packageName)
+{
+if (!isset(self::$installed['versions'][$packageName])) {
+throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+}
+
+if (!isset(self::$installed['versions'][$packageName]['reference'])) {
+return null;
+}
+
+return self::$installed['versions'][$packageName]['reference'];
+}
+
+
+
+
+
+public static function getRootPackage()
+{
+return self::$installed['root'];
+}
+
+
+
+
+
+
+
+public static function getRawData()
+{
+return self::$installed;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+public static function reload($data)
+{
+self::$installed = $data;
+}
+}
index cedefd86a7a2f9fba308e9ef8a266dd6f83e65dc..33a49c649b1200e79521a135fa99f7c227afcbaa 100644 (file)
@@ -6,6 +6,7 @@ $vendorDir = dirname(dirname(__FILE__));
 $baseDir = $vendorDir;
 
 return array(
+    'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
     'PEAR_Exception' => $vendorDir . '/pear/pear_exception/PEAR/Exception.php',
     'PHP_ICO' => $vendorDir . '/chrisjean/php-ico/class-php-ico.php',
 );
index 9e0a9611c889835af0fab9555184435a5033da5f..d61e79adfc819518464b771c83769521ba6f5f86 100644 (file)
@@ -22,6 +22,8 @@ class ComposerAutoloaderInita1f5f7c74275d47a45049a2936db1d0d
             return self::$loader;
         }
 
+        require __DIR__ . '/platform_check.php';
+
         spl_autoload_register(array('ComposerAutoloaderInita1f5f7c74275d47a45049a2936db1d0d', 'loadClassLoader'), true, false);
         self::$loader = $loader = new \Composer\Autoload\ClassLoader();
         spl_autoload_unregister(array('ComposerAutoloaderInita1f5f7c74275d47a45049a2936db1d0d', 'loadClassLoader'));
@@ -32,7 +34,7 @@ class ComposerAutoloaderInita1f5f7c74275d47a45049a2936db1d0d
 
         $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
         if ($useStaticLoader) {
-            require_once __DIR__ . '/autoload_static.php';
+            require __DIR__ . '/autoload_static.php';
 
             call_user_func(\Composer\Autoload\ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d::getInitializer($loader));
         } else {
index 9bca0fb94c4c97a5722eb440d610dbf2cecf5951..9c790a6ca17df33d13573e8361f2720f02588e43 100644 (file)
@@ -108,6 +108,7 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d
     );
 
     public static $classMap = array (
+        'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
         'PEAR_Exception' => __DIR__ . '/..' . '/pear/pear_exception/PEAR/Exception.php',
         'PHP_ICO' => __DIR__ . '/..' . '/chrisjean/php-ico/class-php-ico.php',
     );
index b09fe39c3e826e5f4e0cb12c8af2446249f2d968..0b1d5a156526ed82a508cecef5c2e9a9733a4ee9 100644 (file)
-[
-    {
-        "name": "chrisjean/php-ico",
-        "version": "1.0.4",
-        "version_normalized": "1.0.4.0",
-        "source": {
-            "type": "git",
-            "url": "https://github.com/chrisbliss18/php-ico.git",
-            "reference": "ccd5c0d56554f3ddcd7a823e695be83e0d1e43b6"
-        },
-        "dist": {
-            "type": "zip",
-            "url": "https://api.github.com/repos/chrisbliss18/php-ico/zipball/ccd5c0d56554f3ddcd7a823e695be83e0d1e43b6",
-            "reference": "ccd5c0d56554f3ddcd7a823e695be83e0d1e43b6",
-            "shasum": ""
-        },
-        "require": {
-            "ext-gd": "*",
-            "php": ">=5.2.4"
-        },
-        "time": "2016-09-27T22:00:56+00:00",
-        "type": "library",
-        "installation-source": "dist",
-        "autoload": {
-            "classmap": [
-                "class-php-ico.php"
-            ]
-        },
-        "notification-url": "https://packagist.org/downloads/",
-        "license": [
-            "GPL-2.0+"
-        ],
-        "authors": [
-            {
-                "name": "Chris Jean",
-                "homepage": "https://chrisjean.com",
-                "role": "Developer"
-            }
-        ],
-        "description": "An easy-to-use library to generate valid ICO files.",
-        "homepage": "https://github.com/chrisbliss18/php-ico",
-        "keywords": [
-            "favicon",
-            "ico"
-        ]
-    },
-    {
-        "name": "erusev/parsedown",
-        "version": "1.7.4",
-        "version_normalized": "1.7.4.0",
-        "source": {
-            "type": "git",
-            "url": "https://github.com/erusev/parsedown.git",
-            "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3"
-        },
-        "dist": {
-            "type": "zip",
-            "url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
-            "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
-            "shasum": ""
-        },
-        "require": {
-            "ext-mbstring": "*",
-            "php": ">=5.3.0"
-        },
-        "require-dev": {
-            "phpunit/phpunit": "^4.8.35"
-        },
-        "time": "2019-12-30T22:54:17+00:00",
-        "type": "library",
-        "installation-source": "dist",
-        "autoload": {
-            "psr-0": {
-                "Parsedown": ""
-            }
-        },
-        "notification-url": "https://packagist.org/downloads/",
-        "license": [
-            "MIT"
-        ],
-        "authors": [
-            {
-                "name": "Emanuil Rusev",
-                "email": "hello@erusev.com",
-                "homepage": "http://erusev.com"
-            }
-        ],
-        "description": "Parser for Markdown.",
-        "homepage": "http://parsedown.org",
-        "keywords": [
-            "markdown",
-            "parser"
-        ]
-    },
-    {
-        "name": "ezyang/htmlpurifier",
-        "version": "v4.13.0",
-        "version_normalized": "4.13.0.0",
-        "source": {
-            "type": "git",
-            "url": "https://github.com/ezyang/htmlpurifier.git",
-            "reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75"
-        },
-        "dist": {
-            "type": "zip",
-            "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
-            "reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
-            "shasum": ""
-        },
-        "require": {
-            "php": ">=5.2"
-        },
-        "require-dev": {
-            "simpletest/simpletest": "dev-master#72de02a7b80c6bb8864ef9bf66d41d2f58f826bd"
-        },
-        "time": "2020-06-29T00:56:53+00:00",
-        "type": "library",
-        "installation-source": "dist",
-        "autoload": {
-            "psr-0": {
-                "HTMLPurifier": "library/"
-            },
-            "files": [
-                "library/HTMLPurifier.composer.php"
-            ],
-            "exclude-from-classmap": [
-                "/library/HTMLPurifier/Language/"
-            ]
-        },
-        "notification-url": "https://packagist.org/downloads/",
-        "license": [
-            "LGPL-2.1-or-later"
-        ],
-        "authors": [
-            {
-                "name": "Edward Z. Yang",
-                "email": "admin@htmlpurifier.org",
-                "homepage": "http://ezyang.com"
-            }
-        ],
-        "description": "Standards compliant HTML filter written in PHP",
-        "homepage": "http://htmlpurifier.org/",
-        "keywords": [
-            "html"
-        ]
-    },
-    {
-        "name": "guzzlehttp/guzzle",
-        "version": "7.2.0",
-        "version_normalized": "7.2.0.0",
-        "source": {
-            "type": "git",
-            "url": "https://github.com/guzzle/guzzle.git",
-            "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79"
-        },
-        "dist": {
-            "type": "zip",
-            "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0aa74dfb41ae110835923ef10a9d803a22d50e79",
-            "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79",
-            "shasum": ""
-        },
-        "require": {
-            "ext-json": "*",
-            "guzzlehttp/promises": "^1.4",
-            "guzzlehttp/psr7": "^1.7",
-            "php": "^7.2.5 || ^8.0",
-            "psr/http-client": "^1.0"
-        },
-        "provide": {
-            "psr/http-client-implementation": "1.0"
-        },
-        "require-dev": {
-            "ext-curl": "*",
-            "php-http/client-integration-tests": "^3.0",
-            "phpunit/phpunit": "^8.5.5 || ^9.3.5",
-            "psr/log": "^1.1"
-        },
-        "suggest": {
-            "ext-curl": "Required for CURL handler support",
-            "ext-intl": "Required for Internationalized Domain Name (IDN) support",
-            "psr/log": "Required for using the Log middleware"
-        },
-        "time": "2020-10-10T11:47:56+00:00",
-        "type": "library",
-        "extra": {
-            "branch-alias": {
-                "dev-master": "7.1-dev"
-            }
-        },
-        "installation-source": "dist",
-        "autoload": {
-            "psr-4": {
-                "GuzzleHttp\\": "src/"
-            },
-            "files": [
-                "src/functions_include.php"
-            ]
-        },
-        "notification-url": "https://packagist.org/downloads/",
-        "license": [
-            "MIT"
-        ],
-        "authors": [
-            {
-                "name": "Michael Dowling",
-                "email": "mtdowling@gmail.com",
-                "homepage": "https://github.com/mtdowling"
-            },
-            {
-                "name": "Márk Sági-Kazár",
-                "email": "mark.sagikazar@gmail.com",
-                "homepage": "https://sagikazarmark.hu"
-            }
-        ],
-        "description": "Guzzle is a PHP HTTP client library",
-        "homepage": "http://guzzlephp.org/",
-        "keywords": [
-            "client",
-            "curl",
-            "framework",
-            "http",
-            "http client",
-            "psr-18",
-            "psr-7",
-            "rest",
-            "web service"
-        ],
-        "funding": [
-            {
-                "url": "https://github.com/GrahamCampbell",
-                "type": "github"
-            },
-            {
-                "url": "https://github.com/Nyholm",
-                "type": "github"
-            },
-            {
-                "url": "https://github.com/alexeyshockov",
-                "type": "github"
-            },
-            {
-                "url": "https://github.com/gmponos",
-                "type": "github"
-            }
-        ]
-    },
-    {
-        "name": "guzzlehttp/promises",
-        "version": "1.4.0",
-        "version_normalized": "1.4.0.0",
-        "source": {
-            "type": "git",
-            "url": "https://github.com/guzzle/promises.git",
-            "reference": "60d379c243457e073cff02bc323a2a86cb355631"
-        },
-        "dist": {
-            "type": "zip",
-            "url": "https://api.github.com/repos/guzzle/promises/zipball/60d379c243457e073cff02bc323a2a86cb355631",
-            "reference": "60d379c243457e073cff02bc323a2a86cb355631",
-            "shasum": ""
-        },
-        "require": {
-            "php": ">=5.5"
-        },
-        "require-dev": {
-            "symfony/phpunit-bridge": "^4.4 || ^5.1"
-        },
-        "time": "2020-09-30T07:37:28+00:00",
-        "type": "library",
-        "extra": {
-            "branch-alias": {
-                "dev-master": "1.4-dev"
-            }
-        },
-        "installation-source": "dist",
-        "autoload": {
-            "psr-4": {
-                "GuzzleHttp\\Promise\\": "src/"
-            },
-            "files": [
-                "src/functions_include.php"
-            ]
-        },
-        "notification-url": "https://packagist.org/downloads/",
-        "license": [
-            "MIT"
-        ],
-        "authors": [
-            {
-                "name": "Michael Dowling",
-                "email": "mtdowling@gmail.com",
-                "homepage": "https://github.com/mtdowling"
-            }
-        ],
-        "description": "Guzzle promises library",
-        "keywords": [
-            "promise"
-        ]
-    },
-    {
-        "name": "guzzlehttp/psr7",
-        "version": "1.7.0",
-        "version_normalized": "1.7.0.0",
-        "source": {
-            "type": "git",
-            "url": "https://github.com/guzzle/psr7.git",
-            "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3"
-        },
-        "dist": {
-            "type": "zip",
-            "url": "https://api.github.com/repos/guzzle/psr7/zipball/53330f47520498c0ae1f61f7e2c90f55690c06a3",
-            "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3",
-            "shasum": ""
-        },
-        "require": {
-            "php": ">=5.4.0",
-            "psr/http-message": "~1.0",
-            "ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
-        },
-        "provide": {
-            "psr/http-message-implementation": "1.0"
-        },
-        "require-dev": {
-            "ext-zlib": "*",
-            "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10"
-        },
-        "suggest": {
-            "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
-        },
-        "time": "2020-09-30T07:37:11+00:00",
-        "type": "library",
-        "extra": {
-            "branch-alias": {
-                "dev-master": "1.7-dev"
-            }
-        },
-        "installation-source": "dist",
-        "autoload": {
-            "psr-4": {
-                "GuzzleHttp\\Psr7\\": "src/"
-            },
-            "files": [
-                "src/functions_include.php"
-            ]
-        },
-        "notification-url": "https://packagist.org/downloads/",
-        "license": [
-            "MIT"
-        ],
-        "authors": [
-            {
-                "name": "Michael Dowling",
-                "email": "mtdowling@gmail.com",
-                "homepage": "https://github.com/mtdowling"
-            },
-            {
-                "name": "Tobias Schultze",
-                "homepage": "https://github.com/Tobion"
-            }
-        ],
-        "description": "PSR-7 message implementation that also provides common utility methods",
-        "keywords": [
-            "http",
-            "message",
-            "psr-7",
-            "request",
-            "response",
-            "stream",
-            "uri",
-            "url"
-        ]
-    },
-    {
-        "name": "pear/net_idna2",
-        "version": "v0.2.0",
-        "version_normalized": "0.2.0.0",
-        "source": {
-            "type": "git",
-            "url": "https://github.com/pear/Net_IDNA2.git",
-            "reference": "51734eaf8be2df58e8aad5835b9966459b2fb37c"
-        },
-        "dist": {
-            "type": "zip",
-            "url": "https://api.github.com/repos/pear/Net_IDNA2/zipball/51734eaf8be2df58e8aad5835b9966459b2fb37c",
-            "reference": "51734eaf8be2df58e8aad5835b9966459b2fb37c",
-            "shasum": ""
-        },
-        "require": {
-            "pear/pear_exception": "@stable"
-        },
-        "require-dev": {
-            "phpunit/phpunit": "^4"
-        },
-        "time": "2017-03-06T20:46:41+00:00",
-        "type": "library",
-        "installation-source": "dist",
-        "autoload": {
-            "psr-0": {
-                "Net": "./"
-            }
-        },
-        "notification-url": "https://packagist.org/downloads/",
-        "include-path": [
-            "./"
-        ],
-        "license": [
-            "LGPL"
-        ],
-        "authors": [
-            {
-                "name": "Stefan Neufeind",
-                "email": "pear.neufeind@speedpartner.de",
-                "role": "Lead"
-            },
-            {
-                "name": "Daniel O'Connor",
-                "email": "daniel.oconnor@gmail.com",
-                "role": "Lead"
-            }
-        ],
-        "description": "More info available on: https://pear.php.net/package/Net_IDNA2"
-    },
-    {
-        "name": "pear/pear_exception",
-        "version": "v1.0.1",
-        "version_normalized": "1.0.1.0",
-        "source": {
-            "type": "git",
-            "url": "https://github.com/pear/PEAR_Exception.git",
-            "reference": "dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7"
-        },
-        "dist": {
-            "type": "zip",
-            "url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7",
-            "reference": "dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7",
-            "shasum": ""
-        },
-        "require": {
-            "php": ">=4.4.0"
-        },
-        "require-dev": {
-            "phpunit/phpunit": "*"
-        },
-        "time": "2019-12-10T10:24:42+00:00",
-        "type": "class",
-        "extra": {
-            "branch-alias": {
-                "dev-master": "1.0.x-dev"
-            }
-        },
-        "installation-source": "dist",
-        "autoload": {
-            "classmap": [
-                "PEAR/"
-            ]
-        },
-        "notification-url": "https://packagist.org/downloads/",
-        "include-path": [
-            "."
-        ],
-        "license": [
-            "BSD-2-Clause"
-        ],
-        "authors": [
-            {
-                "name": "Helgi Thormar",
-                "email": "dufuz@php.net"
-            },
-            {
-                "name": "Greg Beaver",
-                "email": "cellog@php.net"
-            }
-        ],
-        "description": "The PEAR Exception base class.",
-        "homepage": "https://github.com/pear/PEAR_Exception",
-        "keywords": [
-            "exception"
-        ]
-    },
-    {
-        "name": "pelago/emogrifier",
-        "version": "v2.1.1",
-        "version_normalized": "2.1.1.0",
-        "source": {
-            "type": "git",
-            "url": "https://github.com/MyIntervals/emogrifier.git",
-            "reference": "8ee7fb5ad772915451ed3415c1992bd3697d4983"
-        },
-        "dist": {
-            "type": "zip",
-            "url": "https://api.github.com/repos/MyIntervals/emogrifier/zipball/8ee7fb5ad772915451ed3415c1992bd3697d4983",
-            "reference": "8ee7fb5ad772915451ed3415c1992bd3697d4983",
-            "shasum": ""
-        },
-        "require": {
-            "ext-dom": "*",
-            "ext-libxml": "*",
-            "php": "^5.5.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0",
-            "symfony/css-selector": "^3.4.0 || ^4.0.0"
-        },
-        "require-dev": {
-            "friendsofphp/php-cs-fixer": "^2.2.0",
-            "phpmd/phpmd": "^2.6.0",
-            "phpunit/phpunit": "^4.8.0",
-            "squizlabs/php_codesniffer": "^3.3.2"
-        },
-        "time": "2018-12-10T10:36:30+00:00",
-        "type": "library",
-        "extra": {
-            "branch-alias": {
-                "dev-master": "2.1.x-dev"
-            }
-        },
-        "installation-source": "dist",
-        "autoload": {
-            "psr-4": {
-                "Pelago\\": "src/"
-            }
-        },
-        "notification-url": "https://packagist.org/downloads/",
-        "license": [
-            "MIT"
-        ],
-        "authors": [
-            {
-                "name": "John Reeve",
-                "email": "jreeve@pelagodesign.com"
-            },
-            {
-                "name": "Cameron Brooks"
-            },
-            {
-                "name": "Jaime Prado"
-            },
-            {
-                "name": "Oliver Klee",
-                "email": "github@oliverklee.de"
-            },
-            {
-                "name": "Zoli Szabó",
-                "email": "zoli.szabo+github@gmail.com"
-            },
-            {
-                "name": "Jake Hotson",
-                "email": "jake@qzdesign.co.uk"
-            }
-        ],
-        "description": "Converts CSS styles into inline style attributes in your HTML code",
-        "homepage": "https://www.myintervals.com/emogrifier.php",
-        "keywords": [
-            "css",
-            "email",
-            "pre-processing"
-        ]
-    },
-    {
-        "name": "psr/http-client",
-        "version": "1.0.1",
-        "version_normalized": "1.0.1.0",
-        "source": {
-            "type": "git",
-            "url": "https://github.com/php-fig/http-client.git",
-            "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
-        },
-        "dist": {
-            "type": "zip",
-            "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
-            "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
-            "shasum": ""
-        },
-        "require": {
-            "php": "^7.0 || ^8.0",
-            "psr/http-message": "^1.0"
-        },
-        "time": "2020-06-29T06:28:15+00:00",
-        "type": "library",
-        "extra": {
-            "branch-alias": {
-                "dev-master": "1.0.x-dev"
-            }
-        },
-        "installation-source": "dist",
-        "autoload": {
-            "psr-4": {
-                "Psr\\Http\\Client\\": "src/"
-            }
-        },
-        "notification-url": "https://packagist.org/downloads/",
-        "license": [
-            "MIT"
-        ],
-        "authors": [
-            {
-                "name": "PHP-FIG",
-                "homepage": "http://www.php-fig.org/"
-            }
-        ],
-        "description": "Common interface for HTTP clients",
-        "homepage": "https://github.com/php-fig/http-client",
-        "keywords": [
-            "http",
-            "http-client",
-            "psr",
-            "psr-18"
-        ]
-    },
-    {
-        "name": "psr/http-message",
-        "version": "1.0.1",
-        "version_normalized": "1.0.1.0",
-        "source": {
-            "type": "git",
-            "url": "https://github.com/php-fig/http-message.git",
-            "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
-        },
-        "dist": {
-            "type": "zip",
-            "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
-            "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
-            "shasum": ""
-        },
-        "require": {
-            "php": ">=5.3.0"
-        },
-        "time": "2016-08-06T14:39:51+00:00",
-        "type": "library",
-        "extra": {
-            "branch-alias": {
-                "dev-master": "1.0.x-dev"
-            }
-        },
-        "installation-source": "dist",
-        "autoload": {
-            "psr-4": {
-                "Psr\\Http\\Message\\": "src/"
-            }
-        },
-        "notification-url": "https://packagist.org/downloads/",
-        "license": [
-            "MIT"
-        ],
-        "authors": [
-            {
-                "name": "PHP-FIG",
-                "homepage": "http://www.php-fig.org/"
-            }
-        ],
-        "description": "Common interface for HTTP messages",
-        "homepage": "https://github.com/php-fig/http-message",
-        "keywords": [
-            "http",
-            "http-message",
-            "psr",
-            "psr-7",
-            "request",
-            "response"
-        ]
-    },
-    {
-        "name": "ralouphie/getallheaders",
-        "version": "3.0.3",
-        "version_normalized": "3.0.3.0",
-        "source": {
-            "type": "git",
-            "url": "https://github.com/ralouphie/getallheaders.git",
-            "reference": "120b605dfeb996808c31b6477290a714d356e822"
-        },
-        "dist": {
-            "type": "zip",
-            "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
-            "reference": "120b605dfeb996808c31b6477290a714d356e822",
-            "shasum": ""
-        },
-        "require": {
-            "php": ">=5.6"
-        },
-        "require-dev": {
-            "php-coveralls/php-coveralls": "^2.1",
-            "phpunit/phpunit": "^5 || ^6.5"
-        },
-        "time": "2019-03-08T08:55:37+00:00",
-        "type": "library",
-        "installation-source": "dist",
-        "autoload": {
-            "files": [
-                "src/getallheaders.php"
-            ]
-        },
-        "notification-url": "https://packagist.org/downloads/",
-        "license": [
-            "MIT"
-        ],
-        "authors": [
-            {
-                "name": "Ralph Khattar",
-                "email": "ralph.khattar@gmail.com"
-            }
-        ],
-        "description": "A polyfill for getallheaders."
-    },
-    {
-        "name": "scssphp/scssphp",
-        "version": "1.2.1",
-        "version_normalized": "1.2.1.0",
-        "source": {
-            "type": "git",
-            "url": "https://github.com/scssphp/scssphp.git",
-            "reference": "a05ea68160b7286ebbfd6e5fd7ae9e1a946ad6e1"
-        },
-        "dist": {
-            "type": "zip",
-            "url": "https://api.github.com/repos/scssphp/scssphp/zipball/a05ea68160b7286ebbfd6e5fd7ae9e1a946ad6e1",
-            "reference": "a05ea68160b7286ebbfd6e5fd7ae9e1a946ad6e1",
-            "shasum": ""
-        },
-        "require": {
-            "ext-ctype": "*",
-            "ext-json": "*",
-            "php": ">=5.6.0"
-        },
-        "require-dev": {
-            "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.3",
-            "sass/sass-spec": "2020.08.10",
-            "squizlabs/php_codesniffer": "~3.5",
-            "twbs/bootstrap": "~4.3",
-            "zurb/foundation": "~6.5"
-        },
-        "time": "2020-09-07T21:15:42+00:00",
-        "bin": [
-            "bin/pscss"
-        ],
-        "type": "library",
-        "installation-source": "dist",
-        "autoload": {
-            "psr-4": {
-                "ScssPhp\\ScssPhp\\": "src/"
-            }
-        },
-        "notification-url": "https://packagist.org/downloads/",
-        "license": [
-            "MIT"
-        ],
-        "authors": [
-            {
-                "name": "Anthon Pang",
-                "email": "apang@softwaredevelopment.ca",
-                "homepage": "https://github.com/robocoder"
-            },
-            {
-                "name": "Cédric Morin",
-                "email": "cedric@yterium.com",
-                "homepage": "https://github.com/Cerdic"
-            }
-        ],
-        "description": "scssphp is a compiler for SCSS written in PHP.",
-        "homepage": "http://scssphp.github.io/scssphp/",
-        "keywords": [
-            "css",
-            "less",
-            "sass",
-            "scss",
-            "stylesheet"
-        ]
-    },
-    {
-        "name": "symfony/css-selector",
-        "version": "v4.4.15",
-        "version_normalized": "4.4.15.0",
-        "source": {
-            "type": "git",
-            "url": "https://github.com/symfony/css-selector.git",
-            "reference": "bf17dc9f6ce144e41f786c32435feea4d8e11dcc"
-        },
-        "dist": {
-            "type": "zip",
-            "url": "https://api.github.com/repos/symfony/css-selector/zipball/bf17dc9f6ce144e41f786c32435feea4d8e11dcc",
-            "reference": "bf17dc9f6ce144e41f786c32435feea4d8e11dcc",
-            "shasum": ""
-        },
-        "require": {
-            "php": ">=7.1.3"
-        },
-        "time": "2020-07-05T09:39:30+00:00",
-        "type": "library",
-        "extra": {
-            "branch-alias": {
-                "dev-master": "4.4-dev"
-            }
-        },
-        "installation-source": "dist",
-        "autoload": {
-            "psr-4": {
-                "Symfony\\Component\\CssSelector\\": ""
-            },
-            "exclude-from-classmap": [
-                "/Tests/"
-            ]
-        },
-        "notification-url": "https://packagist.org/downloads/",
-        "license": [
-            "MIT"
-        ],
-        "authors": [
-            {
-                "name": "Fabien Potencier",
-                "email": "fabien@symfony.com"
-            },
-            {
-                "name": "Jean-François Simon",
-                "email": "jeanfrancois.simon@sensiolabs.com"
-            },
-            {
-                "name": "Symfony Community",
-                "homepage": "https://symfony.com/contributors"
-            }
-        ],
-        "description": "Symfony CssSelector Component",
-        "homepage": "https://symfony.com",
-        "funding": [
-            {
-                "url": "https://symfony.com/sponsor",
-                "type": "custom"
-            },
-            {
-                "url": "https://github.com/fabpot",
-                "type": "github"
-            },
-            {
-                "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
-                "type": "tidelift"
-            }
-        ]
-    },
-    {
-        "name": "symfony/polyfill-mbstring",
-        "version": "v1.18.1",
-        "version_normalized": "1.18.1.0",
-        "source": {
-            "type": "git",
-            "url": "https://github.com/symfony/polyfill-mbstring.git",
-            "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a"
-        },
-        "dist": {
-            "type": "zip",
-            "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a6977d63bf9a0ad4c65cd352709e230876f9904a",
-            "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a",
-            "shasum": ""
-        },
-        "require": {
-            "php": ">=5.3.3"
-        },
-        "suggest": {
-            "ext-mbstring": "For best performance"
-        },
-        "time": "2020-07-14T12:35:20+00:00",
-        "type": "library",
-        "extra": {
-            "branch-alias": {
-                "dev-master": "1.18-dev"
-            },
-            "thanks": {
-                "name": "symfony/polyfill",
-                "url": "https://github.com/symfony/polyfill"
-            }
-        },
-        "installation-source": "dist",
-        "autoload": {
-            "psr-4": {
-                "Symfony\\Polyfill\\Mbstring\\": ""
-            },
-            "files": [
-                "bootstrap.php"
-            ]
-        },
-        "notification-url": "https://packagist.org/downloads/",
-        "license": [
-            "MIT"
-        ],
-        "authors": [
-            {
-                "name": "Nicolas Grekas",
-                "email": "p@tchwork.com"
-            },
-            {
-                "name": "Symfony Community",
-                "homepage": "https://symfony.com/contributors"
-            }
-        ],
-        "description": "Symfony polyfill for the Mbstring extension",
-        "homepage": "https://symfony.com",
-        "keywords": [
-            "compatibility",
-            "mbstring",
-            "polyfill",
-            "portable",
-            "shim"
-        ],
-        "funding": [
-            {
-                "url": "https://symfony.com/sponsor",
-                "type": "custom"
-            },
-            {
-                "url": "https://github.com/fabpot",
-                "type": "github"
-            },
-            {
-                "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
-                "type": "tidelift"
-            }
-        ]
-    },
-    {
-        "name": "true/punycode",
-        "version": "v2.1.1",
-        "version_normalized": "2.1.1.0",
-        "source": {
-            "type": "git",
-            "url": "https://github.com/true/php-punycode.git",
-            "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e"
-        },
-        "dist": {
-            "type": "zip",
-            "url": "https://api.github.com/repos/true/php-punycode/zipball/a4d0c11a36dd7f4e7cd7096076cab6d3378a071e",
-            "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e",
-            "shasum": ""
-        },
-        "require": {
-            "php": ">=5.3.0",
-            "symfony/polyfill-mbstring": "^1.3"
-        },
-        "require-dev": {
-            "phpunit/phpunit": "~4.7",
-            "squizlabs/php_codesniffer": "~2.0"
-        },
-        "time": "2016-11-16T10:37:54+00:00",
-        "type": "library",
-        "installation-source": "dist",
-        "autoload": {
-            "psr-4": {
-                "TrueBV\\": "src/"
-            }
-        },
-        "notification-url": "https://packagist.org/downloads/",
-        "license": [
-            "MIT"
-        ],
-        "authors": [
-            {
-                "name": "Renan Gonçalves",
-                "email": "renan.saddam@gmail.com"
-            }
-        ],
-        "description": "A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)",
-        "homepage": "https://github.com/true/php-punycode",
-        "keywords": [
-            "idna",
-            "punycode"
-        ]
-    }
-]
+{
+    "packages": [
+        {
+            "name": "chrisjean/php-ico",
+            "version": "1.0.4",
+            "version_normalized": "1.0.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/chrisbliss18/php-ico.git",
+                "reference": "ccd5c0d56554f3ddcd7a823e695be83e0d1e43b6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/chrisbliss18/php-ico/zipball/ccd5c0d56554f3ddcd7a823e695be83e0d1e43b6",
+                "reference": "ccd5c0d56554f3ddcd7a823e695be83e0d1e43b6",
+                "shasum": ""
+            },
+            "require": {
+                "ext-gd": "*",
+                "php": ">=5.2.4"
+            },
+            "time": "2016-09-27T22:00:56+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "class-php-ico.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "GPL-2.0+"
+            ],
+            "authors": [
+                {
+                    "name": "Chris Jean",
+                    "homepage": "https://chrisjean.com",
+                    "role": "Developer"
+                }
+            ],
+            "description": "An easy-to-use library to generate valid ICO files.",
+            "homepage": "https://github.com/chrisbliss18/php-ico",
+            "keywords": [
+                "favicon",
+                "ico"
+            ],
+            "install-path": "../chrisjean/php-ico"
+        },
+        {
+            "name": "erusev/parsedown",
+            "version": "1.7.4",
+            "version_normalized": "1.7.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/erusev/parsedown.git",
+                "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
+                "reference": "cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
+                "shasum": ""
+            },
+            "require": {
+                "ext-mbstring": "*",
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8.35"
+            },
+            "time": "2019-12-30T22:54:17+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-0": {
+                    "Parsedown": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Emanuil Rusev",
+                    "email": "hello@erusev.com",
+                    "homepage": "http://erusev.com"
+                }
+            ],
+            "description": "Parser for Markdown.",
+            "homepage": "http://parsedown.org",
+            "keywords": [
+                "markdown",
+                "parser"
+            ],
+            "install-path": "../erusev/parsedown"
+        },
+        {
+            "name": "ezyang/htmlpurifier",
+            "version": "v4.13.0",
+            "version_normalized": "4.13.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ezyang/htmlpurifier.git",
+                "reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
+                "reference": "08e27c97e4c6ed02f37c5b2b20488046c8d90d75",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.2"
+            },
+            "require-dev": {
+                "simpletest/simpletest": "dev-master#72de02a7b80c6bb8864ef9bf66d41d2f58f826bd"
+            },
+            "time": "2020-06-29T00:56:53+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-0": {
+                    "HTMLPurifier": "library/"
+                },
+                "files": [
+                    "library/HTMLPurifier.composer.php"
+                ],
+                "exclude-from-classmap": [
+                    "/library/HTMLPurifier/Language/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "LGPL-2.1-or-later"
+            ],
+            "authors": [
+                {
+                    "name": "Edward Z. Yang",
+                    "email": "admin@htmlpurifier.org",
+                    "homepage": "http://ezyang.com"
+                }
+            ],
+            "description": "Standards compliant HTML filter written in PHP",
+            "homepage": "http://htmlpurifier.org/",
+            "keywords": [
+                "html"
+            ],
+            "install-path": "../ezyang/htmlpurifier"
+        },
+        {
+            "name": "guzzlehttp/guzzle",
+            "version": "7.2.0",
+            "version_normalized": "7.2.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/guzzle.git",
+                "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0aa74dfb41ae110835923ef10a9d803a22d50e79",
+                "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "guzzlehttp/promises": "^1.4",
+                "guzzlehttp/psr7": "^1.7",
+                "php": "^7.2.5 || ^8.0",
+                "psr/http-client": "^1.0"
+            },
+            "provide": {
+                "psr/http-client-implementation": "1.0"
+            },
+            "require-dev": {
+                "ext-curl": "*",
+                "php-http/client-integration-tests": "^3.0",
+                "phpunit/phpunit": "^8.5.5 || ^9.3.5",
+                "psr/log": "^1.1"
+            },
+            "suggest": {
+                "ext-curl": "Required for CURL handler support",
+                "ext-intl": "Required for Internationalized Domain Name (IDN) support",
+                "psr/log": "Required for using the Log middleware"
+            },
+            "time": "2020-10-10T11:47:56+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "7.1-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\": "src/"
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "Márk Sági-Kazár",
+                    "email": "mark.sagikazar@gmail.com",
+                    "homepage": "https://sagikazarmark.hu"
+                }
+            ],
+            "description": "Guzzle is a PHP HTTP client library",
+            "homepage": "http://guzzlephp.org/",
+            "keywords": [
+                "client",
+                "curl",
+                "framework",
+                "http",
+                "http client",
+                "psr-18",
+                "psr-7",
+                "rest",
+                "web service"
+            ],
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/Nyholm",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/alexeyshockov",
+                    "type": "github"
+                },
+                {
+                    "url": "https://github.com/gmponos",
+                    "type": "github"
+                }
+            ],
+            "install-path": "../guzzlehttp/guzzle"
+        },
+        {
+            "name": "guzzlehttp/promises",
+            "version": "1.4.0",
+            "version_normalized": "1.4.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/promises.git",
+                "reference": "60d379c243457e073cff02bc323a2a86cb355631"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/promises/zipball/60d379c243457e073cff02bc323a2a86cb355631",
+                "reference": "60d379c243457e073cff02bc323a2a86cb355631",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5"
+            },
+            "require-dev": {
+                "symfony/phpunit-bridge": "^4.4 || ^5.1"
+            },
+            "time": "2020-09-30T07:37:28+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\Promise\\": "src/"
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                }
+            ],
+            "description": "Guzzle promises library",
+            "keywords": [
+                "promise"
+            ],
+            "install-path": "../guzzlehttp/promises"
+        },
+        {
+            "name": "guzzlehttp/psr7",
+            "version": "1.7.0",
+            "version_normalized": "1.7.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/psr7.git",
+                "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/53330f47520498c0ae1f61f7e2c90f55690c06a3",
+                "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4.0",
+                "psr/http-message": "~1.0",
+                "ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
+            },
+            "provide": {
+                "psr/http-message-implementation": "1.0"
+            },
+            "require-dev": {
+                "ext-zlib": "*",
+                "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10"
+            },
+            "suggest": {
+                "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
+            },
+            "time": "2020-09-30T07:37:11+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.7-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\Psr7\\": "src/"
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "Tobias Schultze",
+                    "homepage": "https://github.com/Tobion"
+                }
+            ],
+            "description": "PSR-7 message implementation that also provides common utility methods",
+            "keywords": [
+                "http",
+                "message",
+                "psr-7",
+                "request",
+                "response",
+                "stream",
+                "uri",
+                "url"
+            ],
+            "install-path": "../guzzlehttp/psr7"
+        },
+        {
+            "name": "pear/net_idna2",
+            "version": "v0.2.0",
+            "version_normalized": "0.2.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/pear/Net_IDNA2.git",
+                "reference": "51734eaf8be2df58e8aad5835b9966459b2fb37c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/pear/Net_IDNA2/zipball/51734eaf8be2df58e8aad5835b9966459b2fb37c",
+                "reference": "51734eaf8be2df58e8aad5835b9966459b2fb37c",
+                "shasum": ""
+            },
+            "require": {
+                "pear/pear_exception": "@stable"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4"
+            },
+            "time": "2017-03-06T20:46:41+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-0": {
+                    "Net": "./"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "include-path": [
+                "./"
+            ],
+            "license": [
+                "LGPL"
+            ],
+            "authors": [
+                {
+                    "name": "Stefan Neufeind",
+                    "email": "pear.neufeind@speedpartner.de",
+                    "role": "Lead"
+                },
+                {
+                    "name": "Daniel O'Connor",
+                    "email": "daniel.oconnor@gmail.com",
+                    "role": "Lead"
+                }
+            ],
+            "description": "More info available on: https://pear.php.net/package/Net_IDNA2",
+            "install-path": "../pear/net_idna2"
+        },
+        {
+            "name": "pear/pear_exception",
+            "version": "v1.0.1",
+            "version_normalized": "1.0.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/pear/PEAR_Exception.git",
+                "reference": "dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7",
+                "reference": "dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=4.4.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "*"
+            },
+            "time": "2019-12-10T10:24:42+00:00",
+            "type": "class",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "classmap": [
+                    "PEAR/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "include-path": [
+                "."
+            ],
+            "license": [
+                "BSD-2-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Helgi Thormar",
+                    "email": "dufuz@php.net"
+                },
+                {
+                    "name": "Greg Beaver",
+                    "email": "cellog@php.net"
+                }
+            ],
+            "description": "The PEAR Exception base class.",
+            "homepage": "https://github.com/pear/PEAR_Exception",
+            "keywords": [
+                "exception"
+            ],
+            "install-path": "../pear/pear_exception"
+        },
+        {
+            "name": "pelago/emogrifier",
+            "version": "v2.1.1",
+            "version_normalized": "2.1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/MyIntervals/emogrifier.git",
+                "reference": "8ee7fb5ad772915451ed3415c1992bd3697d4983"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/MyIntervals/emogrifier/zipball/8ee7fb5ad772915451ed3415c1992bd3697d4983",
+                "reference": "8ee7fb5ad772915451ed3415c1992bd3697d4983",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-libxml": "*",
+                "php": "^5.5.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0",
+                "symfony/css-selector": "^3.4.0 || ^4.0.0"
+            },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^2.2.0",
+                "phpmd/phpmd": "^2.6.0",
+                "phpunit/phpunit": "^4.8.0",
+                "squizlabs/php_codesniffer": "^3.3.2"
+            },
+            "time": "2018-12-10T10:36:30+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.1.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Pelago\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "John Reeve",
+                    "email": "jreeve@pelagodesign.com"
+                },
+                {
+                    "name": "Cameron Brooks"
+                },
+                {
+                    "name": "Jaime Prado"
+                },
+                {
+                    "name": "Oliver Klee",
+                    "email": "github@oliverklee.de"
+                },
+                {
+                    "name": "Zoli Szabó",
+                    "email": "zoli.szabo+github@gmail.com"
+                },
+                {
+                    "name": "Jake Hotson",
+                    "email": "jake@qzdesign.co.uk"
+                }
+            ],
+            "description": "Converts CSS styles into inline style attributes in your HTML code",
+            "homepage": "https://www.myintervals.com/emogrifier.php",
+            "keywords": [
+                "css",
+                "email",
+                "pre-processing"
+            ],
+            "install-path": "../pelago/emogrifier"
+        },
+        {
+            "name": "psr/http-client",
+            "version": "1.0.1",
+            "version_normalized": "1.0.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-client.git",
+                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0 || ^8.0",
+                "psr/http-message": "^1.0"
+            },
+            "time": "2020-06-29T06:28:15+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Client\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP clients",
+            "homepage": "https://github.com/php-fig/http-client",
+            "keywords": [
+                "http",
+                "http-client",
+                "psr",
+                "psr-18"
+            ],
+            "install-path": "../psr/http-client"
+        },
+        {
+            "name": "psr/http-message",
+            "version": "1.0.1",
+            "version_normalized": "1.0.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-message.git",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "time": "2016-08-06T14:39:51+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP messages",
+            "homepage": "https://github.com/php-fig/http-message",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "install-path": "../psr/http-message"
+        },
+        {
+            "name": "ralouphie/getallheaders",
+            "version": "3.0.3",
+            "version_normalized": "3.0.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ralouphie/getallheaders.git",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.6"
+            },
+            "require-dev": {
+                "php-coveralls/php-coveralls": "^2.1",
+                "phpunit/phpunit": "^5 || ^6.5"
+            },
+            "time": "2019-03-08T08:55:37+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "files": [
+                    "src/getallheaders.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ralph Khattar",
+                    "email": "ralph.khattar@gmail.com"
+                }
+            ],
+            "description": "A polyfill for getallheaders.",
+            "install-path": "../ralouphie/getallheaders"
+        },
+        {
+            "name": "scssphp/scssphp",
+            "version": "1.3",
+            "version_normalized": "1.3.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/scssphp/scssphp.git",
+                "reference": "261cd018025d5790e135a1e5b694d6af186e6bca"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/scssphp/scssphp/zipball/261cd018025d5790e135a1e5b694d6af186e6bca",
+                "reference": "261cd018025d5790e135a1e5b694d6af186e6bca",
+                "shasum": ""
+            },
+            "require": {
+                "ext-ctype": "*",
+                "ext-json": "*",
+                "php": ">=5.6.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.3 || ^9.4",
+                "sass/sass-spec": "2020.08.10",
+                "squizlabs/php_codesniffer": "~3.5",
+                "twbs/bootstrap": "~4.3",
+                "zurb/foundation": "~6.5"
+            },
+            "time": "2020-10-29T11:09:57+00:00",
+            "bin": [
+                "bin/pscss"
+            ],
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "ScssPhp\\ScssPhp\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Anthon Pang",
+                    "email": "apang@softwaredevelopment.ca",
+                    "homepage": "https://github.com/robocoder"
+                },
+                {
+                    "name": "Cédric Morin",
+                    "email": "cedric@yterium.com",
+                    "homepage": "https://github.com/Cerdic"
+                }
+            ],
+            "description": "scssphp is a compiler for SCSS written in PHP.",
+            "homepage": "http://scssphp.github.io/scssphp/",
+            "keywords": [
+                "css",
+                "less",
+                "sass",
+                "scss",
+                "stylesheet"
+            ],
+            "support": {
+                "issues": "https://github.com/scssphp/scssphp/issues",
+                "source": "https://github.com/scssphp/scssphp/tree/1.3"
+            },
+            "install-path": "../scssphp/scssphp"
+        },
+        {
+            "name": "symfony/css-selector",
+            "version": "v4.4.15",
+            "version_normalized": "4.4.15.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/css-selector.git",
+                "reference": "bf17dc9f6ce144e41f786c32435feea4d8e11dcc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/css-selector/zipball/bf17dc9f6ce144e41f786c32435feea4d8e11dcc",
+                "reference": "bf17dc9f6ce144e41f786c32435feea4d8e11dcc",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1.3"
+            },
+            "time": "2020-07-05T09:39:30+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.4-dev"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\CssSelector\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Jean-François Simon",
+                    "email": "jeanfrancois.simon@sensiolabs.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony CssSelector Component",
+            "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/css-selector"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.18.1",
+            "version_normalized": "1.18.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a6977d63bf9a0ad4c65cd352709e230876f9904a",
+                "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "time": "2020-07-14T12:35:20+00:00",
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.18-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "install-path": "../symfony/polyfill-mbstring"
+        },
+        {
+            "name": "true/punycode",
+            "version": "v2.1.1",
+            "version_normalized": "2.1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/true/php-punycode.git",
+                "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/true/php-punycode/zipball/a4d0c11a36dd7f4e7cd7096076cab6d3378a071e",
+                "reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0",
+                "symfony/polyfill-mbstring": "^1.3"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.7",
+                "squizlabs/php_codesniffer": "~2.0"
+            },
+            "time": "2016-11-16T10:37:54+00:00",
+            "type": "library",
+            "installation-source": "dist",
+            "autoload": {
+                "psr-4": {
+                    "TrueBV\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Renan Gonçalves",
+                    "email": "renan.saddam@gmail.com"
+                }
+            ],
+            "description": "A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)",
+            "homepage": "https://github.com/true/php-punycode",
+            "keywords": [
+                "idna",
+                "punycode"
+            ],
+            "install-path": "../true/punycode"
+        }
+    ],
+    "dev": false
+}
diff --git a/wcfsetup/install/files/lib/system/api/composer/installed.php b/wcfsetup/install/files/lib/system/api/composer/installed.php
new file mode 100644 (file)
index 0000000..e234b2e
--- /dev/null
@@ -0,0 +1,182 @@
+<?php return array (
+  'root' => 
+  array (
+    'pretty_version' => 'dev-master',
+    'version' => 'dev-master',
+    'aliases' => 
+    array (
+    ),
+    'reference' => 'f448efb8521534c520f84e89a1a38296d4c0e5fd',
+    'name' => '__root__',
+  ),
+  'versions' => 
+  array (
+    '__root__' => 
+    array (
+      'pretty_version' => 'dev-master',
+      'version' => 'dev-master',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'f448efb8521534c520f84e89a1a38296d4c0e5fd',
+    ),
+    'chrisjean/php-ico' => 
+    array (
+      'pretty_version' => '1.0.4',
+      'version' => '1.0.4.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'ccd5c0d56554f3ddcd7a823e695be83e0d1e43b6',
+    ),
+    'erusev/parsedown' => 
+    array (
+      'pretty_version' => '1.7.4',
+      'version' => '1.7.4.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'cb17b6477dfff935958ba01325f2e8a2bfa6dab3',
+    ),
+    'ezyang/htmlpurifier' => 
+    array (
+      'pretty_version' => 'v4.13.0',
+      'version' => '4.13.0.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '08e27c97e4c6ed02f37c5b2b20488046c8d90d75',
+    ),
+    'guzzlehttp/guzzle' => 
+    array (
+      'pretty_version' => '7.2.0',
+      'version' => '7.2.0.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '0aa74dfb41ae110835923ef10a9d803a22d50e79',
+    ),
+    'guzzlehttp/promises' => 
+    array (
+      'pretty_version' => '1.4.0',
+      'version' => '1.4.0.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '60d379c243457e073cff02bc323a2a86cb355631',
+    ),
+    'guzzlehttp/psr7' => 
+    array (
+      'pretty_version' => '1.7.0',
+      'version' => '1.7.0.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '53330f47520498c0ae1f61f7e2c90f55690c06a3',
+    ),
+    'pear/net_idna2' => 
+    array (
+      'pretty_version' => 'v0.2.0',
+      'version' => '0.2.0.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '51734eaf8be2df58e8aad5835b9966459b2fb37c',
+    ),
+    'pear/pear_exception' => 
+    array (
+      'pretty_version' => 'v1.0.1',
+      'version' => '1.0.1.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7',
+    ),
+    'pelago/emogrifier' => 
+    array (
+      'pretty_version' => 'v2.1.1',
+      'version' => '2.1.1.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '8ee7fb5ad772915451ed3415c1992bd3697d4983',
+    ),
+    'psr/http-client' => 
+    array (
+      'pretty_version' => '1.0.1',
+      'version' => '1.0.1.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '2dfb5f6c5eff0e91e20e913f8c5452ed95b86621',
+    ),
+    'psr/http-client-implementation' => 
+    array (
+      'provided' => 
+      array (
+        0 => '1.0',
+      ),
+    ),
+    'psr/http-message' => 
+    array (
+      'pretty_version' => '1.0.1',
+      'version' => '1.0.1.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'f6561bf28d520154e4b0ec72be95418abe6d9363',
+    ),
+    'psr/http-message-implementation' => 
+    array (
+      'provided' => 
+      array (
+        0 => '1.0',
+      ),
+    ),
+    'ralouphie/getallheaders' => 
+    array (
+      'pretty_version' => '3.0.3',
+      'version' => '3.0.3.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '120b605dfeb996808c31b6477290a714d356e822',
+    ),
+    'scssphp/scssphp' => 
+    array (
+      'pretty_version' => '1.3',
+      'version' => '1.3.0.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => '261cd018025d5790e135a1e5b694d6af186e6bca',
+    ),
+    'symfony/css-selector' => 
+    array (
+      'pretty_version' => 'v4.4.15',
+      'version' => '4.4.15.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'bf17dc9f6ce144e41f786c32435feea4d8e11dcc',
+    ),
+    'symfony/polyfill-mbstring' => 
+    array (
+      'pretty_version' => 'v1.18.1',
+      'version' => '1.18.1.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'a6977d63bf9a0ad4c65cd352709e230876f9904a',
+    ),
+    'true/punycode' => 
+    array (
+      'pretty_version' => 'v2.1.1',
+      'version' => '2.1.1.0',
+      'aliases' => 
+      array (
+      ),
+      'reference' => 'a4d0c11a36dd7f4e7cd7096076cab6d3378a071e',
+    ),
+  ),
+);
diff --git a/wcfsetup/install/files/lib/system/api/composer/platform_check.php b/wcfsetup/install/files/lib/system/api/composer/platform_check.php
new file mode 100644 (file)
index 0000000..1e56f6e
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+
+// platform_check.php @generated by Composer
+
+$issues = array();
+
+if (!(PHP_VERSION_ID >= 70205)) {
+    $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.5". You are running ' . PHP_VERSION  .  '.';
+}
+
+$missingExtensions = array();
+extension_loaded('ctype') || $missingExtensions[] = 'ctype';
+extension_loaded('dom') || $missingExtensions[] = 'dom';
+extension_loaded('gd') || $missingExtensions[] = 'gd';
+extension_loaded('json') || $missingExtensions[] = 'json';
+extension_loaded('libxml') || $missingExtensions[] = 'libxml';
+extension_loaded('mbstring') || $missingExtensions[] = 'mbstring';
+
+if ($missingExtensions) {
+    $issues[] = 'Your Composer dependencies require the following PHP extensions to be installed: ' . implode(', ', $missingExtensions);
+}
+
+if ($issues) {
+    echo 'Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues);
+    exit(104);
+}
index cd5fce1de217a7a11d451edd20fe90bf7cf44f04..0b485195e3fedc730721fd4dd73b27112c952bd5 100644 (file)
@@ -1,7 +1,7 @@
 # scssphp
 ### <https://scssphp.github.io/scssphp>
 
-[![Build](https://travis-ci.org/scssphp/scssphp.svg?branch=master)](https://travis-ci.org/scssphp/scssphp)
+[![Build](https://travis-ci.com/scssphp/scssphp.svg?branch=master)](https://travis-ci.com/scssphp/scssphp)
 [![License](https://poser.pugx.org/scssphp/scssphp/license)](https://packagist.org/packages/scssphp/scssphp)
 
 `scssphp` is a compiler for SCSS written in PHP.
index d48af2f87830467cee7122dd2fbe110d5c542454..b3c8a735d6e6196778b934fcb255a81d7130a5c9 100644 (file)
@@ -28,8 +28,6 @@ $loadPaths = null;
 $dumpTree = false;
 $inputFile = null;
 $changeDir = false;
-$debugInfo = false;
-$lineNumbers = false;
 $encoding = false;
 $sourceMap = false;
 
@@ -71,10 +69,10 @@ Options include:
 
     --help              Show this message [-h, -?]
     --continue-on-error [deprecated] Ignored
-    --debug-info        Annotate selectors with CSS referring to the source file and line number [-g]
+    --debug-info        [deprecated] Ignored [-g]
     --dump-tree         Dump formatted parse tree [-T]
     --iso8859-1         Use iso8859-1 encoding instead of default utf-8
-    --line-numbers      Annotate selectors with comments referring to the source file and line number [--line-comments]
+    --line-numbers      [deprecated] Ignored [--line-comments]
     --load-path=PATH    Set import path [-I]
     --precision=N       [deprecated] Ignored. (default 10) [-p]
     --sourcemap         Create source map file
@@ -95,8 +93,9 @@ EOT;
         continue;
     }
 
+    // Keep parsing it to avoid BC breaks for scripts using it
     if ($argv[$i] === '-g' || $argv[$i] === '--debug-info') {
-        $debugInfo = true;
+        // TODO report it as a warning ?
         continue;
     }
 
@@ -105,8 +104,9 @@ EOT;
         continue;
     }
 
+    // Keep parsing it to avoid BC breaks for scripts using it
     if ($argv[$i] === '--line-numbers' || $argv[$i] === '--line-comments') {
-        $lineNumbers = true;
+        // TODO report it as a warning ?
         continue;
     }
 
@@ -177,14 +177,6 @@ if ($dumpTree) {
 
 $scss = new Compiler();
 
-if ($debugInfo) {
-    $scss->setLineNumberStyle(Compiler::DEBUG_INFO);
-}
-
-if ($lineNumbers) {
-    $scss->setLineNumberStyle(Compiler::LINE_COMMENTS);
-}
-
 if ($loadPaths) {
     $scss->setImportPaths(explode(PATH_SEPARATOR, $loadPaths));
 }
index 7f420edf3d45d24a937878549f7551b28aa52843..6ac13125a8626b8cdf01bc93f3ef805592b97970 100644 (file)
@@ -31,9 +31,9 @@
         "ext-ctype": "*"
     },
     "require-dev": {
+        "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.3 || ^9.4",
         "sass/sass-spec": "2020.08.10",
         "squizlabs/php_codesniffer": "~3.5",
-        "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.3",
         "twbs/bootstrap": "~4.3",
         "zurb/foundation": "~6.5"
     },
             }
         }
     ],
-    "minimum-stability": "dev",
     "bin": ["bin/pscss"],
-    "archive": {
-        "exclude": [
-            "/Makefile",
-            "/.gitattributes",
-            "/.gitignore",
-            "/.travis.yml",
-            "/phpunit.xml.dist",
-            "/tests"
-        ]
+    "config": {
+        "sort-packages": true
     }
 }
index abbd9d21a6438dcfa28361efada17212d744e8e2..96532aa6f49d04f34fde236195ea130c939ed1dd 100644 (file)
@@ -12,6 +12,7 @@ if (! class_exists('ScssPhp\ScssPhp\Version', false)) {
     include_once __DIR__ . '/src/Compiler.php';
     include_once __DIR__ . '/src/Compiler/Environment.php';
     include_once __DIR__ . '/src/Exception/SassException.php';
+    include_once __DIR__ . '/src/Exception/SassScriptException.php';
     include_once __DIR__ . '/src/Exception/CompilerException.php';
     include_once __DIR__ . '/src/Exception/ParserException.php';
     include_once __DIR__ . '/src/Exception/RangeException.php';
index 9eb8dec17ad370753d6bbe3784d77b6cedd87581..2840cd5db40240e98d500b7a25b4e91a2676a6a9 100644 (file)
@@ -18,8 +18,10 @@ use ScssPhp\ScssPhp\Cache;
 use ScssPhp\ScssPhp\Colors;
 use ScssPhp\ScssPhp\Compiler\Environment;
 use ScssPhp\ScssPhp\Exception\CompilerException;
+use ScssPhp\ScssPhp\Exception\SassScriptException;
 use ScssPhp\ScssPhp\Formatter\OutputBlock;
 use ScssPhp\ScssPhp\Node;
+use ScssPhp\ScssPhp\Node\Number;
 use ScssPhp\ScssPhp\SourceMap\SourceMapGenerator;
 use ScssPhp\ScssPhp\Type;
 use ScssPhp\ScssPhp\Parser;
@@ -59,12 +61,30 @@ use ScssPhp\ScssPhp\Util;
  */
 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;
@@ -88,7 +108,6 @@ class Compiler
 
         '<='  => 'lte',
         '>='  => 'gte',
-        '<=>' => 'cmp',
     ];
 
     /**
@@ -102,7 +121,9 @@ class Compiler
 
     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, '', []];
@@ -127,6 +148,9 @@ class Compiler
     ];
 
     protected $encoding = null;
+    /**
+     * @deprecated
+     */
     protected $lineNumberStyle = null;
 
     protected $sourceMap = self::SOURCE_MAP_NONE;
@@ -259,46 +283,50 @@ class Compiler
         $this->shouldEvaluate = null;
         $this->ignoreCallStackMessage = false;
 
-        $this->parser = $this->parserFactory($path);
-        $tree         = $this->parser->parse($code);
-        $this->parser = null;
+        try {
+            $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->formatter = new $this->formatter();
+            $this->rootBlock = null;
+            $this->rootEnv   = $this->pushEnv($tree);
 
-        $this->injectVariables($this->registeredVars);
-        $this->compileRoot($tree);
-        $this->popEnv();
+            $this->injectVariables($this->registeredVars);
+            $this->compileRoot($tree);
+            $this->popEnv();
 
-        $sourceMapGenerator = null;
+            $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 ($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);
+                }
             }
-        }
 
-        $out = $this->formatter->format($this->scope, $sourceMapGenerator);
+            $out = $this->formatter->format($this->scope, $sourceMapGenerator);
 
-        if (! empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) {
-            $sourceMap    = $sourceMapGenerator->generateJson();
-            $sourceMapUrl = null;
+            if (! empty($out) && $this->sourceMap && $this->sourceMap !== self::SOURCE_MAP_NONE) {
+                $sourceMap    = $sourceMapGenerator->generateJson();
+                $sourceMapUrl = null;
 
-            switch ($this->sourceMap) {
-                case self::SOURCE_MAP_INLINE:
-                    $sourceMapUrl = sprintf('data:application/json,%s', Util::encodeURIComponent($sourceMap));
-                    break;
+                switch ($this->sourceMap) {
+                    case self::SOURCE_MAP_INLINE:
+                        $sourceMapUrl = sprintf('data:application/json,%s', Util::encodeURIComponent($sourceMap));
+                        break;
 
-                case self::SOURCE_MAP_FILE:
-                    $sourceMapUrl = $sourceMapGenerator->saveMap($sourceMap);
-                    break;
-            }
+                    case self::SOURCE_MAP_FILE:
+                        $sourceMapUrl = $sourceMapGenerator->saveMap($sourceMap);
+                        break;
+                }
 
-            $out .= sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl);
+                $out .= sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl);
+            }
+        } catch (SassScriptException $e) {
+            throw $this->error($e->getMessage());
         }
 
         if ($this->cache && isset($cacheKey) && isset($compileOptions)) {
@@ -310,6 +338,12 @@ class Compiler
             $this->cache->setCache('compile', $cacheKey, $v, $compileOptions);
         }
 
+        if (!$this->charsetSeen && function_exists('mb_strlen')) {
+            if (strlen($out) !== mb_strlen($out)) {
+                $out = '@charset "UTF-8";' . "\n" . $out;
+            }
+        }
+
         return $out;
     }
 
@@ -796,7 +830,7 @@ class Compiler
                 $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);
@@ -1003,30 +1037,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);
@@ -1403,7 +1413,7 @@ class Compiler
                     $s = reset($s);
                 }
 
-                if (\is_object($s) && $s instanceof Node\Number) {
+                if (\is_object($s) && $s instanceof Number) {
                     return $this->testWithWithout('keyframes', $with, $without);
                 }
             }
@@ -1572,30 +1582,6 @@ class Compiler
 
         $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;
-        }
-
         $this->scope->children[] = $out;
 
         if (\count($block->children)) {
@@ -1689,7 +1675,7 @@ class Compiler
             $buffer    = $this->collapseSelectors($selectors);
             $parser    = $this->parserFactory(__METHOD__);
 
-            if ($parser->parseSelector($buffer, $newSelectors)) {
+            if ($parser->parseSelector($buffer, $newSelectors, true)) {
                 $selectors = array_map([$this, 'evalSelector'], $newSelectors);
             }
         }
@@ -1722,8 +1708,8 @@ class Compiler
             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 (
@@ -2653,7 +2639,7 @@ class Compiler
                                 $divider = $this->reduce($divider, true);
                             }
 
-                            if (\intval($divider->dimension) && ! \count($divider->units)) {
+                            if ($divider instanceof Number && \intval($divider->getDimension()) && $divider->unitless()) {
                                 $revert = false;
                             }
                         }
@@ -2677,7 +2663,7 @@ class Compiler
                                                 $divider = $this->reduce($divider, true);
                                             }
 
-                                            if (\intval($divider->dimension) && ! \count($divider->units)) {
+                                            if ($divider instanceof Number && \intval($divider->getDimension()) && $divider->unitless()) {
                                                 $revert = false;
                                             }
                                         }
@@ -2790,17 +2776,11 @@ class Compiler
                     $ret = $this->compileChildren($each->children, $out);
 
                     if ($ret) {
-                        if ($ret[0] !== Type::T_CONTROL) {
-                            $store = $this->env->store;
-                            $this->popEnv();
-                            $this->backPropagateEnv($store, $each->vars);
+                        $store = $this->env->store;
+                        $this->popEnv();
+                        $this->backPropagateEnv($store, $each->vars);
 
-                            return $ret;
-                        }
-
-                        if ($ret[1]) {
-                            break;
-                        }
+                        return $ret;
                     }
                 }
                 $store = $this->env->store;
@@ -2816,13 +2796,7 @@ class Compiler
                     $ret = $this->compileChildren($while->children, $out);
 
                     if ($ret) {
-                        if ($ret[0] !== Type::T_CONTROL) {
-                            return $ret;
-                        }
-
-                        if ($ret[1]) {
-                            break;
-                        }
+                        return $ret;
                     }
                 }
                 break;
@@ -2833,21 +2807,21 @@ class Compiler
                 $start = $this->reduce($for->start, true);
                 $end   = $this->reduce($for->end, true);
 
-                if (! $start instanceof Node\Number) {
+                if (! $start instanceof Number) {
                     throw $this->error('%s is not a number', $start[0]);
                 }
 
-                if (! $end instanceof Node\Number) {
+                if (! $end instanceof Number) {
                     throw $this->error('%s is not a number', $end[0]);
                 }
 
-                if (! ($start[2] == $end[2] || $end->unitless())) {
-                    throw $this->error('Incompatible units: "%s" && "%s".', $start->unitStr(), $end->unitStr());
-                }
+                $start->assertSameUnitOrUnitless($end);
 
-                $unit  = $start[2];
-                $start = $start[1];
-                $end   = $end[1];
+                $numeratorUnits = $start->getNumeratorUnits();
+                $denominatorUnits = $start->getDenominatorUnits();
+
+                $start = $start->getDimension();
+                $end   = $end->getDimension();
 
                 $d = $start < $end ? 1 : -1;
 
@@ -2861,23 +2835,17 @@ class Compiler
                         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) {
-                            $store = $this->env->store;
-                            $this->popEnv();
-                            $this->backPropagateEnv($store, [$for->var]);
-
-                            return $ret;
-                        }
+                        $store = $this->env->store;
+                        $this->popEnv();
+                        $this->backPropagateEnv($store, [$for->var]);
 
-                        if ($ret[1]) {
-                            break;
-                        }
+                        return $ret;
                     }
                 }
 
@@ -2887,12 +2855,6 @@ class Compiler
 
                 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);
 
@@ -3033,9 +2995,6 @@ class Compiler
 
                 throw $this->error("File $fname on line $line ERROR: $value\n");
 
-            case Type::T_CONTROL:
-                throw $this->error('@break/@continue not permitted in this scope');
-
             default:
                 throw $this->error("unknown child type: $child[0]");
         }
@@ -3134,7 +3093,7 @@ class Compiler
      * @param array   $value
      * @param boolean $inExp
      *
-     * @return null|string|array|\ScssPhp\ScssPhp\Node\Number
+     * @return null|string|array|Number
      */
     protected function reduce($value, $inExp = false)
     {
@@ -3157,8 +3116,8 @@ class Compiler
 
                 // special case: looks like css shorthand
                 if (
-                    $opName == 'div' && ! $inParens && ! $inExp && isset($right[2]) &&
-                    (($right[0] !== Type::T_NUMBER && $right[2] != '') ||
+                    $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);
@@ -3188,50 +3147,6 @@ class Compiler
                         \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];
-                        }
-
-                        $baseUnitLeft = $left->isNormalizable();
-                        $baseUnitRight = $right->isNormalizable();
-
-                        if ($baseUnitLeft && $baseUnitRight && $baseUnitLeft === $baseUnitRight) {
-                            $left = $left->normalize();
-                            $right = $right->normalize();
-                        } elseif ($coerceUnit) {
-                            $left = new Node\Number($left[1], []);
-                        }
-                    }
-
                     $shouldEval = $inParens || $inExp;
 
                     if (isset($passOp)) {
@@ -3241,10 +3156,6 @@ class Compiler
                     }
 
                     if (isset($out)) {
-                        if ($coerceUnit && $out[0] === Type::T_NUMBER) {
-                            $out = $out->coerce($targetUnit);
-                        }
-
                         return $out;
                     }
                 }
@@ -3257,13 +3168,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();
                     }
                 }
 
@@ -3494,8 +3405,10 @@ class Compiler
 
     /**
      * Reformat fncall arguments to proper css function output
+     *
      * @param $arg
-     * @return array|\ArrayAccess|Node\Number|string|null
+     *
+     * @return array|\ArrayAccess|Number|string|null
      */
     protected function stringifyFncallArgs($arg)
     {
@@ -3614,9 +3527,6 @@ class Compiler
             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)];
 
@@ -3628,74 +3538,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 ($left[1] == 0) ? static::$NaN : static::$Infinity;
-        }
-
-        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)
     {
-        if ($right[1] == 0) {
-            return static::$NaN;
-        }
-
-        return new Node\Number($left[1] % $right[1], $left[2]);
+        return $left->modulo($right);
     }
 
     /**
@@ -3858,13 +3760,13 @@ 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];
+        $value = $right->getDimension();
 
         return $this->opColorColor(
             $op,
@@ -3877,14 +3779,14 @@ 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];
+        $value = $left->getDimension();
 
         return $this->opColorColor(
             $op,
@@ -3936,70 +3838,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
+     * Compare number1 != number2
      *
-     * @param array $left
-     * @param array $right
+     * @param Number $left
+     * @param Number $right
      *
      * @return array
      */
-    protected function opGtNumberNumber($left, $right)
+    protected function opNeqNumberNumber(Number $left, Number $right)
     {
-        return $this->toBool($left[1] > $right[1]);
+        return $this->toBool(!$left->equals($right));
     }
 
     /**
-     * Compare number1 <= number2
+     * Compare number1 >= number2
      *
-     * @param array $left
-     * @param array $right
+     * @param Number $left
+     * @param Number $right
      *
      * @return array
      */
-    protected function opLteNumberNumber($left, $right)
+    protected function opGteNumberNumber(Number $left, Number $right)
     {
-        return $this->toBool($left[1] <= $right[1]);
+        return $this->toBool($left->greaterThanOrEqual($right));
     }
 
     /**
-     * Compare number1 < number2
+     * Compare number1 > number2
      *
-     * @param array $left
-     * @param array $right
+     * @param Number $left
+     * @param Number $right
      *
      * @return array
      */
-    protected function opLtNumberNumber($left, $right)
+    protected function opGtNumberNumber(Number $left, Number $right)
     {
-        return $this->toBool($left[1] < $right[1]);
+        return $this->toBool($left->greaterThan($right));
     }
 
     /**
-     * Three-way comparison, aka spaceship operator
+     * Compare number1 <= number2
      *
-     * @param array $left
-     * @param array $right
+     * @param Number $left
+     * @param Number $right
      *
-     * @return \ScssPhp\ScssPhp\Node\Number
+     * @return array
      */
-    protected function opCmpNumberNumber($left, $right)
+    protected function opLteNumberNumber(Number $left, Number $right)
     {
-        $n = $left[1] - $right[1];
+        return $this->toBool($left->lessThanOrEqual($right));
+    }
 
-        return new Node\Number($n ? $n / abs($n) : 0, '');
+    /**
+     * Compare number1 < number2
+     *
+     * @param Number $left
+     * @param Number $right
+     *
+     * @return array
+     */
+    protected function opLtNumberNumber(Number $left, Number $right)
+    {
+        return $this->toBool($left->lessThan($right));
     }
 
     /**
@@ -4016,6 +3929,48 @@ class Compiler
         return $thing ? static::$true : static::$false;
     }
 
+    /**
+     * Escape non printable chars in strings output as in dart-sass
+     * @param $string
+     * @return string|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.
      *
@@ -4039,6 +3994,9 @@ class Compiler
 
         switch ($value[0]) {
             case Type::T_KEYWORD:
+                if (is_string($value[1])) {
+                    $value[1] = $this->escapeNonPrintableChars($value[1], true);
+                }
                 return $value[1];
 
             case Type::T_COLOR:
@@ -4063,7 +4021,7 @@ class Compiler
                         }
 
                         if (is_numeric($alpha)) {
-                            $a = new Node\Number($alpha, '');
+                            $a = new Number($alpha, '');
                         } else {
                             $a = $alpha;
                         }
@@ -4098,19 +4056,25 @@ class Compiler
                 $content = $this->compileStringContent($value);
 
                 if ($value[1]) {
+                    $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 &&
-                        strpbrk($content, '{}') !== false
+                        (strpos($content, '"') === false or strpos($content, "'") !== false) &&
+                        strpbrk($content, '{}\\\'') !== false
                     ) {
                         $value[1] = '"';
+                    } elseif (
+                        $value[1] === '"' &&
+                        (strpos($content, '"') !== false and strpos($content, "'") === false)
+                    ) {
+                        $value[1] = "'";
                     }
-                    $content = str_replace(
-                        array('\\a', "\n", "\f" , '\\'  , "\r" , $value[1]),
-                        array("\r" , ' ' , '\\f', '\\\\', '\\a', '\\' . $value[1]),
-                        $content
-                    );
+
+                    $content = str_replace($value[1], '\\' . $value[1], $content);
                 }
 
                 return $value[1] . $content . $value[1];
@@ -4161,10 +4125,26 @@ class Compiler
 
                 $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);
 
@@ -4258,7 +4238,7 @@ 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:
@@ -4947,10 +4927,13 @@ class Compiler
      * @api
      *
      * @param string $lineNumberStyle
+     *
+     * @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);
     }
 
     /**
@@ -5009,9 +4992,13 @@ class Compiler
      * @api
      *
      * @param string $name
+     *
+     * @deprecated Registering additional features is deprecated.
      */
     public function addFeature($name)
     {
+        @trigger_error('Registering additional features is deprecated.', E_USER_DEPRECATED);
+
         $this->registeredFeatures[$name] = true;
     }
 
@@ -5815,7 +5802,7 @@ class Compiler
      *
      * @param mixed $value
      *
-     * @return array|\ScssPhp\ScssPhp\Node\Number
+     * @return array|Number
      */
     protected function coerceValue($value)
     {
@@ -5832,7 +5819,7 @@ class Compiler
         }
 
         if (is_numeric($value)) {
-            return new Node\Number($value, '');
+            return new Number($value, '');
         }
 
         if ($value === '') {
@@ -6037,7 +6024,7 @@ class Compiler
                             if ($color[3] === 255) {
                                 $color[3] = 1; // fully opaque
                             } else {
-                                $color[3] = round($color[3] / 255, Node\Number::PRECISION);
+                                $color[3] = round($color[3] / 255, Number::PRECISION);
                             }
                         }
 
@@ -6060,8 +6047,8 @@ class Compiler
     }
 
     /**
-     * @param integer|\ScssPhp\ScssPhp\Node\Number $value
-     * @param boolean                              $isAlpha
+     * @param integer|Number $value
+     * @param boolean        $isAlpha
      *
      * @return integer|mixed
      */
@@ -6079,36 +6066,27 @@ class Compiler
      * @param integer|float $min
      * @param integer|float $max
      * @param boolean       $isInt
-     * @param boolean       $clamp
-     * @param boolean       $modulo
      *
      * @return integer|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)) {
                 $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;
@@ -6122,18 +6100,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;
         }
@@ -6196,12 +6163,12 @@ class Compiler
      */
     protected function coercePercent($value)
     {
-        if ($value[0] === Type::T_NUMBER) {
-            if (! empty($value[2]['%'])) {
-                return $value[1] / 100;
+        if ($value instanceof Number) {
+            if ($value->hasUnit('%')) {
+                return $value->getDimension() / 100;
             }
 
-            return $value[1];
+            return $value->getDimension();
         }
 
         return 0;
@@ -6274,22 +6241,22 @@ class Compiler
      *
      * @api
      *
-     * @param array $value
+     * @param mixed $value
      * @param string $varName
      *
-     * @return integer|float
+     * @return Number
      *
      * @throws \Exception
      */
     public function assertNumber($value, $varName = null)
     {
-        if ($value[0] !== Type::T_NUMBER) {
+        if (!$value instanceof Number) {
             $value = $this->compileValue($value);
             $var_display = ($varName ? " \${$varName}:" : '');
             throw $this->error("Error:{$var_display} $value is not a number.");
         }
 
-        return $value[1];
+        return $value;
     }
 
     /**
@@ -6307,8 +6274,8 @@ class Compiler
     public function assertInteger($value, $varName = null)
     {
 
-        $value = $this->assertNumber($value, $varName);
-        if (round($value - \intval($value), Node\Number::PRECISION) > 0) {
+        $value = $this->assertNumber($value, $varName)->getDimension();
+        if (round($value - \intval($value), Number::PRECISION) > 0) {
             $var_display = ($varName ? " \${$varName}:" : '');
             throw $this->error("Error:{$var_display} $value is not an integer.");
         }
@@ -6535,8 +6502,25 @@ 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) {
             $values[] = $this->normalizeValue($item);
         }
@@ -6617,7 +6601,7 @@ class Compiler
 
         foreach ([1 => 1, 2 => 2, 3 => 3, 7 => 4] as $iarg => $irgba) {
             if (isset($args[$iarg])) {
-                $val = $this->assertNumber($args[$iarg]);
+                $val = $this->assertNumber($args[$iarg])->getDimension();
 
                 if (! isset($color[$irgba])) {
                     $color[$irgba] = (($irgba < 4) ? 0 : 1);
@@ -6632,7 +6616,7 @@ class Compiler
 
             foreach ([4 => 1, 5 => 2, 6 => 3] as $iarg => $ihsl) {
                 if (! empty($args[$iarg])) {
-                    $val = $this->assertNumber($args[$iarg]);
+                    $val = $this->assertNumber($args[$iarg])->getDimension();
                     $hsl[$ihsl] = \call_user_func($fn, $hsl[$ihsl], $val, $iarg);
                 }
             }
@@ -6776,7 +6760,7 @@ class Compiler
     {
         $value = $args[0];
 
-        if ($value[0] === Type::T_NUMBER) {
+        if ($value instanceof Number) {
             return null;
         }
 
@@ -6840,10 +6824,6 @@ class Compiler
             $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);
-
         foreach ($kwargs as $k => $arg) {
             if (in_array($arg[0], [Type::T_FUNCTION_CALL]) && in_array($arg[1], ['min', 'max'])) {
                 return null;
@@ -6857,7 +6837,6 @@ class Compiler
                 }
 
                 $args[$k] = $this->stringifyFncallArgs($arg);
-                $hue = '';
             }
 
             if (
@@ -6869,22 +6848,31 @@ class Compiler
             }
         }
 
+        $hue = $this->reduce($args[0]);
+        $saturation = $this->reduce($args[1]);
+        $lightness = $this->reduce($args[2]);
         $alpha = null;
 
         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 = $hue->getDimension() % 360;
+
+        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;
@@ -6908,7 +6896,7 @@ class Compiler
         $color = $this->assertColor($args[0]);
         $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'];
@@ -6917,7 +6905,7 @@ class Compiler
         $color = $this->assertColor($args[0]);
         $hsl = $this->toHSL($color[1], $color[2], $color[3]);
 
-        return new Node\Number($hsl[2], '%');
+        return new Number($hsl[2], '%');
     }
 
     protected static $libLightness = ['color'];
@@ -6926,7 +6914,7 @@ class Compiler
         $color = $this->assertColor($args[0]);
         $hsl = $this->toHSL($color[1], $color[2], $color[3]);
 
-        return new Node\Number($hsl[3], '%');
+        return new Number($hsl[3], '%');
     }
 
     protected function adjustHsl($color, $idx, $amount)
@@ -6946,7 +6934,7 @@ class Compiler
     protected function libAdjustHue($args)
     {
         $color = $this->assertColor($args[0]);
-        $degrees = $this->assertNumber($args[1]);
+        $degrees = $this->assertNumber($args[1])->getDimension();
 
         return $this->adjustHsl($color, 1, $degrees);
     }
@@ -6974,7 +6962,7 @@ class Compiler
     {
         $value = $args[0];
 
-        if ($value[0] === Type::T_NUMBER) {
+        if ($value instanceof Number) {
             return null;
         }
 
@@ -7003,7 +6991,7 @@ class Compiler
     {
         $value = $args[0];
 
-        if ($value[0] === Type::T_NUMBER) {
+        if ($value instanceof Number) {
             return null;
         }
 
@@ -7027,7 +7015,7 @@ class Compiler
             $weight = $this->coercePercent($weight);
         }
 
-        if ($value[0] === Type::T_NUMBER) {
+        if ($value instanceof Number) {
             return null;
         }
 
@@ -7038,7 +7026,7 @@ class Compiler
         $inverted[3] = 255 - $inverted[3];
 
         if ($weight < 1) {
-            return $this->libMix([$inverted, $color, [Type::T_NUMBER, $weight]]);
+            return $this->libMix([$inverted, $color, new Number($weight, '')]);
         }
 
         return $inverted;
@@ -7100,6 +7088,7 @@ class Compiler
         $value = $args[0];
 
         if ($value[0] === Type::T_STRING && ! empty($value[1])) {
+            $value[1] = '"';
             return $value;
         }
 
@@ -7109,116 +7098,86 @@ class Compiler
     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 = ['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 = ['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 = ['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 = ['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 function libMin($args)
     {
-        $numbers = $this->getNormalizedNumbers($args);
-        $minOriginal = null;
-        $minNormalized = null;
+        /**
+         * @var Number|null
+         */
+        $min = null;
 
-        foreach ($numbers as $key => $pair) {
-            list($original, $normalized) = $pair;
+        foreach ($args as $arg) {
+            $number = $this->assertNumber($arg);
 
-            if (\is_null($normalized) || \is_null($minNormalized)) {
-                if (\is_null($minOriginal) || $original[1] <= $minOriginal[1]) {
-                    $minOriginal = $original;
-                    $minNormalized = $normalized;
-                }
-            } elseif ($normalized[1] <= $minNormalized[1]) {
-                $minOriginal = $original;
-                $minNormalized = $normalized;
+            if (\is_null($min) || $min->greaterThan($number)) {
+                $min = $number;
             }
         }
 
-        return $minOriginal;
-    }
-
-    protected function libMax($args)
-    {
-        $numbers = $this->getNormalizedNumbers($args);
-        $maxOriginal = null;
-        $maxNormalized = null;
-
-        foreach ($numbers as $key => $pair) {
-            list($original, $normalized) = $pair;
-
-            if (\is_null($normalized) || \is_null($maxNormalized)) {
-                if (\is_null($maxOriginal) || $original[1] >= $maxOriginal[1]) {
-                    $maxOriginal = $original;
-                    $maxNormalized = $normalized;
-                }
-            } elseif ($normalized[1] >= $maxNormalized[1]) {
-                $maxOriginal = $original;
-                $maxNormalized = $normalized;
-            }
+        if (!\is_null($min)) {
+            return $min;
         }
 
-        return $maxOriginal;
+        throw $this->error('At least one argument must be passed.');
     }
 
-    /**
-     * Helper to normalize args containing numbers
-     *
-     * @param array $args
-     *
-     * @return array
-     */
-    protected function getNormalizedNumbers($args)
+    protected function libMax($args)
     {
-        $unit         = null;
-        $originalUnit = null;
-        $numbers      = [];
-
-        foreach ($args as $key => $item) {
-            $this->assertNumber($item);
+        /**
+         * @var Number|null
+         */
+        $max = null;
 
-            $number = $item->normalize();
+        foreach ($args as $arg) {
+            $number = $this->assertNumber($arg);
 
-            if (empty($unit)) {
-                $unit = $number[2];
-                $originalUnit = $item->unitStr();
-            } elseif ($number[1] && $unit !== $number[2] && ! empty($number[2])) {
-                throw $this->error('Incompatible units: "%s" and "%s".', $originalUnit, $item->unitStr());
+            if (\is_null($max) || $max->lessThan($number)) {
+                $max = $number;
             }
+        }
 
-            $numbers[$key] = [$args[$key], empty($number[2]) ? null : $number];
+        if (!\is_null($max)) {
+            return $max;
         }
 
-        return $numbers;
+        throw $this->error('At least one argument must be passed.');
     }
 
     protected static $libLength = ['list'];
@@ -7257,7 +7216,7 @@ class Compiler
     protected function libNth($args)
     {
         $list = $this->coerceList($args[0], ',', false);
-        $n = $this->assertNumber($args[1]);
+        $n = $this->assertNumber($args[1])->getDimension();
 
         if ($n > 0) {
             $n--;
@@ -7272,7 +7231,7 @@ 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--;
@@ -7570,7 +7529,7 @@ class Compiler
     {
         $num = $args[0];
 
-        if ($num[0] === Type::T_NUMBER) {
+        if ($num instanceof Number) {
             return [Type::T_STRING, '"', [$num->unitStr()]];
         }
 
@@ -7582,7 +7541,7 @@ class Compiler
     {
         $value = $args[0];
 
-        return $value[0] === Type::T_NUMBER && $value->unitless();
+        return $value instanceof Number && $value->unitless();
     }
 
     protected static $libComparable = [
@@ -7594,16 +7553,13 @@ class Compiler
         list($number1, $number2) = $args;
 
         if (
-            ! isset($number1[0]) || $number1[0] !== Type::T_NUMBER ||
-            ! isset($number2[0]) || $number2[0] !== Type::T_NUMBER
+            ! $number1 instanceof Number ||
+            ! $number2 instanceof Number
         ) {
             throw $this->error('Invalid argument(s) for "comparable"');
         }
 
-        $number1 = $number1->normalize();
-        $number2 = $number2->normalize();
-
-        return $number1[2] === $number2[2] || $number1->unitless() || $number2->unitless();
+        return $number1->isComparableTo($number2);
     }
 
     protected static $libStrIndex = ['string', 'substring'];
@@ -7621,7 +7577,7 @@ class Compiler
             $result = strpos($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'];
@@ -7656,7 +7612,7 @@ class Compiler
         $string = $this->assertString($args[0], 'string');
         $stringContent = $this->compileStringContent($string);
 
-        return new Node\Number(Util::mbStrlen($stringContent), '');
+        return new Number(Util::mbStrlen($stringContent), '');
     }
 
     protected static $libStrSlice = ['string', 'start-at', 'end-at:-1'];
@@ -7786,21 +7742,21 @@ class Compiler
     protected function libRandom($args)
     {
         if (isset($args[0]) & $args[0] !== static::$null) {
-            $n = $this->assertNumber($args[0]);
+            $n = $this->assertNumber($args[0])->getDimension();
 
             if ($n < 1) {
                 throw $this->error("\$limit must be greater than or equal to 1");
             }
 
-            if (round($n - \intval($n), Node\Number::PRECISION) > 0) {
+            if (round($n - \intval($n), Number::PRECISION) > 0) {
                 throw $this->error("Expected \$limit to be an integer but got $n for `random`");
             }
 
-            return new Node\Number(mt_rand(1, \intval($n)), '');
+            return new Number(mt_rand(1, \intval($n)), '');
         }
 
         $max = mt_getrandmax();
-        return new Node\Number(mt_rand(0, $max - 1) / $max, '');
+        return new Number(mt_rand(0, $max - 1) / $max, '');
     }
 
     protected function libUniqueId()
@@ -7888,7 +7844,7 @@ class Compiler
 
         $parsedSelector = [];
 
-        if ($parser->parseSelector($arg, $parsedSelector)) {
+        if ($parser->parseSelector($arg, $parsedSelector, true)) {
             $selector = $this->evalSelectors($parsedSelector);
             $gluedSelector = $this->glueFunctionSelectors($selector);
 
index 0f2099f58f0269acdab2a0b2e6357f78fed3a565..1ebd30d7ab02f4b8bb10ba52385e11f656d3e874 100644 (file)
@@ -13,6 +13,7 @@
 namespace ScssPhp\ScssPhp\Node;
 
 use ScssPhp\ScssPhp\Compiler;
+use ScssPhp\ScssPhp\Exception\SassScriptException;
 use ScssPhp\ScssPhp\Node;
 use ScssPhp\ScssPhp\Type;
 
@@ -76,74 +77,55 @@ class Number extends Node implements \ArrayAccess
     /**
      * @var integer|float
      */
-    public $dimension;
+    private $dimension;
 
-    /**
-     * @var array
-     */
-    public $units;
+    private $numeratorUnits;
+    private $denominatorUnits;
 
     /**
      * Initialize number
      *
-     * @param mixed $dimension
-     * @param mixed $initialUnit
+     * @param integer|float   $dimension
+     * @param string[]|string $numeratorUnits
+     * @param string[]        $denominatorUnits
      */
-    public function __construct($dimension, $initialUnit)
+    public function __construct($dimension, $numeratorUnits, array $denominatorUnits = [])
     {
-        $this->type      = Type::T_NUMBER;
+        if (is_string($numeratorUnits)) {
+            $numeratorUnits = $numeratorUnits ? [$numeratorUnits] : [];
+        } elseif (isset($numeratorUnits['numerator_units'], $numeratorUnits['denominator_units'])) {
+            // TODO get rid of this once `$number[2]` is not used anymore
+            $denominatorUnits = $numeratorUnits['denominator_units'];
+            $numeratorUnits = $numeratorUnits['numerator_units'];
+        }
+
         $this->dimension = $dimension;
-        $this->units     = \is_array($initialUnit)
-            ? $initialUnit
-            : ($initialUnit ? [$initialUnit => 1]
-                            : []);
+        $this->numeratorUnits = $numeratorUnits;
+        $this->denominatorUnits = $denominatorUnits;
     }
 
     /**
-     * Coerce number to target units
-     *
-     * @param array $units
-     *
-     * @return \ScssPhp\ScssPhp\Node\Number
+     * @return float|int
      */
-    public function coerce($units)
+    public function getDimension()
     {
-        if ($this->unitless()) {
-            return new Number($this->dimension, $units);
-        }
-
-        $dimension = $this->dimension;
-
-        if (\count($units)) {
-            $baseUnit = array_keys($units);
-            $baseUnit = reset($baseUnit);
-            $baseUnit = $this->findBaseUnit($baseUnit);
-            if ($baseUnit && isset(static::$unitTable[$baseUnit])) {
-                foreach (static::$unitTable[$baseUnit] 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);
+        return $this->numeratorUnits;
+    }
 
-        return new Number($dimension, $units);
+    /**
+     * @return string[]
+     */
+    public function getDenominatorUnits()
+    {
+        return $this->denominatorUnits;
     }
 
     /**
@@ -187,13 +169,13 @@ 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);
         }
     }
 
@@ -202,17 +184,7 @@ class Number extends Node implements \ArrayAccess
      */
     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');
     }
 
     /**
@@ -220,17 +192,7 @@ class Number extends Node implements \ArrayAccess
      */
     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');
     }
 
     /**
@@ -240,61 +202,238 @@ class Number extends Node implements \ArrayAccess
      */
     public function unitless()
     {
-        return ! array_sum($this->units);
+        return \count($this->numeratorUnits) === 0 && \count($this->denominatorUnits) === 0;
     }
 
     /**
-     * Test if a number can be normalized in a base unit
-     * ie if its units are homogeneous
+     * Checks whether the number has exactly this unit
      *
-     * @return boolean
+     * @param string $unit
+     *
+     * @return bool
+     */
+    public function hasUnit($unit)
+    {
+        return \count($this->numeratorUnits) === 1 && \count($this->denominatorUnits) === 0 && $this->numeratorUnits[0] === $unit;
+    }
+
+    /**
+     * Returns unit(s) as the product of numerator units divided by the product of denominator units
+     *
+     * @return string
      */
-    public function isNormalizable()
+    public function unitStr()
+    {
+        if ($this->unitless()) {
+            return '';
+        }
+
+        return self::getUnitString($this->numeratorUnits, $this->denominatorUnits);
+    }
+
+    public function assertNoUnits($varName = null)
     {
         if ($this->unitless()) {
+            return;
+        }
+
+        $varDisplay = !\is_null($varName) ? "\${$varName}: " : '';
+
+        throw new SassScriptException(sprintf('%sExpected %s to have no units', $varDisplay, $this));
+    }
+
+    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)
+        ));
+    }
+
+    /**
+     * @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;
+        });
+    }
 
-        $baseUnit = null;
+    /**
+     * @param Number $other
+     *
+     * @return bool
+     */
+    public function lessThanOrEqual(Number $other)
+    {
+        return $this->coerceUnits($other, function ($num1, $num2) {
+            return $num1 <= $num2;
+        });
+    }
 
-        foreach ($this->units as $unit => $exp) {
-            $b = $this->findBaseUnit($unit);
+    /**
+     * @param Number $other
+     *
+     * @return bool
+     */
+    public function greaterThan(Number $other)
+    {
+        return $this->coerceUnits($other, function ($num1, $num2) {
+            return $num1 > $num2;
+        });
+    }
 
-            if (\is_null($baseUnit)) {
-                $baseUnit = $b;
+    /**
+     * @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;
             }
 
-            if (\is_null($b) or $b !== $baseUnit) {
-                return false;
+            return $num1 % $num2;
+        });
+    }
+
+    /**
+     * @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 $baseUnit;
+        return $this->multiplyUnits($value, $this->numeratorUnits, $this->denominatorUnits, $other->denominatorUnits, $other->numeratorUnits);
     }
 
     /**
-     * Returns unit(s) as the product of numerator units divided by the product of denominator units
+     * @param Number $other
      *
-     * @return string
+     * @return bool
      */
-    public function unitStr()
+    public function equals(Number $other)
     {
-        $numerators   = [];
-        $denominators = [];
+        // Unitless numbers are convertable to unit numbers, but not equal, so we special-case unitless here.
+        if ($this->unitless() !== $other->unitless()) {
+            return false;
+        }
 
-        foreach ($this->units as $unit => $unitSize) {
-            if ($unitSize > 0) {
-                $numerators = array_pad($numerators, \count($numerators) + $unitSize, $unit);
-                continue;
-            }
+        // 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 ($unitSize < 0) {
-                $denominators = array_pad($denominators, \count($denominators) - $unitSize, $unit);
-                continue;
-            }
+        if ($this->unitless()) {
+            return round($this->dimension, self::PRECISION) == round($other->dimension, self::PRECISION);
         }
 
-        return implode('*', $numerators) . (\count($denominators) ? '/' . implode('*', $denominators) : '');
+        try {
+            return $this->coerceUnits($other, function ($num1, $num2) {
+                return round($num1,self::PRECISION) == round($num2, self::PRECISION);
+            });
+        } catch (SassScriptException $e) {
+            return false;
+        }
     }
 
     /**
@@ -308,35 +447,29 @@ class Number extends Node implements \ArrayAccess
     {
         $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);
+        if (is_nan($dimension)) {
+            return 'NaN';
+        }
 
-            $dimension = round($dimension, self::PRECISION);
-            $units     = array_filter($units, function ($unitSize) {
-                return $unitSize;
-            });
+        if ($dimension === INF) {
+            return 'Infinity';
         }
 
-        $unitSize = array_sum($units);
+        if ($dimension === -INF) {
+            return '-Infinity';
+        }
 
-        if ($compiler && ($unitSize > 1 || $unitSize < 0 || \count($units) > 1)) {
-            $this->units = $units;
+        if ($compiler) {
             $unit = $this->unitStr();
+        } elseif (isset($this->numeratorUnits[0])) {
+            $unit = $this->numeratorUnits[0];
         } else {
-            reset($units);
-            $unit = key($units);
+            $unit = '';
         }
 
         $dimension = number_format($dimension, self::PRECISION, '.', '');
 
-        return (self::PRECISION ? rtrim(rtrim($dimension, '0'), '.') : $dimension) . $unit;
+        return rtrim(rtrim($dimension, '0'), '.') . $unit;
     }
 
     /**
@@ -348,48 +481,216 @@ class Number extends Node implements \ArrayAccess
     }
 
     /**
-     * Normalize units
+     * @param Number   $other
+     * @param callable $operation
+     *
+     * @return Number
+     *
+     * @phpstan-param callable(int|float, int|float): int|float $operation
+     */
+    private function coerceNumber(Number $other, $operation)
+    {
+        $result = $this->coerceUnits($other, $operation);
+
+        if (!$this->unitless()) {
+            return new Number($result, $this->numeratorUnits, $this->denominatorUnits);
+        }
+
+        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
+     */
+    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;
+            }
+
+            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
      *
-     * @param integer|float $dimension
-     * @param array         $units
-     * @param string        $baseUnit
+     * @return Number
      */
-    private function normalizeUnits(&$dimension, &$units, $baseUnit = null)
+    private function multiplyUnits($value, array $numerators1, array $denominators1, array $numerators2, array $denominators2)
     {
-        $dimension = $this->dimension;
-        $units     = [];
+        $newNumerators = array();
+
+        foreach ($numerators1 as $numerator) {
+            foreach ($denominators2 as $key => $denominator) {
+                $conversionFactor = self::getConversionFactor($numerator, $denominator);
 
-        foreach ($this->units as $unit => $exp) {
-            if (! $baseUnit) {
-                $baseUnit = $this->findBaseUnit($unit);
+                if (\is_null($conversionFactor)) {
+                    continue;
+                }
+
+                $value /= $conversionFactor;
+                unset($denominators2[$key]);
+                continue 2;
             }
 
-            if ($baseUnit && isset(static::$unitTable[$baseUnit][$unit])) {
-                $factor = pow(static::$unitTable[$baseUnit][$unit], $exp);
+            $newNumerators[] = $numerator;
+        }
+
+        foreach ($numerators2 as $numerator) {
+            foreach ($denominators1 as $key => $denominator) {
+                $conversionFactor = self::getConversionFactor($numerator, $denominator);
 
-                $unit = $baseUnit;
-                $dimension /= $factor;
+                if (\is_null($conversionFactor)) {
+                    continue;
+                }
+
+                $value /= $conversionFactor;
+                unset($denominators1[$key]);
+                continue 2;
             }
 
-            $units[$unit] = $exp + (isset($units[$unit]) ? $units[$unit] : 0);
+            $newNumerators[] = $numerator;
         }
+
+        $newDenominators = array_values(array_merge($denominators1, $denominators2));
+
+        return new Number($value, $newNumerators, $newDenominators);
     }
 
     /**
-     * Find the base unit family for a given unit
+     * Returns the number of [unit1]s per [unit2].
      *
-     * @param string $unit
+     * Equivalently, `1unit1 * conversionFactor(unit1, unit2) = 1unit2`.
+     *
+     * @param string $unit1
+     * @param string $unit2
      *
-     * @return string|null
+     * @return float|int|null
      */
-    private function findBaseUnit($unit)
+    private static function getConversionFactor($unit1, $unit2)
     {
-        foreach (static::$unitTable as $baseUnit => $unitVariants) {
-            if (isset($unitVariants[$unit])) {
-                return $baseUnit;
+        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
+     *
+     * @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) : '');
+    }
 }
index 7b47f15330a841a7e0c40ea9e0347178cd686371..55371e53a34f2f3e851359f22863910da88faf75 100644 (file)
@@ -39,7 +39,6 @@ class Parser
         'and' => 2,
         '=='  => 3,
         '!='  => 3,
-        '<=>' => 3,
         '<='  => 4,
         '>='  => 4,
         '<'   => 4,
@@ -98,7 +97,7 @@ class Parser
         $this->cssOnly          = $cssOnly;
 
         if (empty(static::$operatorPattern)) {
-            static::$operatorPattern = '([*\/%+-]|[!=]\=|\>\=?|\<\=\>|\<\=?|and|or)';
+            static::$operatorPattern = '([*\/%+-]|[!=]\=|\>\=?|\<\=?|and|or)';
 
             $commentSingle      = '\/\/';
             $commentMultiLeft   = '\/\*';
@@ -135,8 +134,29 @@ class Parser
      * @param string $msg
      *
      * @throws \ScssPhp\ScssPhp\Exception\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);
 
@@ -150,7 +170,7 @@ class Parser
             $e = new ParserException("$msg: failed at `$m[1]` $loc");
             $e->setSourcePosition([$this->sourceName, $line, $column]);
 
-            throw $e;
+            return $e;
         }
 
         $this->restoreEncoding();
@@ -158,7 +178,7 @@ class Parser
         $e = new ParserException("$msg: $loc");
         $e->setSourcePosition([$this->sourceName, $line, $column]);
 
-        throw $e;
+        return $e;
     }
 
     /**
@@ -209,11 +229,11 @@ class Parser
         }
 
         if ($this->count !== \strlen($this->buffer)) {
-            $this->throwParseError();
+            throw $this->parseError();
         }
 
         if (! empty($this->env->parent)) {
-            $this->throwParseError('unclosed block');
+            throw $this->parseError('unclosed block');
         }
 
         if ($this->charset) {
@@ -248,6 +268,7 @@ class Parser
         $this->buffer          = (string) $buffer;
 
         $this->saveEncoding();
+        $this->extractLineNumbers($this->buffer);
 
         $list = $this->valueList($out);
 
@@ -263,10 +284,11 @@ class Parser
      *
      * @param string       $buffer
      * @param string|array $out
+     * @param bool         $shouldValidate
      *
      * @return boolean
      */
-    public function parseSelector($buffer, &$out)
+    public function parseSelector($buffer, &$out, $shouldValidate = true)
     {
         $this->count           = 0;
         $this->env             = null;
@@ -275,11 +297,16 @@ class Parser
         $this->buffer          = (string) $buffer;
 
         $this->saveEncoding();
+        $this->extractLineNumbers($this->buffer);
 
         $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;
     }
 
@@ -302,6 +329,7 @@ class Parser
         $this->buffer          = (string) $buffer;
 
         $this->saveEncoding();
+        $this->extractLineNumbers($this->buffer);
 
         $isMediaQuery = $this->mediaQueryList($out);
 
@@ -460,7 +488,7 @@ class Parser
                 $this->end()
             ) {
                 if ($this->cssOnly) {
-                    $this->assertPlainCssValid($importPath, $s);
+                    $this->assertPlainCssValid([Type::T_IMPORT, $importPath], $s);
                     $this->append([Type::T_COMMENT, rtrim(substr($this->buffer, $s, $this->count - $s))]);
                     return true;
                 }
@@ -478,7 +506,7 @@ class Parser
                 $this->end()
             ) {
                 if ($this->cssOnly) {
-                    $this->assertPlainCssValid($importPath, $s);
+                    $this->assertPlainCssValid([Type::T_IMPORT, $importPath], $s);
                     $this->append([Type::T_COMMENT, rtrim(substr($this->buffer, $s, $this->count - $s))]);
                     return true;
                 }
@@ -523,32 +551,6 @@ class Parser
 
             $this->seek($s);
 
-            if (
-                $this->literal('@break', 6) &&
-                $this->end()
-            ) {
-                ! $this->cssOnly || $this->assertPlainCssValid(false, $s);
-
-                $this->append([Type::T_BREAK], $s);
-
-                return true;
-            }
-
-            $this->seek($s);
-
-            if (
-                $this->literal('@continue', 9) &&
-                $this->end()
-            ) {
-                ! $this->cssOnly || $this->assertPlainCssValid(false, $s);
-
-                $this->append([Type::T_CONTINUE], $s);
-
-                return true;
-            }
-
-            $this->seek($s);
-
             if (
                 $this->literal('@return', 7) &&
                 ($this->valueList($retVal) || true) &&
@@ -723,8 +725,7 @@ class Parser
                         $else = $this->pushSpecialBlock(Type::T_ELSE, $s);
                     } elseif (
                         $this->literal('if', 2) &&
-                        $this->valueList($cond) &&
-                        $this->matchChar('{', false)
+                        $this->functionCallArgumentsList($cond, false, '{', false)
                     ) {
                         $else = $this->pushSpecialBlock(Type::T_ELSEIF, $s);
                         $else->cond = $cond;
@@ -824,7 +825,7 @@ class Parser
                     ! \in_array($this->env->type, [Type::T_DIRECTIVE, Type::T_MEDIA])
                 ) {
                     $plain = \trim(\substr($this->buffer, $s, $this->count - $s));
-                    $this->throwParseError(
+                    throw $this->parseError(
                         "Unknown directive `{$plain}` not allowed in `" . $this->env->type . "` block"
                     );
                 }
@@ -965,7 +966,7 @@ class Parser
 
             if ($this->valueList($value)) {
                 if (empty($this->env->parent)) {
-                    $this->throwParseError('expected "{"');
+                    throw $this->parseError('expected "{"');
                 }
 
                 $this->append([Type::T_ASSIGN, $name, $value], $s);
@@ -1115,7 +1116,7 @@ class Parser
         $block = $this->env;
 
         if (empty($block->parent)) {
-            $this->throwParseError('unexpected }');
+            throw $this->parseError('unexpected }');
         }
 
         if ($block->type == Type::T_AT_ROOT) {
@@ -1185,7 +1186,7 @@ class Parser
             if ($type) {
                 $message .= " ($type)";
             }
-            $this->throwParseError($message);
+            throw $this->parseError($message);
         }
 
         return $parsed;
@@ -1198,6 +1199,11 @@ class Parser
      */
     protected function isPlainCssValidElement($parsed, $allowExpression = false)
     {
+        // keep string as is
+        if (is_string($parsed)) {
+            return $parsed;
+        }
+
         if (
             \in_array($parsed[0], [Type::T_FUNCTION, Type::T_FUNCTION_CALL]) &&
             !\in_array($parsed[1], [
@@ -1232,6 +1238,7 @@ class Parser
             case Type::T_KEYWORD:
             case Type::T_NULL:
             case Type::T_NUMBER:
+            case Type::T_MEDIA:
                 return $parsed;
 
             case Type::T_COMMENT:
@@ -1250,6 +1257,16 @@ class Parser
 
                 return $parsed;
 
+            case Type::T_IMPORT:
+                if ($parsed[1][0] === Type::T_LIST) {
+                    return false;
+                }
+                $parsed[1] = $this->isPlainCssValidElement($parsed[1]);
+                if ($parsed[1] === false) {
+                    return false;
+                }
+                return $parsed;
+
             case Type::T_STRING:
                 foreach ($parsed[2] as $k => $substr) {
                     if (\is_array($substr)) {
@@ -1261,6 +1278,18 @@ class Parser
                 }
                 return $parsed;
 
+            case Type::T_LIST:
+                if (!empty($parsed['enclosing'])) {
+                    return false;
+                }
+                foreach ($parsed[2] as $k => $listElement) {
+                    $parsed[2][$k] = $this->isPlainCssValidElement($listElement);
+                    if (! $parsed[2][$k]) {
+                        return false;
+                    }
+                }
+                return $parsed;
+
             case Type::T_ASSIGN:
                 foreach ([1, 2, 3] as $k) {
                     if (! empty($parsed[$k])) {
@@ -2755,7 +2784,7 @@ class Parser
                 $sss = $this->count;
 
                 if (! $this->matchChar(')')) {
-                    $this->throwParseError('... has to be after the final argument');
+                    throw $this->parseError('... has to be after the final argument');
                 }
 
                 $arg[2] = true;
@@ -2917,7 +2946,7 @@ class Parser
                     $content[] = '#{'; // ignore it
                 }
             } elseif ($m[2] === "\r") {
-                $content[] = '\\a';
+                $content[] = chr(10);
                 // TODO : warning
                 # DEPRECATION WARNING on line x, column y of zzz:
                 # Unescaped multiline strings are deprecated and will be removed in a future version of Sass.
@@ -2936,7 +2965,7 @@ class Parser
                 } elseif ($this->matchEscapeCharacter($c)) {
                     $content[] = $c;
                 } else {
-                    $this->throwParseError('Unterminated escape sequence');
+                    throw $this->parseError('Unterminated escape sequence');
                 }
             } else {
                 $this->count -= \strlen($delim);
@@ -2961,8 +2990,9 @@ class Parser
         return false;
     }
 
-    protected function matchEscapeCharacter(&$out)
+    protected function matchEscapeCharacter(&$out, $inKeywords = false)
     {
+        $s = $this->count;
         if ($this->match('[a-f0-9]', $m, false)) {
             $hex = $m[0];
 
@@ -2976,8 +3006,10 @@ class Parser
 
             $value = hexdec($hex);
 
-            if ($value == 0 || ($value >= 0xD800 && $value <= 0xDFFF) || $value >= 0x10FFFF) {
-                $out = "\u{FFFD}";
+            if (!$inKeywords && ($value == 0 || ($value >= 0xD800 && $value <= 0xDFFF) || $value >= 0x10FFFF)) {
+                $out = "\xEF\xBF\xBD"; // "\u{FFFD}" but with a syntax supported on PHP 5
+            } elseif ($value < 0x20) {
+                $out = Util::mbChr($value);
             } else {
                 $out = Util::mbChr($value);
             }
@@ -2986,6 +3018,10 @@ class Parser
         }
 
         if ($this->match('.', $m, false)) {
+            if ($inKeywords && in_array($m[0], ["'",'"','@','&',' ','\\',':','/','%'])) {
+                $this->seek($s);
+                return false;
+            }
             $out = $m[0];
 
             return true;
@@ -3338,6 +3374,9 @@ class Parser
     {
         $selector = [];
 
+        $discardComments = $this->discardComments;
+        $this->discardComments = true;
+
         for (;;) {
             $s = $this->count;
 
@@ -3355,18 +3394,15 @@ class Parser
 
             if ($this->selectorSingle($part, $subSelector)) {
                 $selector[] = $part;
-                $this->match('\s+', $m);
-                continue;
-            }
-
-            if ($this->match('\/[^\/]+\/', $m, true)) {
-                $selector[] = [$m[0]];
+                $this->whitespace();
                 continue;
             }
 
             break;
         }
 
+        $this->discardComments = $discardComments;
+
         if (! $selector) {
             return false;
         }
@@ -3524,7 +3560,7 @@ class Parser
                             $this->seek($ss);
                         }
                     } elseif (
-                        $this->matchChar('(') &&
+                        $this->matchChar('(', true) &&
                         ($this->openString(')', $str, '(') || true) &&
                         $this->matchChar(')')
                     ) {
@@ -3639,17 +3675,51 @@ class Parser
      */
     protected function keyword(&$word, $eatWhitespace = null)
     {
+        $s = $this->count;
         $match = $this->match(
             $this->utf8
                 ? '(([\pL\w\x{00A0}-\x{10FFFF}_\-\*!"\']|[\\\\].)([\pL\w\x{00A0}-\x{10FFFF}\-_"\']|[\\\\].)*)'
                 : '(([\w_\-\*!"\']|[\\\\].)([\w\-_"\']|[\\\\].)*)',
             $m,
-            $eatWhitespace
+            false
         );
 
         if ($match) {
             $word = $m[1];
 
+            // handling of escaping in keyword : get the escaped char
+            if (strpos($word, '\\') !== false) {
+                $send = $this->count;
+                $escapedWord = [];
+                $this->seek($s);
+                $previousEscape = false;
+                while ($this->count < $send) {
+                    $char = $this->buffer[$this->count];
+                    $this->count++;
+                    if (
+                        $this->count < $send
+                        && $char === '\\'
+                        && !$previousEscape
+                        && $this->matchEscapeCharacter($out, true)
+                    ) {
+                        $escapedWord[] = $out;
+                    } else {
+                        if ($previousEscape) {
+                            $previousEscape = false;
+                        } elseif ($char === '\\') {
+                            $previousEscape = true;
+                        }
+                        $escapedWord[] = $char;
+                    }
+                }
+
+                $word = implode('', $escapedWord);
+            }
+
+            if (is_null($eatWhitespace) ? $this->eatWhiteDefault : $eatWhitespace) {
+                $this->whitespace();
+            }
+
             return true;
         }
 
index 124aa820436aa3c8b3baf3804116af98b0628fdd..980f570af42324963897600388e15d10161c0064 100644 (file)
@@ -82,8 +82,8 @@ class Util
      */
     public static function mbChr($code)
     {
-        // Use the native implementation if available.
-        if (\function_exists('mb_chr')) {
+        // Use the native implementation if available, but not on PHP 7.2 as mb_chr(0) is buggy there
+        if (\PHP_VERSION_ID > 70300 && \function_exists('mb_chr')) {
             return mb_chr($code, 'UTF-8');
         }
 
index 8445434c5c28fc8c9ed955df39e22a690d12defb..cd3e190f159d6811c1ae51e27c8fdae0ef40b0f2 100644 (file)
@@ -19,5 +19,5 @@ namespace ScssPhp\ScssPhp;
  */
 class Version
 {
-    const VERSION = '1.2.1';
+    const VERSION = '1.3';
 }