$forInclusion = $flags & Parser::PTD_FOR_INCLUSION;
$xmlishElements = $this->parser->getStripList();
+ $xmlishAllowMissingEndTag = [ 'includeonly', 'noinclude', 'onlyinclude' ];
$enableOnlyinclude = false;
if ( $forInclusion ) {
$ignoredTags = [ 'includeonly', '/includeonly' ];
$inHeading = false;
// True if there are no more greater-than (>) signs right of $i
$noMoreGT = false;
+ // Map of tag name => true if there are no more closing tags of given type right of $i
+ $noMoreClosingTag = [];
// True to ignore all input up to the next <onlyinclude>
$findOnlyinclude = $enableOnlyinclude;
// Do a line-start run without outputting an LF character
} else {
$attrEnd = $tagEndPos;
// Find closing tag
- if ( preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i",
+ if (
+ !isset( $noMoreClosingTag[$name] ) &&
+ preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i",
$text, $matches, PREG_OFFSET_CAPTURE, $tagEndPos + 1 )
) {
$inner = substr( $text, $tagEndPos + 1, $matches[0][1] - $tagEndPos - 1 );
$i = $matches[0][1] + strlen( $matches[0][0] );
$close = $matches[0][0];
} else {
- // No end tag -- let it run out to the end of the text.
- $inner = substr( $text, $tagEndPos + 1 );
- $i = $lengthText;
- $close = null;
+ // No end tag
+ if ( in_array( $name, $xmlishAllowMissingEndTag ) ) {
+ // Let it run out to the end of the text.
+ $inner = substr( $text, $tagEndPos + 1 );
+ $i = $lengthText;
+ $close = null;
+ } else {
+ // Don't match the tag, treat opening tag as literal and resume parsing.
+ $i = $tagEndPos + 1;
+ $accum->addLiteral( substr( $text, $tagStartPos, $tagEndPos + 1 - $tagStartPos ) );
+ // Cache results, otherwise we have O(N^2) performance for input like <foo><foo><foo>...
+ $noMoreClosingTag[$name] = true;
+ continue;
+ }
}
}
// <includeonly> and <noinclude> just become <ignore> tags
}
/**
- * @param string $name
- * @return bool
+ * @param int|string $name
+ * @return bool Always false in this implementation.
*/
public function getArgument( $name ) {
return false;
/**
* @param int $index
- * @return array|bool
+ * @return string|bool
*/
public function getNumberedArgument( $index ) {
if ( !isset( $this->numberedArgs[$index] ) ) {
/**
* @param string $name
- * @return bool
+ * @return string|bool
*/
public function getNamedArgument( $name ) {
if ( !isset( $this->namedArgs[$name] ) ) {
}
/**
- * @param string $name
- * @return array|bool
+ * @param int|string $name
+ * @return string|bool
*/
public function getArgument( $name ) {
$text = $this->getNumberedArgument( $name );
}
/**
- * @param int $index
- * @return bool
+ * @param int|string $index
+ * @return string|bool
*/
public function getArgument( $index ) {
if ( !isset( $this->args[$index] ) ) {