File indexing completed on 2024-05-12 10:06:14
0001 <?php 0002 0003 /** 0004 * Error collection class that enables HTML Purifier to report HTML 0005 * problems back to the user 0006 */ 0007 class HTMLPurifier_ErrorCollector 0008 { 0009 0010 /** 0011 * Identifiers for the returned error array. These are purposely numeric 0012 * so list() can be used. 0013 */ 0014 const LINENO = 0; 0015 const SEVERITY = 1; 0016 const MESSAGE = 2; 0017 const CHILDREN = 3; 0018 0019 /** 0020 * @type array 0021 */ 0022 protected $errors; 0023 0024 /** 0025 * @type array 0026 */ 0027 protected $_current; 0028 0029 /** 0030 * @type array 0031 */ 0032 protected $_stacks = array(array()); 0033 0034 /** 0035 * @type HTMLPurifier_Language 0036 */ 0037 protected $locale; 0038 0039 /** 0040 * @type HTMLPurifier_Generator 0041 */ 0042 protected $generator; 0043 0044 /** 0045 * @type HTMLPurifier_Context 0046 */ 0047 protected $context; 0048 0049 /** 0050 * @type array 0051 */ 0052 protected $lines = array(); 0053 0054 /** 0055 * @param HTMLPurifier_Context $context 0056 */ 0057 public function __construct($context) 0058 { 0059 $this->locale =& $context->get('Locale'); 0060 $this->context = $context; 0061 $this->_current =& $this->_stacks[0]; 0062 $this->errors =& $this->_stacks[0]; 0063 } 0064 0065 /** 0066 * Sends an error message to the collector for later use 0067 * @param int $severity Error severity, PHP error style (don't use E_USER_) 0068 * @param string $msg Error message text 0069 */ 0070 public function send($severity, $msg) 0071 { 0072 $args = array(); 0073 if (func_num_args() > 2) { 0074 $args = func_get_args(); 0075 array_shift($args); 0076 unset($args[0]); 0077 } 0078 0079 $token = $this->context->get('CurrentToken', true); 0080 $line = $token ? $token->line : $this->context->get('CurrentLine', true); 0081 $col = $token ? $token->col : $this->context->get('CurrentCol', true); 0082 $attr = $this->context->get('CurrentAttr', true); 0083 0084 // perform special substitutions, also add custom parameters 0085 $subst = array(); 0086 if (!is_null($token)) { 0087 $args['CurrentToken'] = $token; 0088 } 0089 if (!is_null($attr)) { 0090 $subst['$CurrentAttr.Name'] = $attr; 0091 if (isset($token->attr[$attr])) { 0092 $subst['$CurrentAttr.Value'] = $token->attr[$attr]; 0093 } 0094 } 0095 0096 if (empty($args)) { 0097 $msg = $this->locale->getMessage($msg); 0098 } else { 0099 $msg = $this->locale->formatMessage($msg, $args); 0100 } 0101 0102 if (!empty($subst)) { 0103 $msg = strtr($msg, $subst); 0104 } 0105 0106 // (numerically indexed) 0107 $error = array( 0108 self::LINENO => $line, 0109 self::SEVERITY => $severity, 0110 self::MESSAGE => $msg, 0111 self::CHILDREN => array() 0112 ); 0113 $this->_current[] = $error; 0114 0115 // NEW CODE BELOW ... 0116 // Top-level errors are either: 0117 // TOKEN type, if $value is set appropriately, or 0118 // "syntax" type, if $value is null 0119 $new_struct = new HTMLPurifier_ErrorStruct(); 0120 $new_struct->type = HTMLPurifier_ErrorStruct::TOKEN; 0121 if ($token) { 0122 $new_struct->value = clone $token; 0123 } 0124 if (is_int($line) && is_int($col)) { 0125 if (isset($this->lines[$line][$col])) { 0126 $struct = $this->lines[$line][$col]; 0127 } else { 0128 $struct = $this->lines[$line][$col] = $new_struct; 0129 } 0130 // These ksorts may present a performance problem 0131 ksort($this->lines[$line], SORT_NUMERIC); 0132 } else { 0133 if (isset($this->lines[-1])) { 0134 $struct = $this->lines[-1]; 0135 } else { 0136 $struct = $this->lines[-1] = $new_struct; 0137 } 0138 } 0139 ksort($this->lines, SORT_NUMERIC); 0140 0141 // Now, check if we need to operate on a lower structure 0142 if (!empty($attr)) { 0143 $struct = $struct->getChild(HTMLPurifier_ErrorStruct::ATTR, $attr); 0144 if (!$struct->value) { 0145 $struct->value = array($attr, 'PUT VALUE HERE'); 0146 } 0147 } 0148 if (!empty($cssprop)) { 0149 $struct = $struct->getChild(HTMLPurifier_ErrorStruct::CSSPROP, $cssprop); 0150 if (!$struct->value) { 0151 // if we tokenize CSS this might be a little more difficult to do 0152 $struct->value = array($cssprop, 'PUT VALUE HERE'); 0153 } 0154 } 0155 0156 // Ok, structs are all setup, now time to register the error 0157 $struct->addError($severity, $msg); 0158 } 0159 0160 /** 0161 * Retrieves raw error data for custom formatter to use 0162 */ 0163 public function getRaw() 0164 { 0165 return $this->errors; 0166 } 0167 0168 /** 0169 * Default HTML formatting implementation for error messages 0170 * @param HTMLPurifier_Config $config Configuration, vital for HTML output nature 0171 * @param array $errors Errors array to display; used for recursion. 0172 * @return string 0173 */ 0174 public function getHTMLFormatted($config, $errors = null) 0175 { 0176 $ret = array(); 0177 0178 $this->generator = new HTMLPurifier_Generator($config, $this->context); 0179 if ($errors === null) { 0180 $errors = $this->errors; 0181 } 0182 0183 // 'At line' message needs to be removed 0184 0185 // generation code for new structure goes here. It needs to be recursive. 0186 foreach ($this->lines as $line => $col_array) { 0187 if ($line == -1) { 0188 continue; 0189 } 0190 foreach ($col_array as $col => $struct) { 0191 $this->_renderStruct($ret, $struct, $line, $col); 0192 } 0193 } 0194 if (isset($this->lines[-1])) { 0195 $this->_renderStruct($ret, $this->lines[-1]); 0196 } 0197 0198 if (empty($errors)) { 0199 return '<p>' . $this->locale->getMessage('ErrorCollector: No errors') . '</p>'; 0200 } else { 0201 return '<ul><li>' . implode('</li><li>', $ret) . '</li></ul>'; 0202 } 0203 0204 } 0205 0206 private function _renderStruct(&$ret, $struct, $line = null, $col = null) 0207 { 0208 $stack = array($struct); 0209 $context_stack = array(array()); 0210 while ($current = array_pop($stack)) { 0211 $context = array_pop($context_stack); 0212 foreach ($current->errors as $error) { 0213 list($severity, $msg) = $error; 0214 $string = ''; 0215 $string .= '<div>'; 0216 // W3C uses an icon to indicate the severity of the error. 0217 $error = $this->locale->getErrorName($severity); 0218 $string .= "<span class=\"error e$severity\"><strong>$error</strong></span> "; 0219 if (!is_null($line) && !is_null($col)) { 0220 $string .= "<em class=\"location\">Line $line, Column $col: </em> "; 0221 } else { 0222 $string .= '<em class="location">End of Document: </em> '; 0223 } 0224 $string .= '<strong class="description">' . $this->generator->escape($msg) . '</strong> '; 0225 $string .= '</div>'; 0226 // Here, have a marker for the character on the column appropriate. 0227 // Be sure to clip extremely long lines. 0228 //$string .= '<pre>'; 0229 //$string .= ''; 0230 //$string .= '</pre>'; 0231 $ret[] = $string; 0232 } 0233 foreach ($current->children as $array) { 0234 $context[] = $current; 0235 $stack = array_merge($stack, array_reverse($array, true)); 0236 for ($i = count($array); $i > 0; $i--) { 0237 $context_stack[] = $context; 0238 } 0239 } 0240 } 0241 } 0242 } 0243 0244 // vim: et sw=4 sts=4