File indexing completed on 2024-04-28 05:58:53

0001 <?php
0002 
0003 /**
0004  * @todo Unit test
0005  */
0006 class HTMLPurifier_ContentSets
0007 {
0008 
0009     /**
0010      * List of content set strings (pipe separators) indexed by name.
0011      * @type array
0012      */
0013     public $info = array();
0014 
0015     /**
0016      * List of content set lookups (element => true) indexed by name.
0017      * @type array
0018      * @note This is in HTMLPurifier_HTMLDefinition->info_content_sets
0019      */
0020     public $lookup = array();
0021 
0022     /**
0023      * Synchronized list of defined content sets (keys of info).
0024      * @type array
0025      */
0026     protected $keys = array();
0027     /**
0028      * Synchronized list of defined content values (values of info).
0029      * @type array
0030      */
0031     protected $values = array();
0032 
0033     /**
0034      * Merges in module's content sets, expands identifiers in the content
0035      * sets and populates the keys, values and lookup member variables.
0036      * @param HTMLPurifier_HTMLModule[] $modules List of HTMLPurifier_HTMLModule
0037      */
0038     public function __construct($modules)
0039     {
0040         if (!is_array($modules)) {
0041             $modules = array($modules);
0042         }
0043         // populate content_sets based on module hints
0044         // sorry, no way of overloading
0045         foreach ($modules as $module) {
0046             foreach ($module->content_sets as $key => $value) {
0047                 $temp = $this->convertToLookup($value);
0048                 if (isset($this->lookup[$key])) {
0049                     // add it into the existing content set
0050                     $this->lookup[$key] = array_merge($this->lookup[$key], $temp);
0051                 } else {
0052                     $this->lookup[$key] = $temp;
0053                 }
0054             }
0055         }
0056         $old_lookup = false;
0057         while ($old_lookup !== $this->lookup) {
0058             $old_lookup = $this->lookup;
0059             foreach ($this->lookup as $i => $set) {
0060                 $add = array();
0061                 foreach ($set as $element => $x) {
0062                     if (isset($this->lookup[$element])) {
0063                         $add += $this->lookup[$element];
0064                         unset($this->lookup[$i][$element]);
0065                     }
0066                 }
0067                 $this->lookup[$i] += $add;
0068             }
0069         }
0070 
0071         foreach ($this->lookup as $key => $lookup) {
0072             $this->info[$key] = implode(' | ', array_keys($lookup));
0073         }
0074         $this->keys   = array_keys($this->info);
0075         $this->values = array_values($this->info);
0076     }
0077 
0078     /**
0079      * Accepts a definition; generates and assigns a ChildDef for it
0080      * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef reference
0081      * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef
0082      */
0083     public function generateChildDef(&$def, $module)
0084     {
0085         if (!empty($def->child)) { // already done!
0086             return;
0087         }
0088         $content_model = $def->content_model;
0089         if (is_string($content_model)) {
0090             // Assume that $this->keys is alphanumeric
0091             $def->content_model = preg_replace_callback(
0092                 '/\b(' . implode('|', $this->keys) . ')\b/',
0093                 array($this, 'generateChildDefCallback'),
0094                 $content_model
0095             );
0096             //$def->content_model = str_replace(
0097             //    $this->keys, $this->values, $content_model);
0098         }
0099         $def->child = $this->getChildDef($def, $module);
0100     }
0101 
0102     public function generateChildDefCallback($matches)
0103     {
0104         return $this->info[$matches[0]];
0105     }
0106 
0107     /**
0108      * Instantiates a ChildDef based on content_model and content_model_type
0109      * member variables in HTMLPurifier_ElementDef
0110      * @note This will also defer to modules for custom HTMLPurifier_ChildDef
0111      *       subclasses that need content set expansion
0112      * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef to have ChildDef extracted
0113      * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef
0114      * @return HTMLPurifier_ChildDef corresponding to ElementDef
0115      */
0116     public function getChildDef($def, $module)
0117     {
0118         $value = $def->content_model;
0119         if (is_object($value)) {
0120             trigger_error(
0121                 'Literal object child definitions should be stored in '.
0122                 'ElementDef->child not ElementDef->content_model',
0123                 E_USER_NOTICE
0124             );
0125             return $value;
0126         }
0127         switch ($def->content_model_type) {
0128             case 'required':
0129                 return new HTMLPurifier_ChildDef_Required($value);
0130             case 'optional':
0131                 return new HTMLPurifier_ChildDef_Optional($value);
0132             case 'empty':
0133                 return new HTMLPurifier_ChildDef_Empty();
0134             case 'custom':
0135                 return new HTMLPurifier_ChildDef_Custom($value);
0136         }
0137         // defer to its module
0138         $return = false;
0139         if ($module->defines_child_def) { // save a func call
0140             $return = $module->getChildDef($def);
0141         }
0142         if ($return !== false) {
0143             return $return;
0144         }
0145         // error-out
0146         trigger_error(
0147             'Could not determine which ChildDef class to instantiate',
0148             E_USER_ERROR
0149         );
0150         return false;
0151     }
0152 
0153     /**
0154      * Converts a string list of elements separated by pipes into
0155      * a lookup array.
0156      * @param string $string List of elements
0157      * @return array Lookup array of elements
0158      */
0159     protected function convertToLookup($string)
0160     {
0161         $array = explode('|', str_replace(' ', '', $string));
0162         $ret = array();
0163         foreach ($array as $k) {
0164             $ret[$k] = true;
0165         }
0166         return $ret;
0167     }
0168 }
0169 
0170 // vim: et sw=4 sts=4