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

MyIntervals / PHP-CSS-Parser / 21410963225

27 Jan 2026 07:24PM UTC coverage: 70.34% (-1.0%) from 71.315%
21410963225

Pull #1484

github

web-flow
Merge 2320afc9e into 96410045c
Pull Request #1484: Remove `thecodingmachine/safe` dependency (2)

25 of 70 new or added lines in 8 files covered. (35.71%)

5 existing lines in 3 files now uncovered.

1449 of 2060 relevant lines covered (70.34%)

30.37 hits per line

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

87.5
/src/Property/Selector/SpecificityCalculator.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Sabberworm\CSS\Property\Selector;
6

7
/**
8
 * Utility class to calculate the specificity of a CSS selector.
9
 *
10
 * The results are cached to avoid recalculating the specificity of the same selector multiple times.
11
 */
12
final class SpecificityCalculator
13
{
14
    /**
15
     * regexp for specificity calculations
16
     */
17
    private const NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX = '/
18
        (\\.[\\w]+)                   # classes
19
        |
20
        \\[(\\w+)                     # attributes
21
        |
22
        (\\:(                         # pseudo classes
23
            link|visited|active
24
            |hover|focus
25
            |lang
26
            |target
27
            |enabled|disabled|checked|indeterminate
28
            |root
29
            |nth-child|nth-last-child|nth-of-type|nth-last-of-type
30
            |first-child|last-child|first-of-type|last-of-type
31
            |only-child|only-of-type
32
            |empty|contains
33
        ))
34
        /ix';
35

36
    /**
37
     * regexp for specificity calculations
38
     */
39
    private const ELEMENTS_AND_PSEUDO_ELEMENTS_RX = '/
40
        ((^|[\\s\\+\\>\\~]+)[\\w]+   # elements
41
        |
42
        \\:{1,2}(                    # pseudo-elements
43
            after|before|first-letter|first-line|selection
44
        ))
45
        /ix';
46

47
    /**
48
     * @var array<string, int<0, max>>
49
     */
50
    private static $cache = [];
51

52
    /**
53
     * Calculates the specificity of the given CSS selector.
54
     *
55
     * @return int<0, max>
56
     *
57
     * @internal
58
     */
59
    public static function calculate(string $selector): int
16✔
60
    {
61
        if (!isset(self::$cache[$selector])) {
16✔
62
            $a = 0;
16✔
63
            /// @todo should exclude \# as well as "#"
64
            $matches = null;
16✔
65
            $b = \substr_count($selector, '#');
16✔
66
            /** @phpstan-ignore theCodingMachineSafe.function */
67
            $c = \preg_match_all(self::NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX, $selector, $matches);
16✔
68
            if ($c === false) {
16✔
NEW
69
                throw new \RuntimeException('Unexpected error');
×
70
            }
71
            /** @phpstan-ignore theCodingMachineSafe.function */
72
            $d = \preg_match_all(self::ELEMENTS_AND_PSEUDO_ELEMENTS_RX, $selector, $matches);
16✔
73
            if ($d === false) {
16✔
NEW
74
                throw new \RuntimeException('Unexpected error');
×
75
            }
76
            self::$cache[$selector] = ($a * 1000) + ($b * 100) + ($c * 10) + $d;
16✔
77
        }
78

79
        return self::$cache[$selector];
16✔
80
    }
81

82
    /**
83
     * Clears the cache in order to lower memory usage.
84
     */
85
    public static function clearCache(): void
16✔
86
    {
87
        self::$cache = [];
16✔
88
    }
16✔
89
}
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