mirror of
https://github.com/ezyang/htmlpurifier.git
synced 2024-12-22 08:21:52 +00:00
fac747bdbd
With minor corrections. Signed-off-by: Marcus Bointon <marcus@synchromedia.co.uk> Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
245 lines
7.4 KiB
PHP
245 lines
7.4 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Error collection class that enables HTML Purifier to report HTML
|
|
* problems back to the user
|
|
*/
|
|
class HTMLPurifier_ErrorCollector
|
|
{
|
|
|
|
/**
|
|
* Identifiers for the returned error array. These are purposely numeric
|
|
* so list() can be used.
|
|
*/
|
|
const LINENO = 0;
|
|
const SEVERITY = 1;
|
|
const MESSAGE = 2;
|
|
const CHILDREN = 3;
|
|
|
|
/**
|
|
* @type array
|
|
*/
|
|
protected $errors;
|
|
|
|
/**
|
|
* @type array
|
|
*/
|
|
protected $_current;
|
|
|
|
/**
|
|
* @type array
|
|
*/
|
|
protected $_stacks = array(array());
|
|
|
|
/**
|
|
* @type HTMLPurifier_Language
|
|
*/
|
|
protected $locale;
|
|
|
|
/**
|
|
* @type HTMLPurifier_Generator
|
|
*/
|
|
protected $generator;
|
|
|
|
/**
|
|
* @type HTMLPurifier_Context
|
|
*/
|
|
protected $context;
|
|
|
|
/**
|
|
* @type array
|
|
*/
|
|
protected $lines = array();
|
|
|
|
/**
|
|
* @param HTMLPurifier_Context $context
|
|
*/
|
|
public function __construct($context)
|
|
{
|
|
$this->locale =& $context->get('Locale');
|
|
$this->context = $context;
|
|
$this->_current =& $this->_stacks[0];
|
|
$this->errors =& $this->_stacks[0];
|
|
}
|
|
|
|
/**
|
|
* Sends an error message to the collector for later use
|
|
* @param int $severity Error severity, PHP error style (don't use E_USER_)
|
|
* @param string $msg Error message text
|
|
*/
|
|
public function send($severity, $msg)
|
|
{
|
|
$args = array();
|
|
if (func_num_args() > 2) {
|
|
$args = func_get_args();
|
|
array_shift($args);
|
|
unset($args[0]);
|
|
}
|
|
|
|
$token = $this->context->get('CurrentToken', true);
|
|
$line = $token ? $token->line : $this->context->get('CurrentLine', true);
|
|
$col = $token ? $token->col : $this->context->get('CurrentCol', true);
|
|
$attr = $this->context->get('CurrentAttr', true);
|
|
|
|
// perform special substitutions, also add custom parameters
|
|
$subst = array();
|
|
if (!is_null($token)) {
|
|
$args['CurrentToken'] = $token;
|
|
}
|
|
if (!is_null($attr)) {
|
|
$subst['$CurrentAttr.Name'] = $attr;
|
|
if (isset($token->attr[$attr])) {
|
|
$subst['$CurrentAttr.Value'] = $token->attr[$attr];
|
|
}
|
|
}
|
|
|
|
if (empty($args)) {
|
|
$msg = $this->locale->getMessage($msg);
|
|
} else {
|
|
$msg = $this->locale->formatMessage($msg, $args);
|
|
}
|
|
|
|
if (!empty($subst)) {
|
|
$msg = strtr($msg, $subst);
|
|
}
|
|
|
|
// (numerically indexed)
|
|
$error = array(
|
|
self::LINENO => $line,
|
|
self::SEVERITY => $severity,
|
|
self::MESSAGE => $msg,
|
|
self::CHILDREN => array()
|
|
);
|
|
$this->_current[] = $error;
|
|
|
|
// NEW CODE BELOW ...
|
|
// Top-level errors are either:
|
|
// TOKEN type, if $value is set appropriately, or
|
|
// "syntax" type, if $value is null
|
|
$new_struct = new HTMLPurifier_ErrorStruct();
|
|
$new_struct->type = HTMLPurifier_ErrorStruct::TOKEN;
|
|
if ($token) {
|
|
$new_struct->value = clone $token;
|
|
}
|
|
if (is_int($line) && is_int($col)) {
|
|
if (isset($this->lines[$line][$col])) {
|
|
$struct = $this->lines[$line][$col];
|
|
} else {
|
|
$struct = $this->lines[$line][$col] = $new_struct;
|
|
}
|
|
// These ksorts may present a performance problem
|
|
ksort($this->lines[$line], SORT_NUMERIC);
|
|
} else {
|
|
if (isset($this->lines[-1])) {
|
|
$struct = $this->lines[-1];
|
|
} else {
|
|
$struct = $this->lines[-1] = $new_struct;
|
|
}
|
|
}
|
|
ksort($this->lines, SORT_NUMERIC);
|
|
|
|
// Now, check if we need to operate on a lower structure
|
|
if (!empty($attr)) {
|
|
$struct = $struct->getChild(HTMLPurifier_ErrorStruct::ATTR, $attr);
|
|
if (!$struct->value) {
|
|
$struct->value = array($attr, 'PUT VALUE HERE');
|
|
}
|
|
}
|
|
if (!empty($cssprop)) {
|
|
$struct = $struct->getChild(HTMLPurifier_ErrorStruct::CSSPROP, $cssprop);
|
|
if (!$struct->value) {
|
|
// if we tokenize CSS this might be a little more difficult to do
|
|
$struct->value = array($cssprop, 'PUT VALUE HERE');
|
|
}
|
|
}
|
|
|
|
// Ok, structs are all setup, now time to register the error
|
|
$struct->addError($severity, $msg);
|
|
}
|
|
|
|
/**
|
|
* Retrieves raw error data for custom formatter to use
|
|
*/
|
|
public function getRaw()
|
|
{
|
|
return $this->errors;
|
|
}
|
|
|
|
/**
|
|
* Default HTML formatting implementation for error messages
|
|
* @param HTMLPurifier_Config $config Configuration, vital for HTML output nature
|
|
* @param array $errors Errors array to display; used for recursion.
|
|
* @return string
|
|
*/
|
|
public function getHTMLFormatted($config, $errors = null)
|
|
{
|
|
$ret = array();
|
|
|
|
$this->generator = new HTMLPurifier_Generator($config, $this->context);
|
|
if ($errors === null) {
|
|
$errors = $this->errors;
|
|
}
|
|
|
|
// 'At line' message needs to be removed
|
|
|
|
// generation code for new structure goes here. It needs to be recursive.
|
|
foreach ($this->lines as $line => $col_array) {
|
|
if ($line == -1) {
|
|
continue;
|
|
}
|
|
foreach ($col_array as $col => $struct) {
|
|
$this->_renderStruct($ret, $struct, $line, $col);
|
|
}
|
|
}
|
|
if (isset($this->lines[-1])) {
|
|
$this->_renderStruct($ret, $this->lines[-1]);
|
|
}
|
|
|
|
if (empty($errors)) {
|
|
return '<p>' . $this->locale->getMessage('ErrorCollector: No errors') . '</p>';
|
|
} else {
|
|
return '<ul><li>' . implode('</li><li>', $ret) . '</li></ul>';
|
|
}
|
|
|
|
}
|
|
|
|
private function _renderStruct(&$ret, $struct, $line = null, $col = null)
|
|
{
|
|
$stack = array($struct);
|
|
$context_stack = array(array());
|
|
while ($current = array_pop($stack)) {
|
|
$context = array_pop($context_stack);
|
|
foreach ($current->errors as $error) {
|
|
list($severity, $msg) = $error;
|
|
$string = '';
|
|
$string .= '<div>';
|
|
// W3C uses an icon to indicate the severity of the error.
|
|
$error = $this->locale->getErrorName($severity);
|
|
$string .= "<span class=\"error e$severity\"><strong>$error</strong></span> ";
|
|
if (!is_null($line) && !is_null($col)) {
|
|
$string .= "<em class=\"location\">Line $line, Column $col: </em> ";
|
|
} else {
|
|
$string .= '<em class="location">End of Document: </em> ';
|
|
}
|
|
$string .= '<strong class="description">' . $this->generator->escape($msg) . '</strong> ';
|
|
$string .= '</div>';
|
|
// Here, have a marker for the character on the column appropriate.
|
|
// Be sure to clip extremely long lines.
|
|
//$string .= '<pre>';
|
|
//$string .= '';
|
|
//$string .= '</pre>';
|
|
$ret[] = $string;
|
|
}
|
|
foreach ($current->children as $array) {
|
|
$context[] = $current;
|
|
$stack = array_merge($stack, array_reverse($array, true));
|
|
for ($i = count($array); $i > 0; $i--) {
|
|
$context_stack[] = $context;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// vim: et sw=4 sts=4
|