Changeset 94428 in spip-zone


Ignore:
Timestamp:
Jan 4, 2016, 2:30:57 PM (5 years ago)
Author:
real3t@…
Message:

Mise à jour en version 4.7.0 (à tester)

Location:
_plugins_/htmlpurifier/lib
Files:
3 deleted
25 edited

Legend:

Unmodified
Added
Removed
  • _plugins_/htmlpurifier/lib/HTMLPurifier.standalone.php

    r43383 r94428  
    88 * FILE, changes will be overwritten the next time the script is run.
    99 *
    10  * @version 4.2.0
     10 * @version 4.7.0
    1111 *
    1212 * @warning
     
    4040
    4141/*
    42     HTML Purifier 4.2.0 - Standards Compliant HTML Filtering
     42    HTML Purifier 4.7.0 - Standards Compliant HTML Filtering
    4343    Copyright (C) 2006-2008 Edward Z. Yang
    4444
     
    7575{
    7676
    77     /** Version of HTML Purifier */
    78     public $version = '4.2.0';
    79 
    80     /** Constant with version of HTML Purifier */
    81     const VERSION = '4.2.0';
    82 
    83     /** Global configuration object */
     77    /**
     78     * Version of HTML Purifier.
     79     * @type string
     80     */
     81    public $version = '4.7.0';
     82
     83    /**
     84     * Constant with version of HTML Purifier.
     85     */
     86    const VERSION = '4.7.0';
     87
     88    /**
     89     * Global configuration object.
     90     * @type HTMLPurifier_Config
     91     */
    8492    public $config;
    8593
    86     /** Array of extra HTMLPurifier_Filter objects to run on HTML, for backwards compatibility */
     94    /**
     95     * Array of extra filter objects to run on HTML,
     96     * for backwards compatibility.
     97     * @type HTMLPurifier_Filter[]
     98     */
    8799    private $filters = array();
    88100
    89     /** Single instance of HTML Purifier */
     101    /**
     102     * Single instance of HTML Purifier.
     103     * @type HTMLPurifier
     104     */
    90105    private static $instance;
    91106
    92     protected $strategy, $generator;
    93 
    94     /**
    95      * Resultant HTMLPurifier_Context of last run purification. Is an array
    96      * of contexts if the last called method was purifyArray().
     107    /**
     108     * @type HTMLPurifier_Strategy_Core
     109     */
     110    protected $strategy;
     111
     112    /**
     113     * @type HTMLPurifier_Generator
     114     */
     115    protected $generator;
     116
     117    /**
     118     * Resultant context of last run purification.
     119     * Is an array of contexts if the last called method was purifyArray().
     120     * @type HTMLPurifier_Context
    97121     */
    98122    public $context;
     
    100124    /**
    101125     * Initializes the purifier.
    102      * @param $config Optional HTMLPurifier_Config object for all instances of
    103      *                the purifier, if omitted, a default configuration is
    104      *                supplied (which can be overridden on a per-use basis).
     126     *
     127     * @param HTMLPurifier_Config $config Optional HTMLPurifier_Config object
     128     *                for all instances of the purifier, if omitted, a default
     129     *                configuration is supplied (which can be overridden on a
     130     *                per-use basis).
    105131     *                The parameter can also be any type that
    106132     *                HTMLPurifier_Config::create() supports.
    107133     */
    108     public function __construct($config = null) {
    109 
     134    public function __construct($config = null)
     135    {
    110136        $this->config = HTMLPurifier_Config::create($config);
    111 
    112         $this->strategy     = new HTMLPurifier_Strategy_Core();
    113 
     137        $this->strategy = new HTMLPurifier_Strategy_Core();
    114138    }
    115139
    116140    /**
    117141     * Adds a filter to process the output. First come first serve
    118      * @param $filter HTMLPurifier_Filter object
    119      */
    120     public function addFilter($filter) {
    121         trigger_error('HTMLPurifier->addFilter() is deprecated, use configuration directives in the Filter namespace or Filter.Custom', E_USER_WARNING);
     142     *
     143     * @param HTMLPurifier_Filter $filter HTMLPurifier_Filter object
     144     */
     145    public function addFilter($filter)
     146    {
     147        trigger_error(
     148            'HTMLPurifier->addFilter() is deprecated, use configuration directives' .
     149            ' in the Filter namespace or Filter.Custom',
     150            E_USER_WARNING
     151        );
    122152        $this->filters[] = $filter;
    123153    }
     
    126156     * Filters an HTML snippet/document to be XSS-free and standards-compliant.
    127157     *
    128      * @param $html String of HTML to purify
    129      * @param $config HTMLPurifier_Config object for this operation, if omitted,
    130      *                defaults to the config object specified during this
     158     * @param string $html String of HTML to purify
     159     * @param HTMLPurifier_Config $config Config object for this operation,
     160     *                if omitted, defaults to the config object specified during this
    131161     *                object's construction. The parameter can also be any type
    132162     *                that HTMLPurifier_Config::create() supports.
    133      * @return Purified HTML
    134      */
    135     public function purify($html, $config = null) {
    136 
     163     *
     164     * @return string Purified HTML
     165     */
     166    public function purify($html, $config = null)
     167    {
    137168        // :TODO: make the config merge in, instead of replace
    138169        $config = $config ? HTMLPurifier_Config::create($config) : $this->config;
     
    172203        $filters = array();
    173204        foreach ($filter_flags as $filter => $flag) {
    174             if (!$flag) continue;
    175             if (strpos($filter, '.') !== false) continue;
     205            if (!$flag) {
     206                continue;
     207            }
     208            if (strpos($filter, '.') !== false) {
     209                continue;
     210            }
    176211            $class = "HTMLPurifier_Filter_$filter";
    177212            $filters[] = new $class;
     
    196231                    $lexer->tokenizeHTML(
    197232                        // un-purified HTML
    198                         $html, $config, $context
     233                        $html,
     234                        $config,
     235                        $context
    199236                    ),
    200                     $config, $context
     237                    $config,
     238                    $context
    201239                )
    202240            );
     
    213251    /**
    214252     * Filters an array of HTML snippets
    215      * @param $config Optional HTMLPurifier_Config object for this operation.
     253     *
     254     * @param string[] $array_of_html Array of html snippets
     255     * @param HTMLPurifier_Config $config Optional config object for this operation.
    216256     *                See HTMLPurifier::purify() for more details.
    217      * @return Array of purified HTML
    218      */
    219     public function purifyArray($array_of_html, $config = null) {
     257     *
     258     * @return string[] Array of purified HTML
     259     */
     260    public function purifyArray($array_of_html, $config = null)
     261    {
    220262        $context_array = array();
    221263        foreach ($array_of_html as $key => $html) {
     
    229271    /**
    230272     * Singleton for enforcing just one HTML Purifier in your system
    231      * @param $prototype Optional prototype HTMLPurifier instance to
    232      *                   overload singleton with, or HTMLPurifier_Config
    233      *                   instance to configure the generated version with.
    234      */
    235     public static function instance($prototype = null) {
     273     *
     274     * @param HTMLPurifier|HTMLPurifier_Config $prototype Optional prototype
     275     *                   HTMLPurifier instance to overload singleton with,
     276     *                   or HTMLPurifier_Config instance to configure the
     277     *                   generated version with.
     278     *
     279     * @return HTMLPurifier
     280     */
     281    public static function instance($prototype = null)
     282    {
    236283        if (!self::$instance || $prototype) {
    237284            if ($prototype instanceof HTMLPurifier) {
     
    247294
    248295    /**
     296     * Singleton for enforcing just one HTML Purifier in your system
     297     *
     298     * @param HTMLPurifier|HTMLPurifier_Config $prototype Optional prototype
     299     *                   HTMLPurifier instance to overload singleton with,
     300     *                   or HTMLPurifier_Config instance to configure the
     301     *                   generated version with.
     302     *
     303     * @return HTMLPurifier
    249304     * @note Backwards compatibility, see instance()
    250305     */
    251     public static function getInstance($prototype = null) {
     306    public static function getInstance($prototype = null)
     307    {
    252308        return HTMLPurifier::instance($prototype);
    253309    }
    254 
    255310}
    256311
    257312
     313
     314
     315
     316/**
     317 * Converts a stream of HTMLPurifier_Token into an HTMLPurifier_Node,
     318 * and back again.
     319 *
     320 * @note This transformation is not an equivalence.  We mutate the input
     321 * token stream to make it so; see all [MUT] markers in code.
     322 */
     323class HTMLPurifier_Arborize
     324{
     325    public static function arborize($tokens, $config, $context) {
     326        $definition = $config->getHTMLDefinition();
     327        $parent = new HTMLPurifier_Token_Start($definition->info_parent);
     328        $stack = array($parent->toNode());
     329        foreach ($tokens as $token) {
     330            $token->skip = null; // [MUT]
     331            $token->carryover = null; // [MUT]
     332            if ($token instanceof HTMLPurifier_Token_End) {
     333                $token->start = null; // [MUT]
     334                $r = array_pop($stack);
     335                assert($r->name === $token->name);
     336                assert(empty($token->attr));
     337                $r->endCol = $token->col;
     338                $r->endLine = $token->line;
     339                $r->endArmor = $token->armor;
     340                continue;
     341            }
     342            $node = $token->toNode();
     343            $stack[count($stack)-1]->children[] = $node;
     344            if ($token instanceof HTMLPurifier_Token_Start) {
     345                $stack[] = $node;
     346            }
     347        }
     348        assert(count($stack) == 1);
     349        return $stack[0];
     350    }
     351
     352    public static function flatten($node, $config, $context) {
     353        $level = 0;
     354        $nodes = array($level => new HTMLPurifier_Queue(array($node)));
     355        $closingTokens = array();
     356        $tokens = array();
     357        do {
     358            while (!$nodes[$level]->isEmpty()) {
     359                $node = $nodes[$level]->shift(); // FIFO
     360                list($start, $end) = $node->toTokenPair();
     361                if ($level > 0) {
     362                    $tokens[] = $start;
     363                }
     364                if ($end !== NULL) {
     365                    $closingTokens[$level][] = $end;
     366                }
     367                if ($node instanceof HTMLPurifier_Node_Element) {
     368                    $level++;
     369                    $nodes[$level] = new HTMLPurifier_Queue();
     370                    foreach ($node->children as $childNode) {
     371                        $nodes[$level]->push($childNode);
     372                    }
     373                }
     374            }
     375            $level--;
     376            if ($level && isset($closingTokens[$level])) {
     377                while ($token = array_pop($closingTokens[$level])) {
     378                    $tokens[] = $token;
     379                }
     380            }
     381        } while ($level > 0);
     382        return $tokens;
     383    }
     384}
    258385
    259386
     
    267394
    268395    /**
    269      * Associative array of attribute collections, indexed by name
     396     * Associative array of attribute collections, indexed by name.
     397     * @type array
    270398     */
    271399    public $info = array();
     
    275403     * It also collects all attribute collection extensions from
    276404     * modules
    277      * @param $attr_types HTMLPurifier_AttrTypes instance
    278      * @param $modules Hash array of HTMLPurifier_HTMLModule members
    279      */
    280     public function __construct($attr_types, $modules) {
     405     * @param HTMLPurifier_AttrTypes $attr_types HTMLPurifier_AttrTypes instance
     406     * @param HTMLPurifier_HTMLModule[] $modules Hash array of HTMLPurifier_HTMLModule members
     407     */
     408    public function __construct($attr_types, $modules)
     409    {
    281410        // load extensions from the modules
    282411        foreach ($modules as $module) {
     
    289418                        // merge in includes
    290419                        $this->info[$coll_i][$attr_i] = array_merge(
    291                             $this->info[$coll_i][$attr_i], $attr);
     420                            $this->info[$coll_i][$attr_i],
     421                            $attr
     422                        );
    292423                        continue;
    293424                    }
     
    308439     * Takes a reference to an attribute associative array and performs
    309440     * all inclusions specified by the zero index.
    310      * @param &$attr Reference to attribute array
    311      */
    312     public function performInclusions(&$attr) {
    313         if (!isset($attr[0])) return;
     441     * @param array &$attr Reference to attribute array
     442     */
     443    public function performInclusions(&$attr)
     444    {
     445        if (!isset($attr[0])) {
     446            return;
     447        }
    314448        $merge = $attr[0];
    315449        $seen  = array(); // recursion guard
    316450        // loop through all the inclusions
    317451        for ($i = 0; isset($merge[$i]); $i++) {
    318             if (isset($seen[$merge[$i]])) continue;
     452            if (isset($seen[$merge[$i]])) {
     453                continue;
     454            }
    319455            $seen[$merge[$i]] = true;
    320456            // foreach attribute of the inclusion, copy it over
    321             if (!isset($this->info[$merge[$i]])) continue;
     457            if (!isset($this->info[$merge[$i]])) {
     458                continue;
     459            }
    322460            foreach ($this->info[$merge[$i]] as $key => $value) {
    323                 if (isset($attr[$key])) continue; // also catches more inclusions
     461                if (isset($attr[$key])) {
     462                    continue;
     463                } // also catches more inclusions
    324464                $attr[$key] = $value;
    325465            }
     
    335475     * Expands all string identifiers in an attribute array by replacing
    336476     * them with the appropriate values inside HTMLPurifier_AttrTypes
    337      * @param &$attr Reference to attribute array
    338      * @param $attr_types HTMLPurifier_AttrTypes instance
    339      */
    340     public function expandIdentifiers(&$attr, $attr_types) {
    341 
     477     * @param array &$attr Reference to attribute array
     478     * @param HTMLPurifier_AttrTypes $attr_types HTMLPurifier_AttrTypes instance
     479     */
     480    public function expandIdentifiers(&$attr, $attr_types)
     481    {
    342482        // because foreach will process new elements we add, make sure we
    343483        // skip duplicates
     
    346486        foreach ($attr as $def_i => $def) {
    347487            // skip inclusions
    348             if ($def_i === 0) continue;
    349 
    350             if (isset($processed[$def_i])) continue;
     488            if ($def_i === 0) {
     489                continue;
     490            }
     491
     492            if (isset($processed[$def_i])) {
     493                continue;
     494            }
    351495
    352496            // determine whether or not attribute is required
     
    379523            }
    380524        }
    381 
    382     }
    383 
     525    }
    384526}
    385527
     
    402544
    403545    /**
    404      * Tells us whether or not an HTML attribute is minimized. Has no
    405      * meaning in other contexts.
     546     * Tells us whether or not an HTML attribute is minimized.
     547     * Has no meaning in other contexts.
     548     * @type bool
    406549     */
    407550    public $minimized = false;
    408551
    409552    /**
    410      * Tells us whether or not an HTML attribute is required. Has no
    411      * meaning in other contexts
     553     * Tells us whether or not an HTML attribute is required.
     554     * Has no meaning in other contexts
     555     * @type bool
    412556     */
    413557    public $required = false;
     
    416560     * Validates and cleans passed string according to a definition.
    417561     *
    418      * @param $string String to be validated and cleaned.
    419      * @param $config Mandatory HTMLPurifier_Config object.
    420      * @param $context Mandatory HTMLPurifier_AttrContext object.
     562     * @param string $string String to be validated and cleaned.
     563     * @param HTMLPurifier_Config $config Mandatory HTMLPurifier_Config object.
     564     * @param HTMLPurifier_Context $context Mandatory HTMLPurifier_Context object.
    421565     */
    422566    abstract public function validate($string, $config, $context);
     
    443587     *          assume that newlines have been normalized.
    444588     */
    445     public function parseCDATA($string) {
     589    public function parseCDATA($string)
     590    {
    446591        $string = trim($string);
    447592        $string = str_replace(array("\n", "\t", "\r"), ' ', $string);
     
    451596    /**
    452597     * Factory method for creating this class from a string.
    453      * @param $string String construction info
    454      * @return Created AttrDef object corresponding to $string
    455      */
    456     public function make($string) {
     598     * @param string $string String construction info
     599     * @return HTMLPurifier_AttrDef Created AttrDef object corresponding to $string
     600     */
     601    public function make($string)
     602    {
    457603        // default implementation, return a flyweight of this object.
    458604        // If $string has an effect on the returned object (i.e. you
     
    465611     * Removes spaces from rgb(0, 0, 0) so that shorthand CSS properties work
    466612     * properly. THIS IS A HACK!
    467      */
    468     protected function mungeRgb($string) {
     613     * @param string $string a CSS colour definition
     614     * @return string
     615     */
     616    protected function mungeRgb($string)
     617    {
    469618        return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string);
    470619    }
    471620
    472621    /**
    473      * Parses a possibly escaped CSS string and returns the "pure" 
     622     * Parses a possibly escaped CSS string and returns the "pure"
    474623     * version of it.
    475624     */
    476     protected function expandCSSEscape($string) {
     625    protected function expandCSSEscape($string)
     626    {
    477627        // flexibly parse it
    478628        $ret = '';
     
    487637                    $code = $string[$i];
    488638                    for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) {
    489                         if (!ctype_xdigit($string[$i])) break;
     639                        if (!ctype_xdigit($string[$i])) {
     640                            break;
     641                        }
    490642                        $code .= $string[$i];
    491643                    }
     
    494646                    // the encoding.
    495647                    $char = HTMLPurifier_Encoder::unichr(hexdec($code));
    496                     if (HTMLPurifier_Encoder::cleanUTF8($char) === '') continue;
     648                    if (HTMLPurifier_Encoder::cleanUTF8($char) === '') {
     649                        continue;
     650                    }
    497651                    $ret .= $char;
    498                     if ($i < $c && trim($string[$i]) !== '') $i--;
     652                    if ($i < $c && trim($string[$i]) !== '') {
     653                        $i--;
     654                    }
    499655                    continue;
    500656                }
    501                 if ($string[$i] === "\n") continue;
     657                if ($string[$i] === "\n") {
     658                    continue;
     659                }
    502660            }
    503661            $ret .= $string[$i];
     
    505663        return $ret;
    506664    }
    507 
    508665}
    509666
     
    532689     * Abstract: makes changes to the attributes dependent on multiple values.
    533690     *
    534      * @param $attr Assoc array of attributes, usually from
     691     * @param array $attr Assoc array of attributes, usually from
    535692     *              HTMLPurifier_Token_Tag::$attr
    536      * @param $config Mandatory HTMLPurifier_Config object.
    537      * @param $context Mandatory HTMLPurifier_Context object
    538      * @returns Processed attribute array.
     693     * @param HTMLPurifier_Config $config Mandatory HTMLPurifier_Config object.
     694     * @param HTMLPurifier_Context $context Mandatory HTMLPurifier_Context object
     695     * @return array Processed attribute array.
    539696     */
    540697    abstract public function transform($attr, $config, $context);
     
    543700     * Prepends CSS properties to the style attribute, creating the
    544701     * attribute if it doesn't exist.
    545      * @param $attr Attribute array to process (passed by reference)
    546      * @param $css CSS to prepend
    547      */
    548     public function prependCSS(&$attr, $css) {
     702     * @param array &$attr Attribute array to process (passed by reference)
     703     * @param string $css CSS to prepend
     704     */
     705    public function prependCSS(&$attr, $css)
     706    {
    549707        $attr['style'] = isset($attr['style']) ? $attr['style'] : '';
    550708        $attr['style'] = $css . $attr['style'];
     
    553711    /**
    554712     * Retrieves and removes an attribute
    555      * @param $attr Attribute array to process (passed by reference)
    556      * @param $key Key of attribute to confiscate
    557      */
    558     public function confiscateAttr(&$attr, $key) {
    559         if (!isset($attr[$key])) return null;
     713     * @param array &$attr Attribute array to process (passed by reference)
     714     * @param mixed $key Key of attribute to confiscate
     715     * @return mixed
     716     */
     717    public function confiscateAttr(&$attr, $key)
     718    {
     719        if (!isset($attr[$key])) {
     720            return null;
     721        }
    560722        $value = $attr[$key];
    561723        unset($attr[$key]);
    562724        return $value;
    563725    }
    564 
    565726}
    566727
     
    575736{
    576737    /**
    577      * Lookup array of attribute string identifiers to concrete implementations
     738     * Lookup array of attribute string identifiers to concrete implementations.
     739     * @type HTMLPurifier_AttrDef[]
    578740     */
    579741    protected $info = array();
     
    583745     * types.
    584746     */
    585     public function __construct() {
     747    public function __construct()
     748    {
     749        // XXX This is kind of poor, since we don't actually /clone/
     750        // instances; instead, we use the supplied make() attribute. So,
     751        // the underlying class must know how to deal with arguments.
     752        // With the old implementation of Enum, that ignored its
     753        // arguments when handling a make dispatch, the IAlign
     754        // definition wouldn't work.
     755
    586756        // pseudo-types, must be instantiated via shorthand
    587757        $this->info['Enum']    = new HTMLPurifier_AttrDef_Enum();
     
    598768        $this->info['LanguageCode'] = new HTMLPurifier_AttrDef_Lang();
    599769        $this->info['Color']    = new HTMLPurifier_AttrDef_HTML_Color();
     770        $this->info['IAlign']   = self::makeEnum('top,middle,bottom,left,right');
     771        $this->info['LAlign']   = self::makeEnum('top,bottom,left,right');
     772        $this->info['FrameTarget'] = new HTMLPurifier_AttrDef_HTML_FrameTarget();
    600773
    601774        // unimplemented aliases
     
    613786    }
    614787
     788    private static function makeEnum($in)
     789    {
     790        return new HTMLPurifier_AttrDef_Clone(new HTMLPurifier_AttrDef_Enum(explode(',', $in)));
     791    }
     792
    615793    /**
    616794     * Retrieves a type
    617      * @param $type String type name
    618      * @return Object AttrDef for type
    619      */
    620     public function get($type) {
    621 
     795     * @param string $type String type name
     796     * @return HTMLPurifier_AttrDef Object AttrDef for type
     797     */
     798    public function get($type)
     799    {
    622800        // determine if there is any extra info tacked on
    623         if (strpos($type, '#') !== false) list($type, $string) = explode('#', $type, 2);
    624         else $string = '';
     801        if (strpos($type, '#') !== false) {
     802            list($type, $string) = explode('#', $type, 2);
     803        } else {
     804            $string = '';
     805        }
    625806
    626807        if (!isset($this->info[$type])) {
     
    628809            return;
    629810        }
    630 
    631811        return $this->info[$type]->make($string);
    632 
    633812    }
    634813
    635814    /**
    636815     * Sets a new implementation for a type
    637      * @param $type String type name
    638      * @param $impl Object AttrDef for type
    639      */
    640     public function set($type, $impl) {
     816     * @param string $type String type name
     817     * @param HTMLPurifier_AttrDef $impl Object AttrDef for type
     818     */
     819    public function set($type, $impl)
     820    {
    641821        $this->info[$type] = $impl;
    642822    }
     
    656836
    657837    /**
    658      * Validates the attributes of a token, returning a modified token
     838     * Validates the attributes of a token, mutating it as necessary.
    659839     * that has valid tokens
    660      * @param $token Reference to token to validate. We require a reference
    661      *     because the operation this class performs on the token are
    662      *     not atomic, so the context CurrentToken to be updated
    663      *     throughout
    664      * @param $config Instance of HTMLPurifier_Config
    665      * @param $context Instance of HTMLPurifier_Context
    666      */
    667     public function validateToken(&$token, &$config, $context) {
    668 
     840     * @param HTMLPurifier_Token $token Token to validate.
     841     * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config
     842     * @param HTMLPurifier_Context $context Instance of HTMLPurifier_Context
     843     */
     844    public function validateToken($token, $config, $context)
     845    {
    669846        $definition = $config->getHTMLDefinition();
    670847        $e =& $context->get('ErrorCollector', true);
     
    679856        // initialize CurrentToken if necessary
    680857        $current_token =& $context->get('CurrentToken', true);
    681         if (!$current_token) $context->register('CurrentToken', $token);
    682 
    683         if (
    684             !$token instanceof HTMLPurifier_Token_Start &&
     858        if (!$current_token) {
     859            $context->register('CurrentToken', $token);
     860        }
     861
     862        if (!$token instanceof HTMLPurifier_Token_Start &&
    685863            !$token instanceof HTMLPurifier_Token_Empty
    686         ) return $token;
     864        ) {
     865            return;
     866        }
    687867
    688868        // create alias to global definition array, see also $defs
     
    698878            $attr = $transform->transform($o = $attr, $config, $context);
    699879            if ($e) {
    700                 if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
     880                if ($attr != $o) {
     881                    $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
     882                }
    701883            }
    702884        }
     
    707889            $attr = $transform->transform($o = $attr, $config, $context);
    708890            if ($e) {
    709                 if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
     891                if ($attr != $o) {
     892                    $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
     893                }
    710894            }
    711895        }
     
    724908
    725909            // call the definition
    726             if ( isset($defs[$attr_key]) ) {
     910            if (isset($defs[$attr_key])) {
    727911                // there is a local definition defined
    728912                if ($defs[$attr_key] === false) {
     
    736920                    // validate according to the element's definition
    737921                    $result = $defs[$attr_key]->validate(
    738                                     $value, $config, $context
    739                                );
     922                        $value,
     923                        $config,
     924                        $context
     925                    );
    740926                }
    741             } elseif ( isset($d_defs[$attr_key]) ) {
     927            } elseif (isset($d_defs[$attr_key])) {
    742928                // there is a global definition defined, validate according
    743929                // to the global definition
    744930                $result = $d_defs[$attr_key]->validate(
    745                                 $value, $config, $context
    746                            );
     931                    $value,
     932                    $config,
     933                    $context
     934                );
    747935            } else {
    748936                // system never heard of the attribute? DELETE!
     
    754942                // this is a generic error message that should replaced
    755943                // with more specific ones when possible
    756                 if ($e) $e->send(E_ERROR, 'AttrValidator: Attribute removed');
     944                if ($e) {
     945                    $e->send(E_ERROR, 'AttrValidator: Attribute removed');
     946                }
    757947
    758948                // remove the attribute
     
    784974            $attr = $transform->transform($o = $attr, $config, $context);
    785975            if ($e) {
    786                 if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
     976                if ($attr != $o) {
     977                    $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
     978                }
    787979            }
    788980        }
     
    792984            $attr = $transform->transform($o = $attr, $config, $context);
    793985            if ($e) {
    794                 if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
     986                if ($attr != $o) {
     987                    $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr);
     988                }
    795989            }
    796990        }
     
    799993
    800994        // destroy CurrentToken if we made it ourselves
    801         if (!$current_token) $context->destroy('CurrentToken');
     995        if (!$current_token) {
     996            $context->destroy('CurrentToken');
     997        }
    802998
    803999    }
     
    8431039    /**
    8441040     * Autoload function for HTML Purifier
    845      * @param $class Class to load
    846      */
    847     public static function autoload($class) {
     1041     * @param string $class Class to load
     1042     * @return bool
     1043     */
     1044    public static function autoload($class)
     1045    {
    8481046        $file = HTMLPurifier_Bootstrap::getPath($class);
    849         if (!$file) return false;
    850         require HTMLPURIFIER_PREFIX . '/' . $file;
     1047        if (!$file) {
     1048            return false;
     1049        }
     1050        // Technically speaking, it should be ok and more efficient to
     1051        // just do 'require', but Antonio Parraga reports that with
     1052        // Zend extensions such as Zend debugger and APC, this invariant
     1053        // may be broken.  Since we have efficient alternatives, pay
     1054        // the cost here and avoid the bug.
     1055        require_once HTMLPURIFIER_PREFIX . '/' . $file;
    8511056        return true;
    8521057    }
     
    8541059    /**
    8551060     * Returns the path for a specific class.
    856      */
    857     public static function getPath($class) {
    858         if (strncmp('HTMLPurifier', $class, 12) !== 0) return false;
     1061     * @param string $class Class path to get
     1062     * @return string
     1063     */
     1064    public static function getPath($class)
     1065    {
     1066        if (strncmp('HTMLPurifier', $class, 12) !== 0) {
     1067            return false;
     1068        }
    8591069        // Custom implementations
    8601070        if (strncmp('HTMLPurifier_Language_', $class, 22) === 0) {
     
    8641074            $file = str_replace('_', '/', $class) . '.php';
    8651075        }
    866         if (!file_exists(HTMLPURIFIER_PREFIX . '/' . $file)) return false;
     1076        if (!file_exists(HTMLPURIFIER_PREFIX . '/' . $file)) {
     1077            return false;
     1078        }
    8671079        return $file;
    8681080    }
     
    8711083     * "Pre-registers" our autoloader on the SPL stack.
    8721084     */
    873     public static function registerAutoload() {
     1085    public static function registerAutoload()
     1086    {
    8741087        $autoload = array('HTMLPurifier_Bootstrap', 'autoload');
    875         if ( ($funcs = spl_autoload_functions()) === false ) {
     1088        if (($funcs = spl_autoload_functions()) === false) {
    8761089            spl_autoload_register($autoload);
    8771090        } elseif (function_exists('spl_autoload_unregister')) {
    878             $compat = version_compare(PHP_VERSION, '5.1.2', '<=') &&
    879                       version_compare(PHP_VERSION, '5.1.0', '>=');
    880             foreach ($funcs as $func) {
    881                 if (is_array($func)) {
    882                     // :TRICKY: There are some compatibility issues and some
    883                     // places where we need to error out
    884                     $reflector = new ReflectionMethod($func[0], $func[1]);
    885                     if (!$reflector->isStatic()) {
    886                         throw new Exception('
    887                             HTML Purifier autoloader registrar is not compatible
    888                             with non-static object methods due to PHP Bug #44144;
    889                             Please do not use HTMLPurifier.autoload.php (or any
    890                             file that includes this file); instead, place the code:
    891                             spl_autoload_register(array(\'HTMLPurifier_Bootstrap\', \'autoload\'))
    892                             after your own autoloaders.
    893                         ');
     1091            if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
     1092                // prepend flag exists, no need for shenanigans
     1093                spl_autoload_register($autoload, true, true);
     1094            } else {
     1095                $buggy  = version_compare(PHP_VERSION, '5.2.11', '<');
     1096                $compat = version_compare(PHP_VERSION, '5.1.2', '<=') &&
     1097                          version_compare(PHP_VERSION, '5.1.0', '>=');
     1098                foreach ($funcs as $func) {
     1099                    if ($buggy && is_array($func)) {
     1100                        // :TRICKY: There are some compatibility issues and some
     1101                        // places where we need to error out
     1102                        $reflector = new ReflectionMethod($func[0], $func[1]);
     1103                        if (!$reflector->isStatic()) {
     1104                            throw new Exception(
     1105                                'HTML Purifier autoloader registrar is not compatible
     1106                                with non-static object methods due to PHP Bug #44144;
     1107                                Please do not use HTMLPurifier.autoload.php (or any
     1108                                file that includes this file); instead, place the code:
     1109                                spl_autoload_register(array(\'HTMLPurifier_Bootstrap\', \'autoload\'))
     1110                                after your own autoloaders.'
     1111                            );
     1112                        }
     1113                        // Suprisingly, spl_autoload_register supports the
     1114                        // Class::staticMethod callback format, although call_user_func doesn't
     1115                        if ($compat) {
     1116                            $func = implode('::', $func);
     1117                        }
    8941118                    }
    895                     // Suprisingly, spl_autoload_register supports the
    896                     // Class::staticMethod callback format, although call_user_func doesn't
    897                     if ($compat) $func = implode('::', $func);
     1119                    spl_autoload_unregister($func);
    8981120                }
    899                 spl_autoload_unregister($func);
    900             }
    901             spl_autoload_register($autoload);
    902             foreach ($funcs as $func) spl_autoload_register($func);
    903         }
    904     }
    905 
     1121                spl_autoload_register($autoload);
     1122                foreach ($funcs as $func) {
     1123                    spl_autoload_register($func);
     1124                }
     1125            }
     1126        }
     1127    }
    9061128}
    9071129
     
    9191141    /**
    9201142     * Has setup() been called yet?
     1143     * @type bool
    9211144     */
    9221145    public $setup = false;
    9231146
    9241147    /**
     1148     * If true, write out the final definition object to the cache after
     1149     * setup.  This will be true only if all invocations to get a raw
     1150     * definition object are also optimized.  This does not cause file
     1151     * system thrashing because on subsequent calls the cached object
     1152     * is used and any writes to the raw definition object are short
     1153     * circuited.  See enduser-customize.html for the high-level
     1154     * picture.
     1155     * @type bool
     1156     */
     1157    public $optimized = null;
     1158
     1159    /**
    9251160     * What type of definition is it?
     1161     * @type string
    9261162     */
    9271163    public $type;
     
    9301166     * Sets up the definition object into the final form, something
    9311167     * not done by the constructor
    932      * @param $config HTMLPurifier_Config instance
     1168     * @param HTMLPurifier_Config $config
    9331169     */
    9341170    abstract protected function doSetup($config);
     
    9361172    /**
    9371173     * Setup function that aborts if already setup
    938      * @param $config HTMLPurifier_Config instance
    939      */
    940     public function setup($config) {
    941         if ($this->setup) return;
     1174     * @param HTMLPurifier_Config $config
     1175     */
     1176    public function setup($config)
     1177    {
     1178        if ($this->setup) {
     1179            return;
     1180        }
    9421181        $this->setup = true;
    9431182        $this->doSetup($config);
    9441183    }
    945 
    9461184}
    9471185
     
    9611199    /**
    9621200     * Assoc array of attribute name to definition object.
     1201     * @type HTMLPurifier_AttrDef[]
    9631202     */
    9641203    public $info = array();
     
    9661205    /**
    9671206     * Constructs the info array.  The meat of this class.
    968      */
    969     protected function doSetup($config) {
    970 
     1207     * @param HTMLPurifier_Config $config
     1208     */
     1209    protected function doSetup($config)
     1210    {
    9711211        $this->info['text-align'] = new HTMLPurifier_AttrDef_Enum(
    972             array('left', 'right', 'center', 'justify'), false);
     1212            array('left', 'right', 'center', 'justify'),
     1213            false
     1214        );
    9731215
    9741216        $border_style =
    975         $this->info['border-bottom-style'] =
    976         $this->info['border-right-style'] =
    977         $this->info['border-left-style'] =
    978         $this->info['border-top-style'] =  new HTMLPurifier_AttrDef_Enum(
    979             array('none', 'hidden', 'dotted', 'dashed', 'solid', 'double',
    980             'groove', 'ridge', 'inset', 'outset'), false);
     1217            $this->info['border-bottom-style'] =
     1218            $this->info['border-right-style'] =
     1219            $this->info['border-left-style'] =
     1220            $this->info['border-top-style'] = new HTMLPurifier_AttrDef_Enum(
     1221                array(
     1222                    'none',
     1223                    'hidden',
     1224                    'dotted',
     1225                    'dashed',
     1226                    'solid',
     1227                    'double',
     1228                    'groove',
     1229                    'ridge',
     1230                    'inset',
     1231                    'outset'
     1232                ),
     1233                false
     1234            );
    9811235
    9821236        $this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style);
    9831237
    9841238        $this->info['clear'] = new HTMLPurifier_AttrDef_Enum(
    985             array('none', 'left', 'right', 'both'), false);
     1239            array('none', 'left', 'right', 'both'),
     1240            false
     1241        );
    9861242        $this->info['float'] = new HTMLPurifier_AttrDef_Enum(
    987             array('none', 'left', 'right'), false);
     1243            array('none', 'left', 'right'),
     1244            false
     1245        );
    9881246        $this->info['font-style'] = new HTMLPurifier_AttrDef_Enum(
    989             array('normal', 'italic', 'oblique'), false);
     1247            array('normal', 'italic', 'oblique'),
     1248            false
     1249        );
    9901250        $this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum(
    991             array('normal', 'small-caps'), false);
     1251            array('normal', 'small-caps'),
     1252            false
     1253        );
    9921254
    9931255        $uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite(
     
    9991261
    10001262        $this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum(
    1001             array('inside', 'outside'), false);
     1263            array('inside', 'outside'),
     1264            false
     1265        );
    10021266        $this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum(
    1003             array('disc', 'circle', 'square', 'decimal', 'lower-roman',
    1004             'upper-roman', 'lower-alpha', 'upper-alpha', 'none'), false);
     1267            array(
     1268                'disc',
     1269                'circle',
     1270                'square',
     1271                'decimal',
     1272                'lower-roman',
     1273                'upper-roman',
     1274                'lower-alpha',
     1275                'upper-alpha',
     1276                'none'
     1277            ),
     1278            false
     1279        );
    10051280        $this->info['list-style-image'] = $uri_or_none;
    10061281
     
    10081283
    10091284        $this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum(
    1010             array('capitalize', 'uppercase', 'lowercase', 'none'), false);
     1285            array('capitalize', 'uppercase', 'lowercase', 'none'),
     1286            false
     1287        );
    10111288        $this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color();
    10121289
     
    10211298
    10221299        $border_color =
    1023         $this->info['border-top-color'] =
    1024         $this->info['border-bottom-color'] =
    1025         $this->info['border-left-color'] =
    1026         $this->info['border-right-color'] =
    1027         $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
    1028             new HTMLPurifier_AttrDef_Enum(array('transparent')),
    1029             new HTMLPurifier_AttrDef_CSS_Color()
    1030         ));
     1300            $this->info['border-top-color'] =
     1301            $this->info['border-bottom-color'] =
     1302            $this->info['border-left-color'] =
     1303            $this->info['border-right-color'] =
     1304            $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(
     1305                array(
     1306                    new HTMLPurifier_AttrDef_Enum(array('transparent')),
     1307                    new HTMLPurifier_AttrDef_CSS_Color()
     1308                )
     1309            );
    10311310
    10321311        $this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config);
     
    10351314
    10361315        $border_width =
    1037         $this->info['border-top-width'] =
    1038         $this->info['border-bottom-width'] =
    1039         $this->info['border-left-width'] =
    1040         $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
    1041             new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')),
    1042             new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative
    1043         ));
     1316            $this->info['border-top-width'] =
     1317            $this->info['border-bottom-width'] =
     1318            $this->info['border-left-width'] =
     1319            $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(
     1320                array(
     1321                    new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')),
     1322                    new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative
     1323                )
     1324            );
    10441325
    10451326        $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width);
    10461327
    1047         $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
    1048             new HTMLPurifier_AttrDef_Enum(array('normal')),
    1049             new HTMLPurifier_AttrDef_CSS_Length()
    1050         ));
    1051 
    1052         $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
    1053             new HTMLPurifier_AttrDef_Enum(array('normal')),
    1054             new HTMLPurifier_AttrDef_CSS_Length()
    1055         ));
    1056 
    1057         $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
    1058             new HTMLPurifier_AttrDef_Enum(array('xx-small', 'x-small',
    1059                 'small', 'medium', 'large', 'x-large', 'xx-large',
    1060                 'larger', 'smaller')),
    1061             new HTMLPurifier_AttrDef_CSS_Percentage(),
    1062             new HTMLPurifier_AttrDef_CSS_Length()
    1063         ));
    1064 
    1065         $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
    1066             new HTMLPurifier_AttrDef_Enum(array('normal')),
    1067             new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives
    1068             new HTMLPurifier_AttrDef_CSS_Length('0'),
    1069             new HTMLPurifier_AttrDef_CSS_Percentage(true)
    1070         ));
     1328        $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(
     1329            array(
     1330                new HTMLPurifier_AttrDef_Enum(array('normal')),
     1331                new HTMLPurifier_AttrDef_CSS_Length()
     1332            )
     1333        );
     1334
     1335        $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(
     1336            array(
     1337                new HTMLPurifier_AttrDef_Enum(array('normal')),
     1338                new HTMLPurifier_AttrDef_CSS_Length()
     1339            )
     1340        );
     1341
     1342        $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(
     1343            array(
     1344                new HTMLPurifier_AttrDef_Enum(
     1345                    array(
     1346                        'xx-small',
     1347                        'x-small',
     1348                        'small',
     1349                        'medium',
     1350                        'large',
     1351                        'x-large',
     1352                        'xx-large',
     1353                        'larger',
     1354                        'smaller'
     1355                    )
     1356                ),
     1357                new HTMLPurifier_AttrDef_CSS_Percentage(),
     1358                new HTMLPurifier_AttrDef_CSS_Length()
     1359            )
     1360        );
     1361
     1362        $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(
     1363            array(
     1364                new HTMLPurifier_AttrDef_Enum(array('normal')),
     1365                new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives
     1366                new HTMLPurifier_AttrDef_CSS_Length('0'),
     1367                new HTMLPurifier_AttrDef_CSS_Percentage(true)
     1368            )
     1369        );
    10711370
    10721371        $margin =
    1073         $this->info['margin-top'] =
    1074         $this->info['margin-bottom'] =
    1075         $this->info['margin-left'] =
    1076         $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
    1077             new HTMLPurifier_AttrDef_CSS_Length(),
    1078             new HTMLPurifier_AttrDef_CSS_Percentage(),
    1079             new HTMLPurifier_AttrDef_Enum(array('auto'))
    1080         ));
     1372            $this->info['margin-top'] =
     1373            $this->info['margin-bottom'] =
     1374            $this->info['margin-left'] =
     1375            $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(
     1376                array(
     1377                    new HTMLPurifier_AttrDef_CSS_Length(),
     1378                    new HTMLPurifier_AttrDef_CSS_Percentage(),
     1379                    new HTMLPurifier_AttrDef_Enum(array('auto'))
     1380                )
     1381            );
    10811382
    10821383        $this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin);
     
    10841385        // non-negative
    10851386        $padding =
    1086         $this->info['padding-top'] =
    1087         $this->info['padding-bottom'] =
    1088         $this->info['padding-left'] =
    1089         $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
    1090             new HTMLPurifier_AttrDef_CSS_Length('0'),
    1091             new HTMLPurifier_AttrDef_CSS_Percentage(true)
    1092         ));
     1387            $this->info['padding-top'] =
     1388            $this->info['padding-bottom'] =
     1389            $this->info['padding-left'] =
     1390            $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(
     1391                array(
     1392                    new HTMLPurifier_AttrDef_CSS_Length('0'),
     1393                    new HTMLPurifier_AttrDef_CSS_Percentage(true)
     1394                )
     1395            );
    10931396
    10941397        $this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding);
    10951398
    1096         $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
    1097             new HTMLPurifier_AttrDef_CSS_Length(),
    1098             new HTMLPurifier_AttrDef_CSS_Percentage()
    1099         ));
    1100 
    1101         $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(array(
    1102             new HTMLPurifier_AttrDef_CSS_Length('0'),
    1103             new HTMLPurifier_AttrDef_CSS_Percentage(true),
    1104             new HTMLPurifier_AttrDef_Enum(array('auto'))
    1105         ));
     1399        $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(
     1400            array(
     1401                new HTMLPurifier_AttrDef_CSS_Length(),
     1402                new HTMLPurifier_AttrDef_CSS_Percentage()
     1403            )
     1404        );
     1405
     1406        $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(
     1407            array(
     1408                new HTMLPurifier_AttrDef_CSS_Length('0'),
     1409                new HTMLPurifier_AttrDef_CSS_Percentage(true),
     1410                new HTMLPurifier_AttrDef_Enum(array('auto'))
     1411            )
     1412        );
    11061413        $max = $config->get('CSS.MaxImgLength');
    11071414
     
    11091416        $this->info['height'] =
    11101417            $max === null ?
    1111             $trusted_wh :
    1112             new HTMLPurifier_AttrDef_Switch('img',
    1113                 // For img tags:
    1114                 new HTMLPurifier_AttrDef_CSS_Composite(array(
    1115                     new HTMLPurifier_AttrDef_CSS_Length('0', $max),
    1116                     new HTMLPurifier_AttrDef_Enum(array('auto'))
    1117                 )),
    1118                 // For everyone else:
    1119                 $trusted_wh
    1120             );
     1418                $trusted_wh :
     1419                new HTMLPurifier_AttrDef_Switch(
     1420                    'img',
     1421                    // For img tags:
     1422                    new HTMLPurifier_AttrDef_CSS_Composite(
     1423                        array(
     1424                            new HTMLPurifier_AttrDef_CSS_Length('0', $max),
     1425                            new HTMLPurifier_AttrDef_Enum(array('auto'))
     1426                        )
     1427                    ),
     1428                    // For everyone else:
     1429                    $trusted_wh
     1430                );
    11211431
    11221432        $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration();
     
    11261436        // this could use specialized code
    11271437        $this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum(
    1128             array('normal', 'bold', 'bolder', 'lighter', '100', '200', '300',
    1129             '400', '500', '600', '700', '800', '900'), false);
     1438            array(
     1439                'normal',
     1440                'bold',
     1441                'bolder',
     1442                'lighter',
     1443                '100',
     1444                '200',
     1445                '300',
     1446                '400',
     1447                '500',
     1448                '600',
     1449                '700',
     1450                '800',
     1451                '900'
     1452            ),
     1453            false
     1454        );
    11301455
    11311456        // MUST be called after other font properties, as it references
     
    11401465        $this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config);
    11411466
    1142         $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(array(
    1143             'collapse', 'separate'));
    1144 
    1145         $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(array(
    1146             'top', 'bottom'));
    1147 
    1148         $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(array(
    1149             'auto', 'fixed'));
    1150 
    1151         $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(array(
    1152             new HTMLPurifier_AttrDef_Enum(array('baseline', 'sub', 'super',
    1153                 'top', 'text-top', 'middle', 'bottom', 'text-bottom')),
    1154             new HTMLPurifier_AttrDef_CSS_Length(),
    1155             new HTMLPurifier_AttrDef_CSS_Percentage()
    1156         ));
     1467        $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(
     1468            array('collapse', 'separate')
     1469        );
     1470
     1471        $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(
     1472            array('top', 'bottom')
     1473        );
     1474
     1475        $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(
     1476            array('auto', 'fixed')
     1477        );
     1478
     1479        $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(
     1480            array(
     1481                new HTMLPurifier_AttrDef_Enum(
     1482                    array(
     1483                        'baseline',
     1484                        'sub',
     1485                        'super',
     1486                        'top',
     1487                        'text-top',
     1488                        'middle',
     1489                        'bottom',
     1490                        'text-bottom'
     1491                    )
     1492                ),
     1493                new HTMLPurifier_AttrDef_CSS_Length(),
     1494                new HTMLPurifier_AttrDef_CSS_Percentage()
     1495            )
     1496        );
    11571497
    11581498        $this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2);
    11591499
    1160         // partial support
    1161         $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(array('nowrap'));
     1500        // These CSS properties don't work on many browsers, but we live
     1501        // in THE FUTURE!
     1502        $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(
     1503            array('nowrap', 'normal', 'pre', 'pre-wrap', 'pre-line')
     1504        );
    11621505
    11631506        if ($config->get('CSS.Proprietary')) {
     
    11671510        if ($config->get('CSS.AllowTricky')) {
    11681511            $this->doSetupTricky($config);
     1512        }
     1513
     1514        if ($config->get('CSS.Trusted')) {
     1515            $this->doSetupTrusted($config);
    11691516        }
    11701517
     
    11781525    }
    11791526
    1180     protected function doSetupProprietary($config) {
     1527    /**
     1528     * @param HTMLPurifier_Config $config
     1529     */
     1530    protected function doSetupProprietary($config)
     1531    {
    11811532        // Internet Explorer only scrollbar colors
    1182         $this->info['scrollbar-arrow-color']        = new HTMLPurifier_AttrDef_CSS_Color();
    1183         $this->info['scrollbar-base-color']         = new HTMLPurifier_AttrDef_CSS_Color();
    1184         $this->info['scrollbar-darkshadow-color']   = new HTMLPurifier_AttrDef_CSS_Color();
    1185         $this->info['scrollbar-face-color']         = new HTMLPurifier_AttrDef_CSS_Color();
    1186         $this->info['scrollbar-highlight-color']    = new HTMLPurifier_AttrDef_CSS_Color();
    1187         $this->info['scrollbar-shadow-color']       = new HTMLPurifier_AttrDef_CSS_Color();
    1188 
    1189         // technically not proprietary, but CSS3, and no one supports it
    1190         $this->info['opacity']          = new HTMLPurifier_AttrDef_CSS_AlphaValue();
    1191         $this->info['-moz-opacity']     = new HTMLPurifier_AttrDef_CSS_AlphaValue();
    1192         $this->info['-khtml-opacity']   = new HTMLPurifier_AttrDef_CSS_AlphaValue();
     1533        $this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
     1534        $this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color();
     1535        $this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
     1536        $this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color();
     1537        $this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color();
     1538        $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
     1539
     1540        // vendor specific prefixes of opacity
     1541        $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
     1542        $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
    11931543
    11941544        // only opacity, for now
    11951545        $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter();
    11961546
    1197     }
    1198 
    1199     protected function doSetupTricky($config) {
    1200         $this->info['display'] = new HTMLPurifier_AttrDef_Enum(array(
    1201             'inline', 'block', 'list-item', 'run-in', 'compact',
    1202             'marker', 'table', 'inline-table', 'table-row-group',
    1203             'table-header-group', 'table-footer-group', 'table-row',
    1204             'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none'
    1205         ));
    1206         $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(array(
    1207             'visible', 'hidden', 'collapse'
    1208         ));
     1547        // more CSS3
     1548        $this->info['page-break-after'] =
     1549        $this->info['page-break-before'] = new HTMLPurifier_AttrDef_Enum(
     1550            array(
     1551                'auto',
     1552                'always',
     1553                'avoid',
     1554                'left',
     1555                'right'
     1556            )
     1557        );
     1558        $this->info['page-break-inside'] = new HTMLPurifier_AttrDef_Enum(array('auto', 'avoid'));
     1559
     1560    }
     1561
     1562    /**
     1563     * @param HTMLPurifier_Config $config
     1564     */
     1565    protected function doSetupTricky($config)
     1566    {
     1567        $this->info['display'] = new HTMLPurifier_AttrDef_Enum(
     1568            array(
     1569                'inline',
     1570                'block',
     1571                'list-item',
     1572                'run-in',
     1573                'compact',
     1574                'marker',
     1575                'table',
     1576                'inline-block',
     1577                'inline-table',
     1578                'table-row-group',
     1579                'table-header-group',
     1580                'table-footer-group',
     1581                'table-row',
     1582                'table-column-group',
     1583                'table-column',
     1584                'table-cell',
     1585                'table-caption',
     1586                'none'
     1587            )
     1588        );
     1589        $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(
     1590            array('visible', 'hidden', 'collapse')
     1591        );
    12091592        $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll'));
    1210     }
    1211 
     1593        $this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
     1594    }
     1595
     1596    /**
     1597     * @param HTMLPurifier_Config $config
     1598     */
     1599    protected function doSetupTrusted($config)
     1600    {
     1601        $this->info['position'] = new HTMLPurifier_AttrDef_Enum(
     1602            array('static', 'relative', 'absolute', 'fixed')
     1603        );
     1604        $this->info['top'] =
     1605        $this->info['left'] =
     1606        $this->info['right'] =
     1607        $this->info['bottom'] = new HTMLPurifier_AttrDef_CSS_Composite(
     1608            array(
     1609                new HTMLPurifier_AttrDef_CSS_Length(),
     1610                new HTMLPurifier_AttrDef_CSS_Percentage(),
     1611                new HTMLPurifier_AttrDef_Enum(array('auto')),
     1612            )
     1613        );
     1614        $this->info['z-index'] = new HTMLPurifier_AttrDef_CSS_Composite(
     1615            array(
     1616                new HTMLPurifier_AttrDef_Integer(),
     1617                new HTMLPurifier_AttrDef_Enum(array('auto')),
     1618            )
     1619        );
     1620    }
    12121621
    12131622    /**
    12141623     * Performs extra config-based processing. Based off of
    12151624     * HTMLPurifier_HTMLDefinition.
     1625     * @param HTMLPurifier_Config $config
    12161626     * @todo Refactor duplicate elements into common class (probably using
    12171627     *       composition, not inheritance).
    12181628     */
    1219     protected function setupConfigStuff($config) {
    1220 
     1629    protected function setupConfigStuff($config)
     1630    {
    12211631        // setup allowed elements
    1222         $support = "(for information on implementing this, see the ".
    1223                    "support forums) ";
     1632        $support = "(for information on implementing this, see the " .
     1633            "support forums) ";
    12241634        $allowed_properties = $config->get('CSS.AllowedProperties');
    12251635        if ($allowed_properties !== null) {
    12261636            foreach ($this->info as $name => $d) {
    1227                 if(!isset($allowed_properties[$name])) unset($this->info[$name]);
     1637                if (!isset($allowed_properties[$name])) {
     1638                    unset($this->info[$name]);
     1639                }
    12281640                unset($allowed_properties[$name]);
    12291641            }
     
    12441656            }
    12451657        }
    1246 
    12471658    }
    12481659}
     
    12531664
    12541665/**
    1255  * Defines allowed child nodes and validates tokens against it.
     1666 * Defines allowed child nodes and validates nodes against it.
    12561667 */
    12571668abstract class HTMLPurifier_ChildDef
     
    12601671     * Type of child definition, usually right-most part of class name lowercase.
    12611672     * Used occasionally in terms of context.
     1673     * @type string
    12621674     */
    12631675    public $type;
    12641676
    12651677    /**
    1266      * Bool that indicates whether or not an empty array of children is okay
     1678     * Indicates whether or not an empty array of children is okay.
    12671679     *
    12681680     * This is necessary for redundant checking when changes affecting
    12691681     * a child node may cause a parent node to now be disallowed.
     1682     * @type bool
    12701683     */
    12711684    public $allow_empty;
    12721685
    12731686    /**
    1274      * Lookup array of all elements that this definition could possibly allow
     1687     * Lookup array of all elements that this definition could possibly allow.
     1688     * @type array
    12751689     */
    12761690    public $elements = array();
     
    12791693     * Get lookup of tag names that should not close this element automatically.
    12801694     * All other elements will do so.
    1281      */
    1282     public function getAllowedElements($config) {
     1695     * @param HTMLPurifier_Config $config HTMLPurifier_Config object
     1696     * @return array
     1697     */
     1698    public function getAllowedElements($config)
     1699    {
    12831700        return $this->elements;
    12841701    }
     
    12871704     * Validates nodes according to definition and returns modification.
    12881705     *
    1289      * @param $tokens_of_children Array of HTMLPurifier_Token
    1290      * @param $config HTMLPurifier_Config object
    1291      * @param $context HTMLPurifier_Context object
    1292      * @return bool true to leave nodes as is
    1293      * @return bool false to remove parent node
    1294      * @return array of replacement child tokens
    1295      */
    1296     abstract public function validateChildren($tokens_of_children, $config, $context);
     1706     * @param HTMLPurifier_Node[] $children Array of HTMLPurifier_Node
     1707     * @param HTMLPurifier_Config $config HTMLPurifier_Config object
     1708     * @param HTMLPurifier_Context $context HTMLPurifier_Context object
     1709     * @return bool|array true to leave nodes as is, false to remove parent node, array of replacement children
     1710     */
     1711    abstract public function validateChildren($children, $config, $context);
    12971712}
    12981713
     
    13201735    /**
    13211736     * HTML Purifier's version
    1322      */
    1323     public $version = '4.2.0';
    1324 
    1325     /**
    1326      * Bool indicator whether or not to automatically finalize
    1327      * the object if a read operation is done
     1737     * @type string
     1738     */
     1739    public $version = '4.7.0';
     1740
     1741    /**
     1742     * Whether or not to automatically finalize
     1743     * the object if a read operation is done.
     1744     * @type bool
    13281745     */
    13291746    public $autoFinalize = true;
     
    13321749
    13331750    /**
    1334      * Namespace indexed array of serials for specific namespaces (see
    1335      * getSerial() for more info).
     1751     * Namespace indexed array of serials for specific namespaces.
     1752     * @see getSerial() for more info.
     1753     * @type string[]
    13361754     */
    13371755    protected $serials = array();
    13381756
    13391757    /**
    1340      * Serial for entire configuration object
     1758     * Serial for entire configuration object.
     1759     * @type string
    13411760     */
    13421761    protected $serial;
    13431762
    13441763    /**
    1345      * Parser for variables
    1346      */
    1347     protected $parser;
    1348 
    1349     /**
    1350      * Reference HTMLPurifier_ConfigSchema for value checking
     1764     * Parser for variables.
     1765     * @type HTMLPurifier_VarParser_Flexible
     1766     */
     1767    protected $parser = null;
     1768
     1769    /**
     1770     * Reference HTMLPurifier_ConfigSchema for value checking.
     1771     * @type HTMLPurifier_ConfigSchema
    13511772     * @note This is public for introspective purposes. Please don't
    13521773     *       abuse!
     
    13551776
    13561777    /**
    1357      * Indexed array of definitions
     1778     * Indexed array of definitions.
     1779     * @type HTMLPurifier_Definition[]
    13581780     */
    13591781    protected $definitions;
    13601782
    13611783    /**
    1362      * Bool indicator whether or not config is finalized
     1784     * Whether or not config is finalized.
     1785     * @type bool
    13631786     */
    13641787    protected $finalized = false;
     
    13661789    /**
    13671790     * Property list containing configuration directives.
     1791     * @type array
    13681792     */
    13691793    protected $plist;
    13701794
    13711795    /**
    1372      * Whether or not a set is taking place due to an
    1373      * alias lookup.
     1796     * Whether or not a set is taking place due to an alias lookup.
     1797     * @type bool
    13741798     */
    13751799    private $aliasMode;
    13761800
    13771801    /**
    1378      * Set to false if you do not want line and file numbers in errors
    1379      * (useful when unit testing)
     1802     * Set to false if you do not want line and file numbers in errors.
     1803     * (useful when unit testing).  This will also compress some errors
     1804     * and exceptions.
     1805     * @type bool
    13801806     */
    13811807    public $chatty = true;
     
    13831809    /**
    13841810     * Current lock; only gets to this namespace are allowed.
     1811     * @type string
    13851812     */
    13861813    private $lock;
    13871814
    13881815    /**
    1389      * @param $definition HTMLPurifier_ConfigSchema that defines what directives
    1390      *                    are allowed.
    1391      */
    1392     public function __construct($definition, $parent = null) {
     1816     * Constructor
     1817     * @param HTMLPurifier_ConfigSchema $definition ConfigSchema that defines
     1818     * what directives are allowed.
     1819     * @param HTMLPurifier_PropertyList $parent
     1820     */
     1821    public function __construct($definition, $parent = null)
     1822    {
    13931823        $parent = $parent ? $parent : $definition->defaultPlist;
    13941824        $this->plist = new HTMLPurifier_PropertyList($parent);
     
    14031833     *                      an array of directives based on loadArray(),
    14041834     *                      or a string filename of an ini file.
    1405      * @param HTMLPurifier_ConfigSchema Schema object
    1406      * @return Configured HTMLPurifier_Config object
    1407      */
    1408     public static function create($config, $schema = null) {
     1835     * @param HTMLPurifier_ConfigSchema $schema Schema object
     1836     * @return HTMLPurifier_Config Configured object
     1837     */
     1838    public static function create($config, $schema = null)
     1839    {
    14091840        if ($config instanceof HTMLPurifier_Config) {
    14101841            // pass-through
     
    14161847            $ret = new HTMLPurifier_Config($schema);
    14171848        }
    1418         if (is_string($config)) $ret->loadIni($config);
    1419         elseif (is_array($config)) $ret->loadArray($config);
     1849        if (is_string($config)) {
     1850            $ret->loadIni($config);
     1851        } elseif (is_array($config)) $ret->loadArray($config);
    14201852        return $ret;
    14211853    }
     
    14231855    /**
    14241856     * Creates a new config object that inherits from a previous one.
    1425      * @param HTMLPurifier_Config $config Configuration object to inherit
    1426      *        from.
     1857     * @param HTMLPurifier_Config $config Configuration object to inherit from.
    14271858     * @return HTMLPurifier_Config object with $config as its parent.
    14281859     */
    1429     public static function inherit(HTMLPurifier_Config $config) {
     1860    public static function inherit(HTMLPurifier_Config $config)
     1861    {
    14301862        return new HTMLPurifier_Config($config->def, $config->plist);
    14311863    }
     
    14331865    /**
    14341866     * Convenience constructor that creates a default configuration object.
    1435      * @return Default HTMLPurifier_Config object.
    1436      */
    1437     public static function createDefault() {
     1867     * @return HTMLPurifier_Config default object.
     1868     */
     1869    public static function createDefault()
     1870    {
    14381871        $definition = HTMLPurifier_ConfigSchema::instance();
    14391872        $config = new HTMLPurifier_Config($definition);
     
    14421875
    14431876    /**
    1444      * Retreives a value from the configuration.
    1445      * @param $key String key
    1446      */
    1447     public function get($key, $a = null) {
     1877     * Retrieves a value from the configuration.
     1878     *
     1879     * @param string $key String key
     1880     * @param mixed $a
     1881     *
     1882     * @return mixed
     1883     */
     1884    public function get($key, $a = null)
     1885    {
    14481886        if ($a !== null) {
    1449             $this->triggerError("Using deprecated API: use \$config->get('$key.$a') instead", E_USER_WARNING);
     1887            $this->triggerError(
     1888                "Using deprecated API: use \$config->get('$key.$a') instead",
     1889                E_USER_WARNING
     1890            );
    14501891            $key = "$key.$a";
    14511892        }
    1452         if (!$this->finalized) $this->autoFinalize();
     1893        if (!$this->finalized) {
     1894            $this->autoFinalize();
     1895        }
    14531896        if (!isset($this->def->info[$key])) {
    14541897            // can't add % due to SimpleTest bug
    1455             $this->triggerError('Cannot retrieve value of undefined directive ' . htmlspecialchars($key),
    1456                 E_USER_WARNING);
     1898            $this->triggerError(
     1899                'Cannot retrieve value of undefined directive ' . htmlspecialchars($key),
     1900                E_USER_WARNING
     1901            );
    14571902            return;
    14581903        }
    14591904        if (isset($this->def->info[$key]->isAlias)) {
    14601905            $d = $this->def->info[$key];
    1461             $this->triggerError('Cannot get value from aliased directive, use real name ' . $d->key,
    1462                 E_USER_ERROR);
     1906            $this->triggerError(
     1907                'Cannot get value from aliased directive, use real name ' . $d->key,
     1908                E_USER_ERROR
     1909            );
    14631910            return;
    14641911        }
     
    14661913            list($ns) = explode('.', $key);
    14671914            if ($ns !== $this->lock) {
    1468                 $this->triggerError('Cannot get value of namespace ' . $ns . ' when lock for ' . $this->lock . ' is active, this probably indicates a Definition setup method is accessing directives that are not within its namespace', E_USER_ERROR);
     1915                $this->triggerError(
     1916                    'Cannot get value of namespace ' . $ns . ' when lock for ' .
     1917                    $this->lock .
     1918                    ' is active, this probably indicates a Definition setup method ' .
     1919                    'is accessing directives that are not within its namespace',
     1920                    E_USER_ERROR
     1921                );
    14691922                return;
    14701923            }
     
    14741927
    14751928    /**
    1476      * Retreives an array of directives to values from a given namespace
    1477      * @param $namespace String namespace
    1478      */
    1479     public function getBatch($namespace) {
    1480         if (!$this->finalized) $this->autoFinalize();
     1929     * Retrieves an array of directives to values from a given namespace
     1930     *
     1931     * @param string $namespace String namespace
     1932     *
     1933     * @return array
     1934     */
     1935    public function getBatch($namespace)
     1936    {
     1937        if (!$this->finalized) {
     1938            $this->autoFinalize();
     1939        }
    14811940        $full = $this->getAll();
    14821941        if (!isset($full[$namespace])) {
    1483             $this->triggerError('Cannot retrieve undefined namespace ' . htmlspecialchars($namespace),
    1484                 E_USER_WARNING);
     1942            $this->triggerError(
     1943                'Cannot retrieve undefined namespace ' .
     1944                htmlspecialchars($namespace),
     1945                E_USER_WARNING
     1946            );
    14851947            return;
    14861948        }
     
    14891951
    14901952    /**
    1491      * Returns a md5 signature of a segment of the configuration object
     1953     * Returns a SHA-1 signature of a segment of the configuration object
    14921954     * that uniquely identifies that particular configuration
     1955     *
     1956     * @param string $namespace Namespace to get serial for
     1957     *
     1958     * @return string
    14931959     * @note Revision is handled specially and is removed from the batch
    14941960     *       before processing!
    1495      * @param $namespace Namespace to get serial for
    1496      */
    1497     public function getBatchSerial($namespace) {
     1961     */
     1962    public function getBatchSerial($namespace)
     1963    {
    14981964        if (empty($this->serials[$namespace])) {
    14991965            $batch = $this->getBatch($namespace);
    15001966            unset($batch['DefinitionRev']);
    1501             $this->serials[$namespace] = md5(serialize($batch));
     1967            $this->serials[$namespace] = sha1(serialize($batch));
    15021968        }
    15031969        return $this->serials[$namespace];
     
    15051971
    15061972    /**
    1507      * Returns a md5 signature for the entire configuration object
     1973     * Returns a SHA-1 signature for the entire configuration object
    15081974     * that uniquely identifies that particular configuration
    1509      */
    1510     public function getSerial() {
     1975     *
     1976     * @return string
     1977     */
     1978    public function getSerial()
     1979    {
    15111980        if (empty($this->serial)) {
    1512             $this->serial = md5(serialize($this->getAll()));
     1981            $this->serial = sha1(serialize($this->getAll()));
    15131982        }
    15141983        return $this->serial;
     
    15171986    /**
    15181987     * Retrieves all directives, organized by namespace
     1988     *
    15191989     * @warning This is a pretty inefficient function, avoid if you can
    15201990     */
    1521     public function getAll() {
    1522         if (!$this->finalized) $this->autoFinalize();
     1991    public function getAll()
     1992    {
     1993        if (!$this->finalized) {
     1994            $this->autoFinalize();
     1995        }
    15231996        $ret = array();
    15241997        foreach ($this->plist->squash() as $name => $value) {
     
    15312004    /**
    15322005     * Sets a value to configuration.
    1533      * @param $key String key
    1534      * @param $value Mixed value
    1535      */
    1536     public function set($key, $value, $a = null) {
     2006     *
     2007     * @param string $key key
     2008     * @param mixed $value value
     2009     * @param mixed $a
     2010     */
     2011    public function set($key, $value, $a = null)
     2012    {
    15372013        if (strpos($key, '.') === false) {
    15382014            $namespace = $key;
     
    15442020            list($namespace) = explode('.', $key);
    15452021        }
    1546         if ($this->isFinalized('Cannot set directive after finalization')) return;
     2022        if ($this->isFinalized('Cannot set directive after finalization')) {
     2023            return;
     2024        }
    15472025        if (!isset($this->def->info[$key])) {
    1548             $this->triggerError('Cannot set undefined directive ' . htmlspecialchars($key) . ' to value',
    1549                 E_USER_WARNING);
     2026            $this->triggerError(
     2027                'Cannot set undefined directive ' . htmlspecialchars($key) . ' to value',
     2028                E_USER_WARNING
     2029            );
    15502030            return;
    15512031        }
     
    15542034        if (isset($def->isAlias)) {
    15552035            if ($this->aliasMode) {
    1556                 $this->triggerError('Double-aliases not allowed, please fix '.
    1557                     'ConfigSchema bug with' . $key, E_USER_ERROR);
     2036                $this->triggerError(
     2037                    'Double-aliases not allowed, please fix '.
     2038                    'ConfigSchema bug with' . $key,
     2039                    E_USER_ERROR
     2040                );
    15582041                return;
    15592042            }
     
    15792062            $value = $this->parser->parse($value, $type, $allow_null);
    15802063        } catch (HTMLPurifier_VarParserException $e) {
    1581             $this->triggerError('Value for ' . $key . ' is of invalid type, should be ' . HTMLPurifier_VarParser::getTypeName($type), E_USER_WARNING);
     2064            $this->triggerError(
     2065                'Value for ' . $key . ' is of invalid type, should be ' .
     2066                HTMLPurifier_VarParser::getTypeName($type),
     2067                E_USER_WARNING
     2068            );
    15822069            return;
    15832070        }
     
    15892076            // check to see if the value is allowed
    15902077            if (isset($def->allowed) && !isset($def->allowed[$value])) {
    1591                 $this->triggerError('Value not supported, valid values are: ' .
    1592                     $this->_listify($def->allowed), E_USER_WARNING);
     2078                $this->triggerError(
     2079                    'Value not supported, valid values are: ' .
     2080                    $this->_listify($def->allowed),
     2081                    E_USER_WARNING
     2082                );
    15932083                return;
    15942084            }
     
    16082098    /**
    16092099     * Convenience function for error reporting
    1610      */
    1611     private function _listify($lookup) {
     2100     *
     2101     * @param array $lookup
     2102     *
     2103     * @return string
     2104     */
     2105    private function _listify($lookup)
     2106    {
    16122107        $list = array();
    1613         foreach ($lookup as $name => $b) $list[] = $name;
     2108        foreach ($lookup as $name => $b) {
     2109            $list[] = $name;
     2110        }
    16142111        return implode(', ', $list);
    16152112    }
     
    16172114    /**
    16182115     * Retrieves object reference to the HTML definition.
    1619      * @param $raw Return a copy that has not been setup yet. Must be
     2116     *
     2117     * @param bool $raw Return a copy that has not been setup yet. Must be
    16202118     *             called before it's been setup, otherwise won't work.
    1621      */
    1622     public function getHTMLDefinition($raw = false) {
    1623         return $this->getDefinition('HTML', $raw);
     2119     * @param bool $optimized If true, this method may return null, to
     2120     *             indicate that a cached version of the modified
     2121     *             definition object is available and no further edits
     2122     *             are necessary.  Consider using
     2123     *             maybeGetRawHTMLDefinition, which is more explicitly
     2124     *             named, instead.
     2125     *
     2126     * @return HTMLPurifier_HTMLDefinition
     2127     */
     2128    public function getHTMLDefinition($raw = false, $optimized = false)
     2129    {
     2130        return $this->getDefinition('HTML', $raw, $optimized);
    16242131    }
    16252132
    16262133    /**
    16272134     * Retrieves object reference to the CSS definition
    1628      * @param $raw Return a copy that has not been setup yet. Must be
     2135     *
     2136     * @param bool $raw Return a copy that has not been setup yet. Must be
    16292137     *             called before it's been setup, otherwise won't work.
    1630      */
    1631     public function getCSSDefinition($raw = false) {
    1632         return $this->getDefinition('CSS', $raw);
     2138     * @param bool $optimized If true, this method may return null, to
     2139     *             indicate that a cached version of the modified
     2140     *             definition object is available and no further edits
     2141     *             are necessary.  Consider using
     2142     *             maybeGetRawCSSDefinition, which is more explicitly
     2143     *             named, instead.
     2144     *
     2145     * @return HTMLPurifier_CSSDefinition
     2146     */
     2147    public function getCSSDefinition($raw = false, $optimized = false)
     2148    {
     2149        return $this->getDefinition('CSS', $raw, $optimized);
     2150    }
     2151
     2152    /**
     2153     * Retrieves object reference to the URI definition
     2154     *
     2155     * @param bool $raw Return a copy that has not been setup yet. Must be
     2156     *             called before it's been setup, otherwise won't work.
     2157     * @param bool $optimized If true, this method may return null, to
     2158     *             indicate that a cached version of the modified
     2159     *             definition object is available and no further edits
     2160     *             are necessary.  Consider using
     2161     *             maybeGetRawURIDefinition, which is more explicitly
     2162     *             named, instead.
     2163     *
     2164     * @return HTMLPurifier_URIDefinition
     2165     */
     2166    public function getURIDefinition($raw = false, $optimized = false)
     2167    {
     2168        return $this->getDefinition('URI', $raw, $optimized);
    16332169    }
    16342170
    16352171    /**
    16362172     * Retrieves a definition
    1637      * @param $type Type of definition: HTML, CSS, etc
    1638      * @param $raw  Whether or not definition should be returned raw
    1639      */
    1640     public function getDefinition($type, $raw = false) {
    1641         if (!$this->finalized) $this->autoFinalize();
     2173     *
     2174     * @param string $type Type of definition: HTML, CSS, etc
     2175     * @param bool $raw Whether or not definition should be returned raw
     2176     * @param bool $optimized Only has an effect when $raw is true.  Whether
     2177     *        or not to return null if the result is already present in
     2178     *        the cache.  This is off by default for backwards
     2179     *        compatibility reasons, but you need to do things this
     2180     *        way in order to ensure that caching is done properly.
     2181     *        Check out enduser-customize.html for more details.
     2182     *        We probably won't ever change this default, as much as the
     2183     *        maybe semantics is the "right thing to do."
     2184     *
     2185     * @throws HTMLPurifier_Exception
     2186     * @return HTMLPurifier_Definition
     2187     */
     2188    public function getDefinition($type, $raw = false, $optimized = false)
     2189    {
     2190        if ($optimized && !$raw) {
     2191            throw new HTMLPurifier_Exception("Cannot set optimized = true when raw = false");
     2192        }
     2193        if (!$this->finalized) {
     2194            $this->autoFinalize();
     2195        }
    16422196        // temporarily suspend locks, so we can handle recursive definition calls
    16432197        $lock = $this->lock;
     
    16472201        $this->lock = $lock;
    16482202        if (!$raw) {
    1649             // see if we can quickly supply a definition
     2203            // full definition
     2204            // ---------------
     2205            // check if definition is in memory
    16502206            if (!empty($this->definitions[$type])) {
    1651                 if (!$this->definitions[$type]->setup) {
    1652                     $this->definitions[$type]->setup($this);
    1653                     $cache->set($this->definitions[$type], $this);
     2207                $def = $this->definitions[$type];
     2208                // check if the definition is setup
     2209                if ($def->setup) {
     2210                    return $def;
     2211                } else {
     2212                    $def->setup($this);
     2213                    if ($def->optimized) {
     2214                        $cache->add($def, $this);
     2215                    }
     2216                    return $def;
    16542217                }
    1655                 return $this->definitions[$type];
    1656             }
    1657             // memory check missed, try cache
    1658             $this->definitions[$type] = $cache->get($this);
    1659             if ($this->definitions[$type]) {
    1660                 // definition in cache, return it
    1661                 return $this->definitions[$type];
    1662             }
    1663         } elseif (
    1664             !empty($this->definitions[$type]) &&
    1665             !$this->definitions[$type]->setup
    1666         ) {
    1667             // raw requested, raw in memory, quick return
    1668             return $this->definitions[$type];
    1669         }
     2218            }
     2219            // check if definition is in cache
     2220            $def = $cache->get($this);
     2221            if ($def) {
     2222                // definition in cache, save to memory and return it
     2223                $this->definitions[$type] = $def;
     2224                return $def;
     2225            }
     2226            // initialize it
     2227            $def = $this->initDefinition($type);
     2228            // set it up
     2229            $this->lock = $type;
     2230            $def->setup($this);
     2231            $this->lock = null;
     2232            // save in cache
     2233            $cache->add($def, $this);
     2234            // return it
     2235            return $def;
     2236        } else {
     2237            // raw definition
     2238            // --------------
     2239            // check preconditions
     2240            $def = null;
     2241            if ($optimized) {
     2242                if (is_null($this->get($type . '.DefinitionID'))) {
     2243                    // fatally error out if definition ID not set
     2244                    throw new HTMLPurifier_Exception(
     2245                        "Cannot retrieve raw version without specifying %$type.DefinitionID"
     2246                    );
     2247                }
     2248            }
     2249            if (!empty($this->definitions[$type])) {
     2250                $def = $this->definitions[$type];
     2251                if ($def->setup && !$optimized) {
     2252                    $extra = $this->chatty ?
     2253                        " (try moving this code block earlier in your initialization)" :
     2254                        "";
     2255                    throw new HTMLPurifier_Exception(
     2256                        "Cannot retrieve raw definition after it has already been setup" .
     2257                        $extra
     2258                    );
     2259                }
     2260                if ($def->optimized === null) {
     2261                    $extra = $this->chatty ? " (try flushing your cache)" : "";
     2262                    throw new HTMLPurifier_Exception(
     2263                        "Optimization status of definition is unknown" . $extra
     2264                    );
     2265                }
     2266                if ($def->optimized !== $optimized) {
     2267                    $msg = $optimized ? "optimized" : "unoptimized";
     2268                    $extra = $this->chatty ?
     2269                        " (this backtrace is for the first inconsistent call, which was for a $msg raw definition)"
     2270                        : "";
     2271                    throw new HTMLPurifier_Exception(
     2272                        "Inconsistent use of optimized and unoptimized raw definition retrievals" . $extra
     2273                    );
     2274                }
     2275            }
     2276            // check if definition was in memory
     2277            if ($def) {
     2278                if ($def->setup) {
     2279                    // invariant: $optimized === true (checked above)
     2280                    return null;
     2281                } else {
     2282                    return $def;
     2283                }
     2284            }
     2285            // if optimized, check if definition was in cache
     2286            // (because we do the memory check first, this formulation
     2287            // is prone to cache slamming, but I think
     2288            // guaranteeing that either /all/ of the raw
     2289            // setup code or /none/ of it is run is more important.)
     2290            if ($optimized) {
     2291                // This code path only gets run once; once we put
     2292                // something in $definitions (which is guaranteed by the
     2293                // trailing code), we always short-circuit above.
     2294                $def = $cache->get($this);
     2295                if ($def) {
     2296                    // save the full definition for later, but don't
     2297                    // return it yet
     2298                    $this->definitions[$type] = $def;
     2299                    return null;
     2300                }
     2301            }
     2302            // check invariants for creation
     2303            if (!$optimized) {
     2304                if (!is_null($this->get($type . '.DefinitionID'))) {
     2305                    if ($this->chatty) {
     2306                        $this->triggerError(
     2307                            'Due to a documentation error in previous version of HTML Purifier, your ' .
     2308                            'definitions are not being cached.  If this is OK, you can remove the ' .
     2309                            '%$type.DefinitionRev and %$type.DefinitionID declaration.  Otherwise, ' .
     2310                            'modify your code to use maybeGetRawDefinition, and test if the returned ' .
     2311                            'value is null before making any edits (if it is null, that means that a ' .
     2312                            'cached version is available, and no raw operations are necessary).  See ' .
     2313                            '<a href="http://htmlpurifier.org/docs/enduser-customize.html#optimized">' .
     2314                            'Customize</a> for more details',
     2315                            E_USER_WARNING
     2316                        );
     2317                    } else {
     2318                        $this->triggerError(
     2319                            "Useless DefinitionID declaration",
     2320                            E_USER_WARNING
     2321                        );
     2322                    }
     2323                }
     2324            }
     2325            // initialize it
     2326            $def = $this->initDefinition($type);
     2327            $def->optimized = $optimized;
     2328            return $def;
     2329        }
     2330        throw new HTMLPurifier_Exception("The impossible happened!");
     2331    }
     2332
     2333    /**
     2334     * Initialise definition
     2335     *
     2336     * @param string $type What type of definition to create
     2337     *
     2338     * @return HTMLPurifier_CSSDefinition|HTMLPurifier_HTMLDefinition|HTMLPurifier_URIDefinition
     2339     * @throws HTMLPurifier_Exception
     2340     */
     2341    private function initDefinition($type)
     2342    {
    16702343        // quick checks failed, let's create the object
    16712344        if ($type == 'HTML') {
    1672             $this->definitions[$type] = new HTMLPurifier_HTMLDefinition();
     2345            $def = new HTMLPurifier_HTMLDefinition();
    16732346        } elseif ($type == 'CSS') {
    1674             $this->definitions[$type] = new HTMLPurifier_CSSDefinition();
     2347            $def = new HTMLPurifier_CSSDefinition();
    16752348        } elseif ($type == 'URI') {
    1676             $this->definitions[$type] = new HTMLPurifier_URIDefinition();
     2349            $def = new HTMLPurifier_URIDefinition();
    16772350        } else {
    1678             throw new HTMLPurifier_Exception("Definition of $type type not supported");
    1679         }
    1680         // quick abort if raw
    1681         if ($raw) {
    1682             if (is_null($this->get($type . '.DefinitionID'))) {
    1683                 // fatally error out if definition ID not set
    1684                 throw new HTMLPurifier_Exception("Cannot retrieve raw version without specifying %$type.DefinitionID");
    1685             }
    1686             return $this->definitions[$type];
    1687         }
    1688         // set it up
    1689         $this->lock = $type;
    1690         $this->definitions[$type]->setup($this);
    1691         $this->lock = null;
    1692         // save in cache
    1693         $cache->set($this->definitions[$type], $this);
    1694         return $this->definitions[$type];
     2351            throw new HTMLPurifier_Exception(
     2352                "Definition of $type type not supported"
     2353            );
     2354        }
     2355        $this->definitions[$type] = $def;
     2356        return $def;
     2357    }
     2358
     2359    public function maybeGetRawDefinition($name)
     2360    {
     2361        return $this->getDefinition($name, true, true);
     2362    }
     2363
     2364    /**
     2365     * @return HTMLPurifier_HTMLDefinition
     2366     */
     2367    public function maybeGetRawHTMLDefinition()
     2368    {
     2369        return $this->getDefinition('HTML', true, true);
     2370    }
     2371   
     2372    /**
     2373     * @return HTMLPurifier_CSSDefinition
     2374     */
     2375    public function maybeGetRawCSSDefinition()
     2376    {
     2377        return $this->getDefinition('CSS', true, true);
     2378    }
     2379   
     2380    /**
     2381     * @return HTMLPurifier_URIDefinition
     2382     */
     2383    public function maybeGetRawURIDefinition()
     2384    {
     2385        return $this->getDefinition('URI', true, true);
    16952386    }
    16962387
     
    16982389     * Loads configuration values from an array with the following structure:
    16992390     * Namespace.Directive => Value
    1700      * @param $config_array Configuration associative array
    1701      */
    1702     public function loadArray($config_array) {
    1703         if ($this->isFinalized('Cannot load directives after finalization')) return;
     2391     *
     2392     * @param array $config_array Configuration associative array
     2393     */
     2394    public function loadArray($config_array)
     2395    {
     2396        if ($this->isFinalized('Cannot load directives after finalization')) {
     2397            return;
     2398        }
    17042399        foreach ($config_array as $key => $value) {
    17052400            $key = str_replace('_', '.', $key);
     
    17092404                $namespace = $key;
    17102405                $namespace_values = $value;
    1711                 foreach ($namespace_values as $directive => $value) {
    1712                     $this->set($namespace .'.'. $directive, $value);
     2406                foreach ($namespace_values as $directive => $value2) {
     2407                    $this->set($namespace .'.'. $directive, $value2);
    17132408                }
    17142409            }
     
    17202415     * that are allowed in a web-form context as per an allowed
    17212416     * namespaces/directives list.
    1722      * @param $allowed List of allowed namespaces/directives
    1723      */
    1724     public static function getAllowedDirectivesForForm($allowed, $schema = null) {
     2417     *
     2418     * @param array $allowed List of allowed namespaces/directives
     2419     * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy
     2420     *
     2421     * @return array
     2422     */
     2423    public static function getAllowedDirectivesForForm($allowed, $schema = null)
     2424    {
    17252425        if (!$schema) {
    17262426            $schema = HTMLPurifier_ConfigSchema::instance();
    17272427        }
    17282428        if ($allowed !== true) {
    1729              if (is_string($allowed)) $allowed = array($allowed);
    1730              $allowed_ns = array();
    1731              $allowed_directives = array();
    1732              $blacklisted_directives = array();
    1733              foreach ($allowed as $ns_or_directive) {
    1734                  if (strpos($ns_or_directive, '.') !== false) {
    1735                      // directive
    1736                      if ($ns_or_directive[0] == '-') {
    1737                          $blacklisted_directives[substr($ns_or_directive, 1)] = true;
    1738                      } else {
    1739                          $allowed_directives[$ns_or_directive] = true;
    1740                      }
    1741                  } else {
    1742                      // namespace
    1743                      $allowed_ns[$ns_or_directive] = true;
    1744                  }
    1745              }
     2429            if (is_string($allowed)) {
     2430                $allowed = array($allowed);
     2431            }
     2432            $allowed_ns = array();
     2433            $allowed_directives = array();
     2434            $blacklisted_directives = array();
     2435            foreach ($allowed as $ns_or_directive) {
     2436                if (strpos($ns_or_directive, '.') !== false) {
     2437                    // directive
     2438                    if ($ns_or_directive[0] == '-') {
     2439                        $blacklisted_directives[substr($ns_or_directive, 1)] = true;
     2440                    } else {
     2441                        $allowed_directives[$ns_or_directive] = true;
     2442                    }
     2443                } else {
     2444                    // namespace
     2445                    $allowed_ns[$ns_or_directive] = true;
     2446                }
     2447            }
    17462448        }
    17472449        $ret = array();
     
    17492451            list($ns, $directive) = explode('.', $key, 2);
    17502452            if ($allowed !== true) {
    1751                 if (isset($blacklisted_directives["$ns.$directive"])) continue;
    1752                 if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) continue;
    1753             }
    1754             if (isset($def->isAlias)) continue;
    1755             if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') continue;
     2453                if (isset($blacklisted_directives["$ns.$directive"])) {
     2454                    continue;
     2455                }
     2456                if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) {
     2457                    continue;
     2458                }
     2459            }
     2460            if (isset($def->isAlias)) {
     2461                continue;
     2462            }
     2463            if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') {
     2464                continue;
     2465            }
    17562466            $ret[] = array($ns, $directive);
    17572467        }
     
    17622472     * Loads configuration values from $_GET/$_POST that were posted
    17632473     * via ConfigForm
    1764      * @param $array $_GET or $_POST array to import
    1765      * @param $index Index/name that the config variables are in
    1766      * @param $allowed List of allowed namespaces/directives
    1767      * @param $mq_fix Boolean whether or not to enable magic quotes fix
    1768      * @param $schema Instance of HTMLPurifier_ConfigSchema to use, if not global copy
    1769      */
    1770     public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) {
     2474     *
     2475     * @param array $array $_GET or $_POST array to import
     2476     * @param string|bool $index Index/name that the config variables are in
     2477     * @param array|bool $allowed List of allowed namespaces/directives
     2478     * @param bool $mq_fix Boolean whether or not to enable magic quotes fix
     2479     * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy
     2480     *
     2481     * @return mixed
     2482     */
     2483    public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null)
     2484    {
    17712485        $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $schema);
    17722486        $config = HTMLPurifier_Config::create($ret, $schema);
     
    17762490    /**
    17772491     * Merges in configuration values from $_GET/$_POST to object. NOT STATIC.
    1778      * @note Same parameters as loadArrayFromForm
    1779      */
    1780     public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true) {
     2492     *
     2493     * @param array $array $_GET or $_POST array to import
     2494     * @param string|bool $index Index/name that the config variables are in
     2495     * @param array|bool $allowed List of allowed namespaces/directives
     2496     * @param bool $mq_fix Boolean whether or not to enable magic quotes fix
     2497     */
     2498    public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true)
     2499    {
    17812500         $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $this->def);
    17822501         $this->loadArray($ret);
     
    17862505     * Prepares an array from a form into something usable for the more
    17872506     * strict parts of HTMLPurifier_Config
    1788      */
    1789     public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) {
    1790         if ($index !== false) $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array();
     2507     *
     2508     * @param array $array $_GET or $_POST array to import
     2509     * @param string|bool $index Index/name that the config variables are in
     2510     * @param array|bool $allowed List of allowed namespaces/directives
     2511     * @param bool $mq_fix Boolean whether or not to enable magic quotes fix
     2512     * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy
     2513     *
     2514     * @return array
     2515     */
     2516    public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null)
     2517    {
     2518        if ($index !== false) {
     2519            $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array();
     2520        }
    17912521        $mq = $mq_fix && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc();
    17922522
     
    18002530                continue;
    18012531            }
    1802             if (!isset($array[$skey])) continue;
     2532            if (!isset($array[$skey])) {
     2533                continue;
     2534            }
    18032535            $value = $mq ? stripslashes($array[$skey]) : $array[$skey];
    18042536            $ret[$ns][$directive] = $value;
     
    18092541    /**
    18102542     * Loads configuration values from an ini file
    1811      * @param $filename Name of ini file
    1812      */
    1813     public function loadIni($filename) {
    1814         if ($this->isFinalized('Cannot load directives after finalization')) return;
     2543     *
     2544     * @param string $filename Name of ini file
     2545     */
     2546    public function loadIni($filename)
     2547    {
     2548        if ($this->isFinalized('Cannot load directives after finalization')) {
     2549            return;
     2550        }
    18152551        $array = parse_ini_file($filename, true);
    18162552        $this->loadArray($array);
     
    18192555    /**
    18202556     * Checks whether or not the configuration object is finalized.
    1821      * @param $error String error message, or false for no error
    1822      */
    1823     public function isFinalized($error = false) {
     2557     *
     2558     * @param string|bool $error String error message, or false for no error
     2559     *
     2560     * @return bool
     2561     */
     2562    public function isFinalized($error = false)
     2563    {
    18242564        if ($this->finalized && $error) {
    18252565            $this->triggerError($error, E_USER_ERROR);
     
    18322572     * already finalized
    18332573     */
    1834     public function autoFinalize() {
     2574    public function autoFinalize()
     2575    {
    18352576        if ($this->autoFinalize) {
    18362577            $this->finalize();
     
    18432584     * Finalizes a configuration object, prohibiting further change
    18442585     */
    1845     public function finalize() {
     2586    public function finalize()
     2587    {
    18462588        $this->finalized = true;
    1847         unset($this->parser);
     2589        $this->parser = null;
    18482590    }
    18492591
    18502592    /**
    18512593     * Produces a nicely formatted error message by supplying the
    1852      * stack frame information from two levels up and OUTSIDE of
    1853      * HTMLPurifier_Config.
    1854      */
    1855     protected function triggerError($msg, $no) {
     2594     * stack frame information OUTSIDE of HTMLPurifier_Config.
     2595     *
     2596     * @param string $msg An error message
     2597     * @param int $no An error number
     2598     */
     2599    protected function triggerError($msg, $no)
     2600    {
    18562601        // determine previous stack frame
    1857         $backtrace = debug_backtrace();
    1858         if ($this->chatty && isset($backtrace[1])) {
    1859             $frame = $backtrace[1];
    1860             $extra = " on line {$frame['line']} in file {$frame['file']}";
    1861         } else {
    1862             $extra = '';
     2602        $extra = '';
     2603        if ($this->chatty) {
     2604            $trace = debug_backtrace();
     2605            // zip(tail(trace), trace) -- but PHP is not Haskell har har
     2606            for ($i = 0, $c = count($trace); $i < $c - 1; $i++) {
     2607                // XXX this is not correct on some versions of HTML Purifier
     2608                if ($trace[$i + 1]['class'] === 'HTMLPurifier_Config') {
     2609                    continue;
     2610                }
     2611                $frame = $trace[$i];
     2612                $extra = " invoked on line {$frame['line']} in file {$frame['file']}";
     2613                break;
     2614            }
    18632615        }
    18642616        trigger_error($msg . $extra, $no);
     
    18682620     * Returns a serialized form of the configuration object that can
    18692621     * be reconstituted.
    1870      */
    1871     public function serialize() {
     2622     *
     2623     * @return string
     2624     */
     2625    public function serialize()
     2626    {
    18722627        $this->getDefinition('HTML');
    18732628        $this->getDefinition('CSS');
     
    18852640 * Configuration definition, defines directives and their defaults.
    18862641 */
    1887 class HTMLPurifier_ConfigSchema {
    1888 
     2642class HTMLPurifier_ConfigSchema
     2643{
    18892644    /**
    18902645     * Defaults of the directives and namespaces.
     2646     * @type array
    18912647     * @note This shares the exact same structure as HTMLPurifier_Config::$conf
    18922648     */
     
    18952651    /**
    18962652     * The default property list. Do not edit this property list.
     2653     * @type array
    18972654     */
    18982655    public $defaultPlist;
    18992656
    19002657    /**
    1901      * Definition of the directives. The structure of this is:
     2658     * Definition of the directives.
     2659     * The structure of this is:
    19022660     *
    19032661     *  array(
     
    19262684     * about the schema, you're better of using the ConfigSchema_Interchange,
    19272685     * which uses more memory but has much richer information.
     2686     * @type array
    19282687     */
    19292688    public $info = array();
     
    19312690    /**
    19322691     * Application-wide singleton
    1933      */
    1934     static protected $singleton;
    1935 
    1936     public function __construct() {
     2692     * @type HTMLPurifier_ConfigSchema
     2693     */
     2694    protected static $singleton;
     2695
     2696    public function __construct()
     2697    {
    19372698        $this->defaultPlist = new HTMLPurifier_PropertyList();
    19382699    }
     
    19402701    /**
    19412702     * Unserializes the default ConfigSchema.
    1942      */
    1943     public static function makeFromSerial() {
    1944         return unserialize(file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema.ser'));
     2703     * @return HTMLPurifier_ConfigSchema
     2704     */
     2705    public static function makeFromSerial()
     2706    {
     2707        $contents = file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema.ser');
     2708        $r = unserialize($contents);
     2709        if (!$r) {
     2710            $hash = sha1($contents);
     2711            trigger_error("Unserialization of configuration schema failed, sha1 of file was $hash", E_USER_ERROR);
     2712        }
     2713        return $r;
    19452714    }
    19462715
    19472716    /**
    19482717     * Retrieves an instance of the application-wide configuration definition.
    1949      */
    1950     public static function instance($prototype = null) {
     2718     * @param HTMLPurifier_ConfigSchema $prototype
     2719     * @return HTMLPurifier_ConfigSchema
     2720     */
     2721    public static function instance($prototype = null)
     2722    {
    19512723        if ($prototype !== null) {
    19522724            HTMLPurifier_ConfigSchema::$singleton = $prototype;
     
    19622734     * @warning This method's signature is slightly different from the legacy
    19632735     *          define() static method! Beware!
    1964      * @param $namespace Namespace the directive is in
    1965      * @param $name Key of directive
    1966      * @param $default Default value of directive
    1967      * @param $type Allowed type of the directive. See
     2736     * @param string $key Name of directive
     2737     * @param mixed $default Default value of directive
     2738     * @param string $type Allowed type of the directive. See
    19682739     *      HTMLPurifier_DirectiveDef::$type for allowed values
    1969      * @param $allow_null Whether or not to allow null values
    1970      */
    1971     public function add($key, $default, $type, $allow_null) {
     2740     * @param bool $allow_null Whether or not to allow null values
     2741     */
     2742    public function add($key, $default, $type, $allow_null)
     2743    {
    19722744        $obj = new stdclass();
    19732745        $obj->type = is_int($type) ? $type : HTMLPurifier_VarParser::$types[$type];
    1974         if ($allow_null) $obj->allow_null = true;
     2746        if ($allow_null) {
     2747            $obj->allow_null = true;
     2748        }
    19752749        $this->info[$key] = $obj;
    19762750        $this->defaults[$key] = $default;
     
    19832757     * Directive value aliases are convenient for developers because it lets
    19842758     * them set a directive to several values and get the same result.
    1985      * @param $namespace Directive's namespace
    1986      * @param $name Name of Directive
    1987      * @param $aliases Hash of aliased values to the real alias
    1988      */
    1989     public function addValueAliases($key, $aliases) {
     2759     * @param string $key Name of Directive
     2760     * @param array $aliases Hash of aliased values to the real alias
     2761     */
     2762    public function addValueAliases($key, $aliases)
     2763    {
    19902764        if (!isset($this->info[$key]->aliases)) {
    19912765            $this->info[$key]->aliases = array();
     
    20002774     * @warning This is slightly different from the corresponding static
    20012775     *          method definition.
    2002      * @param $namespace Namespace of directive
    2003      * @param $name Name of directive
    2004      * @param $allowed Lookup array of allowed values
    2005      */
    2006     public function addAllowedValues($key, $allowed) {
     2776     * @param string $key Name of directive
     2777     * @param array $allowed Lookup array of allowed values
     2778     */
     2779    public function addAllowedValues($key, $allowed)
     2780    {
    20072781        $this->info[$key]->allowed = $allowed;
    20082782    }
     
    20102784    /**
    20112785     * Defines a directive alias for backwards compatibility
    2012      * @param $namespace
    2013      * @param $name Directive that will be aliased
    2014      * @param $new_namespace
    2015      * @param $new_name Directive that the alias will be to
    2016      */
    2017     public function addAlias($key, $new_key) {
     2786     * @param string $key Directive that will be aliased
     2787     * @param string $new_key Directive that the alias will be to
     2788     */
     2789    public function addAlias($key, $new_key)
     2790    {
    20182791        $obj = new stdclass;
    20192792        $obj->key = $new_key;
     
    20252798     * Replaces any stdclass that only has the type property with type integer.
    20262799     */
    2027     public function postProcess() {
     2800    public function postProcess()
     2801    {
    20282802        foreach ($this->info as $key => $v) {
    20292803            if (count((array) $v) == 1) {
     
    20342808        }
    20352809    }
    2036 
    20372810}
    20382811
     
    20482821
    20492822    /**
    2050      * List of content set strings (pipe seperators) indexed by name.
     2823     * List of content set strings (pipe separators) indexed by name.
     2824     * @type array
    20512825     */
    20522826    public $info = array();
     
    20542828    /**
    20552829     * List of content set lookups (element => true) indexed by name.
     2830     * @type array
    20562831     * @note This is in HTMLPurifier_HTMLDefinition->info_content_sets
    20572832     */
     
    20592834
    20602835    /**
    2061      * Synchronized list of defined content sets (keys of info)
     2836     * Synchronized list of defined content sets (keys of info).
     2837     * @type array
    20622838     */
    20632839    protected $keys = array();
    20642840    /**
    2065      * Synchronized list of defined content values (values of info)
     2841     * Synchronized list of defined content values (values of info).
     2842     * @type array
    20662843     */
    20672844    protected $values = array();
     
    20702847     * Merges in module's content sets, expands identifiers in the content
    20712848     * sets and populates the keys, values and lookup member variables.
    2072      * @param $modules List of HTMLPurifier_HTMLModule
    2073      */
    2074     public function __construct($modules) {
    2075         if (!is_array($modules)) $modules = array($modules);
     2849     * @param HTMLPurifier_HTMLModule[] $modules List of HTMLPurifier_HTMLModule
     2850     */
     2851    public function __construct($modules)
     2852    {
     2853        if (!is_array($modules)) {
     2854            $modules = array($modules);
     2855        }
    20762856        // populate content_sets based on module hints
    20772857        // sorry, no way of overloading
    2078         foreach ($modules as $module_i => $module) {
     2858        foreach ($modules as $module) {
    20792859            foreach ($module->content_sets as $key => $value) {
    20802860                $temp = $this->convertToLookup($value);
     
    21112891    /**
    21122892     * Accepts a definition; generates and assigns a ChildDef for it
    2113      * @param $def HTMLPurifier_ElementDef reference
    2114      * @param $module Module that defined the ElementDef
    2115      */
    2116     public function generateChildDef(&$def, $module) {
    2117         if (!empty($def->child)) return; // already done!
     2893     * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef reference
     2894     * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef
     2895     */
     2896    public function generateChildDef(&$def, $module)
     2897    {
     2898        if (!empty($def->child)) { // already done!
     2899            return;
     2900        }
    21182901        $content_model = $def->content_model;
    21192902        if (is_string($content_model)) {
     
    21302913    }
    21312914
    2132     public function generateChildDefCallback($matches) {
     2915    public function generateChildDefCallback($matches)
     2916    {
    21332917        return $this->info[$matches[0]];
    21342918    }
     
    21392923     * @note This will also defer to modules for custom HTMLPurifier_ChildDef
    21402924     *       subclasses that need content set expansion
    2141      * @param $def HTMLPurifier_ElementDef to have ChildDef extracted
     2925     * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef to have ChildDef extracted
     2926     * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef
    21422927     * @return HTMLPurifier_ChildDef corresponding to ElementDef
    21432928     */
    2144     public function getChildDef($def, $module) {
     2929    public function getChildDef($def, $module)
     2930    {
    21452931        $value = $def->content_model;
    21462932        if (is_object($value)) {
     
    21672953            $return = $module->getChildDef($def);
    21682954        }
    2169         if ($return !== false) return $return;
     2955        if ($return !== false) {
     2956            return $return;
     2957        }
    21702958        // error-out
    21712959        trigger_error(
     
    21792967     * Converts a string list of elements separated by pipes into
    21802968     * a lookup array.
    2181      * @param $string List of elements
    2182      * @return Lookup array of elements
    2183      */
    2184     protected function convertToLookup($string) {
     2969     * @param string $string List of elements
     2970     * @return array Lookup array of elements
     2971     */
     2972    protected function convertToLookup($string)
     2973    {
    21852974        $array = explode('|', str_replace(' ', '', $string));
    21862975        $ret = array();
    2187         foreach ($array as $i => $k) {
     2976        foreach ($array as $k) {
    21882977            $ret[$k] = true;
    21892978        }
    21902979        return $ret;
    21912980    }
    2192 
    21932981}
    21942982
     
    22092997    /**
    22102998     * Private array that stores the references.
     2999     * @type array
    22113000     */
    22123001    private $_storage = array();
     
    22143003    /**
    22153004     * Registers a variable into the context.
    2216      * @param $name String name
    2217      * @param $ref Reference to variable to be registered
    2218      */
    2219     public function register($name, &$ref) {
    2220         if (isset($this->_storage[$name])) {
    2221             trigger_error("Name $name produces collision, cannot re-register",
    2222                           E_USER_ERROR);
     3005     * @param string $name String name
     3006     * @param mixed $ref Reference to variable to be registered
     3007     */
     3008    public function register($name, &$ref)
     3009    {
     3010        if (array_key_exists($name, $this->_storage)) {
     3011            trigger_error(
     3012                "Name $name produces collision, cannot re-register",
     3013                E_USER_ERROR
     3014            );
    22233015            return;
    22243016        }
     
    22283020    /**
    22293021     * Retrieves a variable reference from the context.
    2230      * @param $name String name
    2231      * @param $ignore_error Boolean whether or not to ignore error
    2232      */
    2233     public function &get($name, $ignore_error = false) {
    2234         if (!isset($this->_storage[$name])) {
     3022     * @param string $name String name
     3023     * @param bool $ignore_error Boolean whether or not to ignore error
     3024     * @return mixed
     3025     */
     3026    public function &get($name, $ignore_error = false)
     3027    {
     3028        if (!array_key_exists($name, $this->_storage)) {
    22353029            if (!$ignore_error) {
    2236                 trigger_error("Attempted to retrieve non-existent variable $name",
    2237                               E_USER_ERROR);
     3030                trigger_error(
     3031                    "Attempted to retrieve non-existent variable $name",
     3032                    E_USER_ERROR
     3033                );
    22383034            }
    22393035            $var = null; // so we can return by reference
     
    22443040
    22453041    /**
    2246      * Destorys a variable in the context.
    2247      * @param $name String name
    2248      */
    2249     public function destroy($name) {
    2250         if (!isset($this->_storage[$name])) {
    2251             trigger_error("Attempted to destroy non-existent variable $name",
    2252                           E_USER_ERROR);
     3042     * Destroys a variable in the context.
     3043     * @param string $name String name
     3044     */
     3045    public function destroy($name)
     3046    {
     3047        if (!array_key_exists($name, $this->_storage)) {
     3048            trigger_error(
     3049                "Attempted to destroy non-existent variable $name",
     3050                E_USER_ERROR
     3051            );
    22533052            return;
    22543053        }
     
    22583057    /**
    22593058     * Checks whether or not the variable exists.
    2260      * @param $name String name
    2261      */
    2262     public function exists($name) {
    2263         return isset($this->_storage[$name]);
     3059     * @param string $name String name
     3060     * @return bool
     3061     */
     3062    public function exists($name)
     3063    {
     3064        return array_key_exists($name, $this->_storage);
    22643065    }
    22653066
    22663067    /**
    22673068     * Loads a series of variables from an associative array
    2268      * @param $context_array Assoc array of variables to load
    2269      */
    2270     public function loadArray($context_array) {
     3069     * @param array $context_array Assoc array of variables to load
     3070     */
     3071    public function loadArray($context_array)
     3072    {
    22713073        foreach ($context_array as $key => $discard) {
    22723074            $this->register($key, $context_array[$key]);
    22733075        }
    22743076    }
    2275 
    22763077}
    22773078
     
    22903091abstract class HTMLPurifier_DefinitionCache
    22913092{
    2292 
     3093    /**
     3094     * @type string
     3095     */
    22933096    public $type;
    22943097
    22953098    /**
    2296      * @param $name Type of definition objects this instance of the
     3099     * @param string $type Type of definition objects this instance of the
    22973100     *      cache will handle.
    22983101     */
    2299     public function __construct($type) {
     3102    public function __construct($type)
     3103    {
    23003104        $this->type = $type;
    23013105    }
     
    23033107    /**
    23043108     * Generates a unique identifier for a particular configuration
    2305      * @param Instance of HTMLPurifier_Config
    2306      */
    2307     public function generateKey($config) {
     3109     * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config
     3110     * @return string
     3111     */
     3112    public function generateKey($config)
     3113    {
    23083114        return $config->version . ',' . // possibly replace with function calls
    23093115               $config->getBatchSerial($this->type) . ',' .
     
    23143120     * Tests whether or not a key is old with respect to the configuration's
    23153121     * version and revision number.
    2316      * @param $key Key to test
    2317      * @param $config Instance of HTMLPurifier_Config to test against
    2318      */
    2319     public function isOld($key, $config) {
    2320         if (substr_count($key, ',') < 2) return true;
     3122     * @param string $key Key to test
     3123     * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config to test against
     3124     * @return bool
     3125     */
     3126    public function isOld($key, $config)
     3127    {
     3128        if (substr_count($key, ',') < 2) {
     3129            return true;
     3130        }
    23213131        list($version, $hash, $revision) = explode(',', $key, 3);
    23223132        $compare = version_compare($version, $config->version);
    23233133        // version mismatch, is always old
    2324         if ($compare != 0) return true;
     3134        if ($compare != 0) {
     3135            return true;
     3136        }
    23253137        // versions match, ids match, check revision number
    2326         if (
    2327             $hash == $config->getBatchSerial($this->type) &&
    2328             $revision < $config->get($this->type . '.DefinitionRev')
    2329         ) return true;
     3138        if ($hash == $config->getBatchSerial($this->type) &&
     3139            $revision < $config->get($this->type . '.DefinitionRev')) {
     3140            return true;
     3141        }
    23303142        return false;
    23313143    }
     
    23343146     * Checks if a definition's type jives with the cache's type
    23353147     * @note Throws an error on failure
    2336      * @param $def Definition object to check
    2337      * @return Boolean true if good, false if not
    2338      */
    2339     public function checkDefType($def) {
     3148     * @param HTMLPurifier_Definition $def Definition object to check
     3149     * @return bool true if good, false if not
     3150     */
     3151    public function checkDefType($def)
     3152    {
    23403153        if ($def->type !== $this->type) {
    23413154            trigger_error("Cannot use definition of type {$def->type} in cache for {$this->type}");
     
    23473160    /**
    23483161     * Adds a definition object to the cache
     3162     * @param HTMLPurifier_Definition $def
     3163     * @param HTMLPurifier_Config $config
    23493164     */
    23503165    abstract public function add($def, $config);
     
    23523167    /**
    23533168     * Unconditionally saves a definition object to the cache
     3169     * @param HTMLPurifier_Definition $def
     3170     * @param HTMLPurifier_Config $config
    23543171     */
    23553172    abstract public function set($def, $config);
     
    23573174    /**
    23583175     * Replace an object in the cache
     3176     * @param HTMLPurifier_Definition $def
     3177     * @param HTMLPurifier_Config $config
    23593178     */
    23603179    abstract public function replace($def, $config);
     
    23623181    /**
    23633182     * Retrieves a definition object from the cache
     3183     * @param HTMLPurifier_Config $config
    23643184     */
    23653185    abstract public function get($config);
     
    23673187    /**
    23683188     * Removes a definition object to the cache
     3189     * @param HTMLPurifier_Config $config
    23693190     */
    23703191    abstract public function remove($config);
     
    23723193    /**
    23733194     * Clears all objects from cache
     3195     * @param HTMLPurifier_Config $config
    23743196     */
    23753197    abstract public function flush($config);
     
    23803202     *       not interfere with other Definition types, and cleanup()
    23813203     *       should not be repeatedly called by userland code.
     3204     * @param HTMLPurifier_Config $config
    23823205     */
    23833206    abstract public function cleanup($config);
    2384 
    23853207}
    23863208
     
    23943216class HTMLPurifier_DefinitionCacheFactory
    23953217{
    2396 
     3218    /**
     3219     * @type array
     3220     */
    23973221    protected $caches = array('Serializer' => array());
     3222
     3223    /**
     3224     * @type array
     3225     */
    23983226    protected $implementations = array();
     3227
     3228    /**
     3229     * @type HTMLPurifier_DefinitionCache_Decorator[]
     3230     */
    23993231    protected $decorators = array();
    24003232
     
    24023234     * Initialize default decorators
    24033235     */
    2404     public function setup() {
     3236    public function setup()
     3237    {
    24053238        $this->addDecorator('Cleanup');
    24063239    }
     
    24083241    /**
    24093242     * Retrieves an instance of global definition cache factory.
    2410      */
    2411     public static function instance($prototype = null) {
     3243     * @param HTMLPurifier_DefinitionCacheFactory $prototype
     3244     * @return HTMLPurifier_DefinitionCacheFactory
     3245     */
     3246    public static function instance($prototype = null)
     3247    {
    24123248        static $instance;
    24133249        if ($prototype !== null) {
     
    24223258    /**
    24233259     * Registers a new definition cache object
    2424      * @param $short Short name of cache object, for reference
    2425      * @param $long Full class name of cache object, for construction
    2426      */
    2427     public function register($short, $long) {
     3260     * @param string $short Short name of cache object, for reference
     3261     * @param string $long Full class name of cache object, for construction
     3262     */
     3263    public function register($short, $long)
     3264    {
    24283265        $this->implementations[$short] = $long;
    24293266    }
     
    24313268    /**
    24323269     * Factory method that creates a cache object based on configuration
    2433      * @param $name Name of definitions handled by cache
    2434      * @param $config Instance of HTMLPurifier_Config
    2435      */
    2436     public function create($type, $config) {
     3270     * @param string $type Name of definitions handled by cache
     3271     * @param HTMLPurifier_Config $config Config instance
     3272     * @return mixed
     3273     */
     3274    public function create($type, $config)
     3275    {
    24373276        $method = $config->get('Cache.DefinitionImpl');
    24383277        if ($method === null) {
     
    24423281            return $this->caches[$method][$type];
    24433282        }
    2444         if (
    2445           isset($this->implementations[$method]) &&
    2446           class_exists($class = $this->implementations[$method], false)
    2447         ) {
     3283        if (isset($this->implementations[$method]) &&
     3284            class_exists($class = $this->implementations[$method], false)) {
    24483285            $cache = new $class($type);
    24493286        } else {
     
    24653302    /**
    24663303     * Registers a decorator to add to all new cache objects
    2467      * @param
    2468      */
    2469     public function addDecorator($decorator) {
     3304     * @param HTMLPurifier_DefinitionCache_Decorator|string $decorator An instance or the name of a decorator
     3305     */
     3306    public function addDecorator($decorator)
     3307    {
    24703308        if (is_string($decorator)) {
    24713309            $class = "HTMLPurifier_DefinitionCache_Decorator_$decorator";
     
    24743312        $this->decorators[$decorator->name] = $decorator;
    24753313    }
    2476 
    24773314}
    24783315
     
    24913328    /**
    24923329     * Full name of doctype
     3330     * @type string
    24933331     */
    24943332    public $name;
     
    24973335     * List of standard modules (string identifiers or literal objects)
    24983336     * that this doctype uses
     3337     * @type array
    24993338     */
    25003339    public $modules = array();
     
    25023341    /**
    25033342     * List of modules to use for tidying up code
     3343     * @type array
    25043344     */
    25053345    public $tidyModules = array();
     
    25073347    /**
    25083348     * Is the language derived from XML (i.e. XHTML)?
     3349     * @type bool
    25093350     */
    25103351    public $xml = true;
     
    25123353    /**
    25133354     * List of aliases for this doctype
     3355     * @type array
    25143356     */
    25153357    public $aliases = array();
     
    25173359    /**
    25183360     * Public DTD identifier
     3361     * @type string
    25193362     */
    25203363    public $dtdPublic;
     
    25223365    /**
    25233366     * System DTD identifier
     3367     * @type string
    25243368     */
    25253369    public $dtdSystem;
    25263370
    2527     public function __construct($name = null, $xml = true, $modules = array(),
    2528         $tidyModules = array(), $aliases = array(), $dtd_public = null, $dtd_system = null
     3371    public function __construct(
     3372        $name = null,
     3373        $xml = true,
     3374        $modules = array(),
     3375        $tidyModules = array(),
     3376        $aliases = array(),
     3377        $dtd_public = null,
     3378        $dtd_system = null
    25293379    ) {
    25303380        $this->name         = $name;
     
    25463396
    25473397    /**
    2548      * Hash of doctype names to doctype objects
     3398     * Hash of doctype names to doctype objects.
     3399     * @type array
    25493400     */
    25503401    protected $doctypes;
    25513402
    25523403    /**
    2553      * Lookup table of aliases to real doctype names
     3404     * Lookup table of aliases to real doctype names.
     3405     * @type array
    25543406     */
    25553407    protected $aliases;
     
    25593411     * @note Accepts a fully-formed doctype object, or the
    25603412     *       parameters for constructing a doctype object
    2561      * @param $doctype Name of doctype or literal doctype object
    2562      * @param $modules Modules doctype will load
    2563      * @param $modules_for_modes Modules doctype will load for certain modes
    2564      * @param $aliases Alias names for doctype
    2565      * @return Editable registered doctype
    2566      */
    2567     public function register($doctype, $xml = true, $modules = array(),
    2568         $tidy_modules = array(), $aliases = array(), $dtd_public = null, $dtd_system = null
     3413     * @param string $doctype Name of doctype or literal doctype object
     3414     * @param bool $xml
     3415     * @param array $modules Modules doctype will load
     3416     * @param array $tidy_modules Modules doctype will load for certain modes
     3417     * @param array $aliases Alias names for doctype
     3418     * @param string $dtd_public
     3419     * @param string $dtd_system
     3420     * @return HTMLPurifier_Doctype Editable registered doctype
     3421     */
     3422    public function register(
     3423        $doctype,
     3424        $xml = true,
     3425        $modules = array(),
     3426        $tidy_modules = array(),
     3427        $aliases = array(),
     3428        $dtd_public = null,
     3429        $dtd_system = null
    25693430    ) {
    2570         if (!is_array($modules)) $modules = array($modules);
    2571         if (!is_array($tidy_modules)) $tidy_modules = array($tidy_modules);
    2572         if (!is_array($aliases)) $aliases = array($aliases);
     3431        if (!is_array($modules)) {
     3432            $modules = array($modules);
     3433        }
     3434        if (!is_array($tidy_modules)) {
     3435            $tidy_modules = array($tidy_modules);
     3436        }
     3437        if (!is_array($aliases)) {
     3438            $aliases = array($aliases);
     3439        }
    25733440        if (!is_object($doctype)) {
    25743441            $doctype = new HTMLPurifier_Doctype(
    2575                 $doctype, $xml, $modules, $tidy_modules, $aliases, $dtd_public, $dtd_system
     3442                $doctype,
     3443                $xml,
     3444                $modules,
     3445                $tidy_modules,
     3446                $aliases,
     3447                $dtd_public,
     3448                $dtd_system
    25763449            );
    25773450        }
     
    25803453        // hookup aliases
    25813454        foreach ($doctype->aliases as $alias) {
    2582             if (isset($this->doctypes[$alias])) continue;
     3455            if (isset($this->doctypes[$alias])) {
     3456                continue;
     3457            }
    25833458            $this->aliases[$alias] = $name;
    25843459        }
    25853460        // remove old aliases
    2586         if (isset($this->aliases[$name])) unset($this->aliases[$name]);
     3461        if (isset($this->aliases[$name])) {
     3462            unset($this->aliases[$name]);
     3463        }
    25873464        return $doctype;
    25883465    }
     
    25923469     * @note This function resolves aliases
    25933470     * @note When possible, use the more fully-featured make()
    2594      * @param $doctype Name of doctype
    2595      * @return Editable doctype object
    2596      */
    2597     public function get($doctype) {
    2598         if (isset($this->aliases[$doctype])) $doctype = $this->aliases[$doctype];
     3471     * @param string $doctype Name of doctype
     3472     * @return HTMLPurifier_Doctype Editable doctype object
     3473     */
     3474    public function get($doctype)
     3475    {
     3476        if (isset($this->aliases[$doctype])) {
     3477            $doctype = $this->aliases[$doctype];
     3478        }
    25993479        if (!isset($this->doctypes[$doctype])) {
    26003480            trigger_error('Doctype ' . htmlspecialchars($doctype) . ' does not exist', E_USER_ERROR);
     
    26123492     *       Generator whether or not the current document is XML
    26133493     *       based or not).
    2614      */
    2615     public function make($config) {
     3494     * @param HTMLPurifier_Config $config
     3495     * @return HTMLPurifier_Doctype
     3496     */
     3497    public function make($config)
     3498    {
    26163499        return clone $this->get($this->getDoctypeFromConfig($config));
    26173500    }
     
    26193502    /**
    26203503     * Retrieves the doctype from the configuration object
    2621      */
    2622     public function getDoctypeFromConfig($config) {
     3504     * @param HTMLPurifier_Config $config
     3505     * @return string
     3506     */
     3507    public function getDoctypeFromConfig($config)
     3508    {
    26233509        // recommended test
    26243510        $doctype = $config->get('HTML.Doctype');
    2625         if (!empty($doctype)) return $doctype;
     3511        if (!empty($doctype)) {
     3512            return $doctype;
     3513        }
    26263514        $doctype = $config->get('HTML.CustomDoctype');
    2627         if (!empty($doctype)) return $doctype;
     3515        if (!empty($doctype)) {
     3516            return $doctype;
     3517        }
    26283518        // backwards-compatibility
    26293519        if ($config->get('HTML.XHTML')) {
     
    26393529        return $doctype;
    26403530    }
    2641 
    26423531}
    26433532
     
    26563545class HTMLPurifier_ElementDef
    26573546{
    2658 
    26593547    /**
    26603548     * Does the definition work by itself, or is it created solely
    26613549     * for the purpose of merging into another definition?
     3550     * @type bool
    26623551     */
    26633552    public $standalone = true;
    26643553
    26653554    /**
    2666      * Associative array of attribute name to HTMLPurifier_AttrDef
     3555     * Associative array of attribute name to HTMLPurifier_AttrDef.
     3556     * @type array
    26673557     * @note Before being processed by HTMLPurifier_AttrCollections
    26683558     *       when modules are finalized during
     
    26763566    public $attr = array();
    26773567
    2678     /**
    2679      * Indexed list of tag's HTMLPurifier_AttrTransform to be done before validation
     3568    // XXX: Design note: currently, it's not possible to override
     3569    // previously defined AttrTransforms without messing around with
     3570    // the final generated config. This is by design; a previous version
     3571    // used an associated list of attr_transform, but it was extremely
     3572    // easy to accidentally override other attribute transforms by
     3573    // forgetting to specify an index (and just using 0.)  While we
     3574    // could check this by checking the index number and complaining,
     3575    // there is a second problem which is that it is not at all easy to
     3576    // tell when something is getting overridden. Combine this with a
     3577    // codebase where this isn't really being used, and it's perfect for
     3578    // nuking.
     3579
     3580    /**
     3581     * List of tags HTMLPurifier_AttrTransform to be done before validation.
     3582     * @type array
    26803583     */
    26813584    public $attr_transform_pre = array();
    26823585
    26833586    /**
    2684      * Indexed list of tag's HTMLPurifier_AttrTransform to be done after validation
     3587     * List of tags HTMLPurifier_AttrTransform to be done after validation.
     3588     * @type array
    26853589     */
    26863590    public $attr_transform_post = array();
     
    26883592    /**
    26893593     * HTMLPurifier_ChildDef of this tag.
     3594     * @type HTMLPurifier_ChildDef
    26903595     */
    26913596    public $child;
    26923597
    26933598    /**
    2694      * Abstract string representation of internal ChildDef rules. See
    2695      * HTMLPurifier_ContentSets for how this is parsed and then transformed
     3599     * Abstract string representation of internal ChildDef rules.
     3600     * @see HTMLPurifier_ContentSets for how this is parsed and then transformed
    26963601     * into an HTMLPurifier_ChildDef.
    26973602     * @warning This is a temporary variable that is not available after
    26983603     *      being processed by HTMLDefinition
     3604     * @type string
    26993605     */
    27003606    public $content_model;
     
    27063612     * @warning This is a temporary variable that is not available after
    27073613     *      being processed by HTMLDefinition
     3614     * @type string
    27083615     */
    27093616    public $content_model_type;
    2710 
    2711 
    27123617
    27133618    /**
     
    27163621     * HTMLPurifier_ChildDef_Chameleon. Dynamically set: modules don't
    27173622     * have to worry about this one.
     3623     * @type bool
    27183624     */
    27193625    public $descendants_are_inline = false;
    27203626
    27213627    /**
    2722      * List of the names of required attributes this element has. Dynamically
    2723      * populated by HTMLPurifier_HTMLDefinition::getElement
     3628     * List of the names of required attributes this element has.
     3629     * Dynamically populated by HTMLPurifier_HTMLDefinition::getElement()
     3630     * @type array
    27243631     */
    27253632    public $required_attr = array();
     
    27273634    /**
    27283635     * Lookup table of tags excluded from all descendants of this tag.
     3636     * @type array
    27293637     * @note SGML permits exclusions for all descendants, but this is
    27303638     *       not possible with DTDs or XML Schemas. W3C has elected to
     
    27403648    /**
    27413649     * This tag is explicitly auto-closed by the following tags.
     3650     * @type array
    27423651     */
    27433652    public $autoclose = array();
     
    27473656     * allowed by this sub-element; if it is, instead of closing the
    27483657     * current element, place it inside this element.
     3658     * @type string
    27493659     */
    27503660    public $wrap;
     
    27533663     * Whether or not this is a formatting element affected by the
    27543664     * "Active Formatting Elements" algorithm.
     3665     * @type bool
    27553666     */
    27563667    public $formatting;
     
    27593670     * Low-level factory constructor for creating new standalone element defs
    27603671     */
    2761     public static function create($content_model, $content_model_type, $attr) {
     3672    public static function create($content_model, $content_model_type, $attr)
     3673    {
    27623674        $def = new HTMLPurifier_ElementDef();
    27633675        $def->content_model = $content_model;
     
    27713683     * Values from the new element def take precedence if a value is
    27723684     * not mergeable.
    2773      */
    2774     public function mergeIn($def) {
    2775 
     3685     * @param HTMLPurifier_ElementDef $def
     3686     */
     3687    public function mergeIn($def)
     3688    {
    27763689        // later keys takes precedence
    2777         foreach($def->attr as $k => $v) {
     3690        foreach ($def->attr as $k => $v) {
    27783691            if ($k === 0) {
    27793692                // merge in the includes
     
    27853698            }
    27863699            if ($v === false) {
    2787                 if (isset($this->attr[$k])) unset($this->attr[$k]);
     3700                if (isset($this->attr[$k])) {
     3701                    unset($this->attr[$k]);
     3702                }
    27883703                continue;
    27893704            }
    27903705            $this->attr[$k] = $v;
    27913706        }
    2792         $this->_mergeAssocArray($this->attr_transform_pre, $def->attr_transform_pre);
    2793         $this->_mergeAssocArray($this->attr_transform_post, $def->attr_transform_post);
    27943707        $this->_mergeAssocArray($this->excludes, $def->excludes);
    2795 
    2796         if(!empty($def->content_model)) {
     3708        $this->attr_transform_pre = array_merge($this->attr_transform_pre, $def->attr_transform_pre);
     3709        $this->attr_transform_post = array_merge($this->attr_transform_post, $def->attr_transform_post);
     3710
     3711        if (!empty($def->content_model)) {
    27973712            $this->content_model =
    27983713                str_replace("#SUPER", $this->content_model, $def->content_model);
    27993714            $this->child = false;
    28003715        }
    2801         if(!empty($def->content_model_type)) {
     3716        if (!empty($def->content_model_type)) {
    28023717            $this->content_model_type = $def->content_model_type;
    28033718            $this->child = false;
    28043719        }
    2805         if(!is_null($def->child)) $this->child = $def->child;
    2806         if(!is_null($def->formatting)) $this->formatting = $def->formatting;
    2807         if($def->descendants_are_inline) $this->descendants_are_inline = $def->descendants_are_inline;
    2808 
     3720        if (!is_null($def->child)) {
     3721            $this->child = $def->child;
     3722        }
     3723        if (!is_null($def->formatting)) {
     3724            $this->formatting = $def->formatting;
     3725        }
     3726        if ($def->descendants_are_inline) {
     3727            $this->descendants_are_inline = $def->descendants_are_inline;
     3728        }
    28093729    }
    28103730
     
    28143734     * @param $a2 Array that merges into $a1
    28153735     */
    2816     private function _mergeAssocArray(&$a1, $a2) {
     3736    private function _mergeAssocArray(&$a1, $a2)
     3737    {
    28173738        foreach ($a2 as $k => $v) {
    28183739            if ($v === false) {
    2819                 if (isset($a1[$k])) unset($a1[$k]);
     3740                if (isset($a1[$k])) {
     3741                    unset($a1[$k]);
     3742                }
    28203743                continue;
    28213744            }
     
    28233746        }
    28243747    }
    2825 
    28263748}
    28273749
     
    28403762     * Constructor throws fatal error if you attempt to instantiate class
    28413763     */
    2842     private function __construct() {
     3764    private function __construct()
     3765    {
    28433766        trigger_error('Cannot instantiate encoder, call methods statically', E_USER_ERROR);
    28443767    }
     
    28473770     * Error-handler that mutes errors, alternative to shut-up operator.
    28483771     */
    2849     public static function muteErrorHandler() {}
     3772    public static function muteErrorHandler()
     3773    {
     3774    }
     3775
     3776    /**
     3777     * iconv wrapper which mutes errors, but doesn't work around bugs.
     3778     * @param string $in Input encoding
     3779     * @param string $out Output encoding
     3780     * @param string $text The text to convert
     3781     * @return string
     3782     */
     3783    public static function unsafeIconv($in, $out, $text)
     3784    {
     3785        set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
     3786        $r = iconv($in, $out, $text);
     3787        restore_error_handler();
     3788        return $r;
     3789    }
     3790
     3791    /**
     3792     * iconv wrapper which mutes errors and works around bugs.
     3793     * @param string $in Input encoding
     3794     * @param string $out Output encoding
     3795     * @param string $text The text to convert
     3796     * @param int $max_chunk_size
     3797     * @return string
     3798     */
     3799    public static function iconv($in, $out, $text, $max_chunk_size = 8000)
     3800    {
     3801        $code = self::testIconvTruncateBug();
     3802        if ($code == self::ICONV_OK) {
     3803            return self::unsafeIconv($in, $out, $text);
     3804        } elseif ($code == self::ICONV_TRUNCATES) {
     3805            // we can only work around this if the input character set
     3806            // is utf-8
     3807            if ($in == 'utf-8') {
     3808                if ($max_chunk_size < 4) {
     3809                    trigger_error('max_chunk_size is too small', E_USER_WARNING);
     3810                    return false;
     3811                }
     3812                // split into 8000 byte chunks, but be careful to handle
     3813                // multibyte boundaries properly
     3814                if (($c = strlen($text)) <= $max_chunk_size) {
     3815                    return self::unsafeIconv($in, $out, $text);
     3816                }
     3817                $r = '';
     3818                $i = 0;
     3819                while (true) {
     3820                    if ($i + $max_chunk_size >= $c) {
     3821                        $r .= self::unsafeIconv($in, $out, substr($text, $i));
     3822                        break;
     3823                    }
     3824                    // wibble the boundary
     3825                    if (0x80 != (0xC0 & ord($text[$i + $max_chunk_size]))) {
     3826                        $chunk_size = $max_chunk_size;
     3827                    } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 1]))) {
     3828                        $chunk_size = $max_chunk_size - 1;
     3829                    } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 2]))) {
     3830                        $chunk_size = $max_chunk_size - 2;
     3831                    } elseif (0x80 != (0xC0 & ord($text[$i + $max_chunk_size - 3]))) {
     3832                        $chunk_size = $max_chunk_size - 3;
     3833                    } else {
     3834                        return false; // rather confusing UTF-8...
     3835                    }
     3836                    $chunk = substr($text, $i, $chunk_size); // substr doesn't mind overlong lengths
     3837                    $r .= self::unsafeIconv($in, $out, $chunk);
     3838                    $i += $chunk_size;
     3839                }
     3840                return $r;
     3841            } else {
     3842                return false;
     3843            }
     3844        } else {
     3845            return false;
     3846        }
     3847    }
    28503848
    28513849    /**
     
    28543852     * It will parse according to UTF-8 and return a valid UTF8 string, with
    28553853     * non-SGML codepoints excluded.
     3854     *
     3855     * @param string $str The string to clean
     3856     * @param bool $force_php
     3857     * @return string
    28563858     *
    28573859     * @note Just for reference, the non-SGML code points are 0 to 31 and
     
    28743876     *       Once again, PHP 6 should solve all our problems.
    28753877     */
    2876     public static function cleanUTF8($str, $force_php = false) {
    2877 
     3878    public static function cleanUTF8($str, $force_php = false)
     3879    {
    28783880        // UTF-8 validity is checked since PHP 4.3.5
    28793881        // This is an optimization: if the string is already valid UTF-8, no
     
    28813883        // The regexp matches the XML char production, as well as well as excluding
    28823884        // non-SGML codepoints U+007F to U+009F
    2883         if (preg_match('/^[\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]*$/Du', $str)) {
     3885        if (preg_match(
     3886            '/^[\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]*$/Du',
     3887            $str
     3888        )) {
    28843889            return $str;
    28853890        }
     
    29003905
    29013906        $len = strlen($str);
    2902         for($i = 0; $i < $len; $i++) {
     3907        for ($i = 0; $i < $len; $i++) {
    29033908            $in = ord($str{$i});
    29043909            $char .= $str[$i]; // append byte to char
     
    30534058    // +----------+----------+----------+----------+
    30544059
    3055     public static function unichr($code) {
    3056         if($code > 1114111 or $code < 0 or
     4060    public static function unichr($code)
     4061    {
     4062        if ($code > 1114111 or $code < 0 or
    30574063          ($code >= 55296 and $code <= 57343) ) {
    30584064            // bits are set outside the "valid" range as defined
     
    30724078            } else {
    30734079                $y = (($code & 4032) >> 6) | 128;
    3074                 if($code < 65536) {
     4080                if ($code < 65536) {
    30754081                    $z = (($code >> 12) & 15) | 224;
    30764082                } else {
     
    30824088        // set up the actual character
    30834089        $ret = '';
    3084         if($w) $ret .= chr($w);
    3085         if($z) $ret .= chr($z);
    3086         if($y) $ret .= chr($y);
     4090        if ($w) {
     4091            $ret .= chr($w);
     4092        }
     4093        if ($z) {
     4094            $ret .= chr($z);
     4095        }
     4096        if ($y) {
     4097            $ret .= chr($y);
     4098        }
    30874099        $ret .= chr($x);
    30884100
     
    30914103
    30924104    /**
    3093      * Converts a string to UTF-8 based on configuration.
    3094      */
    3095     public static function convertToUTF8($str, $config, $context) {
     4105     * @return bool
     4106     */
     4107    public static function iconvAvailable()
     4108    {
     4109        static $iconv = null;
     4110        if ($iconv === null) {
     4111            $iconv = function_exists('iconv') && self::testIconvTruncateBug() != self::ICONV_UNUSABLE;
     4112        }
     4113        return $iconv;
     4114    }
     4115
     4116    /**
     4117     * Convert a string to UTF-8 based on configuration.
     4118     * @param string $str The string to convert
     4119     * @param HTMLPurifier_Config $config
     4120     * @param HTMLPurifier_Context $context
     4121     * @return string
     4122     */
     4123    public static function convertToUTF8($str, $config, $context)
     4124    {
    30964125        $encoding = $config->get('Core.Encoding');
    3097         if ($encoding === 'utf-8') return $str;
     4126        if ($encoding === 'utf-8') {
     4127            return $str;
     4128        }
    30984129        static $iconv = null;
    3099         if ($iconv === null) $iconv = function_exists('iconv');
    3100         set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
     4130        if ($iconv === null) {
     4131            $iconv = self::iconvAvailable();
     4132        }
    31014133        if ($iconv && !$config->get('Test.ForceNoIconv')) {
    3102             $str = iconv($encoding, 'utf-8//IGNORE', $str);
     4134            // unaffected by bugs, since UTF-8 support all characters
     4135            $str = self::unsafeIconv($encoding, 'utf-8//IGNORE', $str);
    31034136            if ($str === false) {
    31044137                // $encoding is not a valid encoding
    3105                 restore_error_handler();
    31064138                trigger_error('Invalid encoding ' . $encoding, E_USER_ERROR);
    31074139                return '';
     
    31104142            // that doesn't support all of ASCII, convert the naughty
    31114143            // characters to their true byte-wise ASCII/UTF-8 equivalents.
    3112             $str = strtr($str, HTMLPurifier_Encoder::testEncodingSupportsASCII($encoding));
    3113             restore_error_handler();
     4144            $str = strtr($str, self::testEncodingSupportsASCII($encoding));
    31144145            return $str;
    31154146        } elseif ($encoding === 'iso-8859-1') {
    31164147            $str = utf8_encode($str);
    3117             restore_error_handler();
    31184148            return $str;
    31194149        }
    3120         trigger_error('Encoding not supported, please install iconv', E_USER_ERROR);
     4150        $bug = HTMLPurifier_Encoder::testIconvTruncateBug();
     4151        if ($bug == self::ICONV_OK) {
     4152            trigger_error('Encoding not supported, please install iconv', E_USER_ERROR);
     4153        } else {
     4154            trigger_error(
     4155                'You have a buggy version of iconv, see https://bugs.php.net/bug.php?id=48147 ' .
     4156                'and http://sourceware.org/bugzilla/show_bug.cgi?id=13541',
     4157                E_USER_ERROR
     4158            );
     4159        }
    31214160    }
    31224161
    31234162    /**
    31244163     * Converts a string from UTF-8 based on configuration.
     4164     * @param string $str The string to convert
     4165     * @param HTMLPurifier_Config $config
     4166     * @param HTMLPurifier_Context $context
     4167     * @return string
    31254168     * @note Currently, this is a lossy conversion, with unexpressable
    31264169     *       characters being omitted.
    31274170     */
    3128     public static function convertFromUTF8($str, $config, $context) {
     4171    public static function convertFromUTF8($str, $config, $context)
     4172    {
    31294173        $encoding = $config->get('Core.Encoding');
    3130         if ($encoding === 'utf-8') return $str;
     4174        if ($escape = $config->get('Core.EscapeNonASCIICharacters')) {
     4175            $str = self::convertToASCIIDumbLossless($str);
     4176        }
     4177        if ($encoding === 'utf-8') {
     4178            return $str;
     4179        }
    31314180        static $iconv = null;
    3132         if ($iconv === null) $iconv = function_exists('iconv');
    3133         if ($escape = $config->get('Core.EscapeNonASCIICharacters')) {
    3134             $str = HTMLPurifier_Encoder::convertToASCIIDumbLossless($str);
    3135         }
    3136         set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
     4181        if ($iconv === null) {
     4182            $iconv = self::iconvAvailable();
     4183        }
    31374184        if ($iconv && !$config->get('Test.ForceNoIconv')) {
    31384185            // Undo our previous fix in convertToUTF8, otherwise iconv will barf
    3139             $ascii_fix = HTMLPurifier_Encoder::testEncodingSupportsASCII($encoding);
     4186            $ascii_fix = self::testEncodingSupportsASCII($encoding);
    31404187            if (!$escape && !empty($ascii_fix)) {
    31414188                $clear_fix = array();
    3142                 foreach ($ascii_fix as $utf8 => $native) $clear_fix[$utf8] = '';
     4189                foreach ($ascii_fix as $utf8 => $native) {
     4190                    $clear_fix[$utf8] = '';
     4191                }
    31434192                $str = strtr($str, $clear_fix);
    31444193            }
    31454194            $str = strtr($str, array_flip($ascii_fix));
    31464195            // Normal stuff
    3147             $str = iconv('utf-8', $encoding . '//IGNORE', $str);
    3148             restore_error_handler();
     4196            $str = self::iconv('utf-8', $encoding . '//IGNORE', $str);
    31494197            return $str;
    31504198        } elseif ($encoding === 'iso-8859-1') {
    31514199            $str = utf8_decode($str);
    3152             restore_error_handler();
    31534200            return $str;
    31544201        }
    31554202        trigger_error('Encoding not supported', E_USER_ERROR);
     4203        // You might be tempted to assume that the ASCII representation
     4204        // might be OK, however, this is *not* universally true over all
     4205        // encodings.  So we take the conservative route here, rather
     4206        // than forcibly turn on %Core.EscapeNonASCIICharacters
    31564207    }
    31574208
    31584209    /**
    31594210     * Lossless (character-wise) conversion of HTML to ASCII
    3160      * @param $str UTF-8 string to be converted to ASCII
    3161      * @returns ASCII encoded string with non-ASCII character entity-ized
     4211     * @param string $str UTF-8 string to be converted to ASCII
     4212     * @return string ASCII encoded string with non-ASCII character entity-ized
    31624213     * @warning Adapted from MediaWiki, claiming fair use: this is a common
    31634214     *       algorithm. If you disagree with this license fudgery,
     
    31724223     *       well-formed UTF-8
    31734224     */
    3174     public static function convertToASCIIDumbLossless($str) {
     4225    public static function convertToASCIIDumbLossless($str)
     4226    {
    31754227        $bytesleft = 0;
    31764228        $result = '';
    31774229        $working = 0;
    31784230        $len = strlen($str);
    3179         for( $i = 0; $i < $len; $i++ ) {
    3180             $bytevalue = ord( $str[$i] );
    3181             if( $bytevalue <= 0x7F ) { //0xxx xxxx
    3182                 $result .= chr( $bytevalue );
     4231        for ($i = 0; $i < $len; $i++) {
     4232            $bytevalue = ord($str[$i]);
     4233            if ($bytevalue <= 0x7F) { //0xxx xxxx
     4234                $result .= chr($bytevalue);
    31834235                $bytesleft = 0;
    3184             } elseif( $bytevalue <= 0xBF ) { //10xx xxxx
     4236            } elseif ($bytevalue <= 0xBF) { //10xx xxxx
    31854237                $working = $working << 6;
    31864238                $working += ($bytevalue & 0x3F);
    31874239                $bytesleft--;
    3188                 if( $bytesleft <= 0 ) {
     4240                if ($bytesleft <= 0) {
    31894241                    $result .= "&#" . $working . ";";
    31904242                }
    3191             } elseif( $bytevalue <= 0xDF ) { //110x xxxx
     4243            } elseif ($bytevalue <= 0xDF) { //110x xxxx
    31924244                $working = $bytevalue & 0x1F;
    31934245                $bytesleft = 1;
    3194             } elseif( $bytevalue <= 0xEF ) { //1110 xxxx
     4246            } elseif ($bytevalue <= 0xEF) { //1110 xxxx
    31954247                $working = $bytevalue & 0x0F;
    31964248                $bytesleft = 2;
     
    32014253        }
    32024254        return $result;
     4255    }
     4256
     4257    /** No bugs detected in iconv. */
     4258    const ICONV_OK = 0;
     4259
     4260    /** Iconv truncates output if converting from UTF-8 to another
     4261     *  character set with //IGNORE, and a non-encodable character is found */
     4262    const ICONV_TRUNCATES = 1;
     4263
     4264    /** Iconv does not support //IGNORE, making it unusable for
     4265     *  transcoding purposes */
     4266    const ICONV_UNUSABLE = 2;
     4267
     4268    /**
     4269     * glibc iconv has a known bug where it doesn't handle the magic
     4270     * //IGNORE stanza correctly.  In particular, rather than ignore
     4271     * characters, it will return an EILSEQ after consuming some number
     4272     * of characters, and expect you to restart iconv as if it were
     4273     * an E2BIG.  Old versions of PHP did not respect the errno, and
     4274     * returned the fragment, so as a result you would see iconv
     4275     * mysteriously truncating output. We can work around this by
     4276     * manually chopping our input into segments of about 8000
     4277     * characters, as long as PHP ignores the error code.  If PHP starts
     4278     * paying attention to the error code, iconv becomes unusable.
     4279     *
     4280     * @return int Error code indicating severity of bug.
     4281     */
     4282    public static function testIconvTruncateBug()
     4283    {
     4284        static $code = null;
     4285        if ($code === null) {
     4286            // better not use iconv, otherwise infinite loop!
     4287            $r = self::unsafeIconv('utf-8', 'ascii//IGNORE', "\xCE\xB1" . str_repeat('a', 9000));
     4288            if ($r === false) {
     4289                $code = self::ICONV_UNUSABLE;
     4290            } elseif (($c = strlen($r)) < 9000) {
     4291                $code = self::ICONV_TRUNCATES;
     4292            } elseif ($c > 9000) {
     4293                trigger_error(
     4294                    'Your copy of iconv is extremely buggy. Please notify HTML Purifier maintainers: ' .
     4295                    'include your iconv version as per phpversion()',
     4296                    E_USER_ERROR
     4297                );
     4298            } else {
     4299                $code = self::ICONV_OK;
     4300            }
     4301        }
     4302        return $code;
    32034303    }
    32044304
     
    32144314     *      which can be used to "undo" any overzealous iconv action.
    32154315     */
    3216     public static function testEncodingSupportsASCII($encoding, $bypass = false) {
     4316    public static function testEncodingSupportsASCII($encoding, $bypass = false)
     4317    {
     4318        // All calls to iconv here are unsafe, proof by case analysis:
     4319        // If ICONV_OK, no difference.
     4320        // If ICONV_TRUNCATE, all calls involve one character inputs,
     4321        // so bug is not triggered.
     4322        // If ICONV_UNUSABLE, this call is irrelevant
    32174323        static $encodings = array();
    32184324        if (!$bypass) {
    3219             if (isset($encodings[$encoding])) return $encodings[$encoding];
     4325            if (isset($encodings[$encoding])) {
     4326                return $encodings[$encoding];
     4327            }
    32204328            $lenc = strtolower($encoding);
    32214329            switch ($lenc) {
     
    32254333                    return array("\xE2\x82\xA9" => '\\');
    32264334            }
    3227             if (strpos($lenc, 'iso-8859-') === 0) return array();
     4335            if (strpos($lenc, 'iso-8859-') === 0) {
     4336                return array();
     4337            }
    32284338        }
    32294339        $ret = array();
    3230         set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler'));
    3231         if (iconv('UTF-8', $encoding, 'a') === false) return false;
     4340        if (self::unsafeIconv('UTF-8', $encoding, 'a') === false) {
     4341            return false;
     4342        }
    32324343        for ($i = 0x20; $i <= 0x7E; $i++) { // all printable ASCII chars
    32334344            $c = chr($i); // UTF-8 char
    3234             $r = iconv('UTF-8', "$encoding//IGNORE", $c); // initial conversion
    3235             if (
    3236                 $r === '' ||
     4345            $r = self::unsafeIconv('UTF-8', "$encoding//IGNORE", $c); // initial conversion
     4346            if ($r === '' ||
    32374347                // This line is needed for iconv implementations that do not
    32384348                // omit characters that do not exist in the target character set
    3239                 ($r === $c && iconv($encoding, 'UTF-8//IGNORE', $r) !== $c)
     4349                ($r === $c && self::unsafeIconv($encoding, 'UTF-8//IGNORE', $r) !== $c)
    32404350            ) {
    32414351                // Reverse engineer: what's the UTF-8 equiv of this byte
    32424352                // sequence? This assumes that there's no variable width
    32434353                // encoding that doesn't support ASCII.
    3244                 $ret[iconv($encoding, 'UTF-8//IGNORE', $c)] = $c;
    3245             }
    3246         }
    3247         restore_error_handler();
     4354                $ret[self::unsafeIconv($encoding, 'UTF-8//IGNORE', $c)] = $c;
     4355            }
     4356        }
    32484357        $encodings[$encoding] = $ret;
    32494358        return $ret;
    32504359    }
    3251 
    3252 
    32534360}
    32544361
     
    32604367 * Object that provides entity lookup table from entity name to character
    32614368 */
    3262 class HTMLPurifier_EntityLookup {
    3263 
     4369class HTMLPurifier_EntityLookup
     4370{
    32644371    /**
    32654372     * Assoc array of entity name to character represented.
     4373     * @type array
    32664374     */
    32674375    public $table;
     
    32694377    /**
    32704378     * Sets up the entity lookup table from the serialized file contents.
     4379     * @param bool $file
    32714380     * @note The serialized contents are versioned, but were generated
    32724381     *       using the maintenance script generate_entity_file.php
    32734382     * @warning This is not in constructor to help enforce the Singleton
    32744383     */
    3275     public function setup($file = false) {
     4384    public function setup($file = false)
     4385    {
    32764386        if (!$file) {
    32774387            $file = HTMLPURIFIER_PREFIX . '/HTMLPurifier/EntityLookup/entities.ser';
     
    32824392    /**
    32834393     * Retrieves sole instance of the object.
    3284      * @param Optional prototype of custom lookup table to overload with.
    3285      */
    3286     public static function instance($prototype = false) {
     4394     * @param bool|HTMLPurifier_EntityLookup $prototype Optional prototype of custom lookup table to overload with.
     4395     * @return HTMLPurifier_EntityLookup
     4396     */
     4397    public static function instance($prototype = false)
     4398    {
    32874399        // no references, since PHP doesn't copy unless modified
    32884400        static $instance = null;
     
    32954407        return $instance;
    32964408    }
    3297 
    32984409}
    32994410
     
    33144425    /**
    33154426     * Reference to entity lookup table.
     4427     * @type HTMLPurifier_EntityLookup
    33164428     */
    33174429    protected $_entity_lookup;
     
    33194431    /**
    33204432     * Callback regex string for parsing entities.
     4433     * @type string
    33214434     */
    33224435    protected $_substituteEntitiesRegex =
    3323 '/&(?:[#]x([a-fA-F0-9]+)|[#]0*(\d+)|([A-Za-z_:][A-Za-z0-9.\-_:]*));?/';
    3324 //     1. hex             2. dec      3. string (XML style)
    3325 
     4436        '/&(?:[#]x([a-fA-F0-9]+)|[#]0*(\d+)|([A-Za-z_:][A-Za-z0-9.\-_:]*));?/';
     4437        //     1. hex             2. dec      3. string (XML style)
    33264438
    33274439    /**
    33284440     * Decimal to parsed string conversion table for special entities.
     4441     * @type array
    33294442     */
    33304443    protected $_special_dec2str =
     
    33394452    /**
    33404453     * Stripped entity names to decimal conversion table for special entities.
     4454     * @type array
    33414455     */
    33424456    protected $_special_ent2dec =
     
    33534467     * it before everything else.
    33544468     *
    3355      * @param $string String to have non-special entities parsed.
    3356      * @returns Parsed string.
    3357      */
    3358     public function substituteNonSpecialEntities($string) {
     4469     * @param string $string String to have non-special entities parsed.
     4470     * @return string Parsed string.
     4471     */
     4472    public function substituteNonSpecialEntities($string)
     4473    {
    33594474        // it will try to detect missing semicolons, but don't rely on it
    33604475        return preg_replace_callback(
     
    33624477            array($this, 'nonSpecialEntityCallback'),
    33634478            $string
    3364             );
     4479        );
    33654480    }
    33664481
     
    33684483     * Callback function for substituteNonSpecialEntities() that does the work.
    33694484     *
    3370      * @param $matches  PCRE matches array, with 0 the entire match, and
     4485     * @param array $matches  PCRE matches array, with 0 the entire match, and
    33714486     *                  either index 1, 2 or 3 set with a hex value, dec value,
    33724487     *                  or string (respectively).
    3373      * @returns Replacement string.
    3374      */
    3375 
    3376     protected function nonSpecialEntityCallback($matches) {
     4488     * @return string Replacement string.
     4489     */
     4490
     4491    protected function nonSpecialEntityCallback($matches)
     4492    {
    33774493        // replaces all but big five
    33784494        $entity = $matches[0];
     
    33814497            $is_hex = (@$entity[2] === 'x');
    33824498            $code = $is_hex ? hexdec($matches[1]) : (int) $matches[2];
    3383 
    33844499            // abort for special characters
    3385             if (isset($this->_special_dec2str[$code]))  return $entity;
    3386 
     4500            if (isset($this->_special_dec2str[$code])) {
     4501                return $entity;
     4502            }
    33874503            return HTMLPurifier_Encoder::unichr($code);
    33884504        } else {
    3389             if (isset($this->_special_ent2dec[$matches[3]])) return $entity;
     4505            if (isset($this->_special_ent2dec[$matches[3]])) {
     4506                return $entity;
     4507            }
    33904508            if (!$this->_entity_lookup) {
    33914509                $this->_entity_lookup = HTMLPurifier_EntityLookup::instance();
     
    34054523     * would have to be called a lot (for every parsed section).
    34064524     *
    3407      * @param $string String to have non-special entities parsed.
    3408      * @returns Parsed string.
    3409      */
    3410     public function substituteSpecialEntities($string) {
     4525     * @param string $string String to have non-special entities parsed.
     4526     * @return string Parsed string.
     4527     */
     4528    public function substituteSpecialEntities($string)
     4529    {
    34114530        return preg_replace_callback(
    34124531            $this->_substituteEntitiesRegex,
    34134532            array($this, 'specialEntityCallback'),
    3414             $string);
     4533            $string
     4534        );
    34154535    }
    34164536
     
    34204540     * This callback has same syntax as nonSpecialEntityCallback().
    34214541     *
    3422      * @param $matches  PCRE-style matches array, with 0 the entire match, and
     4542     * @param array $matches  PCRE-style matches array, with 0 the entire match, and
    34234543     *                  either index 1, 2 or 3 set with a hex value, dec value,
    34244544     *                  or string (respectively).
    3425      * @returns Replacement string.
    3426      */
    3427     protected function specialEntityCallback($matches) {
     4545     * @return string Replacement string.
     4546     */
     4547    protected function specialEntityCallback($matches)
     4548    {
    34284549        $entity = $matches[0];
    34294550        $is_num = (@$matches[0][1] === '#');
     
    34404561        }
    34414562    }
    3442 
    34434563}
    34444564
     
    34634583    const CHILDREN = 3;
    34644584
     4585    /**
     4586     * @type array
     4587     */
    34654588    protected $errors;
     4589
     4590    /**
     4591     * @type array
     4592     */
    34664593    protected $_current;
     4594
     4595    /**
     4596     * @type array
     4597     */
    34674598    protected $_stacks = array(array());
     4599
     4600    /**
     4601     * @type HTMLPurifier_Language
     4602     */
    34684603    protected $locale;
     4604
     4605    /**
     4606     * @type HTMLPurifier_Generator
     4607     */
    34694608    protected $generator;
     4609
     4610    /**
     4611     * @type HTMLPurifier_Context
     4612     */
    34704613    protected $context;
    34714614
     4615    /**
     4616     * @type array
     4617     */
    34724618    protected $lines = array();
    34734619
    3474     public function __construct($context) {
     4620    /**
     4621     * @param HTMLPurifier_Context $context
     4622     */
     4623    public function __construct($context)
     4624    {
    34754625        $this->locale    =& $context->get('Locale');
    34764626        $this->context   = $context;
     
    34814631    /**
    34824632     * Sends an error message to the collector for later use
    3483      * @param $severity int Error severity, PHP error style (don't use E_USER_)
    3484      * @param $msg string Error message text
    3485      * @param $subst1 string First substitution for $msg
    3486      * @param $subst2 string ...
    3487      */
    3488     public function send($severity, $msg) {
    3489 
     4633     * @param int $severity Error severity, PHP error style (don't use E_USER_)
     4634     * @param string $msg Error message text
     4635     */
     4636    public function send($severity, $msg)
     4637    {
    34904638        $args = array();
    34914639        if (func_num_args() > 2) {
     
    34974645        $token = $this->context->get('CurrentToken', true);
    34984646        $line  = $token ? $token->line : $this->context->get('CurrentLine', true);
    3499         $col   = $token ? $token->col  : $this->context->get('CurrentCol',  true);
     4647        $col   = $token ? $token->col  : $this->context->get('CurrentCol', true);
    35004648        $attr  = $this->context->get('CurrentAttr', true);
    35014649
     
    35074655        if (!is_null($attr)) {
    35084656            $subst['$CurrentAttr.Name'] = $attr;
    3509             if (isset($token->attr[$attr])) $subst['$CurrentAttr.Value'] = $token->attr[$attr];
     4657            if (isset($token->attr[$attr])) {
     4658                $subst['$CurrentAttr.Value'] = $token->attr[$attr];
     4659            }
    35104660        }
    35114661
     
    35164666        }
    35174667
    3518         if (!empty($subst)) $msg = strtr($msg, $subst);
     4668        if (!empty($subst)) {
     4669            $msg = strtr($msg, $subst);
     4670        }
    35194671
    35204672        // (numerically indexed)
     
    35274679        $this->_current[] = $error;
    35284680
    3529 
    35304681        // NEW CODE BELOW ...
    3531 
    3532         $struct = null;
    35334682        // Top-level errors are either:
    35344683        //  TOKEN type, if $value is set appropriately, or
     
    35364685        $new_struct = new HTMLPurifier_ErrorStruct();
    35374686        $new_struct->type = HTMLPurifier_ErrorStruct::TOKEN;
    3538         if ($token) $new_struct->value = clone $token;
     4687        if ($token) {
     4688            $new_struct->value = clone $token;
     4689        }
    35394690        if (is_int($line) && is_int($col)) {
    35404691            if (isset($this->lines[$line][$col])) {
     
    35754726    /**
    35764727     * Retrieves raw error data for custom formatter to use
    3577      * @param List of arrays in format of array(line of error,
    3578      *        error severity, error message,
    3579      *        recursive sub-errors array)
    3580      */
    3581     public function getRaw() {
     4728     */
     4729    public function getRaw()
     4730    {
    35824731        return $this->errors;
    35834732    }
     
    35854734    /**
    35864735     * Default HTML formatting implementation for error messages
    3587      * @param $config Configuration array, vital for HTML output nature
    3588      * @param $errors Errors array to display; used for recursion.
    3589      */
    3590     public function getHTMLFormatted($config, $errors = null) {
     4736     * @param HTMLPurifier_Config $config Configuration, vital for HTML output nature
     4737     * @param array $errors Errors array to display; used for recursion.
     4738     * @return string
     4739     */
     4740    public function getHTMLFormatted($config, $errors = null)
     4741    {
    35914742        $ret = array();
    35924743
    35934744        $this->generator = new HTMLPurifier_Generator($config, $this->context);
    3594         if ($errors === null) $errors = $this->errors;
     4745        if ($errors === null) {
     4746            $errors = $this->errors;
     4747        }
    35954748
    35964749        // 'At line' message needs to be removed
     
    35984751        // generation code for new structure goes here. It needs to be recursive.
    35994752        foreach ($this->lines as $line => $col_array) {
    3600             if ($line == -1) continue;
     4753            if ($line == -1) {
     4754                continue;
     4755            }
    36014756            foreach ($col_array as $col => $struct) {
    36024757                $this->_renderStruct($ret, $struct, $line, $col);
     
    36154770    }
    36164771
    3617     private function _renderStruct(&$ret, $struct, $line = null, $col = null) {
     4772    private function _renderStruct(&$ret, $struct, $line = null, $col = null)
     4773    {
    36184774        $stack = array($struct);
    36194775        $context_stack = array(array());
     
    36414797                $ret[] = $string;
    36424798            }
    3643             foreach ($current->children as $type => $array) {
     4799            foreach ($current->children as $array) {
    36444800                $context[] = $current;
    36454801                $stack = array_merge($stack, array_reverse($array, true));
     
    36504806        }
    36514807    }
    3652 
    36534808}
    36544809
     
    36764831    /**
    36774832     * Type of this struct.
     4833     * @type string
    36784834     */
    36794835    public $type;
     
    36854841     *  - ATTR: array('attr-name', 'value')
    36864842     *  - CSSPROP: array('prop-name', 'value')
     4843     * @type mixed
    36874844     */
    36884845    public $value;
     
    36904847    /**
    36914848     * Errors registered for this structure.
     4849     * @type array
    36924850     */
    36934851    public $errors = array();
     
    36974855     * ErrorStruct would contain ATTR ErrorStructs. This is a multi-dimensional
    36984856     * array in structure: [TYPE]['identifier']
     4857     * @type array
    36994858     */
    37004859    public $children = array();
    37014860
    3702     public function getChild($type, $id) {
     4861    /**
     4862     * @param string $type
     4863     * @param string $id
     4864     * @return mixed
     4865     */
     4866    public function getChild($type, $id)
     4867    {
    37034868        if (!isset($this->children[$type][$id])) {
    37044869            $this->children[$type][$id] = new HTMLPurifier_ErrorStruct();
     
    37084873    }
    37094874
    3710     public function addError($severity, $message) {
     4875    /**
     4876     * @param int $severity
     4877     * @param string $message
     4878     */
     4879    public function addError($severity, $message)
     4880    {
    37114881        $this->errors[] = array($severity, $message);
    37124882    }
    3713 
    37144883}
    37154884
     
    37544923
    37554924    /**
    3756      * Name of the filter for identification purposes
     4925     * Name of the filter for identification purposes.
     4926     * @type string
    37574927     */
    37584928    public $name;
     
    37604930    /**
    37614931     * Pre-processor function, handles HTML before HTML Purifier
    3762      */
    3763     public function preFilter($html, $config, $context) {
     4932     * @param string $html
     4933     * @param HTMLPurifier_Config $config
     4934     * @param HTMLPurifier_Context $context
     4935     * @return string
     4936     */
     4937    public function preFilter($html, $config, $context)
     4938    {
    37644939        return $html;
    37654940    }
     
    37674942    /**
    37684943     * Post-processor function, handles HTML after HTML Purifier
    3769      */
    3770     public function postFilter($html, $config, $context) {
     4944     * @param string $html
     4945     * @param HTMLPurifier_Config $config
     4946     * @param HTMLPurifier_Context $context
     4947     * @return string
     4948     */
     4949    public function postFilter($html, $config, $context)
     4950    {
    37714951        return $html;
    37724952    }
    3773 
    37744953}
    37754954
     
    37894968
    37904969    /**
    3791      * Whether or not generator should produce XML output
     4970     * Whether or not generator should produce XML output.
     4971     * @type bool
    37924972     */
    37934973    private $_xhtml = true;
    37944974
    37954975    /**
    3796      * :HACK: Whether or not generator should comment the insides of <script> tags
     4976     * :HACK: Whether or not generator should comment the insides of <script> tags.
     4977     * @type bool
    37974978     */
    37984979    private $_scriptFix = false;
     
    38014982     * Cache of HTMLDefinition during HTML output to determine whether or
    38024983     * not attributes should be minimized.
     4984     * @type HTMLPurifier_HTMLDefinition
    38034985     */
    38044986    private $_def;
    38054987
    38064988    /**
    3807      * Cache of %Output.SortAttr
     4989     * Cache of %Output.SortAttr.
     4990     * @type bool
    38084991     */
    38094992    private $_sortAttr;
    38104993
    38114994    /**
    3812      * Cache of %Output.FlashCompat
     4995     * Cache of %Output.FlashCompat.
     4996     * @type bool
    38134997     */
    38144998    private $_flashCompat;
     4999
     5000    /**
     5001     * Cache of %Output.FixInnerHTML.
     5002     * @type bool
     5003     */
     5004    private $_innerHTMLFix;
    38155005
    38165006    /**
    38175007     * Stack for keeping track of object information when outputting IE
    38185008     * compatibility code.
     5009     * @type array
    38195010     */
    38205011    private $_flashStack = array();
     
    38225013    /**
    38235014     * Configuration for the generator
     5015     * @type HTMLPurifier_Config
    38245016     */
    38255017    protected $config;
    38265018
    38275019    /**
    3828      * @param $config Instance of HTMLPurifier_Config
    3829      * @param $context Instance of HTMLPurifier_Context
    3830      */
    3831     public function __construct($config, $context) {
     5020     * @param HTMLPurifier_Config $config
     5021     * @param HTMLPurifier_Context $context
     5022     */
     5023    public function __construct($config, $context)
     5024    {
    38325025        $this->config = $config;
    38335026        $this->_scriptFix = $config->get('Output.CommentScriptContents');
     5027        $this->_innerHTMLFix = $config->get('Output.FixInnerHTML');
    38345028        $this->_sortAttr = $config->get('Output.SortAttr');
    38355029        $this->_flashCompat = $config->get('Output.FlashCompat');
     
    38405034    /**
    38415035     * Generates HTML from an array of tokens.
    3842      * @param $tokens Array of HTMLPurifier_Token
    3843      * @param $config HTMLPurifier_Config object
    3844      * @return Generated HTML
    3845      */
    3846     public function generateFromTokens($tokens) {
    3847         if (!$tokens) return '';
     5036     * @param HTMLPurifier_Token[] $tokens Array of HTMLPurifier_Token
     5037     * @return string Generated HTML
     5038     */
     5039    public function generateFromTokens($tokens)
     5040    {
     5041        if (!$tokens) {
     5042            return '';
     5043        }
    38485044
    38495045        // Basic algorithm
     
    38645060        if (extension_loaded('tidy') && $this->config->get('Output.TidyFormat')) {
    38655061            $tidy = new Tidy;
    3866             $tidy->parseString($html, array(
    3867                'indent'=> true,
    3868                'output-xhtml' => $this->_xhtml,
    3869                'show-body-only' => true,
    3870                'indent-spaces' => 2,
    3871                'wrap' => 68,
    3872             ), 'utf8');
     5062            $tidy->parseString(
     5063                $html,
     5064                array(
     5065                   'indent'=> true,
     5066                   'output-xhtml' => $this->_xhtml,
     5067                   'show-body-only' => true,
     5068                   'indent-spaces' => 2,
     5069                   'wrap' => 68,
     5070                ),
     5071                'utf8'
     5072            );
    38735073            $tidy->cleanRepair();
    38745074            $html = (string) $tidy; // explicit cast necessary
     
    38785078        if ($this->config->get('Core.NormalizeNewlines')) {
    38795079            $nl = $this->config->get('Output.Newline');
    3880             if ($nl === null) $nl = PHP_EOL;
    3881             if ($nl !== "\n") $html = str_replace("\n", $nl, $html);
     5080            if ($nl === null) {
     5081                $nl = PHP_EOL;
     5082            }
     5083            if ($nl !== "\n") {
     5084                $html = str_replace("\n", $nl, $html);
     5085            }
    38825086        }
    38835087        return $html;
     
    38865090    /**
    38875091     * Generates HTML from a single token.
    3888      * @param $token HTMLPurifier_Token object.
    3889      * @return Generated HTML
    3890      */
    3891     public function generateFromToken($token) {
     5092     * @param HTMLPurifier_Token $token HTMLPurifier_Token object.
     5093     * @return string Generated HTML
     5094     */
     5095    public function generateFromToken($token)
     5096    {
    38925097        if (!$token instanceof HTMLPurifier_Token) {
    38935098            trigger_error('Cannot generate HTML from non-HTMLPurifier_Token object', E_USER_WARNING);
     
    39105115            if ($this->_flashCompat) {
    39115116                if ($token->name == "object" && !empty($this->_flashStack)) {
    3912                     $flash = array_pop($this->_flashStack);
    3913                     $compat_token = new HTMLPurifier_Token_Empty("embed");
    3914                     foreach ($flash->attr as $name => $val) {
    3915                         if ($name == "classid") continue;
    3916                         if ($name == "type") continue;
    3917                         if ($name == "data") $name = "src";
    3918                         $compat_token->attr[$name] = $val;
    3919                     }
    3920                     foreach ($flash->param as $name => $val) {
    3921                         if ($name == "movie") $name = "src";
    3922                         $compat_token->attr[$name] = $val;
    3923                     }
    3924                     $_extra = "<!--[if IE]>".$this->generateFromToken($compat_token)."<![endif]-->";
     5117                    // doesn't do anything for now
    39255118                }
    39265119            }
     
    39495142    /**
    39505143     * Special case processor for the contents of script tags
     5144     * @param HTMLPurifier_Token $token HTMLPurifier_Token object.
     5145     * @return string
    39515146     * @warning This runs into problems if there's already a literal
    39525147     *          --> somewhere inside the script contents.
    39535148     */
    3954     public function generateScriptFromToken($token) {
    3955         if (!$token instanceof HTMLPurifier_Token_Text) return $this->generateFromToken($token);
     5149    public function generateScriptFromToken($token)
     5150    {
     5151        if (!$token instanceof HTMLPurifier_Token_Text) {
     5152            return $this->generateFromToken($token);
     5153        }
    39565154        // Thanks <http://lachy.id.au/log/2005/05/script-comments>
    39575155        $data = preg_replace('#//\s*$#', '', $token->data);
     
    39625160     * Generates attribute declarations from attribute array.
    39635161     * @note This does not include the leading or trailing space.
    3964      * @param $assoc_array_of_attributes Attribute array
    3965      * @param $element Name of element attributes are for, used to check
     5162     * @param array $assoc_array_of_attributes Attribute array
     5163     * @param string $element Name of element attributes are for, used to check
    39665164     *        attribute minimization.
    3967      * @return Generate HTML fragment for insertion.
    3968      */
    3969     public function generateAttributes($assoc_array_of_attributes, $element = false) {
     5165     * @return string Generated HTML fragment for insertion.
     5166     */
     5167    public function generateAttributes($assoc_array_of_attributes, $element = '')
     5168    {
    39705169        $html = '';
    3971         if ($this->_sortAttr) ksort($assoc_array_of_attributes);
     5170        if ($this->_sortAttr) {
     5171            ksort($assoc_array_of_attributes);
     5172        }
    39725173        foreach ($assoc_array_of_attributes as $key => $value) {
    39735174            if (!$this->_xhtml) {
    39745175                // Remove namespaced attributes
    3975                 if (strpos($key, ':') !== false) continue;
     5176                if (strpos($key, ':') !== false) {
     5177                    continue;
     5178                }
    39765179                // Check if we should minimize the attribute: val="val" -> val
    39775180                if ($element && !empty($this->_def->info[$element]->attr[$key]->minimized)) {
     
    39805183                }
    39815184            }
     5185            // Workaround for Internet Explorer innerHTML bug.
     5186            // Essentially, Internet Explorer, when calculating
     5187            // innerHTML, omits quotes if there are no instances of
     5188            // angled brackets, quotes or spaces.  However, when parsing
     5189            // HTML (for example, when you assign to innerHTML), it
     5190            // treats backticks as quotes.  Thus,
     5191            //      <img alt="``" />
     5192            // becomes
     5193            //      <img alt=`` />
     5194            // becomes
     5195            //      <img alt='' />
     5196            // Fortunately, all we need to do is trigger an appropriate
     5197            // quoting style, which we do by adding an extra space.
     5198            // This also is consistent with the W3C spec, which states
     5199            // that user agents may ignore leading or trailing
     5200            // whitespace (in fact, most don't, at least for attributes
     5201            // like alt, but an extra space at the end is barely
     5202            // noticeable).  Still, we have a configuration knob for
     5203            // this, since this transformation is not necesary if you
     5204            // don't process user input with innerHTML or you don't plan
     5205            // on supporting Internet Explorer.
     5206            if ($this->_innerHTMLFix) {
     5207                if (strpos($value, '`') !== false) {
     5208                    // check if correct quoting style would not already be
     5209                    // triggered
     5210                    if (strcspn($value, '"\' <>') === strlen($value)) {
     5211                        // protect!
     5212                        $value .= ' ';
     5213                    }
     5214                }
     5215            }
    39825216            $html .= $key.'="'.$this->escape($value).'" ';
    39835217        }
     
    39905224     *       for properly generating HTML here w/o using tokens, it stays
    39915225     *       public.
    3992      * @param $string String data to escape for HTML.
    3993      * @param $quote Quoting style, like htmlspecialchars. ENT_NOQUOTES is
     5226     * @param string $string String data to escape for HTML.
     5227     * @param int $quote Quoting style, like htmlspecialchars. ENT_NOQUOTES is
    39945228     *               permissible for non-attribute output.
    3995      * @return String escaped data.
    3996      */
    3997     public function escape($string, $quote = null) {
     5229     * @return string escaped data.
     5230     */
     5231    public function escape($string, $quote = null)
     5232    {
    39985233        // Workaround for APC bug on Mac Leopard reported by sidepodcast
    39995234        // http://htmlpurifier.org/phorum/read.php?3,4823,4846
    4000         if ($quote === null) $quote = ENT_COMPAT;
     5235        if ($quote === null) {
     5236            $quote = ENT_COMPAT;
     5237        }
    40015238        return htmlspecialchars($string, $quote, 'UTF-8');
    40025239    }
    4003 
    40045240}
    40055241
     
    40375273
    40385274    /**
    4039      * Associative array of element names to HTMLPurifier_ElementDef
     5275     * Associative array of element names to HTMLPurifier_ElementDef.
     5276     * @type HTMLPurifier_ElementDef[]
    40405277     */
    40415278    public $info = array();
     
    40435280    /**
    40445281     * Associative array of global attribute name to attribute definition.
     5282     * @type array
    40455283     */
    40465284    public $info_global_attr = array();
     
    40485286    /**
    40495287     * String name of parent element HTML will be going into.
     5288     * @type string
    40505289     */
    40515290    public $info_parent = 'div';
     
    40545293     * Definition for parent element, allows parent element to be a
    40555294     * tag that's not allowed inside the HTML fragment.
     5295     * @type HTMLPurifier_ElementDef
    40565296     */
    40575297    public $info_parent_def;
    40585298
    40595299    /**
    4060      * String name of element used to wrap inline elements in block context
     5300     * String name of element used to wrap inline elements in block context.
     5301     * @type string
    40615302     * @note This is rarely used except for BLOCKQUOTEs in strict mode
    40625303     */
     
    40645305
    40655306    /**
    4066      * Associative array of deprecated tag name to HTMLPurifier_TagTransform
     5307     * Associative array of deprecated tag name to HTMLPurifier_TagTransform.
     5308     * @type array
    40675309     */
    40685310    public $info_tag_transform = array();
     
    40705312    /**
    40715313     * Indexed list of HTMLPurifier_AttrTransform to be performed before validation.
     5314     * @type HTMLPurifier_AttrTransform[]
    40725315     */
    40735316    public $info_attr_transform_pre = array();
     
    40755318    /**
    40765319     * Indexed list of HTMLPurifier_AttrTransform to be performed after validation.
     5320     * @type HTMLPurifier_AttrTransform[]
    40775321     */
    40785322    public $info_attr_transform_post = array();
     
    40815325     * Nested lookup array of content set name (Block, Inline) to
    40825326     * element name to whether or not it belongs in that content set.
     5327     * @type array
    40835328     */
    40845329    public $info_content_sets = array();
     
    40865331    /**
    40875332     * Indexed list of HTMLPurifier_Injector to be used.
     5333     * @type HTMLPurifier_Injector[]
    40885334     */
    40895335    public $info_injector = array();
     
    40915337    /**
    40925338     * Doctype object
     5339     * @type HTMLPurifier_Doctype
    40935340     */
    40945341    public $doctype;
     
    41025349     * @note This is strictly convenience, and does not have a corresponding
    41035350     *       method in HTMLPurifier_HTMLModule
    4104      * @param $element_name String element name to add attribute to
    4105      * @param $attr_name String name of attribute
    4106      * @param $def Attribute definition, can be string or object, see
     5351     * @param string $element_name Element name to add attribute to
     5352     * @param string $attr_name Name of attribute
     5353     * @param mixed $def Attribute definition, can be string or object, see
    41075354     *             HTMLPurifier_AttrTypes for details
    41085355     */
    4109     public function addAttribute($element_name, $attr_name, $def) {
     5356    public function addAttribute($element_name, $attr_name, $def)
     5357    {
    41105358        $module = $this->getAnonymousModule();
    41115359        if (!isset($module->info[$element_name])) {
     
    41195367    /**
    41205368     * Adds a custom element to your HTML definition
    4121      * @note See HTMLPurifier_HTMLModule::addElement for detailed
     5369     * @see HTMLPurifier_HTMLModule::addElement() for detailed
    41225370     *       parameter and return value descriptions.
    41235371     */
    4124     public function addElement($element_name, $type, $contents, $attr_collections, $attributes = array()) {
     5372    public function addElement($element_name, $type, $contents, $attr_collections, $attributes = array())
     5373    {
    41255374        $module = $this->getAnonymousModule();
    41265375        // assume that if the user is calling this, the element
     
    41335382     * Adds a blank element to your HTML definition, for overriding
    41345383     * existing behavior
    4135      * @note See HTMLPurifier_HTMLModule::addBlankElement for detailed
     5384     * @param string $element_name
     5385     * @return HTMLPurifier_ElementDef
     5386     * @see HTMLPurifier_HTMLModule::addBlankElement() for detailed
    41365387     *       parameter and return value descriptions.
    41375388     */
    4138     public function addBlankElement($element_name) {
     5389    public function addBlankElement($element_name)
     5390    {
    41395391        $module  = $this->getAnonymousModule();
    41405392        $element = $module->addBlankElement($element_name);
     
    41465398     * bust out advanced features without having to make your own
    41475399     * module.
    4148      */
    4149     public function getAnonymousModule() {
     5400     * @return HTMLPurifier_HTMLModule
     5401     */
     5402    public function getAnonymousModule()
     5403    {
    41505404        if (!$this->_anonModule) {
    41515405            $this->_anonModule = new HTMLPurifier_HTMLModule();
     
    41555409    }
    41565410
    4157     private $_anonModule;
    4158 
     5411    private $_anonModule = null;
    41595412
    41605413    // PUBLIC BUT INTERNAL VARIABLES --------------------------------------
    41615414
     5415    /**
     5416     * @type string
     5417     */
    41625418    public $type = 'HTML';
    4163     public $manager; /**< Instance of HTMLPurifier_HTMLModuleManager */
     5419
     5420    /**
     5421     * @type HTMLPurifier_HTMLModuleManager
     5422     */
     5423    public $manager;
    41645424
    41655425    /**
    41665426     * Performs low-cost, preliminary initialization.
    41675427     */
    4168     public function __construct() {
     5428    public function __construct()
     5429    {
    41695430        $this->manager = new HTMLPurifier_HTMLModuleManager();
    41705431    }
    41715432
    4172     protected function doSetup($config) {
     5433    /**
     5434     * @param HTMLPurifier_Config $config
     5435     */
     5436    protected function doSetup($config)
     5437    {
    41735438        $this->processModules($config);
    41745439        $this->setupConfigStuff($config);
     
    41845449    /**
    41855450     * Extract out the information from the manager
    4186      */
    4187     protected function processModules($config) {
    4188 
     5451     * @param HTMLPurifier_Config $config
     5452     */
     5453    protected function processModules($config)
     5454    {
    41895455        if ($this->_anonModule) {
    41905456            // for user specific changes
     
    41995465
    42005466        foreach ($this->manager->modules as $module) {
    4201             foreach($module->info_tag_transform as $k => $v) {
    4202                 if ($v === false) unset($this->info_tag_transform[$k]);
    4203                 else $this->info_tag_transform[$k] = $v;
    4204             }
    4205             foreach($module->info_attr_transform_pre as $k => $v) {
    4206                 if ($v === false) unset($this->info_attr_transform_pre[$k]);
    4207                 else $this->info_attr_transform_pre[$k] = $v;
    4208             }
    4209             foreach($module->info_attr_transform_post as $k => $v) {
    4210                 if ($v === false) unset($this->info_attr_transform_post[$k]);
    4211                 else $this->info_attr_transform_post[$k] = $v;
     5467            foreach ($module->info_tag_transform as $k => $v) {
     5468                if ($v === false) {
     5469                    unset($this->info_tag_transform[$k]);
     5470                } else {
     5471                    $this->info_tag_transform[$k] = $v;
     5472                }
     5473            }
     5474            foreach ($module->info_attr_transform_pre as $k => $v) {
     5475                if ($v === false) {
     5476                    unset($this->info_attr_transform_pre[$k]);
     5477                } else {
     5478                    $this->info_attr_transform_pre[$k] = $v;
     5479                }
     5480            }
     5481            foreach ($module->info_attr_transform_post as $k => $v) {
     5482                if ($v === false) {
     5483                    unset($this->info_attr_transform_post[$k]);
     5484                } else {
     5485                    $this->info_attr_transform_post[$k] = $v;
     5486                }
    42125487            }
    42135488            foreach ($module->info_injector as $k => $v) {
    4214                 if ($v === false) unset($this->info_injector[$k]);
    4215                 else $this->info_injector[$k] = $v;
    4216             }
    4217         }
    4218 
     5489                if ($v === false) {
     5490                    unset($this->info_injector[$k]);
     5491                } else {
     5492                    $this->info_injector[$k] = $v;
     5493                }
     5494            }
     5495        }
    42195496        $this->info = $this->manager->getElements();
    42205497        $this->info_content_sets = $this->manager->contentSets->lookup;
    4221 
    42225498    }
    42235499
    42245500    /**
    42255501     * Sets up stuff based on config. We need a better way of doing this.
    4226      */
    4227     protected function setupConfigStuff($config) {
    4228 
     5502     * @param HTMLPurifier_Config $config
     5503     */
     5504    protected function setupConfigStuff($config)
     5505    {
    42295506        $block_wrapper = $config->get('HTML.BlockWrapper');
    42305507        if (isset($this->info_content_sets['Block'][$block_wrapper])) {
    42315508            $this->info_block_wrapper = $block_wrapper;
    42325509        } else {
    4233             trigger_error('Cannot use non-block element as block wrapper',
    4234                 E_USER_ERROR);
     5510            trigger_error(
     5511                'Cannot use non-block element as block wrapper',
     5512                E_USER_ERROR
     5513            );
    42355514        }
    42365515
     
    42415520            $this->info_parent_def = $def;
    42425521        } else {
    4243             trigger_error('Cannot use unrecognized element as parent',
    4244                 E_USER_ERROR);
     5522            trigger_error(
     5523                'Cannot use unrecognized element as parent',
     5524                E_USER_ERROR
     5525            );
    42455526            $this->info_parent_def = $this->manager->getElement($this->info_parent, true);
    42465527        }
    42475528
    42485529        // support template text
    4249         $support = "(for information on implementing this, see the ".
    4250                    "support forums) ";
     5530        $support = "(for information on implementing this, see the support forums) ";
    42515531
    42525532        // setup allowed elements -----------------------------------------
     
    42645544        if (is_array($allowed_elements)) {
    42655545            foreach ($this->info as $name => $d) {
    4266                 if(!isset($allowed_elements[$name])) unset($this->info[$name]);
     5546                if (!isset($allowed_elements[$name])) {
     5547                    unset($this->info[$name]);
     5548                }
    42675549                unset($allowed_elements[$name]);
    42685550            }
     
    42785560        $allowed_attributes_mutable = $allowed_attributes; // by copy!
    42795561        if (is_array($allowed_attributes)) {
    4280 
    42815562            // This actually doesn't do anything, since we went away from
    42825563            // global attributes. It's possible that userland code uses
     
    42935574                    }
    42945575                }
    4295                 if ($delete) unset($this->info_global_attr[$attr]);
     5576                if ($delete) {
     5577                    unset($this->info_global_attr[$attr]);
     5578                }
    42965579            }
    42975580
     
    43105593                    if ($delete) {
    43115594                        if ($this->info[$tag]->attr[$attr]->required) {
    4312                             trigger_error("Required attribute '$attr' in element '$tag' was not allowed, which means '$tag' will not be allowed either", E_USER_WARNING);
     5595                            trigger_error(
     5596                                "Required attribute '$attr' in element '$tag' " .
     5597                                "was not allowed, which means '$tag' will not be allowed either",
     5598                                E_USER_WARNING
     5599                            );
    43135600                        }
    43145601                        unset($this->info[$tag]->attr[$attr]);
     
    43265613                            $attribute = htmlspecialchars($bits[1]);
    43275614                            if (!isset($this->info[$element])) {
    4328                                 trigger_error("Cannot allow attribute '$attribute' if element '$element' is not allowed/supported $support");
     5615                                trig