* @ingroup Parser
*/
class Preprocessor_DOM implements Preprocessor {
- /** @var Parser */
- public $parser;
- protected $memoryLimit;
+ /**
+ * @var Parser
+ */
+ var $parser;
+
+ var $memoryLimit;
const CACHE_VERSION = 1;
foreach ( $values as $k => $val ) {
if ( is_int( $k ) ) {
- $xml .= "<part><name index=\"$k\"/><value>" . htmlspecialchars( $val ) . "</value></part>";
+ $xml .= "<part><name index=\"$k\"/><value>"
+ . htmlspecialchars( $val ) . "</value></part>";
} else {
- $xml .= "<part><name>" . htmlspecialchars( $k ) . "</name>=<value>" . htmlspecialchars( $val ) . "</value></part>";
+ $xml .= "<part><name>" . htmlspecialchars( $k )
+ . "</name>=<value>" . htmlspecialchars( $val ) . "</value></part>";
}
}
$xml .= "</list>";
+ wfProfileIn( __METHOD__ . '-loadXML' );
$dom = new DOMDocument();
- $dom->loadXML( $xml );
- $root = $dom->documentElement;
+ wfSuppressWarnings();
+ $result = $dom->loadXML( $xml );
+ wfRestoreWarnings();
+ if ( !$result ) {
+ // Try running the XML through UtfNormal to get rid of invalid characters
+ $xml = UtfNormal::cleanUp( $xml );
+ // 1 << 19 == XML_PARSE_HUGE, needed so newer versions of libxml2
+ // don't barf when the XML is >256 levels deep
+ $result = $dom->loadXML( $xml, 1 << 19 );
+ }
+ wfProfileOut( __METHOD__ . '-loadXML' );
+
+ if ( !$result ) {
+ throw new MWException( 'Parameters passed to ' . __METHOD__ . ' result in invalid XML' );
+ }
+ $root = $dom->documentElement;
$node = new PPNode_DOM( $root->childNodes );
return $node;
}
*
* @param string $text The text to parse
* @param int $flags Bitwise combination of:
- * Parser::PTD_FOR_INCLUSION Handle "<noinclude>" and "<includeonly>" as if the text is being
- * included. Default is to assume a direct page view.
+ * Parser::PTD_FOR_INCLUSION Handle "<noinclude>" and "<includeonly>"
+ * as if the text is being included. Default
+ * is to assume a direct page view.
*
* The generated DOM tree must depend only on the input text and the flags.
* The DOM tree must be the same in OT_HTML and OT_WIKI mode, to avoid a regression of bug 4899.
if ( !$result ) {
// Try running the XML through UtfNormal to get rid of invalid characters
$xml = UtfNormal::cleanUp( $xml );
- // 1 << 19 == XML_PARSE_HUGE, needed so newer versions of libxml2 don't barf when the XML is >256 levels deep
+ // 1 << 19 == XML_PARSE_HUGE, needed so newer versions of libxml2
+ // don't barf when the XML is >256 levels deep.
$result = $dom->loadXML( $xml, 1 << 19 );
}
if ( $result ) {
$ignoredTags = array( 'includeonly', '/includeonly' );
$ignoredElements = array( 'noinclude' );
$xmlishElements[] = 'noinclude';
- if ( strpos( $text, '<onlyinclude>' ) !== false && strpos( $text, '</onlyinclude>' ) !== false ) {
+ if ( strpos( $text, '<onlyinclude>' ) !== false
+ && strpos( $text, '</onlyinclude>' ) !== false
+ ) {
$enableOnlyinclude = true;
}
} else {
$stack = new PPDStack;
$searchBase = "[{<\n"; #}
- $revText = strrev( $text ); // For fast reverse searches
+ // For fast reverse searches
+ $revText = strrev( $text );
$lengthText = strlen( $text );
- $i = 0; # Input pointer, starts out pointing to a pseudo-newline before the start
- $accum =& $stack->getAccum(); # Current accumulator
+ // Input pointer, starts out pointing to a pseudo-newline before the start
+ $i = 0;
+ // Current accumulator
+ $accum =& $stack->getAccum();
$accum = '<root>';
- $findEquals = false; # True to find equals signs in arguments
- $findPipe = false; # True to take notice of pipe characters
+ // True to find equals signs in arguments
+ $findEquals = false;
+ // True to take notice of pipe characters
+ $findPipe = false;
$headingIndex = 1;
- $inHeading = false; # True if $i is inside a possible heading
- $noMoreGT = false; # True if there are no more greater-than (>) signs right of $i
- $findOnlyinclude = $enableOnlyinclude; # True to ignore all input up to the next <onlyinclude>
- $fakeLineStart = true; # Do a line-start run without outputting an LF character
+ // True if $i is inside a possible heading
+ $inHeading = false;
+ // True if there are no more greater-than (>) signs right of $i
+ $noMoreGT = false;
+ // True to ignore all input up to the next <onlyinclude>
+ $findOnlyinclude = $enableOnlyinclude;
+ // Do a line-start run without outputting an LF character
+ $fakeLineStart = true;
while ( true ) {
//$this->memCheck();
if ( $found == 'angle' ) {
$matches = false;
// Handle </onlyinclude>
- if ( $enableOnlyinclude && substr( $text, $i, strlen( '</onlyinclude>' ) ) == '</onlyinclude>' ) {
+ if ( $enableOnlyinclude
+ && substr( $text, $i, strlen( '</onlyinclude>' ) ) == '</onlyinclude>'
+ ) {
$findOnlyinclude = true;
continue;
}
// Handle ignored tags
if ( in_array( $lowerName, $ignoredTags ) ) {
- $accum .= '<ignore>' . htmlspecialchars( substr( $text, $i, $tagEndPos - $i + 1 ) ) . '</ignore>';
+ $accum .= '<ignore>'
+ . htmlspecialchars( substr( $text, $i, $tagEndPos - $i + 1 ) )
+ . '</ignore>';
$i = $tagEndPos + 1;
continue;
}
$count = strspn( $text, '=', $i, 6 );
if ( $count == 1 && $findEquals ) {
- // DWIM: This looks kind of like a name/value separator
- // Let's let the equals handler have it and break the potential heading
- // This is heuristic, but AFAICT the methods for completely correct disambiguation are very complex.
+ // DWIM: This looks kind of like a name/value separator.
+ // Let's let the equals handler have it and break the
+ // potential heading. This is heuristic, but AFAICT the
+ // methods for completely correct disambiguation are very
+ // complex.
} elseif ( $count > 0 ) {
$piece = array(
'open' => "\n",
// A heading must be open, otherwise \n wouldn't have been in the search list
assert( '$piece->open == "\n"' );
$part = $piece->getCurrentPart();
- // Search back through the input to see if it has a proper close
- // Do this using the reversed string since the other solutions (end anchor, etc.) are inefficient
+ // Search back through the input to see if it has a proper close.
+ // Do this using the reversed string since the other solutions
+ // (end anchor, etc.) are inefficient.
$wsLength = strspn( $revText, " \t", $lengthText - $i );
$searchStart = $i - $wsLength;
if ( isset( $part->commentEnd ) && $searchStart - 1 == $part->commentEnd ) {
* @ingroup Parser
*/
class PPDStack {
- /** @var array */
- public $stack;
-
- /** @var string */
- public $rootAccum;
-
- /** @var bool|PPDStack */
- public $top;
+ var $stack, $rootAccum;
- /** @var */
- public $out;
-
- /** @var string */
- protected $elementClass = 'PPDStackElement';
+ /**
+ * @var PPDStack
+ */
+ var $top;
+ var $out;
+ var $elementClass = 'PPDStackElement';
- protected static $false = false;
+ static $false = false;
function __construct() {
$this->stack = array();
* @ingroup Parser
*/
class PPDStackElement {
- /** @var string Opening character (\n for heading) */
- public $open;
+ var $open, // Opening character (\n for heading)
+ $close, // Matching closing character
+ $count, // Number of opening characters found (number of "=" for heading)
+ $parts, // Array of PPDPart objects describing pipe-separated parts.
+ $lineStart; // True if the open char appeared at the start of the input line. Not set for headings.
- /** @var string Matching closing character */
- public $close;
-
- /** @var int Number of opening characters found (number of "=" for heading) */
- public $count;
-
- /** @var array PPDPart objects describing pipe-separated parts. */
- public $parts;
-
- /**
- * @var bool True if the open char appeared at the start of the input line.
- * Not set for headings.
- */
- public $lineStart;
-
- /** @var string */
- protected $partClass = 'PPDPart';
+ var $partClass = 'PPDPart';
function __construct( $data = array() ) {
$class = $this->partClass;
/**
* Get the output string that would result if the close is not found.
*
+ * @param bool|int $openingCount
* @return string
*/
function breakSyntax( $openingCount = false ) {
* @ingroup Parser
*/
class PPDPart {
- /** @var string */
- public $out;
+ var $out; // Output accumulator string
// Optional member variables:
// eqpos Position of equals sign in output accumulator
* @ingroup Parser
*/
class PPFrame_DOM implements PPFrame {
- /** @var array */
- public $titleCache;
/**
- * @var array Hashtable listing templates which are disallowed for expansion
- * in this frame, having been encountered previously in parent frames.
+ * @var Preprocessor
*/
- public $loopCheckHash;
+ var $preprocessor;
/**
- * @var int Recursion depth of this frame, top = 0.
- * Note that this is NOT the same as expansion depth in expand()
+ * @var Parser
*/
- public $depth;
+ var $parser;
- /** @var Preprocessor */
- protected $preprocessor;
+ /**
+ * @var Title
+ */
+ var $title;
+ var $titleCache;
+
+ /**
+ * Hashtable listing templates which are disallowed for expansion in this frame,
+ * having been encountered previously in parent frames.
+ */
+ var $loopCheckHash;
+
+ /**
+ * Recursion depth of this frame, top = 0
+ * Note that this is NOT the same as expansion depth in expand()
+ */
+ var $depth;
- /** @var Parser */
- protected $parser;
+ private $volatile = false;
- /** @var Title */
- protected $title;
+ /**
+ * @var array
+ */
+ protected $childExpansionCache;
/**
* Construct a new preprocessor frame.
$this->titleCache = array( $this->title ? $this->title->getPrefixedDBkey() : false );
$this->loopCheckHash = array();
$this->depth = 0;
+ $this->childExpansionCache = array();
}
/**
return new PPTemplateFrame_DOM( $this->preprocessor, $this, $numberedArgs, $namedArgs, $title );
}
+ /**
+ * @throws MWException
+ * @param string|int $key
+ * @param string|PPNode_DOM|DOMDocument $root
+ * @param int $flags
+ * @return string
+ */
+ function cachedExpand( $key, $root, $flags = 0 ) {
+ // we don't have a parent, so we don't have a cache
+ return $this->expand( $root, $flags );
+ }
+
/**
* @throws MWException
* @param string|PPNode_DOM|DOMDocument $root
) {
$out .= '';
} elseif ( $this->parser->ot['wiki'] && !( $flags & PPFrame::RECOVER_COMMENTS ) ) {
- # Add a strip marker in PST mode so that pstPass2() can run some old-fashioned regexes on the result
- # Not in RECOVER_COMMENTS mode (extractSections) though
+ # Add a strip marker in PST mode so that pstPass2() can
+ # run some old-fashioned regexes on the result.
+ # Not in RECOVER_COMMENTS mode (extractSections) though.
$out .= $this->parser->insertStripItem( $contextNode->textContent );
} else {
# Recover the literal comment in RECOVER_COMMENTS and pre+no-remove
# OT_WIKI will only respect <ignore> in substed templates.
# The other output types respect it unless NO_IGNORE is set.
# extractSections() sets NO_IGNORE and so never respects it.
- if ( ( !isset( $this->parent ) && $this->parser->ot['wiki'] ) || ( $flags & PPFrame::NO_IGNORE ) ) {
+ if ( ( !isset( $this->parent ) && $this->parser->ot['wiki'] )
+ || ( $flags & PPFrame::NO_IGNORE )
+ ) {
$out .= $contextNode->textContent;
} else {
$out .= '';
function getTitle() {
return $this->title;
}
+
+ /**
+ * Set the volatile flag
+ *
+ * @param bool $flag
+ */
+ function setVolatile( $flag = true ) {
+ $this->volatile = $flag;
+ }
+
+ /**
+ * Get the volatile flag
+ *
+ * @return bool
+ */
+ function isVolatile() {
+ return $this->volatile;
+ }
}
/**
* @ingroup Parser
*/
class PPTemplateFrame_DOM extends PPFrame_DOM {
- /** @var PPFrame_DOM */
- public $parent;
-
- /** @var array */
- protected $numberedArgs;
+ var $numberedArgs, $namedArgs;
- /** @var array */
- protected $namedArgs;
-
- /** @var array */
- protected $numberedExpansionCache;
-
- /** @var string[] */
- protected $namedExpansionCache;
+ /**
+ * @var PPFrame_DOM
+ */
+ var $parent;
+ var $numberedExpansionCache, $namedExpansionCache;
/**
* @param Preprocessor $preprocessor
- * @param PPFrame_DOM $parent
+ * @param bool|PPFrame_DOM $parent
* @param array $numberedArgs
* @param array $namedArgs
- * @param Title $title
+ * @param bool|Title $title
*/
- function __construct( $preprocessor, $parent = false, $numberedArgs = array(), $namedArgs = array(), $title = false ) {
+ function __construct( $preprocessor, $parent = false, $numberedArgs = array(),
+ $namedArgs = array(), $title = false
+ ) {
parent::__construct( $preprocessor );
$this->parent = $parent;
return $s;
}
+ /**
+ * @throws MWException
+ * @param string|int $key
+ * @param string|PPNode_DOM|DOMDocument $root
+ * @param int $flags
+ * @return string
+ */
+ function cachedExpand( $key, $root, $flags = 0 ) {
+ if ( isset( $this->parent->childExpansionCache[$key] ) ) {
+ return $this->parent->childExpansionCache[$key];
+ }
+ $retval = $this->expand( $root, $flags );
+ if ( !$this->isVolatile() ) {
+ $this->parent->childExpansionCache[$key] = $retval;
+ }
+ return $retval;
+ }
+
/**
* Returns true if there are no arguments in this frame
*
}
if ( !isset( $this->numberedExpansionCache[$index] ) ) {
# No trimming for unnamed arguments
- $this->numberedExpansionCache[$index] = $this->parent->expand( $this->numberedArgs[$index], PPFrame::STRIP_COMMENTS );
+ $this->numberedExpansionCache[$index] = $this->parent->expand(
+ $this->numberedArgs[$index],
+ PPFrame::STRIP_COMMENTS
+ );
}
return $this->numberedExpansionCache[$index];
}
function isTemplate() {
return true;
}
+
+ function setVolatile( $flag = true ) {
+ parent::setVolatile( $flag );
+ $this->parent->setVolatile( $flag );
+ }
}
/**
* @ingroup Parser
*/
class PPCustomFrame_DOM extends PPFrame_DOM {
- protected $args;
+ var $args;
function __construct( $preprocessor, $args ) {
parent::__construct( $preprocessor );
* @ingroup Parser
*/
class PPNode_DOM implements PPNode {
- /** @var DOMElement */
- public $node;
- /** @var DOMXPath */
- protected $xpath;
+ /**
+ * @var DOMElement
+ */
+ var $node;
+ var $xpath;
function __construct( $node, $xpath = false ) {
$this->node = $node;