Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
50 / 50 |
|
100.00% |
2 / 2 |
CRAP | |
100.00% |
1 / 1 |
UrangeMatcher | |
100.00% |
50 / 50 |
|
100.00% |
2 / 2 |
11 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
1 | |||
generateMatches | |
100.00% |
35 / 35 |
|
100.00% |
1 / 1 |
10 |
1 | <?php |
2 | /** |
3 | * @file |
4 | * @license https://opensource.org/licenses/Apache-2.0 Apache-2.0 |
5 | */ |
6 | |
7 | namespace Wikimedia\CSS\Grammar; |
8 | |
9 | use Wikimedia\CSS\Objects\ComponentValueList; |
10 | use Wikimedia\CSS\Objects\Token; |
11 | |
12 | /** |
13 | * Match the special "<urange>" notation |
14 | * |
15 | * If this matcher is marked for capturing, its matches will have submatches |
16 | * "start" and "end" holding T_NUMBER tokens representing the starting and |
17 | * ending codepoints in the range. |
18 | * |
19 | * @see https://www.w3.org/TR/2019/CR-css-syntax-3-20190716/#urange |
20 | */ |
21 | class UrangeMatcher extends Matcher { |
22 | /** @var Matcher Syntax matcher */ |
23 | private $matcher; |
24 | |
25 | public function __construct() { |
26 | $u = new KeywordMatcher( [ 'u' ] ); |
27 | $plus = new DelimMatcher( [ '+' ] ); |
28 | $ident = new TokenMatcher( Token::T_IDENT ); |
29 | $number = new TokenMatcher( Token::T_NUMBER ); |
30 | $dimension = new TokenMatcher( Token::T_DIMENSION ); |
31 | $q = new DelimMatcher( [ '?' ] ); |
32 | $qs = Quantifier::count( $q, 0, 6 ); |
33 | |
34 | // This matches a lot of things; we post-process in generateMatches() to limit it to |
35 | // only what's actually supposed to be accepted. |
36 | $this->matcher = new Alternative( [ |
37 | new Juxtaposition( [ $u, $plus, $ident, $qs ] ), |
38 | new Juxtaposition( [ $u, $number, $dimension ] ), |
39 | new Juxtaposition( [ $u, $number, $number ] ), |
40 | new Juxtaposition( [ $u, $dimension, $qs ] ), |
41 | new Juxtaposition( [ $u, $number, $qs ] ), |
42 | new Juxtaposition( [ $u, $plus, Quantifier::count( $q, 1, 6 ) ] ), |
43 | ] ); |
44 | } |
45 | |
46 | /** @inheritDoc */ |
47 | protected function generateMatches( ComponentValueList $values, $start, array $options ) { |
48 | foreach ( $this->matcher->generateMatches( $values, $start, $options ) as $match ) { |
49 | // <urange> is basically defined as a series of tokens that happens to have a certain string |
50 | // representation. So stringify and regex it to see if it actually matches. |
51 | $v = trim( $match->__toString(), "\n\t " ); |
52 | // Strip interpolated comments |
53 | $v = strtr( $v, [ '/**/' => '' ] ); |
54 | $l = strlen( $v ); |
55 | if ( preg_match( '/^u\+([0-9a-f]{1,6})-([0-9a-f]{1,6})$/iD', $v, $m ) ) { |
56 | $ustart = intval( $m[1], 16 ); |
57 | $uend = intval( $m[2], 16 ); |
58 | } elseif ( $l > 2 && $l <= 8 && preg_match( '/^u\+([0-9a-f]*\?*)$/iD', $v, $m ) ) { |
59 | $ustart = intval( strtr( $m[1], [ '?' => '0' ] ), 16 ); |
60 | $uend = intval( strtr( $m[1], [ '?' => 'f' ] ), 16 ); |
61 | } else { |
62 | continue; |
63 | } |
64 | if ( $ustart >= 0 && $ustart <= $uend && $uend <= 0x10ffff ) { |
65 | $len = $match->getNext() - $start; |
66 | $matches = []; |
67 | if ( $this->captureName !== null ) { |
68 | $tstart = new Token( Token::T_NUMBER, [ 'value' => $ustart, 'typeFlag' => 'integer' ] ); |
69 | $tend = new Token( Token::T_NUMBER, [ 'value' => $uend, 'typeFlag' => 'integer' ] ); |
70 | $matches = [ |
71 | new GrammarMatch( |
72 | new ComponentValueList( $tstart->toComponentValueArray() ), |
73 | 0, |
74 | 1, |
75 | 'start', |
76 | [] |
77 | ), |
78 | new GrammarMatch( |
79 | new ComponentValueList( $tend->toComponentValueArray() ), |
80 | 0, |
81 | 1, |
82 | 'end', |
83 | [] |
84 | ), |
85 | ]; |
86 | } |
87 | |
88 | // Mark the 'U' T_IDENT beginning a <urange>, to later avoid |
89 | // serializing it with extraneous comments. |
90 | // @see Wikimedia\CSS\Util::stringify() |
91 | // @phan-suppress-next-line PhanNonClassMethodCall False positive |
92 | $values[$start]->urangeHack( $len ); |
93 | |
94 | yield new GrammarMatch( $values, $start, $len, $this->captureName, $matches ); |
95 | } |
96 | } |
97 | } |
98 | } |