1: | <?php
|
2: |
|
3: | |
4: | |
5: | |
6: | |
7: | |
8: | |
9: | |
10: |
|
11: | class HTMLPurifier_ConfigSchema_Validator
|
12: | {
|
13: |
|
14: | |
15: | |
16: |
|
17: | protected $interchange;
|
18: |
|
19: | |
20: | |
21: |
|
22: | protected $aliases;
|
23: |
|
24: | |
25: | |
26: | |
27: |
|
28: | protected $context = array();
|
29: |
|
30: | |
31: | |
32: | |
33: |
|
34: | protected $parser;
|
35: |
|
36: | public function __construct()
|
37: | {
|
38: | $this->parser = new HTMLPurifier_VarParser();
|
39: | }
|
40: |
|
41: | |
42: | |
43: | |
44: | |
45: |
|
46: | public function validate($interchange)
|
47: | {
|
48: | $this->interchange = $interchange;
|
49: | $this->aliases = array();
|
50: |
|
51: |
|
52: | foreach ($interchange->directives as $i => $directive) {
|
53: | $id = $directive->id->toString();
|
54: | if ($i != $id) {
|
55: | $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'");
|
56: | }
|
57: | $this->validateDirective($directive);
|
58: | }
|
59: | return true;
|
60: | }
|
61: |
|
62: | |
63: | |
64: | |
65: |
|
66: | public function validateId($id)
|
67: | {
|
68: | $id_string = $id->toString();
|
69: | $this->context[] = "id '$id_string'";
|
70: | if (!$id instanceof HTMLPurifier_ConfigSchema_Interchange_Id) {
|
71: |
|
72: | $this->error(false, 'is not an instance of HTMLPurifier_ConfigSchema_Interchange_Id');
|
73: | }
|
74: |
|
75: |
|
76: | $this->with($id, 'key')
|
77: | ->assertNotEmpty()
|
78: | ->assertIsString();
|
79: | array_pop($this->context);
|
80: | }
|
81: |
|
82: | |
83: | |
84: | |
85: |
|
86: | public function validateDirective($d)
|
87: | {
|
88: | $id = $d->id->toString();
|
89: | $this->context[] = "directive '$id'";
|
90: | $this->validateId($d->id);
|
91: |
|
92: | $this->with($d, 'description')
|
93: | ->assertNotEmpty();
|
94: |
|
95: |
|
96: | $this->with($d, 'type')
|
97: | ->assertNotEmpty();
|
98: | $this->with($d, 'typeAllowsNull')
|
99: | ->assertIsBool();
|
100: | try {
|
101: |
|
102: | $this->parser->parse($d->default, $d->type, $d->typeAllowsNull);
|
103: | } catch (HTMLPurifier_VarParserException $e) {
|
104: | $this->error('default', 'had error: ' . $e->getMessage());
|
105: | }
|
106: |
|
107: |
|
108: | if (!is_null($d->allowed) || !empty($d->valueAliases)) {
|
109: |
|
110: |
|
111: | $d_int = HTMLPurifier_VarParser::$types[$d->type];
|
112: | if (!isset(HTMLPurifier_VarParser::$stringTypes[$d_int])) {
|
113: | $this->error('type', 'must be a string type when used with allowed or value aliases');
|
114: | }
|
115: | }
|
116: |
|
117: | $this->validateDirectiveAllowed($d);
|
118: | $this->validateDirectiveValueAliases($d);
|
119: | $this->validateDirectiveAliases($d);
|
120: |
|
121: | array_pop($this->context);
|
122: | }
|
123: |
|
124: | |
125: | |
126: | |
127: | |
128: |
|
129: | public function validateDirectiveAllowed($d)
|
130: | {
|
131: | if (is_null($d->allowed)) {
|
132: | return;
|
133: | }
|
134: | $this->with($d, 'allowed')
|
135: | ->assertNotEmpty()
|
136: | ->assertIsLookup();
|
137: | if (is_string($d->default) && !isset($d->allowed[$d->default])) {
|
138: | $this->error('default', 'must be an allowed value');
|
139: | }
|
140: | $this->context[] = 'allowed';
|
141: | foreach ($d->allowed as $val => $x) {
|
142: | if (!is_string($val)) {
|
143: | $this->error("value $val", 'must be a string');
|
144: | }
|
145: | }
|
146: | array_pop($this->context);
|
147: | }
|
148: |
|
149: | |
150: | |
151: | |
152: | |
153: |
|
154: | public function validateDirectiveValueAliases($d)
|
155: | {
|
156: | if (is_null($d->valueAliases)) {
|
157: | return;
|
158: | }
|
159: | $this->with($d, 'valueAliases')
|
160: | ->assertIsArray();
|
161: | $this->context[] = 'valueAliases';
|
162: | foreach ($d->valueAliases as $alias => $real) {
|
163: | if (!is_string($alias)) {
|
164: | $this->error("alias $alias", 'must be a string');
|
165: | }
|
166: | if (!is_string($real)) {
|
167: | $this->error("alias target $real from alias '$alias'", 'must be a string');
|
168: | }
|
169: | if ($alias === $real) {
|
170: | $this->error("alias '$alias'", "must not be an alias to itself");
|
171: | }
|
172: | }
|
173: | if (!is_null($d->allowed)) {
|
174: | foreach ($d->valueAliases as $alias => $real) {
|
175: | if (isset($d->allowed[$alias])) {
|
176: | $this->error("alias '$alias'", 'must not be an allowed value');
|
177: | } elseif (!isset($d->allowed[$real])) {
|
178: | $this->error("alias '$alias'", 'must be an alias to an allowed value');
|
179: | }
|
180: | }
|
181: | }
|
182: | array_pop($this->context);
|
183: | }
|
184: |
|
185: | |
186: | |
187: | |
188: | |
189: |
|
190: | public function validateDirectiveAliases($d)
|
191: | {
|
192: | $this->with($d, 'aliases')
|
193: | ->assertIsArray();
|
194: | $this->context[] = 'aliases';
|
195: | foreach ($d->aliases as $alias) {
|
196: | $this->validateId($alias);
|
197: | $s = $alias->toString();
|
198: | if (isset($this->interchange->directives[$s])) {
|
199: | $this->error("alias '$s'", 'collides with another directive');
|
200: | }
|
201: | if (isset($this->aliases[$s])) {
|
202: | $other_directive = $this->aliases[$s];
|
203: | $this->error("alias '$s'", "collides with alias for directive '$other_directive'");
|
204: | }
|
205: | $this->aliases[$s] = $d->id->toString();
|
206: | }
|
207: | array_pop($this->context);
|
208: | }
|
209: |
|
210: |
|
211: |
|
212: | |
213: | |
214: | |
215: | |
216: | |
217: | |
218: |
|
219: | protected function with($obj, $member)
|
220: | {
|
221: | return new HTMLPurifier_ConfigSchema_ValidatorAtom($this->getFormattedContext(), $obj, $member);
|
222: | }
|
223: |
|
224: | |
225: | |
226: | |
227: |
|
228: | protected function error($target, $msg)
|
229: | {
|
230: | if ($target !== false) {
|
231: | $prefix = ucfirst($target) . ' in ' . $this->getFormattedContext();
|
232: | } else {
|
233: | $prefix = ucfirst($this->getFormattedContext());
|
234: | }
|
235: | throw new HTMLPurifier_ConfigSchema_Exception(trim($prefix . ' ' . $msg));
|
236: | }
|
237: |
|
238: | |
239: | |
240: | |
241: |
|
242: | protected function getFormattedContext()
|
243: | {
|
244: | return implode(' in ', array_reverse($this->context));
|
245: | }
|
246: | }
|
247: |
|
248: |
|
249: | |