• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MyIntervals / PHP-CSS-Parser / 13595535696

28 Feb 2025 07:57PM UTC coverage: 55.271% (-0.01%) from 55.282%
13595535696

Pull #1017

github

web-flow
Merge 323921fc4 into 19ffd076e
Pull Request #1017: [TASK] Make `Selector` a `Renderable`

2 of 4 new or added lines in 1 file covered. (50.0%)

2 existing lines in 1 file now uncovered.

1059 of 1916 relevant lines covered (55.27%)

12.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

50.0
/src/Property/Selector.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Sabberworm\CSS\Property;
6

7
use Sabberworm\CSS\OutputFormat;
8
use Sabberworm\CSS\Renderable;
9

10
/**
11
 * Class representing a single CSS selector. Selectors have to be split by the comma prior to being passed into this
12
 * class.
13
 */
14
class Selector implements Renderable
15
{
16
    /**
17
     * regexp for specificity calculations
18
     *
19
     * @var string
20
     */
21
    private const NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX = '/
22
        (\\.[\\w]+)                   # classes
23
        |
24
        \\[(\\w+)                     # attributes
25
        |
26
        (\\:(                         # pseudo classes
27
            link|visited|active
28
            |hover|focus
29
            |lang
30
            |target
31
            |enabled|disabled|checked|indeterminate
32
            |root
33
            |nth-child|nth-last-child|nth-of-type|nth-last-of-type
34
            |first-child|last-child|first-of-type|last-of-type
35
            |only-child|only-of-type
36
            |empty|contains
37
        ))
38
        /ix';
39

40
    /**
41
     * regexp for specificity calculations
42
     *
43
     * @var string
44
     */
45
    private const ELEMENTS_AND_PSEUDO_ELEMENTS_RX = '/
46
        ((^|[\\s\\+\\>\\~]+)[\\w]+   # elements
47
        |
48
        \\:{1,2}(                    # pseudo-elements
49
            after|before|first-letter|first-line|selection
50
        ))
51
        /ix';
52

53
    /**
54
     * regexp for specificity calculations
55
     *
56
     * @var string
57
     *
58
     * @internal since 8.5.2
59
     */
60
    public const SELECTOR_VALIDATION_RX = '/
61
        ^(
62
            (?:
63
                [a-zA-Z0-9\\x{00A0}-\\x{FFFF}_^$|*="\'~\\[\\]()\\-\\s\\.:#+>]* # any sequence of valid unescaped characters
64
                (?:\\\\.)?                                                     # a single escaped character
65
                (?:([\'"]).*?(?<!\\\\)\\2)?                                    # a quoted text like [id="example"]
66
            )*
67
        )$
68
        /ux';
69

70
    /**
71
     * @var string
72
     */
73
    private $selector;
74

75
    /**
76
     * @var int|null
77
     */
78
    private $specificity;
79

80
    /**
81
     * @param string $selector
82
     *
83
     * @return bool
84
     */
85
    public static function isValid($selector)
×
86
    {
87
        return \preg_match(static::SELECTOR_VALIDATION_RX, $selector);
×
88
    }
89

90
    /**
91
     * @param string $selector
92
     * @param bool $calculateSpecificity @deprecated since V8.8.0, will be removed in V9.0.0
93
     */
94
    public function __construct($selector, $calculateSpecificity = false)
8✔
95
    {
96
        $this->setSelector($selector);
8✔
97
        if ($calculateSpecificity) {
8✔
98
            $this->getSpecificity();
×
99
        }
100
    }
8✔
101

102
    /**
103
     * @return string
104
     */
105
    public function getSelector()
7✔
106
    {
107
        return $this->selector;
7✔
108
    }
109

110
    /**
111
     * @param string $selector
112
     */
113
    public function setSelector($selector): void
8✔
114
    {
115
        $this->selector = \trim($selector);
8✔
116
        $this->specificity = null;
8✔
117
    }
8✔
118

119
    /**
120
     * @deprecated in V8.8.0, will be removed in V9.0.0. Use `render` instead.
121
     */
122
    public function __toString(): string
1✔
123
    {
124
        return $this->getSelector();
1✔
125
    }
126

127
    /**
128
     * @return int
129
     */
130
    public function getSpecificity()
×
131
    {
132
        if ($this->specificity === null) {
×
133
            $a = 0;
×
134
            /// @todo should exclude \# as well as "#"
135
            $aMatches = null;
×
136
            $b = \substr_count($this->selector, '#');
×
137
            $c = \preg_match_all(self::NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX, $this->selector, $aMatches);
×
138
            $d = \preg_match_all(self::ELEMENTS_AND_PSEUDO_ELEMENTS_RX, $this->selector, $aMatches);
×
139
            $this->specificity = ($a * 1000) + ($b * 100) + ($c * 10) + $d;
×
140
        }
141
        return $this->specificity;
×
142
    }
143

144
    public function render(OutputFormat $outputFormat): string
4✔
145
    {
146
        return $this->getSelector();
4✔
147
    }
148

149
    /**
150
     * @return never
151
     *
152
     * @throws \BadMethodCallException
153
     */
NEW
UNCOV
154
    public function getLineNo(): int
×
155
    {
NEW
UNCOV
156
        throw new \BadMethodCallException('Not implemented', 1740654131);
×
157
    }
158
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc