Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
92.31% covered (success)
92.31%
12 / 13
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
ComplexSelector
92.31% covered (success)
92.31%
12 / 13
66.67% covered (warning)
66.67%
2 / 3
7.02
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 specificity
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 toString
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
4.03
1<?php
2
3declare(strict_types=1);
4
5namespace Phpdftk\Css\Selector;
6
7/**
8 * One selector in a SelectorList — a sequence of compound selectors joined
9 * by combinators (descendant / `>` / `+` / `~` / `||`).
10 *
11 * Holds the parsed `compounds` list per the cross-package contract. The
12 * right-most compound's `combinatorToNext` is `null`. The raw source text
13 * is preserved for diagnostics / serialization.
14 */
15final readonly class ComplexSelector
16{
17    /**
18     * @param list<CompoundSelectorWithCombinator> $compounds
19     * @param ?Combinator $leadingCombinator When non-null, this is
20     *     a CSS Selectors 4 §17.5 *relative selector* — the
21     *     combinator binds the selector against an implicit
22     *     subject (used inside `:has(...)`). For example
23     *     `:has(> .child)` parses to a relative selector with
24     *     `leadingCombinator = Combinator::Child` and one compound
25     *     `.child`. The Matcher's `hasMatches` dispatches on this
26     *     field to decide whether to walk descendants, just
27     *     children, the next sibling, or subsequent siblings.
28     */
29    public function __construct(
30        public array $compounds,
31        public string $text = '',
32        public ?Combinator $leadingCombinator = null,
33    ) {}
34
35    public function specificity(): Specificity
36    {
37        $total = new Specificity();
38        foreach ($this->compounds as $c) {
39            $total = $total->add($c->compound->specificity());
40        }
41        return $total;
42    }
43
44    public function toString(): string
45    {
46        $parts = [];
47        foreach ($this->compounds as $c) {
48            $parts[] = $c->compound->toString();
49            if ($c->combinatorToNext !== null) {
50                if ($c->combinatorToNext === Combinator::Descendant) {
51                    $parts[] = ' ';
52                } else {
53                    $parts[] = ' ' . $c->combinatorToNext->value . ' ';
54                }
55            }
56        }
57        return implode('', $parts);
58    }
59}