HTMLPurifier 4.4.0
/home/ezyang/Dev/htmlpurifier/library/HTMLPurifier/ConfigSchema/Validator.php
Go to the documentation of this file.
00001 <?php
00002 
00011 class HTMLPurifier_ConfigSchema_Validator
00012 {
00013 
00017     protected $interchange, $aliases;
00018 
00022     protected $context = array();
00023 
00027     protected $parser;
00028 
00029     public function __construct() {
00030         $this->parser = new HTMLPurifier_VarParser();
00031     }
00032 
00037     public function validate($interchange) {
00038         $this->interchange = $interchange;
00039         $this->aliases = array();
00040         // PHP is a bit lax with integer <=> string conversions in
00041         // arrays, so we don't use the identical !== comparison
00042         foreach ($interchange->directives as $i => $directive) {
00043             $id = $directive->id->toString();
00044             if ($i != $id) $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'");
00045             $this->validateDirective($directive);
00046         }
00047         return true;
00048     }
00049 
00053     public function validateId($id) {
00054         $id_string = $id->toString();
00055         $this->context[] = "id '$id_string'";
00056         if (!$id instanceof HTMLPurifier_ConfigSchema_Interchange_Id) {
00057             // handled by InterchangeBuilder
00058             $this->error(false, 'is not an instance of HTMLPurifier_ConfigSchema_Interchange_Id');
00059         }
00060         // keys are now unconstrained (we might want to narrow down to A-Za-z0-9.)
00061         // we probably should check that it has at least one namespace
00062         $this->with($id, 'key')
00063             ->assertNotEmpty()
00064             ->assertIsString(); // implicit assertIsString handled by InterchangeBuilder
00065         array_pop($this->context);
00066     }
00067 
00071     public function validateDirective($d) {
00072         $id = $d->id->toString();
00073         $this->context[] = "directive '$id'";
00074         $this->validateId($d->id);
00075 
00076         $this->with($d, 'description')
00077             ->assertNotEmpty();
00078 
00079         // BEGIN - handled by InterchangeBuilder
00080         $this->with($d, 'type')
00081             ->assertNotEmpty();
00082         $this->with($d, 'typeAllowsNull')
00083             ->assertIsBool();
00084         try {
00085             // This also tests validity of $d->type
00086             $this->parser->parse($d->default, $d->type, $d->typeAllowsNull);
00087         } catch (HTMLPurifier_VarParserException $e) {
00088             $this->error('default', 'had error: ' . $e->getMessage());
00089         }
00090         // END - handled by InterchangeBuilder
00091 
00092         if (!is_null($d->allowed) || !empty($d->valueAliases)) {
00093             // allowed and valueAliases require that we be dealing with
00094             // strings, so check for that early.
00095             $d_int = HTMLPurifier_VarParser::$types[$d->type];
00096             if (!isset(HTMLPurifier_VarParser::$stringTypes[$d_int])) {
00097                 $this->error('type', 'must be a string type when used with allowed or value aliases');
00098             }
00099         }
00100 
00101         $this->validateDirectiveAllowed($d);
00102         $this->validateDirectiveValueAliases($d);
00103         $this->validateDirectiveAliases($d);
00104 
00105         array_pop($this->context);
00106     }
00107 
00112     public function validateDirectiveAllowed($d) {
00113         if (is_null($d->allowed)) return;
00114         $this->with($d, 'allowed')
00115             ->assertNotEmpty()
00116             ->assertIsLookup(); // handled by InterchangeBuilder
00117         if (is_string($d->default) && !isset($d->allowed[$d->default])) {
00118             $this->error('default', 'must be an allowed value');
00119         }
00120         $this->context[] = 'allowed';
00121         foreach ($d->allowed as $val => $x) {
00122             if (!is_string($val)) $this->error("value $val", 'must be a string');
00123         }
00124         array_pop($this->context);
00125     }
00126 
00131     public function validateDirectiveValueAliases($d) {
00132         if (is_null($d->valueAliases)) return;
00133         $this->with($d, 'valueAliases')
00134             ->assertIsArray(); // handled by InterchangeBuilder
00135         $this->context[] = 'valueAliases';
00136         foreach ($d->valueAliases as $alias => $real) {
00137             if (!is_string($alias)) $this->error("alias $alias", 'must be a string');
00138             if (!is_string($real))  $this->error("alias target $real from alias '$alias'",  'must be a string');
00139             if ($alias === $real) {
00140                 $this->error("alias '$alias'", "must not be an alias to itself");
00141             }
00142         }
00143         if (!is_null($d->allowed)) {
00144             foreach ($d->valueAliases as $alias => $real) {
00145                 if (isset($d->allowed[$alias])) {
00146                     $this->error("alias '$alias'", 'must not be an allowed value');
00147                 } elseif (!isset($d->allowed[$real])) {
00148                     $this->error("alias '$alias'", 'must be an alias to an allowed value');
00149                 }
00150             }
00151         }
00152         array_pop($this->context);
00153     }
00154 
00159     public function validateDirectiveAliases($d) {
00160         $this->with($d, 'aliases')
00161             ->assertIsArray(); // handled by InterchangeBuilder
00162         $this->context[] = 'aliases';
00163         foreach ($d->aliases as $alias) {
00164             $this->validateId($alias);
00165             $s = $alias->toString();
00166             if (isset($this->interchange->directives[$s])) {
00167                 $this->error("alias '$s'", 'collides with another directive');
00168             }
00169             if (isset($this->aliases[$s])) {
00170                 $other_directive = $this->aliases[$s];
00171                 $this->error("alias '$s'", "collides with alias for directive '$other_directive'");
00172             }
00173             $this->aliases[$s] = $d->id->toString();
00174         }
00175         array_pop($this->context);
00176     }
00177 
00178     // protected helper functions
00179 
00184     protected function with($obj, $member) {
00185         return new HTMLPurifier_ConfigSchema_ValidatorAtom($this->getFormattedContext(), $obj, $member);
00186     }
00187 
00191     protected function error($target, $msg) {
00192         if ($target !== false) $prefix = ucfirst($target) . ' in ' .  $this->getFormattedContext();
00193         else $prefix = ucfirst($this->getFormattedContext());
00194         throw new HTMLPurifier_ConfigSchema_Exception(trim($prefix . ' ' . $msg));
00195     }
00196 
00200     protected function getFormattedContext() {
00201         return implode(' in ', array_reverse($this->context));
00202     }
00203 
00204 }
00205 
00206 // vim: et sw=4 sts=4