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
00041
00042 foreach ($interchange->namespaces as $i => $namespace) {
00043 if ($i != $namespace->namespace) $this->error(false, "Integrity violation: key '$i' does not match internal id '{$namespace->namespace}'");
00044 $this->validateNamespace($namespace);
00045 }
00046 foreach ($interchange->directives as $i => $directive) {
00047 $id = $directive->id->toString();
00048 if ($i != $id) $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'");
00049 $this->validateDirective($directive);
00050 }
00051 return true;
00052 }
00053
00057 public function validateNamespace($n) {
00058 $this->context[] = "namespace '{$n->namespace}'";
00059 $this->with($n, 'namespace')
00060 ->assertNotEmpty()
00061 ->assertAlnum();
00062 $this->with($n, 'description')
00063 ->assertNotEmpty()
00064 ->assertIsString();
00065 array_pop($this->context);
00066 }
00067
00071 public function validateId($id) {
00072 $id_string = $id->toString();
00073 $this->context[] = "id '$id_string'";
00074 if (!$id instanceof HTMLPurifier_ConfigSchema_Interchange_Id) {
00075
00076 $this->error(false, 'is not an instance of HTMLPurifier_ConfigSchema_Interchange_Id');
00077 }
00078 if (!isset($this->interchange->namespaces[$id->namespace])) {
00079 $this->error('namespace', 'does not exist');
00080 }
00081 $this->with($id, 'directive')
00082 ->assertNotEmpty()
00083 ->assertAlnum();
00084 array_pop($this->context);
00085 }
00086
00090 public function validateDirective($d) {
00091 $id = $d->id->toString();
00092 $this->context[] = "directive '$id'";
00093 $this->validateId($d->id);
00094
00095 $this->with($d, 'description')
00096 ->assertNotEmpty();
00097
00098
00099 $this->with($d, 'type')
00100 ->assertNotEmpty();
00101 $this->with($d, 'typeAllowsNull')
00102 ->assertIsBool();
00103 try {
00104
00105 $this->parser->parse($d->default, $d->type, $d->typeAllowsNull);
00106 } catch (HTMLPurifier_VarParserException $e) {
00107 $this->error('default', 'had error: ' . $e->getMessage());
00108 }
00109
00110
00111 if (!is_null($d->allowed) || !empty($d->valueAliases)) {
00112
00113
00114 $d_int = HTMLPurifier_VarParser::$types[$d->type];
00115 if (!isset(HTMLPurifier_VarParser::$stringTypes[$d_int])) {
00116 $this->error('type', 'must be a string type when used with allowed or value aliases');
00117 }
00118 }
00119
00120 $this->validateDirectiveAllowed($d);
00121 $this->validateDirectiveValueAliases($d);
00122 $this->validateDirectiveAliases($d);
00123
00124 array_pop($this->context);
00125 }
00126
00131 public function validateDirectiveAllowed($d) {
00132 if (is_null($d->allowed)) return;
00133 $this->with($d, 'allowed')
00134 ->assertNotEmpty()
00135 ->assertIsLookup();
00136 if (is_string($d->default) && !isset($d->allowed[$d->default])) {
00137 $this->error('default', 'must be an allowed value');
00138 }
00139 $this->context[] = 'allowed';
00140 foreach ($d->allowed as $val => $x) {
00141 if (!is_string($val)) $this->error("value $val", 'must be a string');
00142 }
00143 array_pop($this->context);
00144 }
00145
00150 public function validateDirectiveValueAliases($d) {
00151 if (is_null($d->valueAliases)) return;
00152 $this->with($d, 'valueAliases')
00153 ->assertIsArray();
00154 $this->context[] = 'valueAliases';
00155 foreach ($d->valueAliases as $alias => $real) {
00156 if (!is_string($alias)) $this->error("alias $alias", 'must be a string');
00157 if (!is_string($real)) $this->error("alias target $real from alias '$alias'", 'must be a string');
00158 if ($alias === $real) {
00159 $this->error("alias '$alias'", "must not be an alias to itself");
00160 }
00161 }
00162 if (!is_null($d->allowed)) {
00163 foreach ($d->valueAliases as $alias => $real) {
00164 if (isset($d->allowed[$alias])) {
00165 $this->error("alias '$alias'", 'must not be an allowed value');
00166 } elseif (!isset($d->allowed[$real])) {
00167 $this->error("alias '$alias'", 'must be an alias to an allowed value');
00168 }
00169 }
00170 }
00171 array_pop($this->context);
00172 }
00173
00178 public function validateDirectiveAliases($d) {
00179 $this->with($d, 'aliases')
00180 ->assertIsArray();
00181 $this->context[] = 'aliases';
00182 foreach ($d->aliases as $alias) {
00183 $this->validateId($alias);
00184 $s = $alias->toString();
00185 if (isset($this->interchange->directives[$s])) {
00186 $this->error("alias '$s'", 'collides with another directive');
00187 }
00188 if (isset($this->aliases[$s])) {
00189 $other_directive = $this->aliases[$s];
00190 $this->error("alias '$s'", "collides with alias for directive '$other_directive'");
00191 }
00192 $this->aliases[$s] = $d->id->toString();
00193 }
00194 array_pop($this->context);
00195 }
00196
00197
00198
00203 protected function with($obj, $member) {
00204 return new HTMLPurifier_ConfigSchema_ValidatorAtom($this->getFormattedContext(), $obj, $member);
00205 }
00206
00210 protected function error($target, $msg) {
00211 if ($target !== false) $prefix = ucfirst($target) . ' in ' . $this->getFormattedContext();
00212 else $prefix = ucfirst($this->getFormattedContext());
00213 throw new HTMLPurifier_ConfigSchema_Exception(trim($prefix . ' ' . $msg));
00214 }
00215
00219 protected function getFormattedContext() {
00220 return implode(' in ', array_reverse($this->context));
00221 }
00222
00223 }