pokemon-showdown-client/lib/css-sanitizer/Wikimedia/CSS/Grammar/UrlMatcher.php

92 lines
2.8 KiB
PHP

<?php
/**
* @file
* @license https://opensource.org/licenses/Apache-2.0 Apache-2.0
*/
namespace Wikimedia\CSS\Grammar;
use Wikimedia\CSS\Objects\ComponentValueList;
use Wikimedia\CSS\Objects\CSSFunction;
use Wikimedia\CSS\Objects\Token;
/**
* Matcher that matches a CSSFunction for a url or a T_URL token
*/
class UrlMatcher extends FunctionMatcher {
/** @var callable|null */
protected $urlCheck;
/**
* @param callable|null $urlCheck Function to check that the URL is really valid.
* Prototype is bool func( string $url, ComponentValue[] $modifiers )
* @param array $options Additional options:
* - modifierMatcher: (Matcher) Matcher for URL modifiers. The default is
* a NothingMatcher.
*/
public function __construct( callable $urlCheck = null, array $options = [] ) {
if ( isset( $options['modifierMatcher'] ) ) {
$modifierMatcher = $options['modifierMatcher'];
if ( !$modifierMatcher instanceof Matcher ) {
throw new \InvalidArgumentException( 'modifierMatcher must be a Matcher' );
}
} else {
$modifierMatcher = new NothingMatcher;
}
$funcContents = new Juxtaposition( [
TokenMatcher::create( Token::T_STRING )->capture( 'url' ),
Quantifier::star( $modifierMatcher->capture( 'modifier' ) ),
] );
$this->urlCheck = $urlCheck;
parent::__construct( 'url', $funcContents );
}
/**
* Return a Matcher for any grammatically-correct modifier
* @return Matcher
*/
public static function anyModifierMatcher() {
return Alternative::create( [
new TokenMatcher( Token::T_IDENT ),
new FunctionMatcher( null, new AnythingMatcher( [ 'quantifier' => '*' ] ) ),
] );
}
protected function generateMatches( ComponentValueList $values, $start, array $options ) {
// First, is it a URL token?
$cv = isset( $values[$start] ) ? $values[$start] : null;
if ( $cv instanceof Token && $cv->type() === Token::T_URL ) {
$url = $cv->value();
if ( !$this->urlCheck || call_user_func( $this->urlCheck, $url, [] ) ) {
$match = new Match( $values, $start, 1, 'url' );
yield $this->makeMatch( $values, $start, $this->next( $values, $start, $options ), $match );
}
return;
}
// Nope. Try it as a FunctionMatcher and extract the URL and modifiers
// for each match.
foreach ( parent::generateMatches( $values, $start, $options ) as $match ) {
$url = null;
$modifiers = [];
foreach ( $match->getCapturedMatches() as $submatch ) {
$cvs = $submatch->getValues();
if ( $submatch->getName() === 'url' ) {
$url = $cvs[0]->value();
} elseif ( $submatch->getName() === 'modifier' ) {
if ( $cvs[0] instanceof CSSFunction ) {
$modifiers[] = $cvs[0];
} elseif ( $cvs[0]->type() === Token::T_IDENT ) {
$modifiers[] = $cvs[0];
}
}
}
if ( $url && ( !$this->urlCheck || call_user_func( $this->urlCheck, $url, $modifiers ) ) ) {
yield $match;
}
}
}
}