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