<?php 
 
/** 
 * Defines common attribute collections that modules reference 
 */ 
 
class HTMLPurifier_AttrCollections 
{ 
 
    /** 
     * Associative array of attribute collections, indexed by name 
     */ 
    public $info = array(); 
 
    /** 
     * Performs all expansions on internal data for use by other inclusions 
     * It also collects all attribute collection extensions from 
     * modules 
     * @param $attr_types HTMLPurifier_AttrTypes instance 
     * @param $modules Hash array of HTMLPurifier_HTMLModule members 
     */ 
    public function __construct($attr_types, $modules) { 
        // load extensions from the modules 
        foreach ($modules as $module) { 
            foreach ($module->attr_collections as $coll_i => $coll) { 
                if (!isset($this->info[$coll_i])) { 
                    $this->info[$coll_i] = array(); 
                } 
                foreach ($coll as $attr_i => $attr) { 
                    if ($attr_i === 0 && isset($this->info[$coll_i][$attr_i])) { 
                        // merge in includes 
                        $this->info[$coll_i][$attr_i] = array_merge( 
                            $this->info[$coll_i][$attr_i], $attr); 
                        continue; 
                    } 
                    $this->info[$coll_i][$attr_i] = $attr; 
                } 
            } 
        } 
        // perform internal expansions and inclusions 
        foreach ($this->info as $name => $attr) { 
            // merge attribute collections that include others 
            $this->performInclusions($this->info[$name]); 
            // replace string identifiers with actual attribute objects 
            $this->expandIdentifiers($this->info[$name], $attr_types); 
        } 
    } 
 
    /** 
     * Takes a reference to an attribute associative array and performs 
     * all inclusions specified by the zero index. 
     * @param &$attr Reference to attribute array 
     */ 
    public function performInclusions(&$attr) { 
        if (!isset($attr[0])) return; 
        $merge = $attr[0]; 
        $seen  = array(); // recursion guard 
        // loop through all the inclusions 
        for ($i = 0; isset($merge[$i]); $i++) { 
            if (isset($seen[$merge[$i]])) continue; 
            $seen[$merge[$i]] = true; 
            // foreach attribute of the inclusion, copy it over 
            if (!isset($this->info[$merge[$i]])) continue; 
            foreach ($this->info[$merge[$i]] as $key => $value) { 
                if (isset($attr[$key])) continue; // also catches more inclusions 
                $attr[$key] = $value; 
            } 
            if (isset($this->info[$merge[$i]][0])) { 
                // recursion 
                $merge = array_merge($merge, $this->info[$merge[$i]][0]); 
            } 
        } 
        unset($attr[0]); 
    } 
 
    /** 
     * Expands all string identifiers in an attribute array by replacing 
     * them with the appropriate values inside HTMLPurifier_AttrTypes 
     * @param &$attr Reference to attribute array 
     * @param $attr_types HTMLPurifier_AttrTypes instance 
     */ 
    public function expandIdentifiers(&$attr, $attr_types) { 
 
        // because foreach will process new elements we add, make sure we 
        // skip duplicates 
        $processed = array(); 
 
        foreach ($attr as $def_i => $def) { 
            // skip inclusions 
            if ($def_i === 0) continue; 
 
            if (isset($processed[$def_i])) continue; 
 
            // determine whether or not attribute is required 
            if ($required = (strpos($def_i, '*') !== false)) { 
                // rename the definition 
                unset($attr[$def_i]); 
                $def_i = trim($def_i, '*'); 
                $attr[$def_i] = $def; 
            } 
 
            $processed[$def_i] = true; 
 
            // if we've already got a literal object, move on 
            if (is_object($def)) { 
                // preserve previous required 
                $attr[$def_i]->required = ($required || $attr[$def_i]->required); 
                continue; 
            } 
 
            if ($def === false) { 
                unset($attr[$def_i]); 
                continue; 
            } 
 
            if ($t = $attr_types->get($def)) { 
                $attr[$def_i] = $t; 
                $attr[$def_i]->required = $required; 
            } else { 
                unset($attr[$def_i]); 
            } 
        } 
 
    } 
 
} 
 
// vim: et sw=4 sts=4 
 
 |