diff --git a/Build/phpstan/phpstan-baseline.neon b/Build/phpstan/phpstan-baseline.neon index 586c73e0..7206e73b 100644 --- a/Build/phpstan/phpstan-baseline.neon +++ b/Build/phpstan/phpstan-baseline.neon @@ -84,6 +84,12 @@ parameters: count: 1 path: ../../src/Value/CSSFunction.php + - + message: '#^Function preg_match is unsafe to use\. It can return FALSE instead of throwing an exception\. Please add ''use function Safe\\preg_match;'' at the beginning of the file to use the variant provided by the ''thecodingmachine/safe'' library\.$#' + identifier: theCodingMachineSafe.function + count: 1 + path: ../../src/Value/CSSString.php + - message: '#^Function preg_match is unsafe to use\. It can return FALSE instead of throwing an exception\. Please add ''use function Safe\\preg_match;'' at the beginning of the file to use the variant provided by the ''thecodingmachine/safe'' library\.$#' identifier: theCodingMachineSafe.function @@ -102,6 +108,18 @@ parameters: count: 3 path: ../../src/Value/Color.php + - + message: '#^Function preg_match is unsafe to use\. It can return FALSE instead of throwing an exception\. Please add ''use function Safe\\preg_match;'' at the beginning of the file to use the variant provided by the ''thecodingmachine/safe'' library\.$#' + identifier: theCodingMachineSafe.function + count: 1 + path: ../../src/Value/Size.php + + - + message: '#^Function preg_replace is unsafe to use\. It can return FALSE instead of throwing an exception\. Please add ''use function Safe\\preg_replace;'' at the beginning of the file to use the variant provided by the ''thecodingmachine/safe'' library\.$#' + identifier: theCodingMachineSafe.function + count: 2 + path: ../../src/Value/Size.php + - message: '#^Strict comparison using \!\=\= between non\-empty\-string and null will always evaluate to true\.$#' identifier: notIdentical.alwaysTrue diff --git a/src/Value/CSSString.php b/src/Value/CSSString.php index 38d234ef..545a7ace 100644 --- a/src/Value/CSSString.php +++ b/src/Value/CSSString.php @@ -11,8 +11,6 @@ use Sabberworm\CSS\Parsing\UnexpectedTokenException; use Sabberworm\CSS\ShortClassNameProvider; -use function Safe\preg_match; - /** * This class is a wrapper for quoted strings to distinguish them from keywords. * @@ -58,7 +56,14 @@ public static function parse(ParserState $parserState): CSSString $result = ''; if ($quote === null) { // Unquoted strings end in whitespace or with braces, brackets, parentheses - while (preg_match('/[\\s{}()<>\\[\\]]/isu', $parserState->peek()) === 0) { + while (true) { + $matchResult = \preg_match('/[\\s{}()<>\\[\\]]/isu', $parserState->peek()); + if (!\is_int($matchResult)) { + throw new \RuntimeException('The CSS is not valid UTF-8.', 1787548272); + } + if ($matchResult !== 0) { + break; + } $result .= $parserState->parseCharacter(false); } } else { diff --git a/src/Value/Size.php b/src/Value/Size.php index ad6d794c..addab489 100644 --- a/src/Value/Size.php +++ b/src/Value/Size.php @@ -10,9 +10,6 @@ use Sabberworm\CSS\Parsing\UnexpectedTokenException; use Sabberworm\CSS\ShortClassNameProvider; -use function Safe\preg_match; -use function Safe\preg_replace; - /** * A `Size` consists of a numeric `size` value and a unit. */ @@ -200,10 +197,19 @@ public function render(OutputFormat $outputFormat): string { $locale = \localeconv(); $decimalPoint = \preg_quote($locale['decimal_point'], '/'); - $size = preg_match('/[\\d\\.]+e[+-]?\\d+/i', (string) $this->size) === 1 - ? preg_replace("/$decimalPoint?0+$/", '', \sprintf('%f', $this->size)) : (string) $this->size; + $matchResult = \preg_match('/[\\d\\.]+e[+-]?\\d+/i', (string) $this->size); + \assert(\is_int($matchResult)); + if ($matchResult === 1) { + $size = \preg_replace("/$decimalPoint?0+$/", '', \sprintf('%f', $this->size)); + \assert(\is_string($size)); + } else { + $size = (string) $this->size; + } + + $renderedSize = \preg_replace(["/$decimalPoint/", '/^(-?)0\\./'], ['.', '$1.'], $size); + \assert(\is_string($renderedSize)); - return preg_replace(["/$decimalPoint/", '/^(-?)0\\./'], ['.', '$1.'], $size) . ($this->unit ?? ''); + return $renderedSize . ($this->unit ?? ''); } /** diff --git a/tests/Unit/Value/CSSStringTest.php b/tests/Unit/Value/CSSStringTest.php index 94196cfa..e1b4e519 100644 --- a/tests/Unit/Value/CSSStringTest.php +++ b/tests/Unit/Value/CSSStringTest.php @@ -6,6 +6,8 @@ use PHPUnit\Framework\TestCase; use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\Parsing\ParserState; +use Sabberworm\CSS\Settings; use Sabberworm\CSS\Value\CSSString; use Sabberworm\CSS\Value\PrimitiveValue; use Sabberworm\CSS\Value\Value; @@ -144,4 +146,18 @@ public function doesNotEscapeDoubleQuotesThatDoNotNeedToBeEscaped(): void self::assertSame("'{$input}'", (new CSSString($input))->render($outputFormat)); } + + /** + * @test + */ + public function parseThrowsForUnquotedStringThatIsNotValidUtf8WithoutMultibyteSupport(): void + { + $parserState = new ParserState("\xFF", Settings::create()->withMultibyteSupport(false)); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('The CSS is not valid UTF-8.'); + $this->expectExceptionCode(1787548272); + + CSSString::parse($parserState); + } }