| 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 | |
|---|