File indexing completed on 2024-12-22 05:36:21

0001 <?php
0002 
0003 /**
0004  * Defines common attribute collections that modules reference
0005  */
0006 
0007 class HTMLPurifier_AttrCollections
0008 {
0009 
0010     /**
0011      * Associative array of attribute collections, indexed by name.
0012      * @type array
0013      */
0014     public $info = array();
0015 
0016     /**
0017      * Performs all expansions on internal data for use by other inclusions
0018      * It also collects all attribute collection extensions from
0019      * modules
0020      * @param HTMLPurifier_AttrTypes $attr_types HTMLPurifier_AttrTypes instance
0021      * @param HTMLPurifier_HTMLModule[] $modules Hash array of HTMLPurifier_HTMLModule members
0022      */
0023     public function __construct($attr_types, $modules)
0024     {
0025         $this->doConstruct($attr_types, $modules);
0026     }
0027 
0028     public function doConstruct($attr_types, $modules)
0029     {
0030         // load extensions from the modules
0031         foreach ($modules as $module) {
0032             foreach ($module->attr_collections as $coll_i => $coll) {
0033                 if (!isset($this->info[$coll_i])) {
0034                     $this->info[$coll_i] = array();
0035                 }
0036                 foreach ($coll as $attr_i => $attr) {
0037                     if ($attr_i === 0 && isset($this->info[$coll_i][$attr_i])) {
0038                         // merge in includes
0039                         $this->info[$coll_i][$attr_i] = array_merge(
0040                             $this->info[$coll_i][$attr_i],
0041                             $attr
0042                         );
0043                         continue;
0044                     }
0045                     $this->info[$coll_i][$attr_i] = $attr;
0046                 }
0047             }
0048         }
0049         // perform internal expansions and inclusions
0050         foreach ($this->info as $name => $attr) {
0051             // merge attribute collections that include others
0052             $this->performInclusions($this->info[$name]);
0053             // replace string identifiers with actual attribute objects
0054             $this->expandIdentifiers($this->info[$name], $attr_types);
0055         }
0056     }
0057 
0058     /**
0059      * Takes a reference to an attribute associative array and performs
0060      * all inclusions specified by the zero index.
0061      * @param array &$attr Reference to attribute array
0062      */
0063     public function performInclusions(&$attr)
0064     {
0065         if (!isset($attr[0])) {
0066             return;
0067         }
0068         $merge = $attr[0];
0069         $seen  = array(); // recursion guard
0070         // loop through all the inclusions
0071         for ($i = 0; isset($merge[$i]); $i++) {
0072             if (isset($seen[$merge[$i]])) {
0073                 continue;
0074             }
0075             $seen[$merge[$i]] = true;
0076             // foreach attribute of the inclusion, copy it over
0077             if (!isset($this->info[$merge[$i]])) {
0078                 continue;
0079             }
0080             foreach ($this->info[$merge[$i]] as $key => $value) {
0081                 if (isset($attr[$key])) {
0082                     continue;
0083                 } // also catches more inclusions
0084                 $attr[$key] = $value;
0085             }
0086             if (isset($this->info[$merge[$i]][0])) {
0087                 // recursion
0088                 $merge = array_merge($merge, $this->info[$merge[$i]][0]);
0089             }
0090         }
0091         unset($attr[0]);
0092     }
0093 
0094     /**
0095      * Expands all string identifiers in an attribute array by replacing
0096      * them with the appropriate values inside HTMLPurifier_AttrTypes
0097      * @param array &$attr Reference to attribute array
0098      * @param HTMLPurifier_AttrTypes $attr_types HTMLPurifier_AttrTypes instance
0099      */
0100     public function expandIdentifiers(&$attr, $attr_types)
0101     {
0102         // because foreach will process new elements we add, make sure we
0103         // skip duplicates
0104         $processed = array();
0105 
0106         foreach ($attr as $def_i => $def) {
0107             // skip inclusions
0108             if ($def_i === 0) {
0109                 continue;
0110             }
0111 
0112             if (isset($processed[$def_i])) {
0113                 continue;
0114             }
0115 
0116             // determine whether or not attribute is required
0117             if ($required = (strpos($def_i, '*') !== false)) {
0118                 // rename the definition
0119                 unset($attr[$def_i]);
0120                 $def_i = trim($def_i, '*');
0121                 $attr[$def_i] = $def;
0122             }
0123 
0124             $processed[$def_i] = true;
0125 
0126             // if we've already got a literal object, move on
0127             if (is_object($def)) {
0128                 // preserve previous required
0129                 $attr[$def_i]->required = ($required || $attr[$def_i]->required);
0130                 continue;
0131             }
0132 
0133             if ($def === false) {
0134                 unset($attr[$def_i]);
0135                 continue;
0136             }
0137 
0138             if ($t = $attr_types->get($def)) {
0139                 $attr[$def_i] = $t;
0140                 $attr[$def_i]->required = $required;
0141             } else {
0142                 unset($attr[$def_i]);
0143             }
0144         }
0145     }
0146 }
0147 
0148 // vim: et sw=4 sts=4