getHTMLDefinition ();
// local variables
$generator = new HTMLPurifier_Generator ( $config, $context );
$escape_invalid_tags = $config->get ( 'Core.EscapeInvalidTags' );
// used for autoclose early abortion
$global_parent_allowed_elements = $definition->info_parent_def->child->getAllowedElements ( $config );
$e = $context->get ( 'ErrorCollector', true );
$i = false; // injector index
list ( $zipper, $token ) = HTMLPurifier_Zipper::fromArray ( $tokens );
if ($token === NULL) {
return array ();
}
$reprocess = false; // whether or not to reprocess the same token
$stack = array ();
// member variables
$this->stack = & $stack;
$this->tokens = & $tokens;
$this->token = & $token;
$this->zipper = & $zipper;
$this->config = $config;
$this->context = $context;
// context variables
$context->register ( 'CurrentNesting', $stack );
$context->register ( 'InputZipper', $zipper );
$context->register ( 'CurrentToken', $token );
// -- begin INJECTOR --
$this->injectors = array ();
$injectors = $config->getBatch ( 'AutoFormat' );
$def_injectors = $definition->info_injector;
$custom_injectors = $injectors ['Custom'];
unset ( $injectors ['Custom'] ); // special case
foreach ( $injectors as $injector => $b ) {
// XXX: Fix with a legitimate lookup table of enabled filters
if (strpos ( $injector, '.' ) !== false) {
continue;
}
$injector = "HTMLPurifier_Injector_$injector";
if (! $b) {
continue;
}
$this->injectors [] = new $injector ();
}
foreach ( $def_injectors as $injector ) {
// assumed to be objects
$this->injectors [] = $injector;
}
foreach ( $custom_injectors as $injector ) {
if (! $injector) {
continue;
}
if (is_string ( $injector )) {
$injector = "HTMLPurifier_Injector_$injector";
$injector = new $injector ();
}
$this->injectors [] = $injector;
}
// give the injectors references to the definition and context
// variables for performance reasons
foreach ( $this->injectors as $ix => $injector ) {
$error = $injector->prepare ( $config, $context );
if (! $error) {
continue;
}
array_splice ( $this->injectors, $ix, 1 ); // rm the injector
trigger_error ( "Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING );
}
// -- end INJECTOR --
// a note on reprocessing:
// In order to reduce code duplication, whenever some code needs
// to make HTML changes in order to make things "correct", the
// new HTML gets sent through the purifier, regardless of its
// status. This means that if we add a start token, because it
// was totally necessary, we don't have to update nesting; we just
// punt ($reprocess = true; continue;) and it does that for us.
// isset is in loop because $tokens size changes during loop exec
for(;;
// only increment if we don't need to reprocess
$reprocess ? $reprocess = false : $token = $zipper->next ( $token )) {
// check for a rewind
if (is_int ( $i )) {
// possibility: disable rewinding if the current token has a
// rewind set on it already. This would offer protection from
// infinite loop, but might hinder some advanced rewinding.
$rewind_offset = $this->injectors [$i]->getRewindOffset ();
if (is_int ( $rewind_offset )) {
for($j = 0; $j < $rewind_offset; $j ++) {
if (empty ( $zipper->front ))
break;
$token = $zipper->prev ( $token );
// indicate that other injectors should not process this token,
// but we need to reprocess it
unset ( $token->skip [$i] );
$token->rewind = $i;
if ($token instanceof HTMLPurifier_Token_Start) {
array_pop ( $this->stack );
} elseif ($token instanceof HTMLPurifier_Token_End) {
$this->stack [] = $token->start;
}
}
}
$i = false;
}
// handle case of document end
if ($token === NULL) {
// kill processing if stack is empty
if (empty ( $this->stack )) {
break;
}
// peek
$top_nesting = array_pop ( $this->stack );
$this->stack [] = $top_nesting;
// send error [TagClosedSuppress]
if ($e && ! isset ( $top_nesting->armor ['MakeWellFormed_TagClosedError'] )) {
$e->send ( E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $top_nesting );
}
// append, don't splice, since this is the end
$token = new HTMLPurifier_Token_End ( $top_nesting->name );
// punt!
$reprocess = true;
continue;
}
// echo '
'; printZipper($zipper, $token);//printTokens($this->stack);
// flush();
// quick-check: if it's not a tag, no need to process
if (empty ( $token->is_tag )) {
if ($token instanceof HTMLPurifier_Token_Text) {
foreach ( $this->injectors as $i => $injector ) {
if (isset ( $token->skip [$i] )) {
continue;
}
if ($token->rewind !== null && $token->rewind !== $i) {
continue;
}
// XXX fuckup
$r = $token;
$injector->handleText ( $r );
$token = $this->processToken ( $r, $i );
$reprocess = true;
break;
}
}
// another possibility is a comment
continue;
}
if (isset ( $definition->info [$token->name] )) {
$type = $definition->info [$token->name]->child->type;
} else {
$type = false; // Type is unknown, treat accordingly
}
// quick tag checks: anything that's *not* an end tag
$ok = false;
if ($type === 'empty' && $token instanceof HTMLPurifier_Token_Start) {
// claims to be a start tag but is empty
$token = new HTMLPurifier_Token_Empty ( $token->name, $token->attr, $token->line, $token->col, $token->armor );
$ok = true;
} elseif ($type && $type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) {
// claims to be empty but really is a start tag
// NB: this assignment is required
$old_token = $token;
$token = new HTMLPurifier_Token_End ( $token->name );
$token = $this->insertBefore ( new HTMLPurifier_Token_Start ( $old_token->name, $old_token->attr, $old_token->line, $old_token->col, $old_token->armor ) );
// punt (since we had to modify the input stream in a non-trivial way)
$reprocess = true;
continue;
} elseif ($token instanceof HTMLPurifier_Token_Empty) {
// real empty token
$ok = true;
} elseif ($token instanceof HTMLPurifier_Token_Start) {
// start tag
// ...unless they also have to close their parent
if (! empty ( $this->stack )) {
// Performance note: you might think that it's rather
// inefficient, recalculating the autoclose information
// for every tag that a token closes (since when we
// do an autoclose, we push a new token into the
// stream and then /process/ that, before
// re-processing this token.) But this is
// necessary, because an injector can make an
// arbitrary transformations to the autoclosing
// tokens we introduce, so things may have changed
// in the meantime. Also, doing the inefficient thing is
// "easy" to reason about (for certain perverse definitions
// of "easy")
$parent = array_pop ( $this->stack );
$this->stack [] = $parent;
$parent_def = null;
$parent_elements = null;
$autoclose = false;
if (isset ( $definition->info [$parent->name] )) {
$parent_def = $definition->info [$parent->name];
$parent_elements = $parent_def->child->getAllowedElements ( $config );
$autoclose = ! isset ( $parent_elements [$token->name] );
}
if ($autoclose && $definition->info [$token->name]->wrap) {
// Check if an element can be wrapped by another
// element to make it valid in a context (for
// example,