From 4a5cf6566e05a3d4b0d8dd743984dcddc80a5b0a Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Tue, 9 Dec 2025 03:17:56 +0000 Subject: [PATCH] [CLEANUP] Extract method `DeclarationBlock::parseSelectors` This now does the selector-parsing that `parse()` used to do itself. Should help with #1324 and https://github.com/MyIntervals/PHP-CSS-Parser/pull/1398#issuecomment-3590773005 --- src/RuleSet/DeclarationBlock.php | 121 ++++++++++++++++++------------- 1 file changed, 69 insertions(+), 52 deletions(-) diff --git a/src/RuleSet/DeclarationBlock.php b/src/RuleSet/DeclarationBlock.php index 8a0f4e48..b5ce1eda 100644 --- a/src/RuleSet/DeclarationBlock.php +++ b/src/RuleSet/DeclarationBlock.php @@ -4,6 +4,7 @@ namespace Sabberworm\CSS\RuleSet; +use Sabberworm\CSS\Comment\Comment; use Sabberworm\CSS\Comment\CommentContainer; use Sabberworm\CSS\CSSElement; use Sabberworm\CSS\CSSList\CSSList; @@ -65,58 +66,7 @@ public static function parse(ParserState $parserState, ?CSSList $list = null): ? $comments = []; $result = new DeclarationBlock($parserState->currentLine()); try { - $selectors = []; - $selectorParts = []; - $stringWrapperCharacter = null; - $functionNestingLevel = 0; - $consumedNextCharacter = false; - static $stopCharacters = ['{', '}', '\'', '"', '(', ')', ',']; - do { - if (!$consumedNextCharacter) { - $selectorParts[] = $parserState->consume(1); - } - $selectorParts[] = $parserState->consumeUntil($stopCharacters, false, false, $comments); - $nextCharacter = $parserState->peek(); - $consumedNextCharacter = false; - switch ($nextCharacter) { - case '\'': - // The fallthrough is intentional. - case '"': - if (!\is_string($stringWrapperCharacter)) { - $stringWrapperCharacter = $nextCharacter; - } elseif ($stringWrapperCharacter === $nextCharacter) { - if (\substr(\end($selectorParts), -1) !== '\\') { - $stringWrapperCharacter = null; - } - } - break; - case '(': - if (!\is_string($stringWrapperCharacter)) { - ++$functionNestingLevel; - } - break; - case ')': - if (!\is_string($stringWrapperCharacter)) { - if ($functionNestingLevel <= 0) { - throw new UnexpectedTokenException('anything but', ')'); - } - --$functionNestingLevel; - } - break; - case ',': - if (!\is_string($stringWrapperCharacter) && $functionNestingLevel === 0) { - $selectors[] = \implode('', $selectorParts); - $selectorParts = []; - $parserState->consume(1); - $consumedNextCharacter = true; - } - break; - } - } while (!\in_array($nextCharacter, ['{', '}'], true) || \is_string($stringWrapperCharacter)); - if ($functionNestingLevel !== 0) { - throw new UnexpectedTokenException(')', $nextCharacter); - } - $selectors[] = \implode('', $selectorParts); // add final or only selector + $selectors = self::parseSelectors($parserState, $comments); $result->setSelectors($selectors, $list); if ($parserState->comes('{')) { $parserState->consume(1); @@ -303,4 +253,71 @@ public function render(OutputFormat $outputFormat): string return $result; } + + /** + * @param array $comments + * + * @return list + * + * @throws UnexpectedTokenException + */ + private static function parseSelectors(ParserState $parserState, array &$comments): array + { + $selectors = []; + $selectorParts = []; + $stringWrapperCharacter = null; + $functionNestingLevel = 0; + $consumedNextCharacter = false; + static $stopCharacters = ['{', '}', '\'', '"', '(', ')', ',']; + + do { + if (!$consumedNextCharacter) { + $selectorParts[] = $parserState->consume(1); + } + $selectorParts[] = $parserState->consumeUntil($stopCharacters, false, false, $comments); + $nextCharacter = $parserState->peek(); + $consumedNextCharacter = false; + switch ($nextCharacter) { + case '\'': + // The fallthrough is intentional. + case '"': + if (!\is_string($stringWrapperCharacter)) { + $stringWrapperCharacter = $nextCharacter; + } elseif ($stringWrapperCharacter === $nextCharacter) { + if (\substr(\end($selectorParts), -1) !== '\\') { + $stringWrapperCharacter = null; + } + } + break; + case '(': + if (!\is_string($stringWrapperCharacter)) { + ++$functionNestingLevel; + } + break; + case ')': + if (!\is_string($stringWrapperCharacter)) { + if ($functionNestingLevel <= 0) { + throw new UnexpectedTokenException('anything but', ')'); + } + --$functionNestingLevel; + } + break; + case ',': + if (!\is_string($stringWrapperCharacter) && $functionNestingLevel === 0) { + $selectors[] = \implode('', $selectorParts); + $selectorParts = []; + $parserState->consume(1); + $consumedNextCharacter = true; + } + break; + } + } while (!\in_array($nextCharacter, ['{', '}'], true) || \is_string($stringWrapperCharacter)); + + if ($functionNestingLevel !== 0) { + throw new UnexpectedTokenException(')', $nextCharacter); + } + $selectors[] = \implode('', $selectorParts); // add final or only selector + + return $selectors; + } }