| 1 | efrain | 1 | <?php
 | 
        
           |  |  | 2 |   | 
        
           |  |  | 3 | /**
 | 
        
           |  |  | 4 |  * Structure that stores an HTML element definition. Used by
 | 
        
           |  |  | 5 |  * HTMLPurifier_HTMLDefinition and HTMLPurifier_HTMLModule.
 | 
        
           |  |  | 6 |  * @note This class is inspected by HTMLPurifier_Printer_HTMLDefinition.
 | 
        
           |  |  | 7 |  *       Please update that class too.
 | 
        
           |  |  | 8 |  * @warning If you add new properties to this class, you MUST update
 | 
        
           |  |  | 9 |  *          the mergeIn() method.
 | 
        
           |  |  | 10 |  */
 | 
        
           |  |  | 11 | class HTMLPurifier_ElementDef
 | 
        
           |  |  | 12 | {
 | 
        
           |  |  | 13 |     /**
 | 
        
           |  |  | 14 |      * Does the definition work by itself, or is it created solely
 | 
        
           |  |  | 15 |      * for the purpose of merging into another definition?
 | 
        
           |  |  | 16 |      * @type bool
 | 
        
           |  |  | 17 |      */
 | 
        
           |  |  | 18 |     public $standalone = true;
 | 
        
           |  |  | 19 |   | 
        
           |  |  | 20 |     /**
 | 
        
           |  |  | 21 |      * Associative array of attribute name to HTMLPurifier_AttrDef.
 | 
        
           |  |  | 22 |      * @type array
 | 
        
           |  |  | 23 |      * @note Before being processed by HTMLPurifier_AttrCollections
 | 
        
           |  |  | 24 |      *       when modules are finalized during
 | 
        
           |  |  | 25 |      *       HTMLPurifier_HTMLDefinition->setup(), this array may also
 | 
        
           |  |  | 26 |      *       contain an array at index 0 that indicates which attribute
 | 
        
           |  |  | 27 |      *       collections to load into the full array. It may also
 | 
        
           |  |  | 28 |      *       contain string indentifiers in lieu of HTMLPurifier_AttrDef,
 | 
        
           |  |  | 29 |      *       see HTMLPurifier_AttrTypes on how they are expanded during
 | 
        
           |  |  | 30 |      *       HTMLPurifier_HTMLDefinition->setup() processing.
 | 
        
           |  |  | 31 |      */
 | 
        
           |  |  | 32 |     public $attr = array();
 | 
        
           |  |  | 33 |   | 
        
           |  |  | 34 |     // XXX: Design note: currently, it's not possible to override
 | 
        
           |  |  | 35 |     // previously defined AttrTransforms without messing around with
 | 
        
           |  |  | 36 |     // the final generated config. This is by design; a previous version
 | 
        
           |  |  | 37 |     // used an associated list of attr_transform, but it was extremely
 | 
        
           |  |  | 38 |     // easy to accidentally override other attribute transforms by
 | 
        
           |  |  | 39 |     // forgetting to specify an index (and just using 0.)  While we
 | 
        
           |  |  | 40 |     // could check this by checking the index number and complaining,
 | 
        
           |  |  | 41 |     // there is a second problem which is that it is not at all easy to
 | 
        
           |  |  | 42 |     // tell when something is getting overridden. Combine this with a
 | 
        
           |  |  | 43 |     // codebase where this isn't really being used, and it's perfect for
 | 
        
           |  |  | 44 |     // nuking.
 | 
        
           |  |  | 45 |   | 
        
           |  |  | 46 |     /**
 | 
        
           |  |  | 47 |      * List of tags HTMLPurifier_AttrTransform to be done before validation.
 | 
        
           |  |  | 48 |      * @type array
 | 
        
           |  |  | 49 |      */
 | 
        
           |  |  | 50 |     public $attr_transform_pre = array();
 | 
        
           |  |  | 51 |   | 
        
           |  |  | 52 |     /**
 | 
        
           |  |  | 53 |      * List of tags HTMLPurifier_AttrTransform to be done after validation.
 | 
        
           |  |  | 54 |      * @type array
 | 
        
           |  |  | 55 |      */
 | 
        
           |  |  | 56 |     public $attr_transform_post = array();
 | 
        
           |  |  | 57 |   | 
        
           |  |  | 58 |     /**
 | 
        
           |  |  | 59 |      * HTMLPurifier_ChildDef of this tag.
 | 
        
           |  |  | 60 |      * @type HTMLPurifier_ChildDef
 | 
        
           |  |  | 61 |      */
 | 
        
           |  |  | 62 |     public $child;
 | 
        
           |  |  | 63 |   | 
        
           |  |  | 64 |     /**
 | 
        
           |  |  | 65 |      * Abstract string representation of internal ChildDef rules.
 | 
        
           |  |  | 66 |      * @see HTMLPurifier_ContentSets for how this is parsed and then transformed
 | 
        
           |  |  | 67 |      * into an HTMLPurifier_ChildDef.
 | 
        
           |  |  | 68 |      * @warning This is a temporary variable that is not available after
 | 
        
           |  |  | 69 |      *      being processed by HTMLDefinition
 | 
        
           |  |  | 70 |      * @type string
 | 
        
           |  |  | 71 |      */
 | 
        
           |  |  | 72 |     public $content_model;
 | 
        
           |  |  | 73 |   | 
        
           |  |  | 74 |     /**
 | 
        
           |  |  | 75 |      * Value of $child->type, used to determine which ChildDef to use,
 | 
        
           |  |  | 76 |      * used in combination with $content_model.
 | 
        
           |  |  | 77 |      * @warning This must be lowercase
 | 
        
           |  |  | 78 |      * @warning This is a temporary variable that is not available after
 | 
        
           |  |  | 79 |      *      being processed by HTMLDefinition
 | 
        
           |  |  | 80 |      * @type string
 | 
        
           |  |  | 81 |      */
 | 
        
           |  |  | 82 |     public $content_model_type;
 | 
        
           |  |  | 83 |   | 
        
           |  |  | 84 |     /**
 | 
        
           |  |  | 85 |      * Does the element have a content model (#PCDATA | Inline)*? This
 | 
        
           |  |  | 86 |      * is important for chameleon ins and del processing in
 | 
        
           |  |  | 87 |      * HTMLPurifier_ChildDef_Chameleon. Dynamically set: modules don't
 | 
        
           |  |  | 88 |      * have to worry about this one.
 | 
        
           |  |  | 89 |      * @type bool
 | 
        
           |  |  | 90 |      */
 | 
        
           |  |  | 91 |     public $descendants_are_inline = false;
 | 
        
           |  |  | 92 |   | 
        
           |  |  | 93 |     /**
 | 
        
           |  |  | 94 |      * List of the names of required attributes this element has.
 | 
        
           |  |  | 95 |      * Dynamically populated by HTMLPurifier_HTMLDefinition::getElement()
 | 
        
           |  |  | 96 |      * @type array
 | 
        
           |  |  | 97 |      */
 | 
        
           |  |  | 98 |     public $required_attr = array();
 | 
        
           |  |  | 99 |   | 
        
           |  |  | 100 |     /**
 | 
        
           |  |  | 101 |      * Lookup table of tags excluded from all descendants of this tag.
 | 
        
           |  |  | 102 |      * @type array
 | 
        
           |  |  | 103 |      * @note SGML permits exclusions for all descendants, but this is
 | 
        
           |  |  | 104 |      *       not possible with DTDs or XML Schemas. W3C has elected to
 | 
        
           |  |  | 105 |      *       use complicated compositions of content_models to simulate
 | 
        
           |  |  | 106 |      *       exclusion for children, but we go the simpler, SGML-style
 | 
        
           |  |  | 107 |      *       route of flat-out exclusions, which correctly apply to
 | 
        
           |  |  | 108 |      *       all descendants and not just children. Note that the XHTML
 | 
        
           |  |  | 109 |      *       Modularization Abstract Modules are blithely unaware of such
 | 
        
           |  |  | 110 |      *       distinctions.
 | 
        
           |  |  | 111 |      */
 | 
        
           |  |  | 112 |     public $excludes = array();
 | 
        
           |  |  | 113 |   | 
        
           |  |  | 114 |     /**
 | 
        
           |  |  | 115 |      * This tag is explicitly auto-closed by the following tags.
 | 
        
           |  |  | 116 |      * @type array
 | 
        
           |  |  | 117 |      */
 | 
        
           |  |  | 118 |     public $autoclose = array();
 | 
        
           |  |  | 119 |   | 
        
           |  |  | 120 |     /**
 | 
        
           |  |  | 121 |      * If a foreign element is found in this element, test if it is
 | 
        
           |  |  | 122 |      * allowed by this sub-element; if it is, instead of closing the
 | 
        
           |  |  | 123 |      * current element, place it inside this element.
 | 
        
           |  |  | 124 |      * @type string
 | 
        
           |  |  | 125 |      */
 | 
        
           |  |  | 126 |     public $wrap;
 | 
        
           |  |  | 127 |   | 
        
           |  |  | 128 |     /**
 | 
        
           |  |  | 129 |      * Whether or not this is a formatting element affected by the
 | 
        
           |  |  | 130 |      * "Active Formatting Elements" algorithm.
 | 
        
           |  |  | 131 |      * @type bool
 | 
        
           |  |  | 132 |      */
 | 
        
           |  |  | 133 |     public $formatting;
 | 
        
           |  |  | 134 |   | 
        
           |  |  | 135 |     /**
 | 
        
           |  |  | 136 |      * Low-level factory constructor for creating new standalone element defs
 | 
        
           |  |  | 137 |      */
 | 
        
           |  |  | 138 |     public static function create($content_model, $content_model_type, $attr)
 | 
        
           |  |  | 139 |     {
 | 
        
           |  |  | 140 |         $def = new HTMLPurifier_ElementDef();
 | 
        
           |  |  | 141 |         $def->content_model = $content_model;
 | 
        
           |  |  | 142 |         $def->content_model_type = $content_model_type;
 | 
        
           |  |  | 143 |         $def->attr = $attr;
 | 
        
           |  |  | 144 |         return $def;
 | 
        
           |  |  | 145 |     }
 | 
        
           |  |  | 146 |   | 
        
           |  |  | 147 |     /**
 | 
        
           |  |  | 148 |      * Merges the values of another element definition into this one.
 | 
        
           |  |  | 149 |      * Values from the new element def take precedence if a value is
 | 
        
           |  |  | 150 |      * not mergeable.
 | 
        
           |  |  | 151 |      * @param HTMLPurifier_ElementDef $def
 | 
        
           |  |  | 152 |      */
 | 
        
           |  |  | 153 |     public function mergeIn($def)
 | 
        
           |  |  | 154 |     {
 | 
        
           |  |  | 155 |         // later keys takes precedence
 | 
        
           |  |  | 156 |         foreach ($def->attr as $k => $v) {
 | 
        
           |  |  | 157 |             if ($k === 0) {
 | 
        
           |  |  | 158 |                 // merge in the includes
 | 
        
           |  |  | 159 |                 // sorry, no way to override an include
 | 
        
           |  |  | 160 |                 foreach ($v as $v2) {
 | 
        
           |  |  | 161 |                     $this->attr[0][] = $v2;
 | 
        
           |  |  | 162 |                 }
 | 
        
           |  |  | 163 |                 continue;
 | 
        
           |  |  | 164 |             }
 | 
        
           |  |  | 165 |             if ($v === false) {
 | 
        
           |  |  | 166 |                 if (isset($this->attr[$k])) {
 | 
        
           |  |  | 167 |                     unset($this->attr[$k]);
 | 
        
           |  |  | 168 |                 }
 | 
        
           |  |  | 169 |                 continue;
 | 
        
           |  |  | 170 |             }
 | 
        
           |  |  | 171 |             $this->attr[$k] = $v;
 | 
        
           |  |  | 172 |         }
 | 
        
           |  |  | 173 |         $this->_mergeAssocArray($this->excludes, $def->excludes);
 | 
        
           |  |  | 174 |         $this->attr_transform_pre = array_merge($this->attr_transform_pre, $def->attr_transform_pre);
 | 
        
           |  |  | 175 |         $this->attr_transform_post = array_merge($this->attr_transform_post, $def->attr_transform_post);
 | 
        
           |  |  | 176 |   | 
        
           |  |  | 177 |         if (!empty($def->content_model)) {
 | 
        
           |  |  | 178 |             $this->content_model =
 | 
        
           |  |  | 179 |                 str_replace("#SUPER", (string)$this->content_model, $def->content_model);
 | 
        
           |  |  | 180 |             $this->child = false;
 | 
        
           |  |  | 181 |         }
 | 
        
           |  |  | 182 |         if (!empty($def->content_model_type)) {
 | 
        
           |  |  | 183 |             $this->content_model_type = $def->content_model_type;
 | 
        
           |  |  | 184 |             $this->child = false;
 | 
        
           |  |  | 185 |         }
 | 
        
           |  |  | 186 |         if (!is_null($def->child)) {
 | 
        
           |  |  | 187 |             $this->child = $def->child;
 | 
        
           |  |  | 188 |         }
 | 
        
           |  |  | 189 |         if (!is_null($def->formatting)) {
 | 
        
           |  |  | 190 |             $this->formatting = $def->formatting;
 | 
        
           |  |  | 191 |         }
 | 
        
           |  |  | 192 |         if ($def->descendants_are_inline) {
 | 
        
           |  |  | 193 |             $this->descendants_are_inline = $def->descendants_are_inline;
 | 
        
           |  |  | 194 |         }
 | 
        
           |  |  | 195 |     }
 | 
        
           |  |  | 196 |   | 
        
           |  |  | 197 |     /**
 | 
        
           |  |  | 198 |      * Merges one array into another, removes values which equal false
 | 
        
           |  |  | 199 |      * @param $a1 Array by reference that is merged into
 | 
        
           |  |  | 200 |      * @param $a2 Array that merges into $a1
 | 
        
           |  |  | 201 |      */
 | 
        
           |  |  | 202 |     private function _mergeAssocArray(&$a1, $a2)
 | 
        
           |  |  | 203 |     {
 | 
        
           |  |  | 204 |         foreach ($a2 as $k => $v) {
 | 
        
           |  |  | 205 |             if ($v === false) {
 | 
        
           |  |  | 206 |                 if (isset($a1[$k])) {
 | 
        
           |  |  | 207 |                     unset($a1[$k]);
 | 
        
           |  |  | 208 |                 }
 | 
        
           |  |  | 209 |                 continue;
 | 
        
           |  |  | 210 |             }
 | 
        
           |  |  | 211 |             $a1[$k] = $v;
 | 
        
           |  |  | 212 |         }
 | 
        
           |  |  | 213 |     }
 | 
        
           |  |  | 214 | }
 | 
        
           |  |  | 215 |   | 
        
           |  |  | 216 | // vim: et sw=4 sts=4
 |