]>
Commit | Line | Data |
---|---|---|
1 | <?php\r | |
2 | \r | |
3 | /**\r | |
4 | * Configuration object that triggers customizable behavior.\r | |
5 | *\r | |
6 | * @warning This class is strongly defined: that means that the class\r | |
7 | * will fail if an undefined directive is retrieved or set.\r | |
8 | *\r | |
9 | * @note Many classes that could (although many times don't) use the\r | |
10 | * configuration object make it a mandatory parameter. This is\r | |
11 | * because a configuration object should always be forwarded,\r | |
12 | * otherwise, you run the risk of missing a parameter and then\r | |
13 | * being stumped when a configuration directive doesn't work.\r | |
14 | *\r | |
15 | * @todo Reconsider some of the public member variables\r | |
16 | */\r | |
17 | class HTMLPurifier_Config\r | |
18 | {\r | |
19 | \r | |
20 | /**\r | |
21 | * HTML Purifier's version\r | |
22 | * @type string\r | |
23 | */\r | |
24 | public $version = '4.6.0';\r | |
25 | \r | |
26 | /**\r | |
27 | * Whether or not to automatically finalize\r | |
28 | * the object if a read operation is done.\r | |
29 | * @type bool\r | |
30 | */\r | |
31 | public $autoFinalize = true;\r | |
32 | \r | |
33 | // protected member variables\r | |
34 | \r | |
35 | /**\r | |
36 | * Namespace indexed array of serials for specific namespaces.\r | |
37 | * @see getSerial() for more info.\r | |
38 | * @type string[]\r | |
39 | */\r | |
40 | protected $serials = array();\r | |
41 | \r | |
42 | /**\r | |
43 | * Serial for entire configuration object.\r | |
44 | * @type string\r | |
45 | */\r | |
46 | protected $serial;\r | |
47 | \r | |
48 | /**\r | |
49 | * Parser for variables.\r | |
50 | * @type HTMLPurifier_VarParser_Flexible\r | |
51 | */\r | |
52 | protected $parser = null;\r | |
53 | \r | |
54 | /**\r | |
55 | * Reference HTMLPurifier_ConfigSchema for value checking.\r | |
56 | * @type HTMLPurifier_ConfigSchema\r | |
57 | * @note This is public for introspective purposes. Please don't\r | |
58 | * abuse!\r | |
59 | */\r | |
60 | public $def;\r | |
61 | \r | |
62 | /**\r | |
63 | * Indexed array of definitions.\r | |
64 | * @type HTMLPurifier_Definition[]\r | |
65 | */\r | |
66 | protected $definitions;\r | |
67 | \r | |
68 | /**\r | |
69 | * Whether or not config is finalized.\r | |
70 | * @type bool\r | |
71 | */\r | |
72 | protected $finalized = false;\r | |
73 | \r | |
74 | /**\r | |
75 | * Property list containing configuration directives.\r | |
76 | * @type array\r | |
77 | */\r | |
78 | protected $plist;\r | |
79 | \r | |
80 | /**\r | |
81 | * Whether or not a set is taking place due to an alias lookup.\r | |
82 | * @type bool\r | |
83 | */\r | |
84 | private $aliasMode;\r | |
85 | \r | |
86 | /**\r | |
87 | * Set to false if you do not want line and file numbers in errors.\r | |
88 | * (useful when unit testing). This will also compress some errors\r | |
89 | * and exceptions.\r | |
90 | * @type bool\r | |
91 | */\r | |
92 | public $chatty = true;\r | |
93 | \r | |
94 | /**\r | |
95 | * Current lock; only gets to this namespace are allowed.\r | |
96 | * @type string\r | |
97 | */\r | |
98 | private $lock;\r | |
99 | \r | |
100 | /**\r | |
101 | * Constructor\r | |
102 | * @param HTMLPurifier_ConfigSchema $definition ConfigSchema that defines\r | |
103 | * what directives are allowed.\r | |
104 | * @param HTMLPurifier_PropertyList $parent\r | |
105 | */\r | |
106 | public function __construct($definition, $parent = null)\r | |
107 | {\r | |
108 | $parent = $parent ? $parent : $definition->defaultPlist;\r | |
109 | $this->plist = new HTMLPurifier_PropertyList($parent);\r | |
110 | $this->def = $definition; // keep a copy around for checking\r | |
111 | $this->parser = new HTMLPurifier_VarParser_Flexible();\r | |
112 | }\r | |
113 | \r | |
114 | /**\r | |
115 | * Convenience constructor that creates a config object based on a mixed var\r | |
116 | * @param mixed $config Variable that defines the state of the config\r | |
117 | * object. Can be: a HTMLPurifier_Config() object,\r | |
118 | * an array of directives based on loadArray(),\r | |
119 | * or a string filename of an ini file.\r | |
120 | * @param HTMLPurifier_ConfigSchema $schema Schema object\r | |
121 | * @return HTMLPurifier_Config Configured object\r | |
122 | */\r | |
123 | public static function create($config, $schema = null)\r | |
124 | {\r | |
125 | if ($config instanceof HTMLPurifier_Config) {\r | |
126 | // pass-through\r | |
127 | return $config;\r | |
128 | }\r | |
129 | if (!$schema) {\r | |
130 | $ret = HTMLPurifier_Config::createDefault();\r | |
131 | } else {\r | |
132 | $ret = new HTMLPurifier_Config($schema);\r | |
133 | }\r | |
134 | if (is_string($config)) {\r | |
135 | $ret->loadIni($config);\r | |
136 | } elseif (is_array($config)) $ret->loadArray($config);\r | |
137 | return $ret;\r | |
138 | }\r | |
139 | \r | |
140 | /**\r | |
141 | * Creates a new config object that inherits from a previous one.\r | |
142 | * @param HTMLPurifier_Config $config Configuration object to inherit from.\r | |
143 | * @return HTMLPurifier_Config object with $config as its parent.\r | |
144 | */\r | |
145 | public static function inherit(HTMLPurifier_Config $config)\r | |
146 | {\r | |
147 | return new HTMLPurifier_Config($config->def, $config->plist);\r | |
148 | }\r | |
149 | \r | |
150 | /**\r | |
151 | * Convenience constructor that creates a default configuration object.\r | |
152 | * @return HTMLPurifier_Config default object.\r | |
153 | */\r | |
154 | public static function createDefault()\r | |
155 | {\r | |
156 | $definition = HTMLPurifier_ConfigSchema::instance();\r | |
157 | $config = new HTMLPurifier_Config($definition);\r | |
158 | return $config;\r | |
159 | }\r | |
160 | \r | |
161 | /**\r | |
162 | * Retrieves a value from the configuration.\r | |
163 | *\r | |
164 | * @param string $key String key\r | |
165 | * @param mixed $a\r | |
166 | *\r | |
167 | * @return mixed\r | |
168 | */\r | |
169 | public function get($key, $a = null)\r | |
170 | {\r | |
171 | if ($a !== null) {\r | |
172 | $this->triggerError(\r | |
173 | "Using deprecated API: use \$config->get('$key.$a') instead",\r | |
174 | E_USER_WARNING\r | |
175 | );\r | |
176 | $key = "$key.$a";\r | |
177 | }\r | |
178 | if (!$this->finalized) {\r | |
179 | $this->autoFinalize();\r | |
180 | }\r | |
181 | if (!isset($this->def->info[$key])) {\r | |
182 | // can't add % due to SimpleTest bug\r | |
183 | $this->triggerError(\r | |
184 | 'Cannot retrieve value of undefined directive ' . htmlspecialchars($key),\r | |
185 | E_USER_WARNING\r | |
186 | );\r | |
187 | return;\r | |
188 | }\r | |
189 | if (isset($this->def->info[$key]->isAlias)) {\r | |
190 | $d = $this->def->info[$key];\r | |
191 | $this->triggerError(\r | |
192 | 'Cannot get value from aliased directive, use real name ' . $d->key,\r | |
193 | E_USER_ERROR\r | |
194 | );\r | |
195 | return;\r | |
196 | }\r | |
197 | if ($this->lock) {\r | |
198 | list($ns) = explode('.', $key);\r | |
199 | if ($ns !== $this->lock) {\r | |
200 | $this->triggerError(\r | |
201 | 'Cannot get value of namespace ' . $ns . ' when lock for ' .\r | |
202 | $this->lock .\r | |
203 | ' is active, this probably indicates a Definition setup method ' .\r | |
204 | 'is accessing directives that are not within its namespace',\r | |
205 | E_USER_ERROR\r | |
206 | );\r | |
207 | return;\r | |
208 | }\r | |
209 | }\r | |
210 | return $this->plist->get($key);\r | |
211 | }\r | |
212 | \r | |
213 | /**\r | |
214 | * Retrieves an array of directives to values from a given namespace\r | |
215 | *\r | |
216 | * @param string $namespace String namespace\r | |
217 | *\r | |
218 | * @return array\r | |
219 | */\r | |
220 | public function getBatch($namespace)\r | |
221 | {\r | |
222 | if (!$this->finalized) {\r | |
223 | $this->autoFinalize();\r | |
224 | }\r | |
225 | $full = $this->getAll();\r | |
226 | if (!isset($full[$namespace])) {\r | |
227 | $this->triggerError(\r | |
228 | 'Cannot retrieve undefined namespace ' .\r | |
229 | htmlspecialchars($namespace),\r | |
230 | E_USER_WARNING\r | |
231 | );\r | |
232 | return;\r | |
233 | }\r | |
234 | return $full[$namespace];\r | |
235 | }\r | |
236 | \r | |
237 | /**\r | |
238 | * Returns a SHA-1 signature of a segment of the configuration object\r | |
239 | * that uniquely identifies that particular configuration\r | |
240 | *\r | |
241 | * @param string $namespace Namespace to get serial for\r | |
242 | *\r | |
243 | * @return string\r | |
244 | * @note Revision is handled specially and is removed from the batch\r | |
245 | * before processing!\r | |
246 | */\r | |
247 | public function getBatchSerial($namespace)\r | |
248 | {\r | |
249 | if (empty($this->serials[$namespace])) {\r | |
250 | $batch = $this->getBatch($namespace);\r | |
251 | unset($batch['DefinitionRev']);\r | |
252 | $this->serials[$namespace] = sha1(serialize($batch));\r | |
253 | }\r | |
254 | return $this->serials[$namespace];\r | |
255 | }\r | |
256 | \r | |
257 | /**\r | |
258 | * Returns a SHA-1 signature for the entire configuration object\r | |
259 | * that uniquely identifies that particular configuration\r | |
260 | *\r | |
261 | * @return string\r | |
262 | */\r | |
263 | public function getSerial()\r | |
264 | {\r | |
265 | if (empty($this->serial)) {\r | |
266 | $this->serial = sha1(serialize($this->getAll()));\r | |
267 | }\r | |
268 | return $this->serial;\r | |
269 | }\r | |
270 | \r | |
271 | /**\r | |
272 | * Retrieves all directives, organized by namespace\r | |
273 | *\r | |
274 | * @warning This is a pretty inefficient function, avoid if you can\r | |
275 | */\r | |
276 | public function getAll()\r | |
277 | {\r | |
278 | if (!$this->finalized) {\r | |
279 | $this->autoFinalize();\r | |
280 | }\r | |
281 | $ret = array();\r | |
282 | foreach ($this->plist->squash() as $name => $value) {\r | |
283 | list($ns, $key) = explode('.', $name, 2);\r | |
284 | $ret[$ns][$key] = $value;\r | |
285 | }\r | |
286 | return $ret;\r | |
287 | }\r | |
288 | \r | |
289 | /**\r | |
290 | * Sets a value to configuration.\r | |
291 | *\r | |
292 | * @param string $key key\r | |
293 | * @param mixed $value value\r | |
294 | * @param mixed $a\r | |
295 | */\r | |
296 | public function set($key, $value, $a = null)\r | |
297 | {\r | |
298 | if (strpos($key, '.') === false) {\r | |
299 | $namespace = $key;\r | |
300 | $directive = $value;\r | |
301 | $value = $a;\r | |
302 | $key = "$key.$directive";\r | |
303 | $this->triggerError("Using deprecated API: use \$config->set('$key', ...) instead", E_USER_NOTICE);\r | |
304 | } else {\r | |
305 | list($namespace) = explode('.', $key);\r | |
306 | }\r | |
307 | if ($this->isFinalized('Cannot set directive after finalization')) {\r | |
308 | return;\r | |
309 | }\r | |
310 | if (!isset($this->def->info[$key])) {\r | |
311 | $this->triggerError(\r | |
312 | 'Cannot set undefined directive ' . htmlspecialchars($key) . ' to value',\r | |
313 | E_USER_WARNING\r | |
314 | );\r | |
315 | return;\r | |
316 | }\r | |
317 | $def = $this->def->info[$key];\r | |
318 | \r | |
319 | if (isset($def->isAlias)) {\r | |
320 | if ($this->aliasMode) {\r | |
321 | $this->triggerError(\r | |
322 | 'Double-aliases not allowed, please fix '.\r | |
323 | 'ConfigSchema bug with' . $key,\r | |
324 | E_USER_ERROR\r | |
325 | );\r | |
326 | return;\r | |
327 | }\r | |
328 | $this->aliasMode = true;\r | |
329 | $this->set($def->key, $value);\r | |
330 | $this->aliasMode = false;\r | |
331 | $this->triggerError("$key is an alias, preferred directive name is {$def->key}", E_USER_NOTICE);\r | |
332 | return;\r | |
333 | }\r | |
334 | \r | |
335 | // Raw type might be negative when using the fully optimized form\r | |
336 | // of stdclass, which indicates allow_null == true\r | |
337 | $rtype = is_int($def) ? $def : $def->type;\r | |
338 | if ($rtype < 0) {\r | |
339 | $type = -$rtype;\r | |
340 | $allow_null = true;\r | |
341 | } else {\r | |
342 | $type = $rtype;\r | |
343 | $allow_null = isset($def->allow_null);\r | |
344 | }\r | |
345 | \r | |
346 | try {\r | |
347 | $value = $this->parser->parse($value, $type, $allow_null);\r | |
348 | } catch (HTMLPurifier_VarParserException $e) {\r | |
349 | $this->triggerError(\r | |
350 | 'Value for ' . $key . ' is of invalid type, should be ' .\r | |
351 | HTMLPurifier_VarParser::getTypeName($type),\r | |
352 | E_USER_WARNING\r | |
353 | );\r | |
354 | return;\r | |
355 | }\r | |
356 | if (is_string($value) && is_object($def)) {\r | |
357 | // resolve value alias if defined\r | |
358 | if (isset($def->aliases[$value])) {\r | |
359 | $value = $def->aliases[$value];\r | |
360 | }\r | |
361 | // check to see if the value is allowed\r | |
362 | if (isset($def->allowed) && !isset($def->allowed[$value])) {\r | |
363 | $this->triggerError(\r | |
364 | 'Value not supported, valid values are: ' .\r | |
365 | $this->_listify($def->allowed),\r | |
366 | E_USER_WARNING\r | |
367 | );\r | |
368 | return;\r | |
369 | }\r | |
370 | }\r | |
371 | $this->plist->set($key, $value);\r | |
372 | \r | |
373 | // reset definitions if the directives they depend on changed\r | |
374 | // this is a very costly process, so it's discouraged\r | |
375 | // with finalization\r | |
376 | if ($namespace == 'HTML' || $namespace == 'CSS' || $namespace == 'URI') {\r | |
377 | $this->definitions[$namespace] = null;\r | |
378 | }\r | |
379 | \r | |
380 | $this->serials[$namespace] = false;\r | |
381 | }\r | |
382 | \r | |
383 | /**\r | |
384 | * Convenience function for error reporting\r | |
385 | *\r | |
386 | * @param array $lookup\r | |
387 | *\r | |
388 | * @return string\r | |
389 | */\r | |
390 | private function _listify($lookup)\r | |
391 | {\r | |
392 | $list = array();\r | |
393 | foreach ($lookup as $name => $b) {\r | |
394 | $list[] = $name;\r | |
395 | }\r | |
396 | return implode(', ', $list);\r | |
397 | }\r | |
398 | \r | |
399 | /**\r | |
400 | * Retrieves object reference to the HTML definition.\r | |
401 | *\r | |
402 | * @param bool $raw Return a copy that has not been setup yet. Must be\r | |
403 | * called before it's been setup, otherwise won't work.\r | |
404 | * @param bool $optimized If true, this method may return null, to\r | |
405 | * indicate that a cached version of the modified\r | |
406 | * definition object is available and no further edits\r | |
407 | * are necessary. Consider using\r | |
408 | * maybeGetRawHTMLDefinition, which is more explicitly\r | |
409 | * named, instead.\r | |
410 | *\r | |
411 | * @return HTMLPurifier_HTMLDefinition\r | |
412 | */\r | |
413 | public function getHTMLDefinition($raw = false, $optimized = false)\r | |
414 | {\r | |
415 | return $this->getDefinition('HTML', $raw, $optimized);\r | |
416 | }\r | |
417 | \r | |
418 | /**\r | |
419 | * Retrieves object reference to the CSS definition\r | |
420 | *\r | |
421 | * @param bool $raw Return a copy that has not been setup yet. Must be\r | |
422 | * called before it's been setup, otherwise won't work.\r | |
423 | * @param bool $optimized If true, this method may return null, to\r | |
424 | * indicate that a cached version of the modified\r | |
425 | * definition object is available and no further edits\r | |
426 | * are necessary. Consider using\r | |
427 | * maybeGetRawCSSDefinition, which is more explicitly\r | |
428 | * named, instead.\r | |
429 | *\r | |
430 | * @return HTMLPurifier_CSSDefinition\r | |
431 | */\r | |
432 | public function getCSSDefinition($raw = false, $optimized = false)\r | |
433 | {\r | |
434 | return $this->getDefinition('CSS', $raw, $optimized);\r | |
435 | }\r | |
436 | \r | |
437 | /**\r | |
438 | * Retrieves object reference to the URI definition\r | |
439 | *\r | |
440 | * @param bool $raw Return a copy that has not been setup yet. Must be\r | |
441 | * called before it's been setup, otherwise won't work.\r | |
442 | * @param bool $optimized If true, this method may return null, to\r | |
443 | * indicate that a cached version of the modified\r | |
444 | * definition object is available and no further edits\r | |
445 | * are necessary. Consider using\r | |
446 | * maybeGetRawURIDefinition, which is more explicitly\r | |
447 | * named, instead.\r | |
448 | *\r | |
449 | * @return HTMLPurifier_URIDefinition\r | |
450 | */\r | |
451 | public function getURIDefinition($raw = false, $optimized = false)\r | |
452 | {\r | |
453 | return $this->getDefinition('URI', $raw, $optimized);\r | |
454 | }\r | |
455 | \r | |
456 | /**\r | |
457 | * Retrieves a definition\r | |
458 | *\r | |
459 | * @param string $type Type of definition: HTML, CSS, etc\r | |
460 | * @param bool $raw Whether or not definition should be returned raw\r | |
461 | * @param bool $optimized Only has an effect when $raw is true. Whether\r | |
462 | * or not to return null if the result is already present in\r | |
463 | * the cache. This is off by default for backwards\r | |
464 | * compatibility reasons, but you need to do things this\r | |
465 | * way in order to ensure that caching is done properly.\r | |
466 | * Check out enduser-customize.html for more details.\r | |
467 | * We probably won't ever change this default, as much as the\r | |
468 | * maybe semantics is the "right thing to do."\r | |
469 | *\r | |
470 | * @throws HTMLPurifier_Exception\r | |
471 | * @return HTMLPurifier_Definition\r | |
472 | */\r | |
473 | public function getDefinition($type, $raw = false, $optimized = false)\r | |
474 | {\r | |
475 | if ($optimized && !$raw) {\r | |
476 | throw new HTMLPurifier_Exception("Cannot set optimized = true when raw = false");\r | |
477 | }\r | |
478 | if (!$this->finalized) {\r | |
479 | $this->autoFinalize();\r | |
480 | }\r | |
481 | // temporarily suspend locks, so we can handle recursive definition calls\r | |
482 | $lock = $this->lock;\r | |
483 | $this->lock = null;\r | |
484 | $factory = HTMLPurifier_DefinitionCacheFactory::instance();\r | |
485 | $cache = $factory->create($type, $this);\r | |
486 | $this->lock = $lock;\r | |
487 | if (!$raw) {\r | |
488 | // full definition\r | |
489 | // ---------------\r | |
490 | // check if definition is in memory\r | |
491 | if (!empty($this->definitions[$type])) {\r | |
492 | $def = $this->definitions[$type];\r | |
493 | // check if the definition is setup\r | |
494 | if ($def->setup) {\r | |
495 | return $def;\r | |
496 | } else {\r | |
497 | $def->setup($this);\r | |
498 | if ($def->optimized) {\r | |
499 | $cache->add($def, $this);\r | |
500 | }\r | |
501 | return $def;\r | |
502 | }\r | |
503 | }\r | |
504 | // check if definition is in cache\r | |
505 | $def = $cache->get($this);\r | |
506 | if ($def) {\r | |
507 | // definition in cache, save to memory and return it\r | |
508 | $this->definitions[$type] = $def;\r | |
509 | return $def;\r | |
510 | }\r | |
511 | // initialize it\r | |
512 | $def = $this->initDefinition($type);\r | |
513 | // set it up\r | |
514 | $this->lock = $type;\r | |
515 | $def->setup($this);\r | |
516 | $this->lock = null;\r | |
517 | // save in cache\r | |
518 | $cache->add($def, $this);\r | |
519 | // return it\r | |
520 | return $def;\r | |
521 | } else {\r | |
522 | // raw definition\r | |
523 | // --------------\r | |
524 | // check preconditions\r | |
525 | $def = null;\r | |
526 | if ($optimized) {\r | |
527 | if (is_null($this->get($type . '.DefinitionID'))) {\r | |
528 | // fatally error out if definition ID not set\r | |
529 | throw new HTMLPurifier_Exception(\r | |
530 | "Cannot retrieve raw version without specifying %$type.DefinitionID"\r | |
531 | );\r | |
532 | }\r | |
533 | }\r | |
534 | if (!empty($this->definitions[$type])) {\r | |
535 | $def = $this->definitions[$type];\r | |
536 | if ($def->setup && !$optimized) {\r | |
537 | $extra = $this->chatty ?\r | |
538 | " (try moving this code block earlier in your initialization)" :\r | |
539 | "";\r | |
540 | throw new HTMLPurifier_Exception(\r | |
541 | "Cannot retrieve raw definition after it has already been setup" .\r | |
542 | $extra\r | |
543 | );\r | |
544 | }\r | |
545 | if ($def->optimized === null) {\r | |
546 | $extra = $this->chatty ? " (try flushing your cache)" : "";\r | |
547 | throw new HTMLPurifier_Exception(\r | |
548 | "Optimization status of definition is unknown" . $extra\r | |
549 | );\r | |
550 | }\r | |
551 | if ($def->optimized !== $optimized) {\r | |
552 | $msg = $optimized ? "optimized" : "unoptimized";\r | |
553 | $extra = $this->chatty ?\r | |
554 | " (this backtrace is for the first inconsistent call, which was for a $msg raw definition)"\r | |
555 | : "";\r | |
556 | throw new HTMLPurifier_Exception(\r | |
557 | "Inconsistent use of optimized and unoptimized raw definition retrievals" . $extra\r | |
558 | );\r | |
559 | }\r | |
560 | }\r | |
561 | // check if definition was in memory\r | |
562 | if ($def) {\r | |
563 | if ($def->setup) {\r | |
564 | // invariant: $optimized === true (checked above)\r | |
565 | return null;\r | |
566 | } else {\r | |
567 | return $def;\r | |
568 | }\r | |
569 | }\r | |
570 | // if optimized, check if definition was in cache\r | |
571 | // (because we do the memory check first, this formulation\r | |
572 | // is prone to cache slamming, but I think\r | |
573 | // guaranteeing that either /all/ of the raw\r | |
574 | // setup code or /none/ of it is run is more important.)\r | |
575 | if ($optimized) {\r | |
576 | // This code path only gets run once; once we put\r | |
577 | // something in $definitions (which is guaranteed by the\r | |
578 | // trailing code), we always short-circuit above.\r | |
579 | $def = $cache->get($this);\r | |
580 | if ($def) {\r | |
581 | // save the full definition for later, but don't\r | |
582 | // return it yet\r | |
583 | $this->definitions[$type] = $def;\r | |
584 | return null;\r | |
585 | }\r | |
586 | }\r | |
587 | // check invariants for creation\r | |
588 | if (!$optimized) {\r | |
589 | if (!is_null($this->get($type . '.DefinitionID'))) {\r | |
590 | if ($this->chatty) {\r | |
591 | $this->triggerError(\r | |
592 | 'Due to a documentation error in previous version of HTML Purifier, your ' .\r | |
593 | 'definitions are not being cached. If this is OK, you can remove the ' .\r | |
594 | '%$type.DefinitionRev and %$type.DefinitionID declaration. Otherwise, ' .\r | |
595 | 'modify your code to use maybeGetRawDefinition, and test if the returned ' .\r | |
596 | 'value is null before making any edits (if it is null, that means that a ' .\r | |
597 | 'cached version is available, and no raw operations are necessary). See ' .\r | |
598 | '<a href="http://htmlpurifier.org/docs/enduser-customize.html#optimized">' .\r | |
599 | 'Customize</a> for more details',\r | |
600 | E_USER_WARNING\r | |
601 | );\r | |
602 | } else {\r | |
603 | $this->triggerError(\r | |
604 | "Useless DefinitionID declaration",\r | |
605 | E_USER_WARNING\r | |
606 | );\r | |
607 | }\r | |
608 | }\r | |
609 | }\r | |
610 | // initialize it\r | |
611 | $def = $this->initDefinition($type);\r | |
612 | $def->optimized = $optimized;\r | |
613 | return $def;\r | |
614 | }\r | |
615 | throw new HTMLPurifier_Exception("The impossible happened!");\r | |
616 | }\r | |
617 | \r | |
618 | /**\r | |
619 | * Initialise definition\r | |
620 | *\r | |
621 | * @param string $type What type of definition to create\r | |
622 | *\r | |
623 | * @return HTMLPurifier_CSSDefinition|HTMLPurifier_HTMLDefinition|HTMLPurifier_URIDefinition\r | |
624 | * @throws HTMLPurifier_Exception\r | |
625 | */\r | |
626 | private function initDefinition($type)\r | |
627 | {\r | |
628 | // quick checks failed, let's create the object\r | |
629 | if ($type == 'HTML') {\r | |
630 | $def = new HTMLPurifier_HTMLDefinition();\r | |
631 | } elseif ($type == 'CSS') {\r | |
632 | $def = new HTMLPurifier_CSSDefinition();\r | |
633 | } elseif ($type == 'URI') {\r | |
634 | $def = new HTMLPurifier_URIDefinition();\r | |
635 | } else {\r | |
636 | throw new HTMLPurifier_Exception(\r | |
637 | "Definition of $type type not supported"\r | |
638 | );\r | |
639 | }\r | |
640 | $this->definitions[$type] = $def;\r | |
641 | return $def;\r | |
642 | }\r | |
643 | \r | |
644 | public function maybeGetRawDefinition($name)\r | |
645 | {\r | |
646 | return $this->getDefinition($name, true, true);\r | |
647 | }\r | |
648 | \r | |
649 | public function maybeGetRawHTMLDefinition()\r | |
650 | {\r | |
651 | return $this->getDefinition('HTML', true, true);\r | |
652 | }\r | |
653 | \r | |
654 | public function maybeGetRawCSSDefinition()\r | |
655 | {\r | |
656 | return $this->getDefinition('CSS', true, true);\r | |
657 | }\r | |
658 | \r | |
659 | public function maybeGetRawURIDefinition()\r | |
660 | {\r | |
661 | return $this->getDefinition('URI', true, true);\r | |
662 | }\r | |
663 | \r | |
664 | /**\r | |
665 | * Loads configuration values from an array with the following structure:\r | |
666 | * Namespace.Directive => Value\r | |
667 | *\r | |
668 | * @param array $config_array Configuration associative array\r | |
669 | */\r | |
670 | public function loadArray($config_array)\r | |
671 | {\r | |
672 | if ($this->isFinalized('Cannot load directives after finalization')) {\r | |
673 | return;\r | |
674 | }\r | |
675 | foreach ($config_array as $key => $value) {\r | |
676 | $key = str_replace('_', '.', $key);\r | |
677 | if (strpos($key, '.') !== false) {\r | |
678 | $this->set($key, $value);\r | |
679 | } else {\r | |
680 | $namespace = $key;\r | |
681 | $namespace_values = $value;\r | |
682 | foreach ($namespace_values as $directive => $value2) {\r | |
683 | $this->set($namespace .'.'. $directive, $value2);\r | |
684 | }\r | |
685 | }\r | |
686 | }\r | |
687 | }\r | |
688 | \r | |
689 | /**\r | |
690 | * Returns a list of array(namespace, directive) for all directives\r | |
691 | * that are allowed in a web-form context as per an allowed\r | |
692 | * namespaces/directives list.\r | |
693 | *\r | |
694 | * @param array $allowed List of allowed namespaces/directives\r | |
695 | * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy\r | |
696 | *\r | |
697 | * @return array\r | |
698 | */\r | |
699 | public static function getAllowedDirectivesForForm($allowed, $schema = null)\r | |
700 | {\r | |
701 | if (!$schema) {\r | |
702 | $schema = HTMLPurifier_ConfigSchema::instance();\r | |
703 | }\r | |
704 | if ($allowed !== true) {\r | |
705 | if (is_string($allowed)) {\r | |
706 | $allowed = array($allowed);\r | |
707 | }\r | |
708 | $allowed_ns = array();\r | |
709 | $allowed_directives = array();\r | |
710 | $blacklisted_directives = array();\r | |
711 | foreach ($allowed as $ns_or_directive) {\r | |
712 | if (strpos($ns_or_directive, '.') !== false) {\r | |
713 | // directive\r | |
714 | if ($ns_or_directive[0] == '-') {\r | |
715 | $blacklisted_directives[substr($ns_or_directive, 1)] = true;\r | |
716 | } else {\r | |
717 | $allowed_directives[$ns_or_directive] = true;\r | |
718 | }\r | |
719 | } else {\r | |
720 | // namespace\r | |
721 | $allowed_ns[$ns_or_directive] = true;\r | |
722 | }\r | |
723 | }\r | |
724 | }\r | |
725 | $ret = array();\r | |
726 | foreach ($schema->info as $key => $def) {\r | |
727 | list($ns, $directive) = explode('.', $key, 2);\r | |
728 | if ($allowed !== true) {\r | |
729 | if (isset($blacklisted_directives["$ns.$directive"])) {\r | |
730 | continue;\r | |
731 | }\r | |
732 | if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) {\r | |
733 | continue;\r | |
734 | }\r | |
735 | }\r | |
736 | if (isset($def->isAlias)) {\r | |
737 | continue;\r | |
738 | }\r | |
739 | if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') {\r | |
740 | continue;\r | |
741 | }\r | |
742 | $ret[] = array($ns, $directive);\r | |
743 | }\r | |
744 | return $ret;\r | |
745 | }\r | |
746 | \r | |
747 | /**\r | |
748 | * Loads configuration values from $_GET/$_POST that were posted\r | |
749 | * via ConfigForm\r | |
750 | *\r | |
751 | * @param array $array $_GET or $_POST array to import\r | |
752 | * @param string|bool $index Index/name that the config variables are in\r | |
753 | * @param array|bool $allowed List of allowed namespaces/directives\r | |
754 | * @param bool $mq_fix Boolean whether or not to enable magic quotes fix\r | |
755 | * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy\r | |
756 | *\r | |
757 | * @return mixed\r | |
758 | */\r | |
759 | public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null)\r | |
760 | {\r | |
761 | $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $schema);\r | |
762 | $config = HTMLPurifier_Config::create($ret, $schema);\r | |
763 | return $config;\r | |
764 | }\r | |
765 | \r | |
766 | /**\r | |
767 | * Merges in configuration values from $_GET/$_POST to object. NOT STATIC.\r | |
768 | *\r | |
769 | * @param array $array $_GET or $_POST array to import\r | |
770 | * @param string|bool $index Index/name that the config variables are in\r | |
771 | * @param array|bool $allowed List of allowed namespaces/directives\r | |
772 | * @param bool $mq_fix Boolean whether or not to enable magic quotes fix\r | |
773 | */\r | |
774 | public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true)\r | |
775 | {\r | |
776 | $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $this->def);\r | |
777 | $this->loadArray($ret);\r | |
778 | }\r | |
779 | \r | |
780 | /**\r | |
781 | * Prepares an array from a form into something usable for the more\r | |
782 | * strict parts of HTMLPurifier_Config\r | |
783 | *\r | |
784 | * @param array $array $_GET or $_POST array to import\r | |
785 | * @param string|bool $index Index/name that the config variables are in\r | |
786 | * @param array|bool $allowed List of allowed namespaces/directives\r | |
787 | * @param bool $mq_fix Boolean whether or not to enable magic quotes fix\r | |
788 | * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy\r | |
789 | *\r | |
790 | * @return array\r | |
791 | */\r | |
792 | public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null)\r | |
793 | {\r | |
794 | if ($index !== false) {\r | |
795 | $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array();\r | |
796 | }\r | |
797 | $mq = $mq_fix && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc();\r | |
798 | \r | |
799 | $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $schema);\r | |
800 | $ret = array();\r | |
801 | foreach ($allowed as $key) {\r | |
802 | list($ns, $directive) = $key;\r | |
803 | $skey = "$ns.$directive";\r | |
804 | if (!empty($array["Null_$skey"])) {\r | |
805 | $ret[$ns][$directive] = null;\r | |
806 | continue;\r | |
807 | }\r | |
808 | if (!isset($array[$skey])) {\r | |
809 | continue;\r | |
810 | }\r | |
811 | $value = $mq ? stripslashes($array[$skey]) : $array[$skey];\r | |
812 | $ret[$ns][$directive] = $value;\r | |
813 | }\r | |
814 | return $ret;\r | |
815 | }\r | |
816 | \r | |
817 | /**\r | |
818 | * Loads configuration values from an ini file\r | |
819 | *\r | |
820 | * @param string $filename Name of ini file\r | |
821 | */\r | |
822 | public function loadIni($filename)\r | |
823 | {\r | |
824 | if ($this->isFinalized('Cannot load directives after finalization')) {\r | |
825 | return;\r | |
826 | }\r | |
827 | $array = parse_ini_file($filename, true);\r | |
828 | $this->loadArray($array);\r | |
829 | }\r | |
830 | \r | |
831 | /**\r | |
832 | * Checks whether or not the configuration object is finalized.\r | |
833 | *\r | |
834 | * @param string|bool $error String error message, or false for no error\r | |
835 | *\r | |
836 | * @return bool\r | |
837 | */\r | |
838 | public function isFinalized($error = false)\r | |
839 | {\r | |
840 | if ($this->finalized && $error) {\r | |
841 | $this->triggerError($error, E_USER_ERROR);\r | |
842 | }\r | |
843 | return $this->finalized;\r | |
844 | }\r | |
845 | \r | |
846 | /**\r | |
847 | * Finalizes configuration only if auto finalize is on and not\r | |
848 | * already finalized\r | |
849 | */\r | |
850 | public function autoFinalize()\r | |
851 | {\r | |
852 | if ($this->autoFinalize) {\r | |
853 | $this->finalize();\r | |
854 | } else {\r | |
855 | $this->plist->squash(true);\r | |
856 | }\r | |
857 | }\r | |
858 | \r | |
859 | /**\r | |
860 | * Finalizes a configuration object, prohibiting further change\r | |
861 | */\r | |
862 | public function finalize()\r | |
863 | {\r | |
864 | $this->finalized = true;\r | |
865 | $this->parser = null;\r | |
866 | }\r | |
867 | \r | |
868 | /**\r | |
869 | * Produces a nicely formatted error message by supplying the\r | |
870 | * stack frame information OUTSIDE of HTMLPurifier_Config.\r | |
871 | *\r | |
872 | * @param string $msg An error message\r | |
873 | * @param int $no An error number\r | |
874 | */\r | |
875 | protected function triggerError($msg, $no)\r | |
876 | {\r | |
877 | // determine previous stack frame\r | |
878 | $extra = '';\r | |
879 | if ($this->chatty) {\r | |
880 | $trace = debug_backtrace();\r | |
881 | // zip(tail(trace), trace) -- but PHP is not Haskell har har\r | |
882 | for ($i = 0, $c = count($trace); $i < $c - 1; $i++) {\r | |
883 | // XXX this is not correct on some versions of HTML Purifier\r | |
884 | if ($trace[$i + 1]['class'] === 'HTMLPurifier_Config') {\r | |
885 | continue;\r | |
886 | }\r | |
887 | $frame = $trace[$i];\r | |
888 | $extra = " invoked on line {$frame['line']} in file {$frame['file']}";\r | |
889 | break;\r | |
890 | }\r | |
891 | }\r | |
892 | trigger_error($msg . $extra, $no);\r | |
893 | }\r | |
894 | \r | |
895 | /**\r | |
896 | * Returns a serialized form of the configuration object that can\r | |
897 | * be reconstituted.\r | |
898 | *\r | |
899 | * @return string\r | |
900 | */\r | |
901 | public function serialize()\r | |
902 | {\r | |
903 | $this->getDefinition('HTML');\r | |
904 | $this->getDefinition('CSS');\r | |
905 | $this->getDefinition('URI');\r | |
906 | return serialize($this);\r | |
907 | }\r | |
908 | \r | |
909 | }\r | |
910 | \r | |
911 | // vim: et sw=4 sts=4\r |