2006-07-23 23:29:12 +00:00
|
|
|
<?php
|
|
|
|
|
2006-08-20 21:59:41 +00:00
|
|
|
/**
|
|
|
|
* Takes tokens makes them well-formed (balance end tags, etc.)
|
|
|
|
*/
|
2006-07-23 23:29:12 +00:00
|
|
|
class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
|
|
|
{
|
|
|
|
|
2007-06-24 17:44:27 +00:00
|
|
|
/**
|
|
|
|
* Locally shared variable references
|
|
|
|
*/
|
2007-11-25 02:24:39 +00:00
|
|
|
protected $inputTokens, $inputIndex, $outputTokens, $currentNesting,
|
2007-06-24 17:44:27 +00:00
|
|
|
$currentInjector, $injectors;
|
|
|
|
|
2008-01-05 00:10:43 +00:00
|
|
|
public function execute($tokens, $config, $context) {
|
2007-06-23 17:11:05 +00:00
|
|
|
|
2006-08-31 20:33:07 +00:00
|
|
|
$definition = $config->getHTMLDefinition();
|
2007-06-23 17:11:05 +00:00
|
|
|
|
2007-10-02 22:50:59 +00:00
|
|
|
// local variables
|
2008-05-26 04:05:48 +00:00
|
|
|
$generator = new HTMLPurifier_Generator($config, $context);
|
2007-10-02 22:50:59 +00:00
|
|
|
$escape_invalid_tags = $config->get('Core', 'EscapeInvalidTags');
|
2007-12-05 01:26:28 +00:00
|
|
|
$e = $context->get('ErrorCollector', true);
|
2007-10-02 22:50:59 +00:00
|
|
|
|
|
|
|
// member variables
|
2007-06-24 17:44:27 +00:00
|
|
|
$this->currentNesting = array();
|
2007-10-02 22:50:59 +00:00
|
|
|
$this->inputIndex = false;
|
|
|
|
$this->inputTokens =& $tokens;
|
Implement Injector->handleEnd, with lots of refactoring for injector.
Previous design of injector streaming involved editability only to start, empty
and text tokens, because they could be safely modified without causing formedness
errors. By modifying notifyEnd to operate before MakeWellFormed's safeguards
kick into effect, it can be converted into a handle function, allowing for
arbitrary modification of end tags.
This change involved quite a bit of restructuring of the MakeWellFormed code,
including the moving of end of document tags to inside the loop, so rewinding
on those tags would be functional, increased reuse of the end tag codepath by
code that inserts end tags (as they could be changed out from under you), and
processToken modified to have an extra parameter to force re-processing of
a token if the original token was an end token.
We're not exactly sure if handleEnd works at this point, but the important
talking point about this refactoring is that nothing else broke. Also, a number
of convenience functions were moved from AutoParagraph to the Injector
supertype (specifically: forward, forwardToEndToken, backward, and current).
Signed-off-by: Edward Z. Yang <edwardzyang@thewritingpot.com>
2008-10-01 04:54:51 +00:00
|
|
|
$this->config = $config;
|
|
|
|
$this->context = $context;
|
2007-06-23 17:11:05 +00:00
|
|
|
|
2007-10-02 22:50:59 +00:00
|
|
|
// context variables
|
|
|
|
$context->register('CurrentNesting', $this->currentNesting);
|
2007-12-05 01:26:28 +00:00
|
|
|
$context->register('InputIndex', $this->inputIndex);
|
|
|
|
$context->register('InputTokens', $tokens);
|
2007-06-26 15:07:07 +00:00
|
|
|
|
2007-06-24 04:22:28 +00:00
|
|
|
// -- begin INJECTOR --
|
|
|
|
|
2007-06-24 17:44:27 +00:00
|
|
|
$this->injectors = array();
|
2007-06-23 17:44:28 +00:00
|
|
|
|
2007-06-24 21:35:34 +00:00
|
|
|
$injectors = $config->getBatch('AutoFormat');
|
2008-06-09 01:23:05 +00:00
|
|
|
$def_injectors = $definition->info_injector;
|
2007-06-24 21:35:34 +00:00
|
|
|
$custom_injectors = $injectors['Custom'];
|
|
|
|
unset($injectors['Custom']); // special case
|
|
|
|
foreach ($injectors as $injector => $b) {
|
|
|
|
$injector = "HTMLPurifier_Injector_$injector";
|
2007-06-28 13:06:15 +00:00
|
|
|
if (!$b) continue;
|
|
|
|
$this->injectors[] = new $injector;
|
2007-06-24 02:27:57 +00:00
|
|
|
}
|
2008-06-09 01:23:05 +00:00
|
|
|
foreach ($def_injectors as $injector) {
|
|
|
|
// assumed to be objects
|
|
|
|
$this->injectors[] = $injector;
|
|
|
|
}
|
2007-06-24 21:35:34 +00:00
|
|
|
foreach ($custom_injectors as $injector) {
|
|
|
|
if (is_string($injector)) {
|
|
|
|
$injector = "HTMLPurifier_Injector_$injector";
|
|
|
|
$injector = new $injector;
|
|
|
|
}
|
|
|
|
$this->injectors[] = $injector;
|
2007-06-24 02:45:38 +00:00
|
|
|
}
|
|
|
|
|
2007-06-24 04:22:28 +00:00
|
|
|
// array index of the injector that resulted in an array
|
|
|
|
// substitution. This enables processTokens() to know which
|
|
|
|
// injectors are affected by the added tokens and which are
|
|
|
|
// not (namely, the ones after the current injector are not
|
|
|
|
// affected)
|
2007-06-24 17:44:27 +00:00
|
|
|
$this->currentInjector = false;
|
2007-06-24 02:27:57 +00:00
|
|
|
|
2007-06-24 17:44:27 +00:00
|
|
|
// give the injectors references to the definition and context
|
|
|
|
// variables for performance reasons
|
2007-12-05 01:26:28 +00:00
|
|
|
foreach ($this->injectors as $i => $injector) {
|
|
|
|
$error = $injector->prepare($config, $context);
|
2007-06-28 13:06:15 +00:00
|
|
|
if (!$error) continue;
|
2007-12-05 01:26:28 +00:00
|
|
|
array_splice($this->injectors, $i, 1); // rm the injector
|
|
|
|
trigger_error("Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING);
|
2007-06-24 17:44:27 +00:00
|
|
|
}
|
2007-06-24 04:22:28 +00:00
|
|
|
|
|
|
|
// -- end INJECTOR --
|
|
|
|
|
2007-06-26 15:07:07 +00:00
|
|
|
$token = false;
|
|
|
|
$context->register('CurrentToken', $token);
|
|
|
|
|
2008-10-01 07:14:28 +00:00
|
|
|
$reprocess = false;
|
|
|
|
$i = false; // injector index
|
|
|
|
|
2007-12-05 01:26:28 +00:00
|
|
|
// isset is in loop because $tokens size changes during loop exec
|
Implement Injector->handleEnd, with lots of refactoring for injector.
Previous design of injector streaming involved editability only to start, empty
and text tokens, because they could be safely modified without causing formedness
errors. By modifying notifyEnd to operate before MakeWellFormed's safeguards
kick into effect, it can be converted into a handle function, allowing for
arbitrary modification of end tags.
This change involved quite a bit of restructuring of the MakeWellFormed code,
including the moving of end of document tags to inside the loop, so rewinding
on those tags would be functional, increased reuse of the end tag codepath by
code that inserts end tags (as they could be changed out from under you), and
processToken modified to have an extra parameter to force re-processing of
a token if the original token was an end token.
We're not exactly sure if handleEnd works at this point, but the important
talking point about this refactoring is that nothing else broke. Also, a number
of convenience functions were moved from AutoParagraph to the Injector
supertype (specifically: forward, forwardToEndToken, backward, and current).
Signed-off-by: Edward Z. Yang <edwardzyang@thewritingpot.com>
2008-10-01 04:54:51 +00:00
|
|
|
for (
|
|
|
|
$this->inputIndex = 0;
|
|
|
|
$this->inputIndex == 0 || isset($tokens[$this->inputIndex - 1]);
|
2008-10-01 07:14:28 +00:00
|
|
|
// only increment if we don't need to reprocess
|
|
|
|
$reprocess ? $reprocess = false : $this->inputIndex++
|
Implement Injector->handleEnd, with lots of refactoring for injector.
Previous design of injector streaming involved editability only to start, empty
and text tokens, because they could be safely modified without causing formedness
errors. By modifying notifyEnd to operate before MakeWellFormed's safeguards
kick into effect, it can be converted into a handle function, allowing for
arbitrary modification of end tags.
This change involved quite a bit of restructuring of the MakeWellFormed code,
including the moving of end of document tags to inside the loop, so rewinding
on those tags would be functional, increased reuse of the end tag codepath by
code that inserts end tags (as they could be changed out from under you), and
processToken modified to have an extra parameter to force re-processing of
a token if the original token was an end token.
We're not exactly sure if handleEnd works at this point, but the important
talking point about this refactoring is that nothing else broke. Also, a number
of convenience functions were moved from AutoParagraph to the Injector
supertype (specifically: forward, forwardToEndToken, backward, and current).
Signed-off-by: Edward Z. Yang <edwardzyang@thewritingpot.com>
2008-10-01 04:54:51 +00:00
|
|
|
) {
|
|
|
|
|
2008-10-01 07:14:28 +00:00
|
|
|
// check for a rewind
|
|
|
|
if (is_int($i) && $i >= 0) {
|
|
|
|
$rewind_to = $this->injectors[$i]->getRewind();
|
|
|
|
if (is_int($rewind_to) && $rewind_to < $this->inputIndex) {
|
|
|
|
if ($rewind_to < 0) $rewind_to = 0;
|
|
|
|
while ($this->inputIndex > $rewind_to) {
|
|
|
|
$this->inputIndex--;
|
|
|
|
$prev = $this->inputTokens[$this->inputIndex];
|
|
|
|
// indicate that other injectors should not process this token,
|
|
|
|
// but we need to reprocess it
|
|
|
|
unset($prev->skip[$i]);
|
|
|
|
$prev->rewind = $i;
|
|
|
|
if ($prev instanceof HTMLPurifier_Token_Start) array_pop($this->currentNesting);
|
|
|
|
elseif ($prev instanceof HTMLPurifier_Token_End) $this->currentNesting[] = $prev->start;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$i = false;
|
Implement Injector->handleEnd, with lots of refactoring for injector.
Previous design of injector streaming involved editability only to start, empty
and text tokens, because they could be safely modified without causing formedness
errors. By modifying notifyEnd to operate before MakeWellFormed's safeguards
kick into effect, it can be converted into a handle function, allowing for
arbitrary modification of end tags.
This change involved quite a bit of restructuring of the MakeWellFormed code,
including the moving of end of document tags to inside the loop, so rewinding
on those tags would be functional, increased reuse of the end tag codepath by
code that inserts end tags (as they could be changed out from under you), and
processToken modified to have an extra parameter to force re-processing of
a token if the original token was an end token.
We're not exactly sure if handleEnd works at this point, but the important
talking point about this refactoring is that nothing else broke. Also, a number
of convenience functions were moved from AutoParagraph to the Injector
supertype (specifically: forward, forwardToEndToken, backward, and current).
Signed-off-by: Edward Z. Yang <edwardzyang@thewritingpot.com>
2008-10-01 04:54:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// handle case of document end
|
|
|
|
if (!isset($tokens[$this->inputIndex])) {
|
2008-10-01 07:14:28 +00:00
|
|
|
// We're at the end now, fix all still unclosed tags.
|
|
|
|
// This would logically go at the end of the loop, but because
|
|
|
|
// of all of the callbacks we need to be able to run the loop
|
|
|
|
// again.
|
|
|
|
|
|
|
|
// kill processing if stack is empty
|
|
|
|
if (empty($this->currentNesting)) {
|
|
|
|
break;
|
Implement Injector->handleEnd, with lots of refactoring for injector.
Previous design of injector streaming involved editability only to start, empty
and text tokens, because they could be safely modified without causing formedness
errors. By modifying notifyEnd to operate before MakeWellFormed's safeguards
kick into effect, it can be converted into a handle function, allowing for
arbitrary modification of end tags.
This change involved quite a bit of restructuring of the MakeWellFormed code,
including the moving of end of document tags to inside the loop, so rewinding
on those tags would be functional, increased reuse of the end tag codepath by
code that inserts end tags (as they could be changed out from under you), and
processToken modified to have an extra parameter to force re-processing of
a token if the original token was an end token.
We're not exactly sure if handleEnd works at this point, but the important
talking point about this refactoring is that nothing else broke. Also, a number
of convenience functions were moved from AutoParagraph to the Injector
supertype (specifically: forward, forwardToEndToken, backward, and current).
Signed-off-by: Edward Z. Yang <edwardzyang@thewritingpot.com>
2008-10-01 04:54:51 +00:00
|
|
|
}
|
2008-10-01 07:14:28 +00:00
|
|
|
|
|
|
|
// peek
|
|
|
|
$top_nesting = array_pop($this->currentNesting);
|
|
|
|
$this->currentNesting[] = $top_nesting;
|
|
|
|
|
|
|
|
// send error
|
|
|
|
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
|
|
|
|
$tokens[] = new HTMLPurifier_Token_End($top_nesting->name);
|
|
|
|
|
|
|
|
// punt!
|
|
|
|
$reprocess = true;
|
|
|
|
continue;
|
Implement Injector->handleEnd, with lots of refactoring for injector.
Previous design of injector streaming involved editability only to start, empty
and text tokens, because they could be safely modified without causing formedness
errors. By modifying notifyEnd to operate before MakeWellFormed's safeguards
kick into effect, it can be converted into a handle function, allowing for
arbitrary modification of end tags.
This change involved quite a bit of restructuring of the MakeWellFormed code,
including the moving of end of document tags to inside the loop, so rewinding
on those tags would be functional, increased reuse of the end tag codepath by
code that inserts end tags (as they could be changed out from under you), and
processToken modified to have an extra parameter to force re-processing of
a token if the original token was an end token.
We're not exactly sure if handleEnd works at this point, but the important
talking point about this refactoring is that nothing else broke. Also, a number
of convenience functions were moved from AutoParagraph to the Injector
supertype (specifically: forward, forwardToEndToken, backward, and current).
Signed-off-by: Edward Z. Yang <edwardzyang@thewritingpot.com>
2008-10-01 04:54:51 +00:00
|
|
|
}
|
2007-06-23 17:11:05 +00:00
|
|
|
|
|
|
|
// if all goes well, this token will be passed through unharmed
|
2007-06-24 17:44:27 +00:00
|
|
|
$token = $tokens[$this->inputIndex];
|
2007-06-23 17:11:05 +00:00
|
|
|
|
2008-06-27 05:33:48 +00:00
|
|
|
//echo '<hr>';
|
2008-10-01 07:14:28 +00:00
|
|
|
//printTokens($this->inputTokens, $this->inputIndex);
|
2008-06-27 05:33:48 +00:00
|
|
|
//var_dump($this->currentNesting);
|
2007-09-27 00:39:05 +00:00
|
|
|
|
2007-06-23 17:11:05 +00:00
|
|
|
// quick-check: if it's not a tag, no need to process
|
2008-10-01 07:14:28 +00:00
|
|
|
if (empty($token->is_tag)) {
|
2008-01-19 20:23:01 +00:00
|
|
|
if ($token instanceof HTMLPurifier_Token_Text) {
|
2008-10-01 07:14:28 +00:00
|
|
|
foreach ($this->injectors as $i => $injector) {
|
|
|
|
if (isset($token->skip[$i])) continue;
|
|
|
|
if ($token->rewind !== null && $token->rewind !== $i) continue;
|
|
|
|
$injector->handleText($token);
|
|
|
|
$this->processToken($token, $i);
|
|
|
|
$reprocess = true;
|
|
|
|
break;
|
|
|
|
}
|
2007-06-22 21:32:56 +00:00
|
|
|
}
|
2006-07-23 23:29:12 +00:00
|
|
|
continue;
|
|
|
|
}
|
2006-07-30 19:11:18 +00:00
|
|
|
|
2008-07-05 07:11:29 +00:00
|
|
|
if (isset($definition->info[$token->name])) {
|
|
|
|
$type = $definition->info[$token->name]->child->type;
|
|
|
|
} else {
|
|
|
|
$type = false; // Type is unknown, treat accordingly
|
|
|
|
}
|
2006-07-23 23:29:12 +00:00
|
|
|
|
2007-06-29 00:24:59 +00:00
|
|
|
// quick tag checks: anything that's *not* an end tag
|
|
|
|
$ok = false;
|
2008-07-05 07:11:29 +00:00
|
|
|
if ($type === 'empty' && $token instanceof HTMLPurifier_Token_Start) {
|
2007-06-29 00:24:59 +00:00
|
|
|
// test if it claims to be a start tag but is empty
|
|
|
|
$token = new HTMLPurifier_Token_Empty($token->name, $token->attr);
|
|
|
|
$ok = true;
|
2008-07-05 07:11:29 +00:00
|
|
|
} elseif ($type && $type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) {
|
2007-06-29 00:24:59 +00:00
|
|
|
// claims to be empty but really is a start tag
|
2008-10-01 07:14:28 +00:00
|
|
|
$this->swap(new HTMLPurifier_Token_End($token->name));
|
|
|
|
$this->insertBefore(new HTMLPurifier_Token_Start($token->name, $token->attr));
|
|
|
|
// punt
|
|
|
|
$reprocess = true;
|
|
|
|
continue;
|
2008-01-19 20:23:01 +00:00
|
|
|
} elseif ($token instanceof HTMLPurifier_Token_Empty) {
|
2007-06-29 00:24:59 +00:00
|
|
|
// real empty token
|
|
|
|
$ok = true;
|
2008-01-19 20:23:01 +00:00
|
|
|
} elseif ($token instanceof HTMLPurifier_Token_Start) {
|
2007-06-29 00:24:59 +00:00
|
|
|
// start tag
|
2006-07-23 23:29:12 +00:00
|
|
|
|
2007-06-23 17:11:05 +00:00
|
|
|
// ...unless they also have to close their parent
|
2007-06-24 17:44:27 +00:00
|
|
|
if (!empty($this->currentNesting)) {
|
2006-07-23 23:29:12 +00:00
|
|
|
|
2007-06-24 17:44:27 +00:00
|
|
|
$parent = array_pop($this->currentNesting);
|
2008-07-05 07:11:29 +00:00
|
|
|
if (isset($definition->info[$parent->name])) {
|
2008-08-02 00:52:06 +00:00
|
|
|
$elements = $definition->info[$parent->name]->child->getNonAutoCloseElements($config);
|
2008-07-05 07:11:29 +00:00
|
|
|
$autoclose = !isset($elements[$token->name]);
|
|
|
|
} else {
|
|
|
|
$autoclose = false;
|
|
|
|
}
|
2006-07-30 22:57:54 +00:00
|
|
|
|
2008-07-05 07:11:29 +00:00
|
|
|
if ($autoclose) {
|
2007-06-26 19:33:37 +00:00
|
|
|
if ($e) $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag auto closed', $parent);
|
2008-06-27 05:33:48 +00:00
|
|
|
// insert parent end tag before this tag;
|
|
|
|
// end tag isn't processed, but this tag is processed again
|
2008-06-27 20:09:14 +00:00
|
|
|
$new_token = new HTMLPurifier_Token_End($parent->name);
|
|
|
|
$new_token->start = $parent;
|
|
|
|
$this->insertBefore($new_token);
|
2006-07-23 23:29:12 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2007-06-24 17:44:27 +00:00
|
|
|
$this->currentNesting[] = $parent; // undo the pop
|
2006-07-23 23:29:12 +00:00
|
|
|
}
|
2007-06-29 00:24:59 +00:00
|
|
|
$ok = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// injector handler code; duplicated for performance reasons
|
|
|
|
if ($ok) {
|
2007-12-05 01:26:28 +00:00
|
|
|
foreach ($this->injectors as $i => $injector) {
|
2008-10-01 07:14:28 +00:00
|
|
|
if (isset($token->skip[$i])) continue;
|
|
|
|
if ($token->rewind !== null && $token->rewind !== $i) continue;
|
|
|
|
$injector->handleElement($token);
|
|
|
|
$this->processToken($token, $i);
|
|
|
|
$reprocess = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!$reprocess) {
|
|
|
|
// ah, nothing interesting happened; do normal processing
|
|
|
|
$this->swap($token);
|
|
|
|
if ($token instanceof HTMLPurifier_Token_Start) {
|
|
|
|
$this->currentNesting[] = $token;
|
|
|
|
} elseif ($token instanceof HTMLPurifier_Token_End) {
|
|
|
|
throw new HTMLPurifier_Exception('Improper handling of end tag in start code; possible error in MakeWellFormed');
|
2007-06-24 02:45:38 +00:00
|
|
|
}
|
2007-06-23 17:44:28 +00:00
|
|
|
}
|
2006-07-23 23:29:12 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2007-06-23 17:11:05 +00:00
|
|
|
// sanity check: we should be dealing with a closing tag
|
2008-06-27 05:33:48 +00:00
|
|
|
if (!$token instanceof HTMLPurifier_Token_End) {
|
|
|
|
$this->remove();
|
|
|
|
continue;
|
|
|
|
}
|
2006-07-23 23:29:12 +00:00
|
|
|
|
|
|
|
// make sure that we have something open
|
2007-06-24 17:44:27 +00:00
|
|
|
if (empty($this->currentNesting)) {
|
2006-08-15 23:58:18 +00:00
|
|
|
if ($escape_invalid_tags) {
|
2007-06-26 19:33:37 +00:00
|
|
|
if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text');
|
2008-06-27 05:33:48 +00:00
|
|
|
$this->swap(new HTMLPurifier_Token_Text(
|
2008-05-26 04:05:48 +00:00
|
|
|
$generator->generateFromToken($token)
|
2008-06-27 05:33:48 +00:00
|
|
|
));
|
|
|
|
} else {
|
|
|
|
$this->remove();
|
|
|
|
if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed');
|
2006-08-15 23:58:18 +00:00
|
|
|
}
|
2006-07-23 23:29:12 +00:00
|
|
|
continue;
|
|
|
|
}
|
2008-10-01 07:14:28 +00:00
|
|
|
foreach ($this->injectors as $i => $injector) {
|
|
|
|
if (isset($token->skip[$i])) continue;
|
|
|
|
if ($token->rewind !== null && $token->rewind !== $i) continue;
|
|
|
|
$injector->handleEnd($token);
|
|
|
|
$this->processToken($token, $i);
|
|
|
|
$reprocess = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ($reprocess) continue;
|
Implement Injector->handleEnd, with lots of refactoring for injector.
Previous design of injector streaming involved editability only to start, empty
and text tokens, because they could be safely modified without causing formedness
errors. By modifying notifyEnd to operate before MakeWellFormed's safeguards
kick into effect, it can be converted into a handle function, allowing for
arbitrary modification of end tags.
This change involved quite a bit of restructuring of the MakeWellFormed code,
including the moving of end of document tags to inside the loop, so rewinding
on those tags would be functional, increased reuse of the end tag codepath by
code that inserts end tags (as they could be changed out from under you), and
processToken modified to have an extra parameter to force re-processing of
a token if the original token was an end token.
We're not exactly sure if handleEnd works at this point, but the important
talking point about this refactoring is that nothing else broke. Also, a number
of convenience functions were moved from AutoParagraph to the Injector
supertype (specifically: forward, forwardToEndToken, backward, and current).
Signed-off-by: Edward Z. Yang <edwardzyang@thewritingpot.com>
2008-10-01 04:54:51 +00:00
|
|
|
|
2006-07-23 23:29:12 +00:00
|
|
|
// first, check for the simplest case: everything closes neatly
|
2007-06-24 17:44:27 +00:00
|
|
|
$current_parent = array_pop($this->currentNesting);
|
2006-07-23 23:29:12 +00:00
|
|
|
if ($current_parent->name == $token->name) {
|
2008-06-27 20:09:14 +00:00
|
|
|
$token->start = $current_parent;
|
2006-07-23 23:29:12 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// okay, so we're trying to close the wrong tag
|
|
|
|
|
2007-06-23 17:11:05 +00:00
|
|
|
// undo the pop previous pop
|
2007-06-24 17:44:27 +00:00
|
|
|
$this->currentNesting[] = $current_parent;
|
2007-06-23 17:11:05 +00:00
|
|
|
|
|
|
|
// scroll back the entire nest, trying to find our tag.
|
|
|
|
// (feature could be to specify how far you'd like to go)
|
2007-06-24 17:44:27 +00:00
|
|
|
$size = count($this->currentNesting);
|
2006-07-23 23:29:12 +00:00
|
|
|
// -2 because -1 is the last element, but we already checked that
|
|
|
|
$skipped_tags = false;
|
|
|
|
for ($i = $size - 2; $i >= 0; $i--) {
|
2007-06-24 17:44:27 +00:00
|
|
|
if ($this->currentNesting[$i]->name == $token->name) {
|
2006-07-23 23:29:12 +00:00
|
|
|
// current nesting is modified
|
2007-06-24 17:44:27 +00:00
|
|
|
$skipped_tags = array_splice($this->currentNesting, $i);
|
2006-07-23 23:29:12 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-23 17:11:05 +00:00
|
|
|
// we still didn't find the tag, so remove
|
2006-07-23 23:29:12 +00:00
|
|
|
if ($skipped_tags === false) {
|
2006-08-15 23:58:18 +00:00
|
|
|
if ($escape_invalid_tags) {
|
2008-06-27 05:33:48 +00:00
|
|
|
$this->swap(new HTMLPurifier_Token_Text(
|
2008-05-26 04:05:48 +00:00
|
|
|
$generator->generateFromToken($token)
|
2008-06-27 05:33:48 +00:00
|
|
|
));
|
2007-06-26 19:33:37 +00:00
|
|
|
if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text');
|
2008-06-27 05:33:48 +00:00
|
|
|
} else {
|
|
|
|
$this->remove();
|
|
|
|
if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed');
|
2006-08-15 23:58:18 +00:00
|
|
|
}
|
2006-07-23 23:29:12 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// okay, we found it, close all the skipped tags
|
|
|
|
// note that skipped tags contains the element we need closed
|
2008-06-27 05:33:48 +00:00
|
|
|
$this->remove();
|
2007-10-02 22:50:59 +00:00
|
|
|
for ($i = count($skipped_tags) - 1; $i >= 0; $i--) {
|
|
|
|
if ($i && $e && !isset($skipped_tags[$i]->armor['MakeWellFormed_TagClosedError'])) {
|
2007-06-26 19:33:37 +00:00
|
|
|
$e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', $skipped_tags[$i]);
|
2007-06-26 15:07:07 +00:00
|
|
|
}
|
2008-06-27 05:33:48 +00:00
|
|
|
$new_token = new HTMLPurifier_Token_End($skipped_tags[$i]->name);
|
2008-06-27 20:09:14 +00:00
|
|
|
$new_token->start = $skipped_tags[$i];
|
2008-06-27 05:33:48 +00:00
|
|
|
$this->insertAfter($new_token);
|
2006-07-23 23:29:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-26 19:33:37 +00:00
|
|
|
$context->destroy('CurrentNesting');
|
|
|
|
$context->destroy('InputTokens');
|
|
|
|
$context->destroy('InputIndex');
|
|
|
|
$context->destroy('CurrentToken');
|
|
|
|
|
2007-06-24 17:44:27 +00:00
|
|
|
unset($this->outputTokens, $this->injectors, $this->currentInjector,
|
|
|
|
$this->currentNesting, $this->inputTokens, $this->inputIndex);
|
2008-06-27 05:33:48 +00:00
|
|
|
return $tokens;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-10-01 07:14:28 +00:00
|
|
|
* Processes arbitrary token values for complicated substitution patterns.
|
|
|
|
* In general:
|
|
|
|
*
|
|
|
|
* If $token is an array, it is a list of tokens to substitute for the
|
|
|
|
* current token. These tokens then get individually processed.
|
|
|
|
*
|
|
|
|
* If $token is a regular token, it is swapped with the current token,
|
|
|
|
* and the stack is updated.
|
|
|
|
*
|
|
|
|
* If $token is false, the current token is deleted.
|
|
|
|
*/
|
|
|
|
protected function processToken($token, $injector = -1) {
|
|
|
|
|
|
|
|
// normalize forms of token
|
|
|
|
if (is_object($token)) $token = array(1, $token);
|
|
|
|
if (is_int($token)) $token = array($token);
|
|
|
|
if ($token === false) $token = array(1);
|
|
|
|
if (!is_array($token)) throw new HTMLPurifier_Exception('Invalid token type from injector');
|
|
|
|
if (!is_int($token[0])) array_unshift($token, 1);
|
|
|
|
if ($token[0] === 0) throw new HTMLPurifier_Exception('Deleting zero tokens is not valid');
|
|
|
|
|
|
|
|
// $token is now an array with the following form:
|
|
|
|
// array(number nodes to delete, new node 1, new node 2, ...)
|
|
|
|
|
|
|
|
$delete = array_shift($token);
|
|
|
|
$old = array_splice($this->inputTokens, $this->inputIndex, $delete, $token);
|
|
|
|
|
|
|
|
if ($injector > -1) {
|
|
|
|
// determine appropriate skips
|
|
|
|
$oldskip = isset($old[0]) ? $old[0]->skip : array();
|
|
|
|
foreach ($token as $object) {
|
|
|
|
$object->skip = $oldskip;
|
|
|
|
$object->skip[$injector] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Inserts a token before the current token. Cursor now points to this token
|
2008-06-27 05:33:48 +00:00
|
|
|
*/
|
|
|
|
protected function insertBefore($token) {
|
|
|
|
array_splice($this->inputTokens, $this->inputIndex, 0, array($token));
|
|
|
|
}
|
2008-10-01 07:14:28 +00:00
|
|
|
|
2008-06-27 05:33:48 +00:00
|
|
|
/**
|
2008-10-01 07:14:28 +00:00
|
|
|
* Inserts a token after the current token. Cursor now points to this token
|
2008-06-27 05:33:48 +00:00
|
|
|
*/
|
|
|
|
protected function insertAfter($token) {
|
|
|
|
array_splice($this->inputTokens, ++$this->inputIndex, 0, array($token));
|
|
|
|
}
|
2008-10-01 07:14:28 +00:00
|
|
|
|
2008-06-27 05:33:48 +00:00
|
|
|
/**
|
|
|
|
* Removes current token. Cursor now points to previous token.
|
|
|
|
*/
|
|
|
|
protected function remove() {
|
|
|
|
array_splice($this->inputTokens, $this->inputIndex--, 1);
|
2006-07-23 23:29:12 +00:00
|
|
|
}
|
|
|
|
|
2008-06-27 05:33:48 +00:00
|
|
|
/**
|
2008-10-01 07:14:28 +00:00
|
|
|
* Swap current token with new token. Cursor points to new token (no change
|
2008-06-27 05:33:48 +00:00
|
|
|
*/
|
|
|
|
protected function swap($token) {
|
|
|
|
array_splice($this->inputTokens, $this->inputIndex, 1, array($token));
|
|
|
|
}
|
|
|
|
|
2006-07-23 23:29:12 +00:00
|
|
|
}
|
|
|
|
|