From c9498978f20e45a84ea5ecf810420a827ffc5abf Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Thu, 5 Feb 2026 20:10:11 +0000 Subject: [PATCH 01/11] [FEATURE] Represent `Selector` as `Component` objects Part of #1325. --- CHANGELOG.md | 3 + src/Property/Selector.php | 85 ++++++++++----- tests/Unit/Property/KeyframeSelectorTest.php | 13 +++ tests/Unit/Property/SelectorTest.php | 103 ++++++++++++++++++- 4 files changed, 175 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 179a6cfa..f14ec704 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,9 @@ Please also have a look at our ### Changed +- `Selector` is now represented as a sequence of `Selector\Component` objects + which can be accessed via `getComponents()`, manipulated individually, or set + via `setComponents()` (#1478, #1486, #1487, #1488, #1494, #1496) - `Selector::setSelector()` and `Selector` constructor will now throw exception upon provision of an invalid selectior (#1498, #1502) - Clean up extra whitespace in CSS selector (#1398) diff --git a/src/Property/Selector.php b/src/Property/Selector.php index 4474d722..6e0646da 100644 --- a/src/Property/Selector.php +++ b/src/Property/Selector.php @@ -11,12 +11,11 @@ use Sabberworm\CSS\Property\Selector\Combinator; use Sabberworm\CSS\Property\Selector\Component; use Sabberworm\CSS\Property\Selector\CompoundSelector; -use Sabberworm\CSS\Property\Selector\SpecificityCalculator; use Sabberworm\CSS\Renderable; +use Sabberworm\CSS\Settings; use Sabberworm\CSS\ShortClassNameProvider; use function Safe\preg_match; -use function Safe\preg_replace; /** * Class representing a single CSS selector. Selectors have to be split by the comma prior to being passed into this @@ -59,9 +58,9 @@ class Selector implements Renderable /ux'; /** - * @var non-empty-string + * @var list */ - private $selector; + private $components = []; /** * @internal since V8.8.0 @@ -79,9 +78,13 @@ public static function isValid(string $selector): bool * * @throws \UnexpectedValueException if the selector is not valid */ - final public function __construct(string $selector) + final public function __construct(string $selector = '') { - $this->setSelector($selector); + // Allow construction of empty object for content to be set via `setComponents()`. + // (`setSelector()` will throw an exception when provided with an empty string.) + if ($selector !== '') { + $this->setSelector($selector); + } } /** @@ -144,17 +147,27 @@ public static function parse(ParserState $parserState, array &$comments = []): s ); } - $selectorString = ''; - foreach ($selectorParts as $selectorPart) { - $selectorPartValue = $selectorPart->getValue(); - if (\in_array($selectorPartValue, ['>', '+', '~'], true)) { - $selectorString .= ' ' . $selectorPartValue . ' '; - } else { - $selectorString .= $selectorPartValue; - } - } + return (new static())->setComponents($selectorParts); + } - return new static($selectorString); + /** + * @return list + */ + public function getComponents(): array + { + return $this->components; + } + + /** + * @param list $components + * This should be an alternating sequence of `CompoundSelector` and `Combinator`, starting and ending with a + * `CompoundSelector`, and may be a single `CompoundSelector`. + */ + public function setComponents(array $components): self + { + $this->components = $components; + + return $this; } /** @@ -162,7 +175,7 @@ public static function parse(ParserState $parserState, array &$comments = []): s */ public function getSelector(): string { - return $this->selector; + return $this->render(new OutputFormat()); } /** @@ -172,16 +185,20 @@ public function getSelector(): string */ public function setSelector(string $selector): void { - if (!self::isValid($selector)) { - throw new \UnexpectedValueException("Selector `$selector` is not valid."); - } + $parserState = new ParserState($selector, Settings::create()); - $selector = \trim($selector); + $components = self::parseComponents($parserState); - $hasAttribute = \strpos($selector, '[') !== false; + // Check that the selector has been fully parsed: + if (!$parserState->isEnd()) { + throw new UnexpectedTokenException( + 'EOF', + $parserState->peek(5), + 'literal' + ); + } - // Whitespace can't be adjusted within an attribute selector, as it would change its meaning - $this->selector = !$hasAttribute ? preg_replace('/\\s++/', ' ', $selector) : $selector; + $this->components = $components; } /** @@ -189,12 +206,22 @@ public function setSelector(string $selector): void */ public function getSpecificity(): int { - return SpecificityCalculator::calculate($this->selector); + return \array_sum(\array_map( + static function (Component $component): int { + return $component->getSpecificity(); + }, + $this->components + )); } public function render(OutputFormat $outputFormat): string { - return $this->getSelector(); + return \implode('', \array_map( + static function (Component $component) use ($outputFormat): string { + return $component->render($outputFormat); + }, + $this->components + )); } /** @@ -206,6 +233,12 @@ public function getArrayRepresentation(): array { return [ 'class' => $this->getShortClassName(), + 'components' => \array_map( + static function (Component $component): array { + return $component->getArrayRepresentation(); + }, + $this->components + ), ]; } } diff --git a/tests/Unit/Property/KeyframeSelectorTest.php b/tests/Unit/Property/KeyframeSelectorTest.php index 4533e677..723fcede 100644 --- a/tests/Unit/Property/KeyframeSelectorTest.php +++ b/tests/Unit/Property/KeyframeSelectorTest.php @@ -6,6 +6,7 @@ use PHPUnit\Framework\TestCase; use Sabberworm\CSS\Property\KeyframeSelector; +use Sabberworm\CSS\Property\Selector\CompoundSelector; /** * @covers \Sabberworm\CSS\Property\KeyframeSelector @@ -24,4 +25,16 @@ public function getArrayRepresentationIncludesClassName(): void self::assertSame('KeyframeSelector', $result['class']); } + + /** + * @test + */ + public function getArrayRepresentationIncludesComponent(): void + { + $subject = (new KeyframeSelector())->setComponents([new CompoundSelector('50%')]); + + $result = $subject->getArrayRepresentation(); + + self::assertSame('50%', $result['components'][0]['value']); + } } diff --git a/tests/Unit/Property/SelectorTest.php b/tests/Unit/Property/SelectorTest.php index 58a98fa6..7dfb0c31 100644 --- a/tests/Unit/Property/SelectorTest.php +++ b/tests/Unit/Property/SelectorTest.php @@ -9,6 +9,9 @@ use Sabberworm\CSS\Parsing\ParserState; use Sabberworm\CSS\Parsing\UnexpectedTokenException; use Sabberworm\CSS\Property\Selector; +use Sabberworm\CSS\Property\Selector\Combinator; +use Sabberworm\CSS\Property\Selector\Component; +use Sabberworm\CSS\Property\Selector\CompoundSelector; use Sabberworm\CSS\Renderable; use Sabberworm\CSS\Settings; use TRegx\PhpUnit\DataProviders\DataProvider; @@ -315,10 +318,17 @@ public function parseExtractsTwoCommentsFromSelector(): void * @test * * @dataProvider provideInvalidSelectors + * @dataProvider provideInvalidSelectorsForParse */ public function constructorThrowsExceptionWithInvalidSelector(string $selector): void { - $this->expectException(\UnexpectedValueException::class); + // An empty string is allowed to construct an empty object + if ($selector === '') { + self::expectNotToPerformAssertions(); + return; + } + + $this->expectException(UnexpectedTokenException::class); new Selector($selector); } @@ -327,16 +337,91 @@ public function constructorThrowsExceptionWithInvalidSelector(string $selector): * @test * * @dataProvider provideInvalidSelectors + * @dataProvider provideInvalidSelectorsForParse */ public function setSelectorThrowsExceptionWithInvalidSelector(string $selector): void { - $this->expectException(\UnexpectedValueException::class); + $this->expectException(UnexpectedTokenException::class); $subject = new Selector('a'); $subject->setSelector($selector); } + /** + * @return array, 1: list}> + */ + public static function provideComponentsAndArrayRepresentation(): array + { + return [ + 'simple selector' => [ + [new CompoundSelector('p')], + [ + [ + 'class' => 'CompoundSelector', + 'value' => 'p', + ], + ], + ], + 'selector with combinator' => [ + [ + new CompoundSelector('ul'), + new Combinator('>'), + new CompoundSelector('li'), + ], + [ + [ + 'class' => 'CompoundSelector', + 'value' => 'ul', + ], + [ + 'class' => 'Combinator', + 'value' => '>', + ], + [ + 'class' => 'CompoundSelector', + 'value' => 'li', + ], + ], + ], + ]; + } + + /** + * @test + * + * @param list $components + * @param list $expectedRepresenation + * + * @dataProvider provideComponentsAndArrayRepresentation + */ + public function setComponentsSetsComponentsProvided(array $components, array $expectedRepresenation): void + { + $subject = new Selector(); + + $subject->setComponents($components); + + $representation = $subject->getArrayRepresentation()['components']; + self::assertSame($expectedRepresenation, $representation); + } + + /** + * @test + * + * @param list $components + * + * @dataProvider provideComponentsAndArrayRepresentation + */ + public function getComponentsReturnsComponentsSet(array $components): void + { + $subject = new Selector(); + $subject->setComponents($components); + + $result = $subject->getComponents(); + + self::assertSame($components, $result); + } + /** * @test * @@ -446,10 +531,22 @@ public function doesNotCleanupSpacesWithinAttributeSelector(): void */ public function getArrayRepresentationIncludesClassName(): void { - $subject = new Selector('a'); + $subject = new Selector(); $result = $subject->getArrayRepresentation(); self::assertSame('Selector', $result['class']); } + + /** + * @test + */ + public function getArrayRepresentationIncludesComponent(): void + { + $subject = (new Selector())->setComponents([new CompoundSelector('p.test')]); + + $result = $subject->getArrayRepresentation(); + + self::assertSame('p.test', $result['components'][0]['value']); + } } From 00383b1383bec8a6fea3892a802a3be72a1da73f Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Sat, 7 Feb 2026 00:55:23 +0000 Subject: [PATCH 02/11] Add deprecation notices --- src/Property/Selector.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Property/Selector.php b/src/Property/Selector.php index 6e0646da..5ca94d02 100644 --- a/src/Property/Selector.php +++ b/src/Property/Selector.php @@ -172,6 +172,8 @@ public function setComponents(array $components): self /** * @return non-empty-string + * + * @deprecated in version 9.2, will be removed in v10.0. Use either `getComponents()` or `render()` instead. */ public function getSelector(): string { @@ -182,6 +184,8 @@ public function getSelector(): string * @param non-empty-string $selector * * @throws \UnexpectedValueException if the selector is not valid + * + * @deprecated in version 9.2, will be removed in v10.0. Use `setComponents()` instead. */ public function setSelector(string $selector): void { From ee4881844b8d5d58587d09aa5af9b134a5a75d4c Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Sat, 7 Feb 2026 11:38:53 +0000 Subject: [PATCH 03/11] Split data providers to include/exclude `''` as necessary --- tests/Unit/Property/SelectorTest.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/Unit/Property/SelectorTest.php b/tests/Unit/Property/SelectorTest.php index 7dfb0c31..3f030c12 100644 --- a/tests/Unit/Property/SelectorTest.php +++ b/tests/Unit/Property/SelectorTest.php @@ -198,6 +198,7 @@ public static function provideInvalidSelectorsForParse(): array * @test * * @dataProvider provideInvalidSelectors + * @dataProvider provideEmptyStringAsInvalidSelector * @dataProvider provideInvalidSelectorsForParse */ public function parseThrowsExceptionWithInvalidSelector(string $selector): void @@ -322,12 +323,6 @@ public function parseExtractsTwoCommentsFromSelector(): void */ public function constructorThrowsExceptionWithInvalidSelector(string $selector): void { - // An empty string is allowed to construct an empty object - if ($selector === '') { - self::expectNotToPerformAssertions(); - return; - } - $this->expectException(UnexpectedTokenException::class); new Selector($selector); @@ -337,6 +332,7 @@ public function constructorThrowsExceptionWithInvalidSelector(string $selector): * @test * * @dataProvider provideInvalidSelectors + * @dataProvider provideEmptyStringAsInvalidSelector * @dataProvider provideInvalidSelectorsForParse */ public function setSelectorThrowsExceptionWithInvalidSelector(string $selector): void From d224995f86d062e3cf7ca98db179e1508e975abc Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Sat, 7 Feb 2026 11:53:50 +0000 Subject: [PATCH 04/11] Add `getComponents` test for empty object --- tests/Unit/Property/SelectorTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/Unit/Property/SelectorTest.php b/tests/Unit/Property/SelectorTest.php index 3f030c12..ab839811 100644 --- a/tests/Unit/Property/SelectorTest.php +++ b/tests/Unit/Property/SelectorTest.php @@ -401,6 +401,18 @@ public function setComponentsSetsComponentsProvided(array $components, array $ex self::assertSame($expectedRepresenation, $representation); } + /** + * @test + */ + public function getComponentsReturnsEmptyArrayIfNotSet(): void + { + $subject = new Selector(); + + $result = $subject->getComponents(); + + self::assertSame([], $result); + } + /** * @test * From 961e8da47b965b05bf4112abe6f4c81574bedb29 Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Sun, 8 Feb 2026 07:06:03 +0000 Subject: [PATCH 05/11] Change `Selector` constructor to accept string or `list` --- src/Property/Selector.php | 13 +++++++------ tests/Unit/Property/KeyframeSelectorTest.php | 2 +- tests/Unit/Property/SelectorTest.php | 10 +++++----- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Property/Selector.php b/src/Property/Selector.php index 5ca94d02..9991aa11 100644 --- a/src/Property/Selector.php +++ b/src/Property/Selector.php @@ -74,16 +74,17 @@ public static function isValid(string $selector): bool } /** - * @param non-empty-string $selector + * @param non-empty-string|list $selector + * Providing a string is deprecated in version 9.2 and will not work from v10.0 * * @throws \UnexpectedValueException if the selector is not valid */ - final public function __construct(string $selector = '') + final public function __construct($selector) { - // Allow construction of empty object for content to be set via `setComponents()`. - // (`setSelector()` will throw an exception when provided with an empty string.) - if ($selector !== '') { + if (\is_string($selector)) { $this->setSelector($selector); + } else { + $this->setComponents($selector); } } @@ -147,7 +148,7 @@ public static function parse(ParserState $parserState, array &$comments = []): s ); } - return (new static())->setComponents($selectorParts); + return new static($selectorParts); } /** diff --git a/tests/Unit/Property/KeyframeSelectorTest.php b/tests/Unit/Property/KeyframeSelectorTest.php index 723fcede..e73bf232 100644 --- a/tests/Unit/Property/KeyframeSelectorTest.php +++ b/tests/Unit/Property/KeyframeSelectorTest.php @@ -31,7 +31,7 @@ public function getArrayRepresentationIncludesClassName(): void */ public function getArrayRepresentationIncludesComponent(): void { - $subject = (new KeyframeSelector())->setComponents([new CompoundSelector('50%')]); + $subject = new KeyframeSelector([new CompoundSelector('50%')]); $result = $subject->getArrayRepresentation(); diff --git a/tests/Unit/Property/SelectorTest.php b/tests/Unit/Property/SelectorTest.php index ab839811..7fa84f1b 100644 --- a/tests/Unit/Property/SelectorTest.php +++ b/tests/Unit/Property/SelectorTest.php @@ -393,7 +393,7 @@ public static function provideComponentsAndArrayRepresentation(): array */ public function setComponentsSetsComponentsProvided(array $components, array $expectedRepresenation): void { - $subject = new Selector(); + $subject = new Selector([]); $subject->setComponents($components); @@ -406,7 +406,7 @@ public function setComponentsSetsComponentsProvided(array $components, array $ex */ public function getComponentsReturnsEmptyArrayIfNotSet(): void { - $subject = new Selector(); + $subject = new Selector([]); $result = $subject->getComponents(); @@ -422,7 +422,7 @@ public function getComponentsReturnsEmptyArrayIfNotSet(): void */ public function getComponentsReturnsComponentsSet(array $components): void { - $subject = new Selector(); + $subject = new Selector([]); $subject->setComponents($components); $result = $subject->getComponents(); @@ -539,7 +539,7 @@ public function doesNotCleanupSpacesWithinAttributeSelector(): void */ public function getArrayRepresentationIncludesClassName(): void { - $subject = new Selector(); + $subject = new Selector([]); $result = $subject->getArrayRepresentation(); @@ -551,7 +551,7 @@ public function getArrayRepresentationIncludesClassName(): void */ public function getArrayRepresentationIncludesComponent(): void { - $subject = (new Selector())->setComponents([new CompoundSelector('p.test')]); + $subject = new Selector([new CompoundSelector('p.test')]); $result = $subject->getArrayRepresentation(); From 0be342b75f67cced090778cc034f68f7828a10cf Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Sun, 8 Feb 2026 10:46:49 +0000 Subject: [PATCH 06/11] Enforce that `Selector` is set/constructed with non-empty list/string --- src/Property/Selector.php | 6 +++--- tests/Unit/Property/SelectorTest.php | 20 +++++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Property/Selector.php b/src/Property/Selector.php index 9991aa11..27a525c2 100644 --- a/src/Property/Selector.php +++ b/src/Property/Selector.php @@ -58,9 +58,9 @@ class Selector implements Renderable /ux'; /** - * @var list + * @var non-empty-list */ - private $components = []; + private $components; /** * @internal since V8.8.0 @@ -74,7 +74,7 @@ public static function isValid(string $selector): bool } /** - * @param non-empty-string|list $selector + * @param non-empty-string|non-empty-list $selector * Providing a string is deprecated in version 9.2 and will not work from v10.0 * * @throws \UnexpectedValueException if the selector is not valid diff --git a/tests/Unit/Property/SelectorTest.php b/tests/Unit/Property/SelectorTest.php index 7fa84f1b..063a6508 100644 --- a/tests/Unit/Property/SelectorTest.php +++ b/tests/Unit/Property/SelectorTest.php @@ -198,7 +198,6 @@ public static function provideInvalidSelectorsForParse(): array * @test * * @dataProvider provideInvalidSelectors - * @dataProvider provideEmptyStringAsInvalidSelector * @dataProvider provideInvalidSelectorsForParse */ public function parseThrowsExceptionWithInvalidSelector(string $selector): void @@ -332,7 +331,6 @@ public function constructorThrowsExceptionWithInvalidSelector(string $selector): * @test * * @dataProvider provideInvalidSelectors - * @dataProvider provideEmptyStringAsInvalidSelector * @dataProvider provideInvalidSelectorsForParse */ public function setSelectorThrowsExceptionWithInvalidSelector(string $selector): void @@ -393,7 +391,7 @@ public static function provideComponentsAndArrayRepresentation(): array */ public function setComponentsSetsComponentsProvided(array $components, array $expectedRepresenation): void { - $subject = new Selector([]); + $subject = new Selector([new CompoundSelector('p')]); $subject->setComponents($components); @@ -403,26 +401,30 @@ public function setComponentsSetsComponentsProvided(array $components, array $ex /** * @test + * + * @param non-empty-list $components + * + * @dataProvider provideComponentsAndArrayRepresentation */ - public function getComponentsReturnsEmptyArrayIfNotSet(): void + public function getComponentsReturnsComponentsProvidedToConstructor(array $components): void { - $subject = new Selector([]); + $subject = new Selector($components); $result = $subject->getComponents(); - self::assertSame([], $result); + self::assertSame($components, $result); } /** * @test * - * @param list $components + * @param non-empty-list $components * * @dataProvider provideComponentsAndArrayRepresentation */ public function getComponentsReturnsComponentsSet(array $components): void { - $subject = new Selector([]); + $subject = new Selector([new CompoundSelector('p')]); $subject->setComponents($components); $result = $subject->getComponents(); @@ -539,7 +541,7 @@ public function doesNotCleanupSpacesWithinAttributeSelector(): void */ public function getArrayRepresentationIncludesClassName(): void { - $subject = new Selector([]); + $subject = new Selector([new CompoundSelector('p')]); $result = $subject->getArrayRepresentation(); From d60ec3ad17edcf026e4fbbd958ff8e9c096c71ec Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Mon, 9 Feb 2026 13:37:47 +0000 Subject: [PATCH 07/11] Tighten type annotation for `get/setComponents` --- src/Property/Selector.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Property/Selector.php b/src/Property/Selector.php index 27a525c2..eaee2506 100644 --- a/src/Property/Selector.php +++ b/src/Property/Selector.php @@ -152,7 +152,7 @@ public static function parse(ParserState $parserState, array &$comments = []): s } /** - * @return list + * @return non-empty-list */ public function getComponents(): array { @@ -160,7 +160,7 @@ public function getComponents(): array } /** - * @param list $components + * @param non-empty-list $components * This should be an alternating sequence of `CompoundSelector` and `Combinator`, starting and ending with a * `CompoundSelector`, and may be a single `CompoundSelector`. */ From 6f95107330ffb5bf50e0981d3aefa14aeccaa8c8 Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Mon, 9 Feb 2026 18:38:42 +0000 Subject: [PATCH 08/11] Add test for constructor with `Component`s --- tests/Unit/Property/SelectorTest.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/Unit/Property/SelectorTest.php b/tests/Unit/Property/SelectorTest.php index 063a6508..0a98a761 100644 --- a/tests/Unit/Property/SelectorTest.php +++ b/tests/Unit/Property/SelectorTest.php @@ -381,6 +381,22 @@ public static function provideComponentsAndArrayRepresentation(): array ]; } + /** + * @test + * + * @param list $components + * @param list $expectedRepresenation + * + * @dataProvider provideComponentsAndArrayRepresentation + */ + public function constructsWithComponentsProvided(array $components, array $expectedRepresenation): void + { + $subject = new Selector($components); + + $representation = $subject->getArrayRepresentation()['components']; + self::assertSame($expectedRepresenation, $representation); + } + /** * @test * From 3c4efc8d30f62ef9b604aa9860f8770ff6de0cd0 Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Mon, 9 Feb 2026 22:16:55 +0000 Subject: [PATCH 09/11] Add fluent interface test for `setComponents()` --- tests/Unit/Property/SelectorTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/Unit/Property/SelectorTest.php b/tests/Unit/Property/SelectorTest.php index 0a98a761..0e98015f 100644 --- a/tests/Unit/Property/SelectorTest.php +++ b/tests/Unit/Property/SelectorTest.php @@ -397,6 +397,18 @@ public function constructsWithComponentsProvided(array $components, array $expec self::assertSame($expectedRepresenation, $representation); } + /** + * @test + */ + public function setComponentsProvidesFluentInterface(): void + { + $subject = new Selector([new CompoundSelector('p')]); + + $result = $subject->setComponents([new CompoundSelector('li')]); + + self::assertSame($subject, $result); + } + /** * @test * From aa1c89d42c002d2c2ca2132f70672c2648c0a9dc Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Mon, 9 Feb 2026 22:27:37 +0000 Subject: [PATCH 10/11] Tighten some type annotations in `SelectorTest` Use `non-empty-list` and `non-empty-string` where applicable. --- tests/Unit/Property/SelectorTest.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/Unit/Property/SelectorTest.php b/tests/Unit/Property/SelectorTest.php index 0e98015f..d22190da 100644 --- a/tests/Unit/Property/SelectorTest.php +++ b/tests/Unit/Property/SelectorTest.php @@ -343,7 +343,13 @@ public function setSelectorThrowsExceptionWithInvalidSelector(string $selector): } /** - * @return array, 1: list}> + * @return array< + * non-empty-string, + * array{ + * 0: non-empty-list, + * 1: non-empty-list + * } + * > */ public static function provideComponentsAndArrayRepresentation(): array { @@ -384,8 +390,8 @@ public static function provideComponentsAndArrayRepresentation(): array /** * @test * - * @param list $components - * @param list $expectedRepresenation + * @param non-empty-list $components + * @param non-empty-list $expectedRepresenation * * @dataProvider provideComponentsAndArrayRepresentation */ @@ -412,8 +418,8 @@ public function setComponentsProvidesFluentInterface(): void /** * @test * - * @param list $components - * @param list $expectedRepresenation + * @param non-empty-list $components + * @param non-empty-list $expectedRepresenation * * @dataProvider provideComponentsAndArrayRepresentation */ From 30fcbe12fd78846792714dc26ad99f8d6cb2ea3f Mon Sep 17 00:00:00 2001 From: Jake Hotson Date: Mon, 9 Feb 2026 22:39:13 +0000 Subject: [PATCH 11/11] Correct `@throws` annotation for `setSelector()` This now may throw an `UnexpectedTokenException` rather than an `UnexpectedValueException`. --- src/Property/Selector.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Property/Selector.php b/src/Property/Selector.php index eaee2506..66c061ad 100644 --- a/src/Property/Selector.php +++ b/src/Property/Selector.php @@ -77,7 +77,7 @@ public static function isValid(string $selector): bool * @param non-empty-string|non-empty-list $selector * Providing a string is deprecated in version 9.2 and will not work from v10.0 * - * @throws \UnexpectedValueException if the selector is not valid + * @throws UnexpectedTokenException if the selector is not valid */ final public function __construct($selector) { @@ -184,7 +184,7 @@ public function getSelector(): string /** * @param non-empty-string $selector * - * @throws \UnexpectedValueException if the selector is not valid + * @throws UnexpectedTokenException if the selector is not valid * * @deprecated in version 9.2, will be removed in v10.0. Use `setComponents()` instead. */