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