diff --git a/Build/phpstan/phpstan-baseline.neon b/Build/phpstan/phpstan-baseline.neon index 0f48f69c..bd9b970c 100644 --- a/Build/phpstan/phpstan-baseline.neon +++ b/Build/phpstan/phpstan-baseline.neon @@ -42,6 +42,30 @@ parameters: count: 1 path: ../../src/Parsing/ParserState.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/Property/Declaration.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/Property/Selector.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/Property/Selector/CompoundSelector.php + + - + message: '#^Function preg_match_all is unsafe to use\. It can return FALSE instead of throwing an exception\. Please add ''use function Safe\\preg_match_all;'' at the beginning of the file to use the variant provided by the ''thecodingmachine/safe'' library\.$#' + identifier: theCodingMachineSafe.function + count: 2 + path: ../../src/Property/Selector/SpecificityCalculator.php + - message: '#^Parameter \#2 \$arguments of class Sabberworm\\CSS\\Value\\CSSFunction constructor expects array\\|Sabberworm\\CSS\\Value\\RuleValueList, Sabberworm\\CSS\\Value\\Value\|string given\.$#' identifier: argument.type diff --git a/src/Property/Declaration.php b/src/Property/Declaration.php index 1c980a67..7b1cc57b 100644 --- a/src/Property/Declaration.php +++ b/src/Property/Declaration.php @@ -18,8 +18,6 @@ use Sabberworm\CSS\Value\RuleValueList; use Sabberworm\CSS\Value\Value; -use function Safe\preg_match; - /** * `Declaration`s just have a string key (the property name) and a 'Value'. * @@ -105,7 +103,9 @@ public static function parse(ParserState $parserState, array $commentsBefore = [ */ private static function getDelimitersForPropertyValue(string $propertyName): array { - if (preg_match('/^font($|-)/', $propertyName) === 1) { + $matchResult = \preg_match('/^font($|-)/', $propertyName); + \assert(\is_int($matchResult)); + if ($matchResult === 1) { return [',', '/', ' ']; } diff --git a/src/Property/Selector.php b/src/Property/Selector.php index 39b13846..c83be0a3 100644 --- a/src/Property/Selector.php +++ b/src/Property/Selector.php @@ -15,8 +15,6 @@ use Sabberworm\CSS\Settings; use Sabberworm\CSS\ShortClassNameProvider; -use function Safe\preg_match; - /** * Class representing a single CSS selector. Selectors have to be split by the comma prior to being passed into this * class. @@ -68,7 +66,10 @@ class Selector implements Renderable public static function isValid(string $selector): bool { // Note: We need to use `static::` here as the constant is overridden in the `KeyframeSelector` class. - $numberOfMatches = preg_match(static::SELECTOR_VALIDATION_RX, $selector); + $numberOfMatches = \preg_match(static::SELECTOR_VALIDATION_RX, $selector); + if (!\is_int($numberOfMatches)) { + throw new \RuntimeException('The selector is not valid UTF-8.', 1787284398); + } return $numberOfMatches === 1; } diff --git a/src/Property/Selector/CompoundSelector.php b/src/Property/Selector/CompoundSelector.php index 80b1670c..16ea2fc6 100644 --- a/src/Property/Selector/CompoundSelector.php +++ b/src/Property/Selector/CompoundSelector.php @@ -10,8 +10,6 @@ use Sabberworm\CSS\Parsing\UnexpectedTokenException; use Sabberworm\CSS\ShortClassNameProvider; -use function Safe\preg_match; - /** * Class representing a CSS compound selector. * Selectors have to be split at combinators (space, `>`, `+`, `~`) before being passed to this class. @@ -277,7 +275,10 @@ public function getArrayRepresentation(): array private static function isValid(string $value): bool { - $numberOfMatches = preg_match(self::SELECTOR_VALIDATION_RX, $value); + $numberOfMatches = \preg_match(self::SELECTOR_VALIDATION_RX, $value); + if (!\is_int($numberOfMatches)) { + throw new \RuntimeException('The selector is not valid UTF-8.', 1787283476); + } return $numberOfMatches === 1; } diff --git a/src/Property/Selector/SpecificityCalculator.php b/src/Property/Selector/SpecificityCalculator.php index 6c7bbccd..e326b944 100644 --- a/src/Property/Selector/SpecificityCalculator.php +++ b/src/Property/Selector/SpecificityCalculator.php @@ -4,8 +4,6 @@ namespace Sabberworm\CSS\Property\Selector; -use function Safe\preg_match_all; - /** * Utility class to calculate the specificity of a CSS selector. * @@ -65,8 +63,10 @@ public static function calculate(string $selector): int /// @todo should exclude \# as well as "#" $matches = null; $b = \substr_count($selector, '#'); - $c = preg_match_all(self::NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX, $selector, $matches); - $d = preg_match_all(self::ELEMENTS_AND_PSEUDO_ELEMENTS_RX, $selector, $matches); + $c = \preg_match_all(self::NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX, $selector, $matches); + \assert(\is_int($c)); + $d = \preg_match_all(self::ELEMENTS_AND_PSEUDO_ELEMENTS_RX, $selector, $matches); + \assert(\is_int($d)); self::$cache[$selector] = ($a * 1000) + ($b * 100) + ($c * 10) + $d; } diff --git a/tests/Unit/Property/KeyframeSelectorTest.php b/tests/Unit/Property/KeyframeSelectorTest.php index e73bf232..27ee26b2 100644 --- a/tests/Unit/Property/KeyframeSelectorTest.php +++ b/tests/Unit/Property/KeyframeSelectorTest.php @@ -37,4 +37,16 @@ public function getArrayRepresentationIncludesComponent(): void self::assertSame('50%', $result['components'][0]['value']); } + + /** + * @test + */ + public function isValidThrowsForSelectorThatIsNotValidUtf8(): void + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('The selector is not valid UTF-8.'); + $this->expectExceptionCode(1787284398); + + KeyframeSelector::isValid("a\xFF"); + } } diff --git a/tests/Unit/Property/Selector/CompoundSelectorTest.php b/tests/Unit/Property/Selector/CompoundSelectorTest.php index 119d47ca..38b0f649 100644 --- a/tests/Unit/Property/Selector/CompoundSelectorTest.php +++ b/tests/Unit/Property/Selector/CompoundSelectorTest.php @@ -371,6 +371,18 @@ public function constructorThrowsExceptionWithInvalidValue(string $value): void new CompoundSelector($value); } + /** + * @test + */ + public function constructorThrowsForValueThatIsNotValidUtf8(): void + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('The selector is not valid UTF-8.'); + $this->expectExceptionCode(1787283476); + + new CompoundSelector("a\xFF"); + } + /** * @test * diff --git a/tests/Unit/Property/SelectorTest.php b/tests/Unit/Property/SelectorTest.php index 05e7e7b4..bedcc818 100644 --- a/tests/Unit/Property/SelectorTest.php +++ b/tests/Unit/Property/SelectorTest.php @@ -523,6 +523,18 @@ public function isValidForInvalidSelectorReturnsFalse(string $selector): void self::assertFalse(Selector::isValid($selector)); } + /** + * @test + */ + public function isValidThrowsForSelectorThatIsNotValidUtf8(): void + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('The selector is not valid UTF-8.'); + $this->expectExceptionCode(1787284398); + + Selector::isValid("a\xFF"); + } + /** * @test */