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

MyIntervals / PHP-CSS-Parser / 13604106976

01 Mar 2025 11:02AM UTC coverage: 55.736% (-0.06%) from 55.799%
13604106976

Pull #1028

github

web-flow
Merge 26eab633a into 50045f732
Pull Request #1028: [TASK] Always calculate selector specificity on demand

1064 of 1909 relevant lines covered (55.74%)

12.34 hits per line

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

90.48
/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
     * @return bool
77
     *
78
     * @internal since V8.8.0
79
     */
80
    public static function isValid(string $selector)
×
81
    {
82
        return \preg_match(static::SELECTOR_VALIDATION_RX, $selector);
×
83
    }
84

85
    public function __construct(string $selector)
22✔
86
    {
87
        $this->setSelector($selector);
22✔
88
    }
22✔
89

90
    public function getSelector(): string
7✔
91
    {
92
        return $this->selector;
7✔
93
    }
94

95
    public function setSelector(string $selector): void
22✔
96
    {
97
        $this->selector = \trim($selector);
22✔
98
    }
22✔
99

100
    /**
101
     * @deprecated in V8.8.0, will be removed in V9.0.0. Use `render` instead.
102
     */
103
    public function __toString(): string
1✔
104
    {
105
        return $this->getSelector();
1✔
106
    }
107

108
    /**
109
     * @return int<0, max>
110
     */
111
    public function getSpecificity(): int
14✔
112
    {
113
        $a = 0;
14✔
114
        /// @todo should exclude \# as well as "#"
115
        $aMatches = null;
14✔
116
        $b = \substr_count($this->selector, '#');
14✔
117
        $c = \preg_match_all(self::NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX, $this->selector, $aMatches);
14✔
118
        $d = \preg_match_all(self::ELEMENTS_AND_PSEUDO_ELEMENTS_RX, $this->selector, $aMatches);
14✔
119

120
        return ($a * 1000) + ($b * 100) + ($c * 10) + $d;
14✔
121
    }
122

123
    public function render(OutputFormat $outputFormat): string
4✔
124
    {
125
        return $this->getSelector();
4✔
126
    }
127
}
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