4 * @license https://opensource.org/licenses/Apache-2.0 Apache-2.0
7 namespace Wikimedia\CSS\Grammar;
9 use Wikimedia\CSS\Objects\ComponentValueList;
10 use Wikimedia\CSS\Objects\CSSFunction;
11 use Wikimedia\CSS\Objects\Token;
14 * Matcher that matches a CSSFunction for a url or a T_URL token
16 class UrlMatcher extends FunctionMatcher {
17 /** @var callable|null */
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
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' );
34 $modifierMatcher = new NothingMatcher;
37 $funcContents = new Juxtaposition( [
38 TokenMatcher::create( Token::T_STRING )->capture( 'url' ),
39 Quantifier::star( $modifierMatcher->capture( 'modifier' ) ),
42 $this->urlCheck = $urlCheck;
43 parent::__construct( 'url', $funcContents );
47 * Return a Matcher for any grammatically-correct modifier
50 public static function anyModifierMatcher() {
51 return Alternative::create( [
52 new TokenMatcher( Token::T_IDENT ),
53 new FunctionMatcher( null, new AnythingMatcher( [ 'quantifier' => '*' ] ) ),
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 ) {
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 );
69 // Nope. Try it as a FunctionMatcher and extract the URL and modifiers
71 foreach ( parent::generateMatches( $values, $start, $options ) as $match ) {
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];
86 if ( $url && ( !$this->urlCheck || call_user_func( $this->urlCheck, $url, $modifiers ) ) ) {