File indexing completed on 2025-01-26 05:29:07
0001 <?php 0002 0003 /** 0004 * Abstract class for a set of proprietary modules that clean up (tidy) 0005 * poorly written HTML. 0006 * @todo Figure out how to protect some of these methods/properties 0007 */ 0008 class HTMLPurifier_HTMLModule_Tidy extends HTMLPurifier_HTMLModule 0009 { 0010 /** 0011 * List of supported levels. 0012 * Index zero is a special case "no fixes" level. 0013 * @type array 0014 */ 0015 public $levels = array(0 => 'none', 'light', 'medium', 'heavy'); 0016 0017 /** 0018 * Default level to place all fixes in. 0019 * Disabled by default. 0020 * @type string 0021 */ 0022 public $defaultLevel = null; 0023 0024 /** 0025 * Lists of fixes used by getFixesForLevel(). 0026 * Format is: 0027 * HTMLModule_Tidy->fixesForLevel[$level] = array('fix-1', 'fix-2'); 0028 * @type array 0029 */ 0030 public $fixesForLevel = array( 0031 'light' => array(), 0032 'medium' => array(), 0033 'heavy' => array() 0034 ); 0035 0036 /** 0037 * Lazy load constructs the module by determining the necessary 0038 * fixes to create and then delegating to the populate() function. 0039 * @param HTMLPurifier_Config $config 0040 * @todo Wildcard matching and error reporting when an added or 0041 * subtracted fix has no effect. 0042 */ 0043 public function setup($config) 0044 { 0045 // create fixes, initialize fixesForLevel 0046 $fixes = $this->makeFixes(); 0047 $this->makeFixesForLevel($fixes); 0048 0049 // figure out which fixes to use 0050 $level = $config->get('HTML.TidyLevel'); 0051 $fixes_lookup = $this->getFixesForLevel($level); 0052 0053 // get custom fix declarations: these need namespace processing 0054 $add_fixes = $config->get('HTML.TidyAdd'); 0055 $remove_fixes = $config->get('HTML.TidyRemove'); 0056 0057 foreach ($fixes as $name => $fix) { 0058 // needs to be refactored a little to implement globbing 0059 if (isset($remove_fixes[$name]) || 0060 (!isset($add_fixes[$name]) && !isset($fixes_lookup[$name]))) { 0061 unset($fixes[$name]); 0062 } 0063 } 0064 0065 // populate this module with necessary fixes 0066 $this->populate($fixes); 0067 } 0068 0069 /** 0070 * Retrieves all fixes per a level, returning fixes for that specific 0071 * level as well as all levels below it. 0072 * @param string $level level identifier, see $levels for valid values 0073 * @return array Lookup up table of fixes 0074 */ 0075 public function getFixesForLevel($level) 0076 { 0077 if ($level == $this->levels[0]) { 0078 return array(); 0079 } 0080 $activated_levels = array(); 0081 for ($i = 1, $c = count($this->levels); $i < $c; $i++) { 0082 $activated_levels[] = $this->levels[$i]; 0083 if ($this->levels[$i] == $level) { 0084 break; 0085 } 0086 } 0087 if ($i == $c) { 0088 trigger_error( 0089 'Tidy level ' . htmlspecialchars($level) . ' not recognized', 0090 E_USER_WARNING 0091 ); 0092 return array(); 0093 } 0094 $ret = array(); 0095 foreach ($activated_levels as $level) { 0096 foreach ($this->fixesForLevel[$level] as $fix) { 0097 $ret[$fix] = true; 0098 } 0099 } 0100 return $ret; 0101 } 0102 0103 /** 0104 * Dynamically populates the $fixesForLevel member variable using 0105 * the fixes array. It may be custom overloaded, used in conjunction 0106 * with $defaultLevel, or not used at all. 0107 * @param array $fixes 0108 */ 0109 public function makeFixesForLevel($fixes) 0110 { 0111 if (!isset($this->defaultLevel)) { 0112 return; 0113 } 0114 if (!isset($this->fixesForLevel[$this->defaultLevel])) { 0115 trigger_error( 0116 'Default level ' . $this->defaultLevel . ' does not exist', 0117 E_USER_ERROR 0118 ); 0119 return; 0120 } 0121 $this->fixesForLevel[$this->defaultLevel] = array_keys($fixes); 0122 } 0123 0124 /** 0125 * Populates the module with transforms and other special-case code 0126 * based on a list of fixes passed to it 0127 * @param array $fixes Lookup table of fixes to activate 0128 */ 0129 public function populate($fixes) 0130 { 0131 foreach ($fixes as $name => $fix) { 0132 // determine what the fix is for 0133 list($type, $params) = $this->getFixType($name); 0134 switch ($type) { 0135 case 'attr_transform_pre': 0136 case 'attr_transform_post': 0137 $attr = $params['attr']; 0138 if (isset($params['element'])) { 0139 $element = $params['element']; 0140 if (empty($this->info[$element])) { 0141 $e = $this->addBlankElement($element); 0142 } else { 0143 $e = $this->info[$element]; 0144 } 0145 } else { 0146 $type = "info_$type"; 0147 $e = $this; 0148 } 0149 // PHP does some weird parsing when I do 0150 // $e->$type[$attr], so I have to assign a ref. 0151 $f =& $e->$type; 0152 $f[$attr] = $fix; 0153 break; 0154 case 'tag_transform': 0155 $this->info_tag_transform[$params['element']] = $fix; 0156 break; 0157 case 'child': 0158 case 'content_model_type': 0159 $element = $params['element']; 0160 if (empty($this->info[$element])) { 0161 $e = $this->addBlankElement($element); 0162 } else { 0163 $e = $this->info[$element]; 0164 } 0165 $e->$type = $fix; 0166 break; 0167 default: 0168 trigger_error("Fix type $type not supported", E_USER_ERROR); 0169 break; 0170 } 0171 } 0172 } 0173 0174 /** 0175 * Parses a fix name and determines what kind of fix it is, as well 0176 * as other information defined by the fix 0177 * @param $name String name of fix 0178 * @return array(string $fix_type, array $fix_parameters) 0179 * @note $fix_parameters is type dependant, see populate() for usage 0180 * of these parameters 0181 */ 0182 public function getFixType($name) 0183 { 0184 // parse it 0185 $property = $attr = null; 0186 if (strpos($name, '#') !== false) { 0187 list($name, $property) = explode('#', $name); 0188 } 0189 if (strpos($name, '@') !== false) { 0190 list($name, $attr) = explode('@', $name); 0191 } 0192 0193 // figure out the parameters 0194 $params = array(); 0195 if ($name !== '') { 0196 $params['element'] = $name; 0197 } 0198 if (!is_null($attr)) { 0199 $params['attr'] = $attr; 0200 } 0201 0202 // special case: attribute transform 0203 if (!is_null($attr)) { 0204 if (is_null($property)) { 0205 $property = 'pre'; 0206 } 0207 $type = 'attr_transform_' . $property; 0208 return array($type, $params); 0209 } 0210 0211 // special case: tag transform 0212 if (is_null($property)) { 0213 return array('tag_transform', $params); 0214 } 0215 0216 return array($property, $params); 0217 0218 } 0219 0220 /** 0221 * Defines all fixes the module will perform in a compact 0222 * associative array of fix name to fix implementation. 0223 * @return array 0224 */ 0225 public function makeFixes() 0226 { 0227 } 0228 } 0229 0230 // vim: et sw=4 sts=4