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