Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
36 / 36 |
|
100.00% |
3 / 3 |
CRAP | |
100.00% |
1 / 1 |
UrlMatcher | |
100.00% |
36 / 36 |
|
100.00% |
3 / 3 |
20 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
3 | |||
anyModifierMatcher | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
generateMatches | |
100.00% |
21 / 21 |
|
100.00% |
1 / 1 |
16 |
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 InvalidArgumentException; |
10 | use Wikimedia\CSS\Objects\ComponentValueList; |
11 | use Wikimedia\CSS\Objects\CSSFunction; |
12 | use Wikimedia\CSS\Objects\Token; |
13 | |
14 | /** |
15 | * Matcher that matches a CSSFunction for a URL or a T_URL token |
16 | */ |
17 | class UrlMatcher extends FunctionMatcher { |
18 | /** @var callable|null */ |
19 | protected $urlCheck; |
20 | |
21 | /** |
22 | * @param callable|null $urlCheck Function to check that the URL is really valid. |
23 | * Prototype is bool func( string $url, ComponentValue[] $modifiers ) |
24 | * @param array $options Additional options: |
25 | * - modifierMatcher: (Matcher) Matcher for URL modifiers. The default is |
26 | * a NothingMatcher. |
27 | */ |
28 | public function __construct( ?callable $urlCheck = null, array $options = [] ) { |
29 | if ( isset( $options['modifierMatcher'] ) ) { |
30 | $modifierMatcher = $options['modifierMatcher']; |
31 | if ( !$modifierMatcher instanceof Matcher ) { |
32 | throw new InvalidArgumentException( 'modifierMatcher must be a Matcher' ); |
33 | } |
34 | } else { |
35 | $modifierMatcher = new NothingMatcher; |
36 | } |
37 | |
38 | $funcContents = new Juxtaposition( [ |
39 | TokenMatcher::create( Token::T_STRING )->capture( 'url' ), |
40 | Quantifier::star( $modifierMatcher->capture( 'modifier' ) ), |
41 | ] ); |
42 | |
43 | $this->urlCheck = $urlCheck; |
44 | parent::__construct( 'url', $funcContents ); |
45 | } |
46 | |
47 | /** |
48 | * Return a Matcher for any grammatically-correct modifier |
49 | * @return Matcher |
50 | */ |
51 | public static function anyModifierMatcher() { |
52 | return Alternative::create( [ |
53 | new TokenMatcher( Token::T_IDENT ), |
54 | new FunctionMatcher( null, new AnythingMatcher( [ 'quantifier' => '*' ] ) ), |
55 | ] ); |
56 | } |
57 | |
58 | /** @inheritDoc */ |
59 | protected function generateMatches( ComponentValueList $values, $start, array $options ) { |
60 | // First, is it a URL token? |
61 | $cv = $values[$start] ?? null; |
62 | if ( $cv instanceof Token && $cv->type() === Token::T_URL ) { |
63 | $url = $cv->value(); |
64 | if ( !$this->urlCheck || call_user_func( $this->urlCheck, $url, [] ) ) { |
65 | $match = new GrammarMatch( $values, $start, 1, 'url' ); |
66 | yield $this->makeMatch( $values, $start, $this->next( $values, $start, $options ), $match ); |
67 | } |
68 | return; |
69 | } |
70 | |
71 | // Nope. Try it as a FunctionMatcher and extract the URL and modifiers |
72 | // for each match. |
73 | foreach ( parent::generateMatches( $values, $start, $options ) as $match ) { |
74 | $url = null; |
75 | $modifiers = []; |
76 | foreach ( $match->getCapturedMatches() as $submatch ) { |
77 | $cvs = $submatch->getValues(); |
78 | if ( $cvs[0] instanceof Token && $submatch->getName() === 'url' ) { |
79 | $url = $cvs[0]->value(); |
80 | } elseif ( $submatch->getName() === 'modifier' ) { |
81 | if ( $cvs[0] instanceof CSSFunction ) { |
82 | $modifiers[] = $cvs[0]; |
83 | } elseif ( $cvs[0] instanceof Token && $cvs[0]->type() === Token::T_IDENT ) { |
84 | $modifiers[] = $cvs[0]; |
85 | } |
86 | } |
87 | } |
88 | if ( $url && ( !$this->urlCheck || call_user_func( $this->urlCheck, $url, $modifiers ) ) ) { |
89 | yield $match; |
90 | } |
91 | } |
92 | } |
93 | } |