| 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 | */ |
|---|
| 9 | class HTMLPurifier_ElementDef |
|---|
| 10 | { |
|---|
| 11 | |
|---|
| 12 | /** |
|---|
| 13 | * Does the definition work by itself, or is it created solely |
|---|
| 14 | * for the purpose of merging into another definition? |
|---|
| 15 | */ |
|---|
| 16 | public $standalone = true; |
|---|
| 17 | |
|---|
| 18 | /** |
|---|
| 19 | * Associative array of attribute name to HTMLPurifier_AttrDef |
|---|
| 20 | * @note Before being processed by HTMLPurifier_AttrCollections |
|---|
| 21 | * when modules are finalized during |
|---|
| 22 | * HTMLPurifier_HTMLDefinition->setup(), this array may also |
|---|
| 23 | * contain an array at index 0 that indicates which attribute |
|---|
| 24 | * collections to load into the full array. It may also |
|---|
| 25 | * contain string indentifiers in lieu of HTMLPurifier_AttrDef, |
|---|
| 26 | * see HTMLPurifier_AttrTypes on how they are expanded during |
|---|
| 27 | * HTMLPurifier_HTMLDefinition->setup() processing. |
|---|
| 28 | */ |
|---|
| 29 | public $attr = array(); |
|---|
| 30 | |
|---|
| 31 | /** |
|---|
| 32 | * Indexed list of tag's HTMLPurifier_AttrTransform to be done before validation |
|---|
| 33 | */ |
|---|
| 34 | public $attr_transform_pre = array(); |
|---|
| 35 | |
|---|
| 36 | /** |
|---|
| 37 | * Indexed list of tag's HTMLPurifier_AttrTransform to be done after validation |
|---|
| 38 | */ |
|---|
| 39 | public $attr_transform_post = array(); |
|---|
| 40 | |
|---|
| 41 | /** |
|---|
| 42 | * HTMLPurifier_ChildDef of this tag. |
|---|
| 43 | */ |
|---|
| 44 | public $child; |
|---|
| 45 | |
|---|
| 46 | /** |
|---|
| 47 | * Abstract string representation of internal ChildDef rules. See |
|---|
| 48 | * HTMLPurifier_ContentSets for how this is parsed and then transformed |
|---|
| 49 | * into an HTMLPurifier_ChildDef. |
|---|
| 50 | * @warning This is a temporary variable that is not available after |
|---|
| 51 | * being processed by HTMLDefinition |
|---|
| 52 | */ |
|---|
| 53 | public $content_model; |
|---|
| 54 | |
|---|
| 55 | /** |
|---|
| 56 | * Value of $child->type, used to determine which ChildDef to use, |
|---|
| 57 | * used in combination with $content_model. |
|---|
| 58 | * @warning This must be lowercase |
|---|
| 59 | * @warning This is a temporary variable that is not available after |
|---|
| 60 | * being processed by HTMLDefinition |
|---|
| 61 | */ |
|---|
| 62 | public $content_model_type; |
|---|
| 63 | |
|---|
| 64 | |
|---|
| 65 | |
|---|
| 66 | /** |
|---|
| 67 | * Does the element have a content model (#PCDATA | Inline)*? This |
|---|
| 68 | * is important for chameleon ins and del processing in |
|---|
| 69 | * HTMLPurifier_ChildDef_Chameleon. Dynamically set: modules don't |
|---|
| 70 | * have to worry about this one. |
|---|
| 71 | */ |
|---|
| 72 | public $descendants_are_inline = false; |
|---|
| 73 | |
|---|
| 74 | /** |
|---|
| 75 | * List of the names of required attributes this element has. Dynamically |
|---|
| 76 | * populated by HTMLPurifier_HTMLDefinition::getElement |
|---|
| 77 | */ |
|---|
| 78 | public $required_attr = array(); |
|---|
| 79 | |
|---|
| 80 | /** |
|---|
| 81 | * Lookup table of tags excluded from all descendants of this tag. |
|---|
| 82 | * @note SGML permits exclusions for all descendants, but this is |
|---|
| 83 | * not possible with DTDs or XML Schemas. W3C has elected to |
|---|
| 84 | * use complicated compositions of content_models to simulate |
|---|
| 85 | * exclusion for children, but we go the simpler, SGML-style |
|---|
| 86 | * route of flat-out exclusions, which correctly apply to |
|---|
| 87 | * all descendants and not just children. Note that the XHTML |
|---|
| 88 | * Modularization Abstract Modules are blithely unaware of such |
|---|
| 89 | * distinctions. |
|---|
| 90 | */ |
|---|
| 91 | public $excludes = array(); |
|---|
| 92 | |
|---|
| 93 | /** |
|---|
| 94 | * Low-level factory constructor for creating new standalone element defs |
|---|
| 95 | */ |
|---|
| 96 | public static function create($content_model, $content_model_type, $attr) { |
|---|
| 97 | $def = new HTMLPurifier_ElementDef(); |
|---|
| 98 | $def->content_model = $content_model; |
|---|
| 99 | $def->content_model_type = $content_model_type; |
|---|
| 100 | $def->attr = $attr; |
|---|
| 101 | return $def; |
|---|
| 102 | } |
|---|
| 103 | |
|---|
| 104 | /** |
|---|
| 105 | * Merges the values of another element definition into this one. |
|---|
| 106 | * Values from the new element def take precedence if a value is |
|---|
| 107 | * not mergeable. |
|---|
| 108 | */ |
|---|
| 109 | public function mergeIn($def) { |
|---|
| 110 | |
|---|
| 111 | // later keys takes precedence |
|---|
| 112 | foreach($def->attr as $k => $v) { |
|---|
| 113 | if ($k === 0) { |
|---|
| 114 | // merge in the includes |
|---|
| 115 | // sorry, no way to override an include |
|---|
| 116 | foreach ($v as $v2) { |
|---|
| 117 | $this->attr[0][] = $v2; |
|---|
| 118 | } |
|---|
| 119 | continue; |
|---|
| 120 | } |
|---|
| 121 | if ($v === false) { |
|---|
| 122 | if (isset($this->attr[$k])) unset($this->attr[$k]); |
|---|
| 123 | continue; |
|---|
| 124 | } |
|---|
| 125 | $this->attr[$k] = $v; |
|---|
| 126 | } |
|---|
| 127 | $this->_mergeAssocArray($this->attr_transform_pre, $def->attr_transform_pre); |
|---|
| 128 | $this->_mergeAssocArray($this->attr_transform_post, $def->attr_transform_post); |
|---|
| 129 | $this->_mergeAssocArray($this->excludes, $def->excludes); |
|---|
| 130 | |
|---|
| 131 | if(!empty($def->content_model)) { |
|---|
| 132 | $this->content_model .= ' | ' . $def->content_model; |
|---|
| 133 | $this->child = false; |
|---|
| 134 | } |
|---|
| 135 | if(!empty($def->content_model_type)) { |
|---|
| 136 | $this->content_model_type = $def->content_model_type; |
|---|
| 137 | $this->child = false; |
|---|
| 138 | } |
|---|
| 139 | if(!is_null($def->child)) $this->child = $def->child; |
|---|
| 140 | if($def->descendants_are_inline) $this->descendants_are_inline = $def->descendants_are_inline; |
|---|
| 141 | |
|---|
| 142 | } |
|---|
| 143 | |
|---|
| 144 | /** |
|---|
| 145 | * Merges one array into another, removes values which equal false |
|---|
| 146 | * @param $a1 Array by reference that is merged into |
|---|
| 147 | * @param $a2 Array that merges into $a1 |
|---|
| 148 | */ |
|---|
| 149 | private function _mergeAssocArray(&$a1, $a2) { |
|---|
| 150 | foreach ($a2 as $k => $v) { |
|---|
| 151 | if ($v === false) { |
|---|
| 152 | if (isset($a1[$k])) unset($a1[$k]); |
|---|
| 153 | continue; |
|---|
| 154 | } |
|---|
| 155 | $a1[$k] = $v; |
|---|
| 156 | } |
|---|
| 157 | } |
|---|
| 158 | |
|---|
| 159 | } |
|---|
| 160 | |
|---|
| 161 | |
|---|