/**
* @ingroup Parser
- * @codingStandardsIgnoreStart
*/
-class Preprocessor_DOM implements Preprocessor {
+// @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
+class Preprocessor_DOM extends Preprocessor {
// @codingStandardsIgnoreEnd
/**
public $memoryLimit;
- const CACHE_VERSION = 1;
+ const CACHE_PREFIX = 'preprocess-xml';
public function __construct( $parser ) {
$this->parser = $parser;
* @return PPNode_DOM
*/
public function preprocessToObj( $text, $flags = 0 ) {
- global $wgMemc, $wgPreprocessorCacheThreshold;
-
- $xml = false;
- $cacheable = ( $wgPreprocessorCacheThreshold !== false
- && strlen( $text ) > $wgPreprocessorCacheThreshold );
- if ( $cacheable ) {
- $cacheKey = wfMemcKey( 'preprocess-xml', md5( $text ), $flags );
- $cacheValue = $wgMemc->get( $cacheKey );
- if ( $cacheValue ) {
- $version = substr( $cacheValue, 0, 8 );
- if ( intval( $version ) == self::CACHE_VERSION ) {
- $xml = substr( $cacheValue, 8 );
- // From the cache
- wfDebugLog( "Preprocessor", "Loaded preprocessor XML from memcached (key $cacheKey)" );
- }
- }
- if ( $xml === false ) {
- $xml = $this->preprocessToXml( $text, $flags );
- $cacheValue = sprintf( "%08d", self::CACHE_VERSION ) . $xml;
- $wgMemc->set( $cacheKey, $cacheValue, 86400 );
- wfDebugLog( "Preprocessor", "Saved preprocessor XML to memcached (key $cacheKey)" );
- }
- } else {
+
+ $xml = $this->cacheGetTree( $text, $flags );
+ if ( $xml === false ) {
$xml = $this->preprocessToXml( $text, $flags );
+ $this->cacheSetTree( $text, $flags, $xml );
}
// Fail if the number of elements exceeds acceptable limits
$this->parser->mGeneratedPPNodeCount += substr_count( $xml, '<' );
$max = $this->parser->mOptions->getMaxGeneratedPPNodeCount();
if ( $this->parser->mGeneratedPPNodeCount > $max ) {
- if ( $cacheable ) {
- }
+ // if ( $cacheable ) { ... }
throw new MWException( __METHOD__ . ': generated node count limit exceeded' );
}
$obj = new PPNode_DOM( $dom->documentElement );
}
- if ( $cacheable ) {
- }
+ // if ( $cacheable ) { ... }
if ( !$result ) {
throw new MWException( __METHOD__ . ' generated invalid XML' );
* @return string
*/
public function preprocessToXml( $text, $flags = 0 ) {
- $rules = array(
- '{' => array(
- 'end' => '}',
- 'names' => array(
- 2 => 'template',
- 3 => 'tplarg',
- ),
- 'min' => 2,
- 'max' => 3,
- ),
- '[' => array(
- 'end' => ']',
- 'names' => array( 2 => null ),
- 'min' => 2,
- 'max' => 2,
- )
- );
-
$forInclusion = $flags & Parser::PTD_FOR_INCLUSION;
$xmlishElements = $this->parser->getStripList();
$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 = array();
// True to ignore all input up to the next <onlyinclude>
$findOnlyinclude = $enableOnlyinclude;
// Do a line-start run without outputting an LF character
}
} elseif ( $curChar == $currentClosing ) {
$found = 'close';
- } elseif ( isset( $rules[$curChar] ) ) {
+ } elseif ( isset( $this->rules[$curChar] ) ) {
$found = 'open';
- $rule = $rules[$curChar];
+ $rule = $this->rules[$curChar];
} else {
# Some versions of PHP have a strcspn which stops on null characters
# Ignore and continue
} 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 = '<close>' . htmlspecialchars( $matches[0][0] ) . '</close>';
} else {
- // No end tag -- let it run out to the end of the text.
- $inner = substr( $text, $tagEndPos + 1 );
- $i = $lengthText;
- $close = '';
+ // No end tag -- don't match the tag, treat opening tag as literal and resume parsing.
+ $i = $tagEndPos + 1;
+ $accum .= htmlspecialchars( 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
# check for maximum matching characters (if there are 5 closing
# characters, we will probably need only 3 - depending on the rules)
- $rule = $rules[$piece->open];
+ $rule = $this->rules[$piece->open];
if ( $count > $rule['max'] ) {
# The specified maximum exists in the callback array, unless the caller
# has made an error
$piece->parts = array( new PPDPart );
$piece->count -= $matchingCount;
# do we still qualify for any callback with remaining count?
- $min = $rules[$piece->open]['min'];
+ $min = $this->rules[$piece->open]['min'];
if ( $piece->count >= $min ) {
$stack->push( $piece );
$accum =& $stack->getAccum();
/**
* An expansion frame, used as a context to expand the result of preprocessToObj()
* @ingroup Parser
- * @codingStandardsIgnoreStart
*/
+// @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
class PPFrame_DOM implements PPFrame {
// @codingStandardsIgnoreEnd
/**
* Expansion frame with template arguments
* @ingroup Parser
- * @codingStandardsIgnoreStart
*/
+// @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
class PPTemplateFrame_DOM extends PPFrame_DOM {
// @codingStandardsIgnoreEnd
/**
* Expansion frame with custom arguments
* @ingroup Parser
- * @codingStandardsIgnoreStart
*/
+// @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
class PPCustomFrame_DOM extends PPFrame_DOM {
// @codingStandardsIgnoreEnd
/**
* @ingroup Parser
- * @codingStandardsIgnoreStart
*/
+// @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
class PPNode_DOM implements PPNode {
// @codingStandardsIgnoreEnd