global $wgAllowDisplayTitle, $wgAllowSlowParserFunctions;
$this->setHook( 'pre', array( $this, 'renderPreTag' ) );
-
- $this->setFunctionHook( 'int', array( 'CoreParserFunctions', 'intFunction' ), SFH_NO_HASH );
- $this->setFunctionHook( 'ns', array( 'CoreParserFunctions', 'ns' ), SFH_NO_HASH );
- $this->setFunctionHook( 'urlencode', array( 'CoreParserFunctions', 'urlencode' ), SFH_NO_HASH );
- $this->setFunctionHook( 'lcfirst', array( 'CoreParserFunctions', 'lcfirst' ), SFH_NO_HASH );
- $this->setFunctionHook( 'ucfirst', array( 'CoreParserFunctions', 'ucfirst' ), SFH_NO_HASH );
- $this->setFunctionHook( 'lc', array( 'CoreParserFunctions', 'lc' ), SFH_NO_HASH );
- $this->setFunctionHook( 'uc', array( 'CoreParserFunctions', 'uc' ), SFH_NO_HASH );
- $this->setFunctionHook( 'localurl', array( 'CoreParserFunctions', 'localurl' ), SFH_NO_HASH );
- $this->setFunctionHook( 'localurle', array( 'CoreParserFunctions', 'localurle' ), SFH_NO_HASH );
- $this->setFunctionHook( 'fullurl', array( 'CoreParserFunctions', 'fullurl' ), SFH_NO_HASH );
- $this->setFunctionHook( 'fullurle', array( 'CoreParserFunctions', 'fullurle' ), SFH_NO_HASH );
- $this->setFunctionHook( 'formatnum', array( 'CoreParserFunctions', 'formatnum' ), SFH_NO_HASH );
- $this->setFunctionHook( 'grammar', array( 'CoreParserFunctions', 'grammar' ), SFH_NO_HASH );
- $this->setFunctionHook( 'plural', array( 'CoreParserFunctions', 'plural' ), SFH_NO_HASH );
- $this->setFunctionHook( 'numberofpages', array( 'CoreParserFunctions', 'numberofpages' ), SFH_NO_HASH );
- $this->setFunctionHook( 'numberofusers', array( 'CoreParserFunctions', 'numberofusers' ), SFH_NO_HASH );
+
+ # Syntax for arguments (see self::setFunctionHook):
+ # "name for lookup in localized magic words array",
+ # function callback,
+ # optional SFH_NO_HASH to omit the hash from calls (e.g. {{int:...}
+ # instead of {{#int:...}})
+ $this->setFunctionHook( 'int', array( 'CoreParserFunctions', 'intFunction' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'ns', array( 'CoreParserFunctions', 'ns' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'urlencode', array( 'CoreParserFunctions', 'urlencode' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'lcfirst', array( 'CoreParserFunctions', 'lcfirst' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'ucfirst', array( 'CoreParserFunctions', 'ucfirst' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'lc', array( 'CoreParserFunctions', 'lc' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'uc', array( 'CoreParserFunctions', 'uc' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'localurl', array( 'CoreParserFunctions', 'localurl' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'localurle', array( 'CoreParserFunctions', 'localurle' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'fullurl', array( 'CoreParserFunctions', 'fullurl' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'fullurle', array( 'CoreParserFunctions', 'fullurle' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'formatnum', array( 'CoreParserFunctions', 'formatnum' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'grammar', array( 'CoreParserFunctions', 'grammar' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'plural', array( 'CoreParserFunctions', 'plural' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'numberofpages', array( 'CoreParserFunctions', 'numberofpages' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'numberofusers', array( 'CoreParserFunctions', 'numberofusers' ), SFH_NO_HASH );
$this->setFunctionHook( 'numberofarticles', array( 'CoreParserFunctions', 'numberofarticles' ), SFH_NO_HASH );
- $this->setFunctionHook( 'numberoffiles', array( 'CoreParserFunctions', 'numberoffiles' ), SFH_NO_HASH );
- $this->setFunctionHook( 'numberofadmins', array( 'CoreParserFunctions', 'numberofadmins' ), SFH_NO_HASH );
- $this->setFunctionHook( 'numberofedits', array( 'CoreParserFunctions', 'numberofedits' ), SFH_NO_HASH );
- $this->setFunctionHook( 'language', array( 'CoreParserFunctions', 'language' ), SFH_NO_HASH );
- $this->setFunctionHook( 'padleft', array( 'CoreParserFunctions', 'padleft' ), SFH_NO_HASH );
- $this->setFunctionHook( 'padright', array( 'CoreParserFunctions', 'padright' ), SFH_NO_HASH );
- $this->setFunctionHook( 'anchorencode', array( 'CoreParserFunctions', 'anchorencode' ), SFH_NO_HASH );
- $this->setFunctionHook( 'special', array( 'CoreParserFunctions', 'special' ) );
- $this->setFunctionHook( 'defaultsort', array( 'CoreParserFunctions', 'defaultsort' ), SFH_NO_HASH );
- $this->setFunctionHook( 'filepath', array( 'CoreParserFunctions', 'filepath' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'numberoffiles', array( 'CoreParserFunctions', 'numberoffiles' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'numberofadmins', array( 'CoreParserFunctions', 'numberofadmins' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'numberofedits', array( 'CoreParserFunctions', 'numberofedits' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'language', array( 'CoreParserFunctions', 'language' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'padleft', array( 'CoreParserFunctions', 'padleft' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'padright', array( 'CoreParserFunctions', 'padright' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'anchorencode', array( 'CoreParserFunctions', 'anchorencode' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'special', array( 'CoreParserFunctions', 'special' ) );
+ $this->setFunctionHook( 'defaultsort', array( 'CoreParserFunctions', 'defaultsort' ), SFH_NO_HASH );
+ $this->setFunctionHook( 'filepath', array( 'CoreParserFunctions', 'filepath' ), SFH_NO_HASH );
if ( $wgAllowDisplayTitle ) {
$this->setFunctionHook( 'displaytitle', array( 'CoreParserFunctions', 'displaytitle' ), SFH_NO_HASH );
* @public
*/
function uniqPrefix() {
+ if( !isset( $this->mUniqPrefix ) ) {
+ // @fixme this is probably *horribly wrong*
+ // LanguageConverter seems to want $wgParser's uniqPrefix, however
+ // if this is called for a parser cache hit, the parser may not
+ // have ever been initialized in the first place.
+ // Not really sure what the heck is supposed to be going on here.
+ return '';
+ //throw new MWException( "Accessing uninitialized mUniqPrefix" );
+ }
return $this->mUniqPrefix;
}
wfRunHooks( 'ParserAfterTidy', array( &$this, &$text ) );
# Information on include size limits, for the benefit of users who try to skirt them
- if ( max( $this->mIncludeSizes ) > 1000 ) {
+ if ( $this->mOptions->getEnableLimitReport() ) {
$max = $this->mOptions->getMaxIncludeSize();
- $text .= "<!-- \n" .
- "Preprocessor node count: {$this->mPPNodeCount}\n" .
- "Post-expand include size: {$this->mIncludeSizes['post-expand']} bytes\n" .
- "Template argument size: {$this->mIncludeSizes['arg']} bytes\n" .
- "Maximum: $max bytes\n" .
- "-->\n";
+ $limitReport =
+ "Preprocessor node count: {$this->mPPNodeCount}/{$this->mOptions->mMaxPPNodeCount}\n" .
+ "Post-expand include size: {$this->mIncludeSizes['post-expand']}/$max bytes\n" .
+ "Template argument size: {$this->mIncludeSizes['arg']}/$max bytes\n";
+ wfRunHooks( 'ParserLimitReport', array( $this, &$limitReport ) );
+ $text .= "\n<!-- \n$limitReport-->\n";
}
$this->mOutput->setText( $text );
$this->mRevisionId = $oldRevisionId;
function insertStripItem( $text ) {
static $n = 0;
$rnd = "{$this->mUniqPrefix}-item-$n-{$this->mMarkerSuffix}";
+ ++$n;
$this->mStripState->general->setPair( $rnd, $text );
return $rnd;
}
* @static
*/
function internalTidy( $text ) {
- global $wgTidyConf, $IP;
+ global $wgTidyConf, $IP, $wgDebugTidy;
$fname = 'Parser::internalTidy';
wfProfileIn( $fname );
} else {
$cleansource = tidy_get_output( $tidy );
}
+ if ( $wgDebugTidy && $tidy->getStatus() > 0 ) {
+ $cleansource .= "<!--\nTidy reports:\n" .
+ str_replace( '-->', '-->', $tidy->errorBuffer ) .
+ "\n-->";
+ }
+
wfProfileOut( $fname );
return $cleansource;
}
$dom = $this->preprocessToDom( $text );
$flags = $argsOnly ? PPFrame::NO_TEMPLATES : 0;
- $text = $frame->expand( $dom, 0, $flags );
+ $text = $frame->expand( $dom, $flags );
wfProfileOut( $fname );
return $text;
# Just replace the arguments, not any double-brace items
# This is used for rendered interwiki transclusion
if ( $isDOM ) {
- $text = $newFrame->expand( $text, 0, PPFrame::NO_TEMPLATES );
+ $text = $newFrame->expand( $text, PPFrame::NO_TEMPLATES );
} else {
$text = $this->replaceVariables( $text, $newFrame, true );
}
$text = $frame->expand( $text );
}
} elseif ( $isDOM ) {
- $text = $frame->expand( $text, 0, PPFrame::NO_TEMPLATES | PPFrame::NO_ARGS );
+ $text = $frame->expand( $text, PPFrame::NO_TEMPLATES | PPFrame::NO_ARGS );
}
# Prune lower levels off the recursion check path
$userText = wfEscapeWikiText( $username );
$nickText = wfEscapeWikiText( $nickname );
if ( $user->isAnon() ) {
- return wfMsgForContent( 'signature-anon', $userText, $nickText );
+ return wfMsgExt( 'signature-anon', array( 'content', 'parsemag' ), $userText, $nickText );
} else {
- return wfMsgForContent( 'signature', $userText, $nickText );
+ return wfMsgExt( 'signature', array( 'content', 'parsemag' ), $userText, $nickText );
}
}
* @return string Signature text
*/
function cleanSig( $text, $parsing = false ) {
- global $wgTitle;
- $this->startExternalParse( $wgTitle, new ParserOptions(), $parsing ? OT_WIKI : OT_MSG );
+ if ( !$parsing ) {
+ global $wgTitle;
+ $this->startExternalParse( $wgTitle, new ParserOptions(), OT_MSG );
+ }
+ # FIXME: regex doesn't respect extension tags or nowiki
+ # => Move this logic to braceSubstitution()
$substWord = MagicWord::get( 'subst' );
$substRegex = '/\{\{(?!(?:' . $substWord->getBaseRegex() . '))/x' . $substWord->getRegexCase();
$substText = '{{' . $substWord->getSynonym( 0 );
$text = preg_replace( $substRegex, $substText, $text );
$text = $this->cleanSigInSig( $text );
- $text = $this->replaceVariables( $text );
+ $dom = $this->preprocessToDom( $text );
+ $frame = new PPFrame( $this );
+ $text = $frame->expand( $dom->documentElement );
+
+ if ( !$parsing ) {
+ $text = $this->mStripState->unstripBoth( $text );
+ }
- $this->clearState();
return $text;
}
$this->setOutputType( OT_MSG );
$this->clearState();
$text = $this->replaceVariables( $text );
+ $text = $this->mStripState->unstripBoth( $text );
$executing = false;
wfProfileOut($fname);
$pdbk = $pdbks[$key];
$searchkey = "<!--LINK $key-->";
$title = $this->mLinkHolders['titles'][$key];
- if ( !isset( $colours[$pdbk] ) ) {
+ if ( !isset( $colours[$pdbk] ) || $colours[$pdbk] == 'new' ) {
$linkCache->addBadLinkObj( $title );
$colours[$pdbk] = 'new';
$this->mOutput->addLink( $title, 0 );
$curIndex++;
}
if ( $mode == 'replace' ) {
- $outText .= $frame->expand( $node );
+ $outText .= $frame->expand( $node, PPFrame::RECOVER_ORIG );
}
$node = $node->nextSibling;
}
}
}
if ( $mode == 'get' ) {
- $outText .= $frame->expand( $node );
+ $outText .= $frame->expand( $node, PPFrame::RECOVER_ORIG );
}
$node = $node->nextSibling;
} while ( $node );
// stripped by the editor, so we need both newlines to restore the paragraph gap
$outText .= $newText . "\n\n";
while ( $node ) {
- $outText .= $frame->expand( $node );
+ $outText .= $frame->expand( $node, PPFrame::RECOVER_ORIG );
$node = $node->nextSibling;
}
}
const NO_ARGS = 1;
const NO_TEMPLATES = 2;
+ const RECOVER_ORIG = 3;
/**
* Construct a new preprocessor frame.
* using the current context
* @param $root the node
*/
- function expand( $root, $shallowFlags = 0, $deepFlags = 0 ) {
+ function expand( $root, $flags = 0 ) {
if ( is_string( $root ) ) {
return $root;
}
{
return $this->parser->insertStripItem( '<!-- node-count limit exceeded -->' );
}
- $flags = $shallowFlags | $deepFlags;
if ( is_array( $root ) ) {
$s = '';
foreach ( $root as $node ) {
- $s .= $this->expand( $node, 0, $deepFlags );
+ $s .= $this->expand( $node, $flags );
}
} elseif ( $root instanceof DOMNodeList ) {
$s = '';
foreach ( $root as $node ) {
- $s .= $this->expand( $node, 0, $deepFlags );
+ $s .= $this->expand( $node, $flags );
}
} elseif ( $root instanceof DOMNode ) {
if ( $root->nodeType == XML_TEXT_NODE ) {
$title = $titles->item( 0 );
$parts = $xpath->query( 'part', $root );
if ( $flags & self::NO_TEMPLATES ) {
- $s = '{{' . $this->implodeWithFlags( '|', 0, $deepFlags, $title, $parts ) . '}}';
+ $s = '{{' . $this->implodeWithFlags( '|', $flags, $title, $parts ) . '}}';
} else {
$lineStart = $root->getAttribute( 'lineStart' );
$params = array(
$title = $titles->item( 0 );
$parts = $xpath->query( 'part', $root );
if ( $flags & self::NO_ARGS || $this->parser->ot['msg'] ) {
- $s = '{{{' . $this->implode( '|', 0, $deepFlags, $title, $parts ) . '}}}';
+ $s = '{{{' . $this->implodeWithFlags( '|', $flags, $title, $parts ) . '}}}';
} else {
$params = array( 'title' => $title, 'parts' => $parts, 'text' => 'FIXME' );
$s = $this->parser->argSubstitution( $params, $this );
$s = $this->parser->extensionSubstitution( $params, $this );
} elseif ( $root->nodeName == 'h' ) {
# Heading
- $s = $this->expand( $root->childNodes, 0, $deepFlags );
+ $s = $this->expand( $root->childNodes, $flags );
if ( $this->parser->ot['html'] ) {
# Insert heading index marker
$serial = count( $this->parser->mHeadings ) - 1;
$marker = "{$this->parser->mUniqPrefix}-h-$serial-{$this->parser->mMarkerSuffix}";
$count = $root->getAttribute( 'level' );
-
- // FIXME: bug-for-bug with old parser
- // Lose whitespace for no apparent reason
- // Remove this after differential testing is done
- if ( true ) {
- // Good version
- $s = substr( $s, 0, $count ) . $marker . substr( $s, $count );
- } else {
- // Bad version
- if ( preg_match( '/^(={1,6})(.*?)(={1,6})\s*?$/', $s, $m ) ) {
- if ( $m[2] != '' ) {
- $s = $m[1] . $marker . $m[2] . $m[3];
- }
- }
- }
+ $s = substr( $s, 0, $count ) . $marker . substr( $s, $count );
$this->parser->mStripState->general->setPair( $marker, '' );
}
} else {
if ( $node->nodeType == XML_TEXT_NODE ) {
$s .= $node->nodeValue;
} elseif ( $node->nodeType == XML_ELEMENT_NODE ) {
- $s .= $this->expand( $node, 0, $deepFlags );
+ $s .= $this->expand( $node, $flags );
}
}
}
return $s;
}
- function implodeWithFlags( $sep, $shallowFlags, $deepFlags /*, ... */ ) {
- $args = array_slice( func_get_args(), 3 );
+ function implodeWithFlags( $sep, $flags /*, ... */ ) {
+ $args = array_slice( func_get_args(), 2 );
$first = true;
$s = '';
} else {
$s .= $sep;
}
- $s .= $this->expand( $node, $shallowFlags, $deepFlags );
+ $s .= $this->expand( $node, $flags );
}
}
return $s;
function implode( $sep /*, ... */ ) {
$args = func_get_args();
- $args = array_merge( array_slice( $args, 0, 1 ), array( 0, 0 ), array_slice( $args, 1 ) );
+ $args = array_merge( array_slice( $args, 0, 1 ), array( 0 ), array_slice( $args, 1 ) );
return call_user_func_array( array( $this, 'implodeWithFlags' ), $args );
}
+ /**
+ * Split an <arg> or <template> node into a three-element array:
+ * DOMNode name, string index and DOMNode value
+ */
+ function splitBraceNode( $node ) {
+ $xpath = new DOMXPath( $arg->ownerDocument );
+ $names = $xpath->query( 'name', $node );
+ $values = $xpath->query( 'value', $node );
+ if ( !$names->length || !$values->length ) {
+ throw new MWException( 'Invalid brace node passed to ' . __METHOD__ );
+ }
+ $name = $names->item( 0 );
+ $index = $name->getAttribute( 'index' );
+ return array( $name, $index, $values->item( 0 ) );
+ }
+
function __toString() {
return 'frame{}';
}