4 * Error collection class that enables HTML Purifier to report HTML
5 * problems back to the user
7 class HTMLPurifier_ErrorCollector
11 * Identifiers for the returned error array. These are purposely numeric
12 * so list() can be used.
21 protected $_stacks = array(array());
26 protected $lines = array();
28 public function __construct($context) {
29 $this->locale =& $context->get('Locale');
30 $this->context = $context;
31 $this->_current =& $this->_stacks[0];
32 $this->errors =& $this->_stacks[0];
36 * Sends an error message to the collector for later use
37 * @param $severity int Error severity, PHP error style (don't use E_USER_)
38 * @param $msg string Error message text
39 * @param $subst1 string First substitution for $msg
40 * @param $subst2 string ...
42 public function send($severity, $msg) {
45 if (func_num_args() > 2) {
46 $args = func_get_args();
51 $token = $this->context->get('CurrentToken', true);
52 $line = $token ? $token->line : $this->context->get('CurrentLine', true);
53 $col = $token ? $token->col : $this->context->get('CurrentCol', true);
54 $attr = $this->context->get('CurrentAttr', true);
56 // perform special substitutions, also add custom parameters
58 if (!is_null($token)) {
59 $args['CurrentToken'] = $token;
61 if (!is_null($attr)) {
62 $subst['$CurrentAttr.Name'] = $attr;
63 if (isset($token->attr[$attr])) $subst['$CurrentAttr.Value'] = $token->attr[$attr];
67 $msg = $this->locale->getMessage($msg);
69 $msg = $this->locale->formatMessage($msg, $args);
72 if (!empty($subst)) $msg = strtr($msg, $subst);
74 // (numerically indexed)
76 self::LINENO => $line,
77 self::SEVERITY => $severity,
78 self::MESSAGE => $msg,
79 self::CHILDREN => array()
81 $this->_current[] = $error;
87 // Top-level errors are either:
88 // TOKEN type, if $value is set appropriately, or
89 // "syntax" type, if $value is null
90 $new_struct = new HTMLPurifier_ErrorStruct();
91 $new_struct->type = HTMLPurifier_ErrorStruct::TOKEN;
92 if ($token) $new_struct->value = clone $token;
93 if (is_int($line) && is_int($col)) {
94 if (isset($this->lines[$line][$col])) {
95 $struct = $this->lines[$line][$col];
97 $struct = $this->lines[$line][$col] = $new_struct;
99 // These ksorts may present a performance problem
100 ksort($this->lines[$line], SORT_NUMERIC);
102 if (isset($this->lines[-1])) {
103 $struct = $this->lines[-1];
105 $struct = $this->lines[-1] = $new_struct;
108 ksort($this->lines, SORT_NUMERIC);
110 // Now, check if we need to operate on a lower structure
112 $struct = $struct->getChild(HTMLPurifier_ErrorStruct::ATTR, $attr);
113 if (!$struct->value) {
114 $struct->value = array($attr, 'PUT VALUE HERE');
117 if (!empty($cssprop)) {
118 $struct = $struct->getChild(HTMLPurifier_ErrorStruct::CSSPROP, $cssprop);
119 if (!$struct->value) {
120 // if we tokenize CSS this might be a little more difficult to do
121 $struct->value = array($cssprop, 'PUT VALUE HERE');
125 // Ok, structs are all setup, now time to register the error
126 $struct->addError($severity, $msg);
130 * Retrieves raw error data for custom formatter to use
131 * @param List of arrays in format of array(line of error,
132 * error severity, error message,
133 * recursive sub-errors array)
135 public function getRaw() {
136 return $this->errors;
140 * Default HTML formatting implementation for error messages
141 * @param $config Configuration array, vital for HTML output nature
142 * @param $errors Errors array to display; used for recursion.
144 public function getHTMLFormatted($config, $errors = null) {
147 $this->generator = new HTMLPurifier_Generator($config, $this->context);
148 if ($errors === null) $errors = $this->errors;
150 // 'At line' message needs to be removed
152 // generation code for new structure goes here. It needs to be recursive.
153 foreach ($this->lines as $line => $col_array) {
154 if ($line == -1) continue;
155 foreach ($col_array as $col => $struct) {
156 $this->_renderStruct($ret, $struct, $line, $col);
159 if (isset($this->lines[-1])) {
160 $this->_renderStruct($ret, $this->lines[-1]);
163 if (empty($errors)) {
164 return '<p>' . $this->locale->getMessage('ErrorCollector: No errors') . '</p>';
166 return '<ul><li>' . implode('</li><li>', $ret) . '</li></ul>';
171 private function _renderStruct(&$ret, $struct, $line = null, $col = null) {
172 $stack = array($struct);
173 $context_stack = array(array());
174 while ($current = array_pop($stack)) {
175 $context = array_pop($context_stack);
176 foreach ($current->errors as $error) {
177 list($severity, $msg) = $error;
180 // W3C uses an icon to indicate the severity of the error.
181 $error = $this->locale->getErrorName($severity);
182 $string .= "<span class=\"error e$severity\"><strong>$error</strong></span> ";
183 if (!is_null($line) && !is_null($col)) {
184 $string .= "<em class=\"location\">Line $line, Column $col: </em> ";
186 $string .= '<em class="location">End of Document: </em> ';
188 $string .= '<strong class="description">' . $this->generator->escape($msg) . '</strong> ';
190 // Here, have a marker for the character on the column appropriate.
191 // Be sure to clip extremely long lines.
192 //$string .= '<pre>';
194 //$string .= '</pre>';
197 foreach ($current->children as $type => $array) {
198 $context[] = $current;
199 $stack = array_merge($stack, array_reverse($array, true));
200 for ($i = count($array); $i > 0; $i--) {
201 $context_stack[] = $context;
209 // vim: et sw=4 sts=4