1: <?php
2:
3: /**
4: * Defines common attribute collections that modules reference
5: */
6:
7: class HTMLPurifier_AttrCollections
8: {
9:
10: /**
11: * Associative array of attribute collections, indexed by name
12: */
13: public $info = array();
14:
15: /**
16: * Performs all expansions on internal data for use by other inclusions
17: * It also collects all attribute collection extensions from
18: * modules
19: * @param $attr_types HTMLPurifier_AttrTypes instance
20: * @param $modules Hash array of HTMLPurifier_HTMLModule members
21: */
22: public function __construct($attr_types, $modules) {
23: // load extensions from the modules
24: foreach ($modules as $module) {
25: foreach ($module->attr_collections as $coll_i => $coll) {
26: if (!isset($this->info[$coll_i])) {
27: $this->info[$coll_i] = array();
28: }
29: foreach ($coll as $attr_i => $attr) {
30: if ($attr_i === 0 && isset($this->info[$coll_i][$attr_i])) {
31: // merge in includes
32: $this->info[$coll_i][$attr_i] = array_merge(
33: $this->info[$coll_i][$attr_i], $attr);
34: continue;
35: }
36: $this->info[$coll_i][$attr_i] = $attr;
37: }
38: }
39: }
40: // perform internal expansions and inclusions
41: foreach ($this->info as $name => $attr) {
42: // merge attribute collections that include others
43: $this->performInclusions($this->info[$name]);
44: // replace string identifiers with actual attribute objects
45: $this->expandIdentifiers($this->info[$name], $attr_types);
46: }
47: }
48:
49: /**
50: * Takes a reference to an attribute associative array and performs
51: * all inclusions specified by the zero index.
52: * @param &$attr Reference to attribute array
53: */
54: public function performInclusions(&$attr) {
55: if (!isset($attr[0])) return;
56: $merge = $attr[0];
57: $seen = array(); // recursion guard
58: // loop through all the inclusions
59: for ($i = 0; isset($merge[$i]); $i++) {
60: if (isset($seen[$merge[$i]])) continue;
61: $seen[$merge[$i]] = true;
62: // foreach attribute of the inclusion, copy it over
63: if (!isset($this->info[$merge[$i]])) continue;
64: foreach ($this->info[$merge[$i]] as $key => $value) {
65: if (isset($attr[$key])) continue; // also catches more inclusions
66: $attr[$key] = $value;
67: }
68: if (isset($this->info[$merge[$i]][0])) {
69: // recursion
70: $merge = array_merge($merge, $this->info[$merge[$i]][0]);
71: }
72: }
73: unset($attr[0]);
74: }
75:
76: /**
77: * Expands all string identifiers in an attribute array by replacing
78: * them with the appropriate values inside HTMLPurifier_AttrTypes
79: * @param &$attr Reference to attribute array
80: * @param $attr_types HTMLPurifier_AttrTypes instance
81: */
82: public function expandIdentifiers(&$attr, $attr_types) {
83:
84: // because foreach will process new elements we add, make sure we
85: // skip duplicates
86: $processed = array();
87:
88: foreach ($attr as $def_i => $def) {
89: // skip inclusions
90: if ($def_i === 0) continue;
91:
92: if (isset($processed[$def_i])) continue;
93:
94: // determine whether or not attribute is required
95: if ($required = (strpos($def_i, '*') !== false)) {
96: // rename the definition
97: unset($attr[$def_i]);
98: $def_i = trim($def_i, '*');
99: $attr[$def_i] = $def;
100: }
101:
102: $processed[$def_i] = true;
103:
104: // if we've already got a literal object, move on
105: if (is_object($def)) {
106: // preserve previous required
107: $attr[$def_i]->required = ($required || $attr[$def_i]->required);
108: continue;
109: }
110:
111: if ($def === false) {
112: unset($attr[$def_i]);
113: continue;
114: }
115:
116: if ($t = $attr_types->get($def)) {
117: $attr[$def_i] = $t;
118: $attr[$def_i]->required = $required;
119: } else {
120: unset($attr[$def_i]);
121: }
122: }
123:
124: }
125:
126: }
127:
128: // vim: et sw=4 sts=4
129: