-
Notifications
You must be signed in to change notification settings - Fork 152
Expand file tree
/
Copy pathSpecificityCalculator.php
More file actions
89 lines (81 loc) · 2.56 KB
/
Copy pathSpecificityCalculator.php
File metadata and controls
89 lines (81 loc) · 2.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<?php
declare(strict_types=1);
namespace Sabberworm\CSS\Property\Selector;
/**
* Utility class to calculate the specificity of a CSS selector.
*
* The results are cached to avoid recalculating the specificity of the same selector multiple times.
*/
final class SpecificityCalculator
{
/**
* regexp for specificity calculations
*/
private const NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX = '/
(\\.[\\w]+) # classes
|
\\[(\\w+) # attributes
|
(\\:( # pseudo classes
link|visited|active
|hover|focus
|lang
|target
|enabled|disabled|checked|indeterminate
|root
|nth-child|nth-last-child|nth-of-type|nth-last-of-type
|first-child|last-child|first-of-type|last-of-type
|only-child|only-of-type
|empty|contains
))
/ix';
/**
* regexp for specificity calculations
*/
private const ELEMENTS_AND_PSEUDO_ELEMENTS_RX = '/
((^|[\\s\\+\\>\\~]+)[\\w]+ # elements
|
\\:{1,2}( # pseudo-elements
after|before|first-letter|first-line|selection
))
/ix';
/**
* @var array<string, int<0, max>>
*/
private static $cache = [];
/**
* Calculates the specificity of the given CSS selector.
*
* @return int<0, max>
*
* @internal
*/
public static function calculate(string $selector): int
{
if (!isset(self::$cache[$selector])) {
$a = 0;
/// @todo should exclude \# as well as "#"
$matches = null;
$b = \substr_count($selector, '#');
/** @phpstan-ignore theCodingMachineSafe.function */
$c = \preg_match_all(self::NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX, $selector, $matches);
if ($c === false) {
throw new \RuntimeException('Unexpected error');
}
/** @phpstan-ignore theCodingMachineSafe.function */
$d = \preg_match_all(self::ELEMENTS_AND_PSEUDO_ELEMENTS_RX, $selector, $matches);
if ($d === false) {
throw new \RuntimeException('Unexpected error');
}
self::$cache[$selector] = ($a * 1000) + ($b * 100) + ($c * 10) + $d;
}
return self::$cache[$selector];
}
/**
* Clears the cache in order to lower memory usage.
*/
public static function clearCache(): void
{
self::$cache = [];
}
}