]>
Commit | Line | Data |
---|---|---|
f45a286b AD |
1 | <?php |
2 | ||
3 | /** | |
4 | * Error collection class that enables HTML Purifier to report HTML | |
5 | * problems back to the user | |
6 | */ | |
7 | class HTMLPurifier_ErrorCollector | |
8 | { | |
9 | ||
10 | /** | |
11 | * Identifiers for the returned error array. These are purposely numeric | |
12 | * so list() can be used. | |
13 | */ | |
14 | const LINENO = 0; | |
15 | const SEVERITY = 1; | |
16 | const MESSAGE = 2; | |
17 | const CHILDREN = 3; | |
18 | ||
19 | protected $errors; | |
20 | protected $_current; | |
21 | protected $_stacks = array(array()); | |
22 | protected $locale; | |
23 | protected $generator; | |
24 | protected $context; | |
25 | ||
26 | protected $lines = array(); | |
27 | ||
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]; | |
33 | } | |
34 | ||
35 | /** | |
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 ... | |
41 | */ | |
42 | public function send($severity, $msg) { | |
43 | ||
44 | $args = array(); | |
45 | if (func_num_args() > 2) { | |
46 | $args = func_get_args(); | |
47 | array_shift($args); | |
48 | unset($args[0]); | |
49 | } | |
50 | ||
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); | |
55 | ||
56 | // perform special substitutions, also add custom parameters | |
57 | $subst = array(); | |
58 | if (!is_null($token)) { | |
59 | $args['CurrentToken'] = $token; | |
60 | } | |
61 | if (!is_null($attr)) { | |
62 | $subst['$CurrentAttr.Name'] = $attr; | |
63 | if (isset($token->attr[$attr])) $subst['$CurrentAttr.Value'] = $token->attr[$attr]; | |
64 | } | |
65 | ||
66 | if (empty($args)) { | |
67 | $msg = $this->locale->getMessage($msg); | |
68 | } else { | |
69 | $msg = $this->locale->formatMessage($msg, $args); | |
70 | } | |
71 | ||
72 | if (!empty($subst)) $msg = strtr($msg, $subst); | |
73 | ||
74 | // (numerically indexed) | |
75 | $error = array( | |
76 | self::LINENO => $line, | |
77 | self::SEVERITY => $severity, | |
78 | self::MESSAGE => $msg, | |
79 | self::CHILDREN => array() | |
80 | ); | |
81 | $this->_current[] = $error; | |
82 | ||
83 | ||
84 | // NEW CODE BELOW ... | |
85 | ||
86 | $struct = null; | |
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]; | |
96 | } else { | |
97 | $struct = $this->lines[$line][$col] = $new_struct; | |
98 | } | |
99 | // These ksorts may present a performance problem | |
100 | ksort($this->lines[$line], SORT_NUMERIC); | |
101 | } else { | |
102 | if (isset($this->lines[-1])) { | |
103 | $struct = $this->lines[-1]; | |
104 | } else { | |
105 | $struct = $this->lines[-1] = $new_struct; | |
106 | } | |
107 | } | |
108 | ksort($this->lines, SORT_NUMERIC); | |
109 | ||
110 | // Now, check if we need to operate on a lower structure | |
111 | if (!empty($attr)) { | |
112 | $struct = $struct->getChild(HTMLPurifier_ErrorStruct::ATTR, $attr); | |
113 | if (!$struct->value) { | |
114 | $struct->value = array($attr, 'PUT VALUE HERE'); | |
115 | } | |
116 | } | |
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'); | |
122 | } | |
123 | } | |
124 | ||
125 | // Ok, structs are all setup, now time to register the error | |
126 | $struct->addError($severity, $msg); | |
127 | } | |
128 | ||
129 | /** | |
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) | |
134 | */ | |
135 | public function getRaw() { | |
136 | return $this->errors; | |
137 | } | |
138 | ||
139 | /** | |
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. | |
143 | */ | |
144 | public function getHTMLFormatted($config, $errors = null) { | |
145 | $ret = array(); | |
146 | ||
147 | $this->generator = new HTMLPurifier_Generator($config, $this->context); | |
148 | if ($errors === null) $errors = $this->errors; | |
149 | ||
150 | // 'At line' message needs to be removed | |
151 | ||
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); | |
157 | } | |
158 | } | |
159 | if (isset($this->lines[-1])) { | |
160 | $this->_renderStruct($ret, $this->lines[-1]); | |
161 | } | |
162 | ||
163 | if (empty($errors)) { | |
164 | return '<p>' . $this->locale->getMessage('ErrorCollector: No errors') . '</p>'; | |
165 | } else { | |
166 | return '<ul><li>' . implode('</li><li>', $ret) . '</li></ul>'; | |
167 | } | |
168 | ||
169 | } | |
170 | ||
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; | |
178 | $string = ''; | |
179 | $string .= '<div>'; | |
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> "; | |
185 | } else { | |
186 | $string .= '<em class="location">End of Document: </em> '; | |
187 | } | |
188 | $string .= '<strong class="description">' . $this->generator->escape($msg) . '</strong> '; | |
189 | $string .= '</div>'; | |
190 | // Here, have a marker for the character on the column appropriate. | |
191 | // Be sure to clip extremely long lines. | |
192 | //$string .= '<pre>'; | |
193 | //$string .= ''; | |
194 | //$string .= '</pre>'; | |
195 | $ret[] = $string; | |
196 | } | |
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; | |
202 | } | |
203 | } | |
204 | } | |
205 | } | |
206 | ||
207 | } | |
208 | ||
209 | // vim: et sw=4 sts=4 |