pokemon-showdown-client/lib/css-sanitizer/Wikimedia/CSS/Sanitizer/PropertySanitizer.php

130 lines
3.8 KiB
PHP

<?php
/**
* @file
* @license https://opensource.org/licenses/Apache-2.0 Apache-2.0
*/
namespace Wikimedia\CSS\Sanitizer;
use InvalidArgumentException;
use Wikimedia\CSS\Grammar\Matcher;
use Wikimedia\CSS\Grammar\NothingMatcher;
use Wikimedia\CSS\Objects\CSSObject;
use Wikimedia\CSS\Objects\ComponentValueList;
use Wikimedia\CSS\Objects\Declaration;
use Wikimedia\CSS\Util;
/**
* Sanitizes a Declaration
*/
class PropertySanitizer extends Sanitizer {
/** @var Matcher[] Array mapping declaration names (lowercase) to Matchers for the values */
private $knownProperties = [];
/** @var Matcher|null Matcher for CSS-wide keywords */
private $cssWideKeywords = null;
/**
* @param Matcher[] $properties Array mapping declaration names (lowercase)
* to Matchers for the values
* @param Matcher $cssWideKeywordsMatcher Matcher for keywords that should
* be recognized for all known properties.
*/
public function __construct( array $properties = [], Matcher $cssWideKeywordsMatcher = null ) {
$this->setKnownProperties( $properties );
$this->setCssWideKeywordsMatcher( $cssWideKeywordsMatcher ?: new NothingMatcher );
}
/**
* Access the list of known properties
* @return Matcher[]
*/
public function getKnownProperties() {
return $this->knownProperties;
}
/**
* Set the list of known properties
* @param Matcher[] $properties Array mapping declaration names (lowercase)
* to Matchers for the values
*/
public function setKnownProperties( array $properties ) {
foreach ( $properties as $prop => $matcher ) {
if ( strtolower( $prop ) !== $prop ) {
throw new InvalidArgumentException( "Property name '$prop' must be lowercased" );
}
if ( !$matcher instanceof Matcher ) {
throw new InvalidArgumentException( "Value for '$prop' is not a Matcher" );
}
}
$this->knownProperties = $properties;
}
/**
* Merge a list of matchers into the list of known properties
* @param Matcher[] $properties Array mapping declaration names (lowercase)
* to Matchers for the values
* @throws InvalidArgumentException if some property is already defined
*/
public function addKnownProperties( $props ) {
$dups = [];
foreach ( $props as $k => $v ) {
if ( isset( $this->knownProperties[$k] ) && $props[$k] !== $this->knownProperties[$k] ) {
$dups[] = $k;
}
}
if ( $dups ) {
throw new InvalidArgumentException(
'Duplicate definitions for properties: ' . join( ' ', $dups )
);
}
$this->setKnownProperties( $this->knownProperties + $props );
}
/**
* Fetch the matcher for keywords that should be recognized for all properties.
* @return Matcher
*/
public function getCssWideKeywordsMatcher() {
return $this->cssWideKeywords;
}
/**
* Set the matcher for keywords that should be recognized for all properties.
* @param Matcher $matcher
*/
public function setCssWideKeywordsMatcher( Matcher $matcher ) {
$this->cssWideKeywords = $matcher;
}
protected function doSanitize( CSSObject $object ) {
if ( !$object instanceof Declaration ) {
$this->sanitizationError( 'expected-declaration', $object );
return null;
}
$knownProperties = $this->getKnownProperties();
$name = strtolower( $object->getName() );
if ( !isset( $knownProperties[$name] ) ) {
$this->sanitizationError( 'unrecognized-property', $object );
return null;
}
$list = $object->getValue();
if ( !$knownProperties[$name]->match( $list, [ 'mark-significance' => true ] ) &&
!$this->getCssWideKeywordsMatcher()->match( $list, [ 'mark-significance' => true ] )
) {
$cv = Util::findFirstNonWhitespace( $list );
if ( $cv ) {
$this->sanitizationError( 'bad-value-for-property', $cv, [ $name ] );
} else {
$this->sanitizationError( 'missing-value-for-property', $object, [ $name ] );
}
return null;
}
return $object;
}
}