File indexing completed on 2024-05-05 06:02:16

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