4 * @license https://opensource.org/licenses/Apache-2.0 Apache-2.0
7 namespace Wikimedia\CSS\Sanitizer;
9 use InvalidArgumentException;
10 use Wikimedia\CSS\Grammar\Matcher;
11 use Wikimedia\CSS\Grammar\NothingMatcher;
12 use Wikimedia\CSS\Objects\CSSObject;
13 use Wikimedia\CSS\Objects\ComponentValueList;
14 use Wikimedia\CSS\Objects\Declaration;
15 use Wikimedia\CSS\Util;
18 * Sanitizes a Declaration
20 class PropertySanitizer extends Sanitizer {
22 /** @var Matcher[] Array mapping declaration names (lowercase) to Matchers for the values */
23 private $knownProperties = [];
25 /** @var Matcher|null Matcher for CSS-wide keywords */
26 private $cssWideKeywords = null;
29 * @param Matcher[] $properties Array mapping declaration names (lowercase)
30 * to Matchers for the values
31 * @param Matcher $cssWideKeywordsMatcher Matcher for keywords that should
32 * be recognized for all known properties.
34 public function __construct( array $properties = [], Matcher $cssWideKeywordsMatcher = null ) {
35 $this->setKnownProperties( $properties );
36 $this->setCssWideKeywordsMatcher( $cssWideKeywordsMatcher ?: new NothingMatcher );
40 * Access the list of known properties
43 public function getKnownProperties() {
44 return $this->knownProperties;
48 * Set the list of known properties
49 * @param Matcher[] $properties Array mapping declaration names (lowercase)
50 * to Matchers for the values
52 public function setKnownProperties( array $properties ) {
53 foreach ( $properties as $prop => $matcher ) {
54 if ( strtolower( $prop ) !== $prop ) {
55 throw new InvalidArgumentException( "Property name '$prop' must be lowercased" );
57 if ( !$matcher instanceof Matcher ) {
58 throw new InvalidArgumentException( "Value for '$prop' is not a Matcher" );
61 $this->knownProperties = $properties;
65 * Merge a list of matchers into the list of known properties
66 * @param Matcher[] $properties Array mapping declaration names (lowercase)
67 * to Matchers for the values
68 * @throws InvalidArgumentException if some property is already defined
70 public function addKnownProperties( $props ) {
72 foreach ( $props as $k => $v ) {
73 if ( isset( $this->knownProperties[$k] ) && $props[$k] !== $this->knownProperties[$k] ) {
78 throw new InvalidArgumentException(
79 'Duplicate definitions for properties: ' . join( ' ', $dups )
82 $this->setKnownProperties( $this->knownProperties + $props );
86 * Fetch the matcher for keywords that should be recognized for all properties.
89 public function getCssWideKeywordsMatcher() {
90 return $this->cssWideKeywords;
94 * Set the matcher for keywords that should be recognized for all properties.
95 * @param Matcher $matcher
97 public function setCssWideKeywordsMatcher( Matcher $matcher ) {
98 $this->cssWideKeywords = $matcher;
101 protected function doSanitize( CSSObject $object ) {
102 if ( !$object instanceof Declaration ) {
103 $this->sanitizationError( 'expected-declaration', $object );
107 $knownProperties = $this->getKnownProperties();
108 $name = strtolower( $object->getName() );
109 if ( !isset( $knownProperties[$name] ) ) {
110 $this->sanitizationError( 'unrecognized-property', $object );
114 $list = $object->getValue();
115 if ( !$knownProperties[$name]->match( $list, [ 'mark-significance' => true ] ) &&
116 !$this->getCssWideKeywordsMatcher()->match( $list, [ 'mark-significance' => true ] )
118 $cv = Util::findFirstNonWhitespace( $list );
120 $this->sanitizationError( 'bad-value-for-property', $cv, [ $name ] );
122 $this->sanitizationError( 'missing-value-for-property', $object, [ $name ] );