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