]> git.immae.eu Git - github/wallabag/wallabag.git/blame - inc/3rdparty/htmlpurifier/HTMLPurifier/Config.php
remove autoload section in composer.json
[github/wallabag/wallabag.git] / inc / 3rdparty / htmlpurifier / HTMLPurifier / Config.php
CommitLineData
d4949327
NL
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
17class 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