4 * Performs validations on HTMLPurifier_ConfigSchema_Interchange
6 * @note If you see '// handled by InterchangeBuilder', that means a
7 * design decision in that class would prevent this validation from
8 * ever being necessary. We have them anyway, however, for
11 class HTMLPurifier_ConfigSchema_Validator
14 protected $interchange;
17 * Context-stack to provide easy to read error messages.
19 protected $context = array();
22 * HTMLPurifier_VarParser to test variable types.
26 public function __construct() {
27 $this->parser
= new HTMLPurifier_VarParser();
31 * Validates a fully-formed interchange object. Throws an
32 * HTMLPurifier_ConfigSchema_Exception if there's a problem.
34 public function validate($interchange) {
35 $this->interchange
= $interchange;
36 // PHP is a bit lax with integer <=> string conversions in
37 // arrays, so we don't use the identical !== comparison
38 foreach ($interchange->namespaces
as $i => $namespace) {
39 if ($i != $namespace->namespace) $this->error(false, "Integrity violation: key '$i' does not match internal id '{$namespace->namespace}'");
40 $this->validateNamespace($namespace);
42 foreach ($interchange->directives
as $i => $directive) {
43 $id = $directive->id
->toString();
44 if ($i != $id) $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'");
45 $this->validateDirective($directive);
49 public function validateNamespace($n) {
50 $this->context
[] = "namespace '{$n->namespace}'";
51 $this->with($n, 'namespace')
53 ->assertAlnum(); // implicit assertIsString handled by InterchangeBuilder
54 $this->with($n, 'description')
56 ->assertIsString(); // handled by InterchangeBuilder
57 array_pop($this->context
);
60 public function validateId($id) {
61 $id_string = $id->toString();
62 $this->context
[] = "id '$id_string'";
63 if (!$id instanceof HTMLPurifier_ConfigSchema_Interchange_Id
) {
64 // handled by InterchangeBuilder
65 $this->error(false, 'is not an instance of HTMLPurifier_ConfigSchema_Interchange_Id');
67 if (!isset($this->interchange
->namespaces
[$id->namespace])) {
68 $this->error('namespace', 'does not exist'); // assumes that the namespace was validated already
70 $this->with($id, 'directive')
72 ->assertAlnum(); // implicit assertIsString handled by InterchangeBuilder
73 array_pop($this->context
);
76 public function validateDirective($d) {
77 $id = $d->id
->toString();
78 $this->context
[] = "directive '$id'";
79 $this->validateId($d->id
);
80 $this->with($d, 'description')
82 $this->with($d, 'type')
83 ->assertNotEmpty(); // handled by InterchangeBuilder
84 // Much stricter default check, since we're using the base implementation.
85 // handled by InterchangeBuilder
87 $this->parser
->parse($d->default, $d->type
, $d->typeAllowsNull
);
88 } catch (HTMLPurifier_VarParserException
$e) {
89 $this->error('default', 'had error: ' . $e->getMessage());
92 array_pop($this->context
);
95 // protected helper functions
97 protected function with($obj, $member) {
98 return new HTMLPurifier_ConfigSchema_ValidatorAtom($this->getFormattedContext(), $obj, $member);
101 protected function error($target, $msg) {
102 if ($target !== false) $prefix = ucfirst($target) . ' in ' . $this->getFormattedContext();
103 else $prefix = ucfirst($this->getFormattedContext());
104 throw new HTMLPurifier_ConfigSchema_Exception(trim($prefix . ' ' . $msg));
107 protected function getFormattedContext() {
108 return implode(' in ', array_reverse($this->context
));