summaryrefslogtreecommitdiff
path: root/includes/parser
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2012-05-03 13:01:35 +0200
committerPierre Schmitz <pierre@archlinux.de>2012-05-03 13:01:35 +0200
commitd9022f63880ce039446fba8364f68e656b7bf4cb (patch)
tree16b40fbf17bf7c9ee6f4ead25b16dd192378050a /includes/parser
parent27cf83d177256813e2e802241085fce5dd0f3fb9 (diff)
Update to MediaWiki 1.19.0
Diffstat (limited to 'includes/parser')
-rw-r--r--includes/parser/CoreParserFunctions.php42
-rw-r--r--includes/parser/LinkHolderArray.php6
-rw-r--r--includes/parser/Parser.php549
-rw-r--r--includes/parser/ParserCache.php23
-rw-r--r--includes/parser/ParserOptions.php327
-rw-r--r--includes/parser/ParserOutput.php87
-rw-r--r--includes/parser/Preprocessor.php11
-rw-r--r--includes/parser/Preprocessor_DOM.php16
-rw-r--r--includes/parser/Preprocessor_Hash.php11
-rw-r--r--includes/parser/Preprocessor_HipHop.hphp16
-rw-r--r--includes/parser/StripState.php7
-rw-r--r--includes/parser/Tidy.php12
12 files changed, 807 insertions, 300 deletions
diff --git a/includes/parser/CoreParserFunctions.php b/includes/parser/CoreParserFunctions.php
index 5dffd978..0e5702b7 100644
--- a/includes/parser/CoreParserFunctions.php
+++ b/includes/parser/CoreParserFunctions.php
@@ -97,7 +97,7 @@ class CoreParserFunctions {
static function intFunction( $parser, $part1 = '' /*, ... */ ) {
if ( strval( $part1 ) !== '' ) {
$args = array_slice( func_get_args(), 2 );
- $message = wfMessage( $part1, $args )->inLanguage( $parser->getOptions()->getUserLang() )->plain();
+ $message = wfMessage( $part1, $args )->inLanguage( $parser->getOptions()->getUserLangObj() )->plain();
return array( $message, 'noparse' => false );
} else {
return array( 'found' => false );
@@ -279,7 +279,14 @@ class CoreParserFunctions {
*/
static function gender( $parser, $username ) {
wfProfileIn( __METHOD__ );
- $forms = array_slice( func_get_args(), 2);
+ $forms = array_slice( func_get_args(), 2 );
+
+ // Some shortcuts to avoid loading user data unnecessarily
+ if ( count( $forms ) === 0 ) {
+ return '';
+ } elseif ( count( $forms ) === 1 ) {
+ return $forms[0];
+ }
$username = trim( $username );
@@ -564,7 +571,11 @@ class CoreParserFunctions {
* to the link cache, so the local cache here should be unnecessary, but
* in fact calling getLength() repeatedly for the same $page does seem to
* run one query for each call?
+ * @todo Document parameters
+ *
* @param $parser Parser
+ * @param $page String TODO DOCUMENT (Default: empty string)
+ * @param $raw TODO DOCUMENT (Default: null)
*/
static function pagesize( $parser, $page = '', $raw = null ) {
static $cache = array();
@@ -625,7 +636,7 @@ class CoreParserFunctions {
/**
* Unicode-safe str_pad with the restriction that $length is forced to be <= 500
- */
+ */
static function pad( $parser, $string, $length, $padding = '0', $direction = STR_PAD_RIGHT ) {
$padding = $parser->killMarkers( $padding );
$lengthOfPadding = mb_strlen( $padding );
@@ -680,23 +691,36 @@ class CoreParserFunctions {
/**
* @param $parser Parser
- * @param $text
+ * @param $text String The sortkey to use
+ * @param $uarg String Either "noreplace" or "noerror" (in en)
+ * both suppress errors, and noreplace does nothing if
+ * a default sortkey already exists.
* @return string
*/
- public static function defaultsort( $parser, $text ) {
+ public static function defaultsort( $parser, $text, $uarg = '' ) {
+ static $magicWords = null;
+ if ( is_null( $magicWords ) ) {
+ $magicWords = new MagicWordArray( array( 'defaultsort_noerror', 'defaultsort_noreplace' ) );
+ }
+ $arg = $magicWords->matchStartToEnd( $uarg );
+
$text = trim( $text );
if( strlen( $text ) == 0 )
return '';
$old = $parser->getCustomDefaultSort();
- $parser->setDefaultSort( $text );
- if( $old === false || $old == $text )
+ if ( $old === false || $arg !== 'defaultsort_noreplace' ) {
+ $parser->setDefaultSort( $text );
+ }
+
+ if( $old === false || $old == $text || $arg ) {
return '';
- else
+ } else {
return( '<span class="error">' .
wfMsgForContent( 'duplicate-defaultsort',
htmlspecialchars( $old ),
htmlspecialchars( $text ) ) .
'</span>' );
+ }
}
// Usage {{filepath|300}}, {{filepath|nowiki}}, {{filepath|nowiki|300}} or {{filepath|300|nowiki}}
@@ -724,7 +748,7 @@ class CoreParserFunctions {
if ( $file ) {
$url = $file->getFullUrl();
- // If a size is requested...
+ // If a size is requested...
if ( is_integer( $size ) ) {
$mto = $file->transform( array( 'width' => $size ) );
// ... and we can
diff --git a/includes/parser/LinkHolderArray.php b/includes/parser/LinkHolderArray.php
index 5418b6e5..fb013047 100644
--- a/includes/parser/LinkHolderArray.php
+++ b/includes/parser/LinkHolderArray.php
@@ -320,7 +320,7 @@ class LinkHolderArray {
foreach ( $res as $s ) {
$title = Title::makeTitle( $s->page_namespace, $s->page_title );
$pdbk = $title->getPrefixedDBkey();
- $linkCache->addGoodLinkObj( $s->page_id, $title, $s->page_len, $s->page_is_redirect, $s->page_latest );
+ $linkCache->addGoodLinkObjFromRow( $title, $s );
$output->addLink( $title, $s->page_id );
# @todo FIXME: Convoluted data flow
# The redirect status and length is passed to getLinkColour via the LinkCache
@@ -490,7 +490,7 @@ class LinkHolderArray {
// construct query
$dbr = wfGetDB( DB_SLAVE );
$varRes = $dbr->select( 'page',
- array( 'page_id', 'page_namespace', 'page_title', 'page_is_redirect', 'page_len' ),
+ array( 'page_id', 'page_namespace', 'page_title', 'page_is_redirect', 'page_len', 'page_latest' ),
$linkBatch->constructSet( 'page', $dbr ),
__METHOD__
);
@@ -507,7 +507,7 @@ class LinkHolderArray {
$holderKeys = array();
if( isset( $variantMap[$varPdbk] ) ) {
$holderKeys = $variantMap[$varPdbk];
- $linkCache->addGoodLinkObj( $s->page_id, $variantTitle, $s->page_len, $s->page_is_redirect );
+ $linkCache->addGoodLinkObjFromRow( $variantTitle, $s );
$output->addLink( $variantTitle, $s->page_id );
}
diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php
index 939d9e3f..2abf1b93 100644
--- a/includes/parser/Parser.php
+++ b/includes/parser/Parser.php
@@ -32,9 +32,9 @@
* Removes <noinclude> sections, and <includeonly> tags.
*
* Globals used:
- * objects: $wgLang, $wgContLang
+ * object: $wgContLang
*
- * NOT $wgUser or $wgTitle. Keep them away!
+ * NOT $wgUser or $wgTitle or $wgRequest or $wgLang. Keep them away!
*
* settings:
* $wgUseDynamicDates*, $wgInterwikiMagic*,
@@ -68,9 +68,11 @@ class Parser {
# Constants needed for external link processing
# Everything except bracket, space, or control characters
- const EXT_LINK_URL_CLASS = '[^][<>"\\x00-\\x20\\x7F]';
- const EXT_IMAGE_REGEX = '/^(http:\/\/|https:\/\/)([^][<>"\\x00-\\x20\\x7F]+)
- \\/([A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF]+)\\.((?i)gif|png|jpg|jpeg)$/Sx';
+ # \p{Zs} is unicode 'separator, space' category. It covers the space 0x20
+ # as well as U+3000 is IDEOGRAPHIC SPACE for bug 19052
+ const EXT_LINK_URL_CLASS = '[^][<>"\\x00-\\x20\\x7F\p{Zs}]';
+ const EXT_IMAGE_REGEX = '/^(http:\/\/|https:\/\/)([^][<>"\\x00-\\x20\\x7F\p{Zs}]+)
+ \\/([A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF]+)\\.((?i)gif|png|jpg|jpeg)$/Sxu';
# State constants for the definition list colon extraction
const COLON_STATE_TEXT = 0;
@@ -146,6 +148,7 @@ class Parser {
var $mTplExpandCache; # empty-frame expansion cache
var $mTplRedirCache, $mTplDomCache, $mHeadings, $mDoubleUnderscores;
var $mExpensiveFunctionCount; # number of expensive parser function calls
+ var $mShowToc, $mForceTocPosition;
/**
* @var User
@@ -179,12 +182,14 @@ class Parser {
/**
* Constructor
+ *
+ * @param $conf array
*/
public function __construct( $conf = array() ) {
$this->mConf = $conf;
$this->mUrlProtocols = wfUrlProtocols();
$this->mExtLinkBracketedRegex = '/\[((' . wfUrlProtocols() . ')'.
- '[^][<>"\\x00-\\x20\\x7F]+) *([^\]\\x00-\\x08\\x0a-\\x1F]*?)\]/S';
+ self::EXT_LINK_URL_CLASS.'+)\p{Zs}*([^\]\\x00-\\x08\\x0a-\\x1F]*?)\]/Su';
if ( isset( $conf['preprocessorClass'] ) ) {
$this->mPreprocessorClass = $conf['preprocessorClass'];
} elseif ( defined( 'MW_COMPILED' ) ) {
@@ -316,7 +321,7 @@ class Parser {
* to internalParse() which does all the real work.
*/
- global $wgUseTidy, $wgAlwaysUseTidy, $wgContLang, $wgDisableLangConversion, $wgDisableTitleConversion;
+ global $wgUseTidy, $wgAlwaysUseTidy, $wgDisableLangConversion, $wgDisableTitleConversion;
$fname = __METHOD__.'-' . wfGetCaller();
wfProfileIn( __METHOD__ );
wfProfileIn( $fname );
@@ -345,7 +350,7 @@ class Parser {
$fixtags = array(
# french spaces, last one Guillemet-left
# only if there is something before the space
- '/(.) (?=\\?|:|;|!|%|\\302\\273)/' => '\\1&#160;\\2',
+ '/(.) (?=\\?|:|;|!|%|\\302\\273)/' => '\\1&#160;',
# french spaces, Guillemet-right
'/(\\302\\253) /' => '\\1&#160;',
'/&#160;(!\s*important)/' => ' \\1', # Beware of CSS magic word !important, bug #11874.
@@ -357,20 +362,24 @@ class Parser {
$this->replaceLinkHolders( $text );
/**
- * The page doesn't get language converted if
+ * The input doesn't get language converted if
* a) It's disabled
* b) Content isn't converted
* c) It's a conversion table
+ * d) it is an interface message (which is in the user language)
*/
if ( !( $wgDisableLangConversion
|| isset( $this->mDoubleUnderscores['nocontentconvert'] )
- || $this->mTitle->isConversionTable() ) ) {
-
- # The position of the convert() call should not be changed. it
- # assumes that the links are all replaced and the only thing left
- # is the <nowiki> mark.
-
- $text = $wgContLang->convert( $text );
+ || $this->mTitle->isConversionTable() ) )
+ {
+ # Run convert unconditionally in 1.18-compatible mode
+ global $wgBug34832TransitionalRollback;
+ if ( $wgBug34832TransitionalRollback || !$this->mOptions->getInterfaceMessage() ) {
+ # The position of the convert() call should not be changed. it
+ # assumes that the links are all replaced and the only thing left
+ # is the <nowiki> mark.
+ $text = $this->getConverterLanguage()->convert( $text );
+ }
}
/**
@@ -386,11 +395,11 @@ class Parser {
|| isset( $this->mDoubleUnderscores['notitleconvert'] )
|| $this->mOutput->getDisplayTitle() !== false ) )
{
- $convruletitle = $wgContLang->getConvRuleTitle();
+ $convruletitle = $this->getConverterLanguage()->getConvRuleTitle();
if ( $convruletitle ) {
$this->mOutput->setTitleText( $convruletitle );
} else {
- $titleText = $wgContLang->convertTitle( $title );
+ $titleText = $this->getConverterLanguage()->convertTitle( $title );
$this->mOutput->setTitleText( $titleText );
}
}
@@ -504,10 +513,32 @@ class Parser {
}
/**
+ * Recursive parser entry point that can be called from an extension tag
+ * hook.
+ *
+ * @param $text String: text to be expanded
+ * @param $frame PPFrame: The frame to use for expanding any template variables
+ * @return String
+ * @since 1.19
+ */
+ public function recursivePreprocess( $text, $frame = false ) {
+ wfProfileIn( __METHOD__ );
+ $text = $this->replaceVariables( $text, $frame );
+ $text = $this->mStripState->unstripBoth( $text );
+ wfProfileOut( __METHOD__ );
+ return $text;
+ }
+
+ /**
* Process the wikitext for the ?preload= feature. (bug 5210)
*
* <noinclude>, <includeonly> etc. are parsed as for template transclusion,
* comments, templates, arguments, tags hooks and parser functions are untouched.
+ *
+ * @param $text String
+ * @param $title Title
+ * @param $options ParserOptions
+ * @return String
*/
public function getPreloadText( $text, Title $title, ParserOptions $options ) {
# Parser (re)initialisation
@@ -664,15 +695,23 @@ class Parser {
}
/**
+ * Get a language object for use in parser functions such as {{FORMATNUM:}}
* @return Language
*/
function getFunctionLang() {
+ return $this->getTargetLanguage();
+ }
+
+ /**
+ * Get the target language for the content being parsed. This is usually the
+ * language that the content is in.
+ */
+ function getTargetLanguage() {
$target = $this->mOptions->getTargetLanguage();
if ( $target !== null ) {
return $target;
} elseif( $this->mOptions->getInterfaceMessage() ) {
- global $wgLang;
- return $wgLang;
+ return $this->mOptions->getUserLangObj();
} elseif( is_null( $this->mTitle ) ) {
throw new MWException( __METHOD__.': $this->mTitle is null' );
}
@@ -680,6 +719,18 @@ class Parser {
}
/**
+ * Get the language object for language conversion
+ */
+ function getConverterLanguage() {
+ global $wgBug34832TransitionalRollback, $wgContLang;
+ if ( $wgBug34832TransitionalRollback ) {
+ return $wgContLang;
+ } else {
+ return $this->getTargetLanguage();
+ }
+ }
+
+ /**
* Get a User object either from $this->mUser, if set, or from the
* ParserOptions object otherwise
*
@@ -797,6 +848,10 @@ class Parser {
* Add an item to the strip state
* Returns the unique tag which must be inserted into the stripped text
* The tag will be replaced with the original text in unstrip()
+ *
+ * @param $text string
+ *
+ * @return string
*/
function insertStripItem( $text ) {
$rnd = "{$this->mUniqPrefix}-item-{$this->mMarkerIndex}-" . self::MARKER_SUFFIX;
@@ -1005,8 +1060,14 @@ class Parser {
* HTML. Only called for $mOutputType == self::OT_HTML.
*
* @private
+ *
+ * @param $text string
+ * @param $isMain bool
+ * @param $frame bool
+ *
+ * @return string
*/
- function internalParse( $text, $isMain = true, $frame=false ) {
+ function internalParse( $text, $isMain = true, $frame = false ) {
wfProfileIn( __METHOD__ );
$origText = $text;
@@ -1072,6 +1133,10 @@ class Parser {
*
* DML
* @private
+ *
+ * @param $text string
+ *
+ * @return string
*/
function doMagicLinks( $text ) {
wfProfileIn( __METHOD__ );
@@ -1088,7 +1153,7 @@ class Parser {
(?: [0-9] [\ \-]? ){9} # 9 digits with opt. delimiters
[0-9Xx] # check digit
\b)
- )!x', array( &$this, 'magicLinkCallback' ), $text );
+ )!xu', array( &$this, 'magicLinkCallback' ), $text );
wfProfileOut( __METHOD__ );
return $text;
}
@@ -1136,7 +1201,7 @@ class Parser {
));
$titleObj = SpecialPage::getTitleFor( 'Booksources', $num );
return'<a href="' .
- $titleObj->escapeLocalUrl() .
+ htmlspecialchars( $titleObj->getLocalUrl() ) .
"\" class=\"internal mw-magiclink-isbn\">ISBN $isbn</a>";
} else {
return $m[0];
@@ -1145,11 +1210,13 @@ class Parser {
/**
* Make a free external link, given a user-supplied URL
- * @return HTML
+ *
+ * @param $url string
+ *
+ * @return string HTML
* @private
*/
function makeFreeExternalLink( $url ) {
- global $wgContLang;
wfProfileIn( __METHOD__ );
$trail = '';
@@ -1182,7 +1249,8 @@ class Parser {
$text = $this->maybeMakeExternalImage( $url );
if ( $text === false ) {
# Not an image, make a link
- $text = Linker::makeExternalLink( $url, $wgContLang->markNoConversion($url), true, 'free',
+ $text = Linker::makeExternalLink( $url,
+ $this->getConverterLanguage()->markNoConversion($url), true, 'free',
$this->getExternalLinkAttribs( $url ) );
# Register it in the output object...
# Replace unnecessary URL escape codes with their equivalent characters
@@ -1198,6 +1266,10 @@ class Parser {
* Parse headers and return html
*
* @private
+ *
+ * @param $text string
+ *
+ * @return string
*/
function doHeadings( $text ) {
wfProfileIn( __METHOD__ );
@@ -1213,6 +1285,9 @@ class Parser {
/**
* Replace single quotes with HTML markup
* @private
+ *
+ * @param $text string
+ *
* @return string the altered text
*/
function doAllQuotes( $text ) {
@@ -1229,6 +1304,10 @@ class Parser {
/**
* Helper function for doAllQuotes()
+ *
+ * @param $text string
+ *
+ * @return string
*/
public function doQuotes( $text ) {
$arr = preg_split( "/(''+)/", $text, -1, PREG_SPLIT_DELIM_CAPTURE );
@@ -1393,9 +1472,12 @@ class Parser {
* Make sure to run maintenance/parserTests.php if you change this code.
*
* @private
+ *
+ * @param $text string
+ *
+ * @return string
*/
function replaceExternalLinks( $text ) {
- global $wgContLang;
wfProfileIn( __METHOD__ );
$bits = preg_split( $this->mExtLinkBracketedRegex, $text, -1, PREG_SPLIT_DELIM_CAPTURE );
@@ -1432,7 +1514,7 @@ class Parser {
# No link text, e.g. [http://domain.tld/some.link]
if ( $text == '' ) {
# Autonumber
- $langObj = $this->getFunctionLang();
+ $langObj = $this->getTargetLanguage();
$text = '[' . $langObj->formatNum( ++$this->mAutonumber ) . ']';
$linktype = 'autonumber';
} else {
@@ -1441,7 +1523,7 @@ class Parser {
list( $dtrail, $trail ) = Linker::splitTrail( $trail );
}
- $text = $wgContLang->markNoConversion( $text );
+ $text = $this->getConverterLanguage()->markNoConversion( $text );
$url = Sanitizer::cleanUrl( $url );
@@ -1469,9 +1551,9 @@ class Parser {
* (depending on configuration, namespace, and the URL's domain) and/or a
* target attribute (depending on configuration).
*
- * @param $url String: optional URL, to extract the domain from for rel =>
+ * @param $url String|bool optional URL, to extract the domain from for rel =>
* nofollow if appropriate
- * @return Array: associative array of HTML attributes
+ * @return Array associative array of HTML attributes
*/
function getExternalLinkAttribs( $url = false ) {
$attribs = array();
@@ -1507,6 +1589,10 @@ class Parser {
/**
* Callback function used in replaceUnusualEscapes().
* Replaces unusual URL escape codes with their equivalent character
+ *
+ * @param $matches array
+ *
+ * @return string
*/
private static function replaceUnusualEscapesCallback( $matches ) {
$char = urldecode( $matches[0] );
@@ -1525,6 +1611,10 @@ class Parser {
* make an image if it's allowed, either through the global
* option, through the exception, or through the on-wiki whitelist
* @private
+ *
+ * $param $url string
+ *
+ * @return string
*/
function maybeMakeExternalImage( $url ) {
$imagesfrom = $this->mOptions->getAllowExternalImagesFrom();
@@ -1571,6 +1661,9 @@ class Parser {
/**
* Process [[ ]] wikilinks
+ *
+ * @param $s string
+ *
* @return String: processed text
*
* @private
@@ -1587,8 +1680,6 @@ class Parser {
* @private
*/
function replaceInternalLinks2( &$s ) {
- global $wgContLang;
-
wfProfileIn( __METHOD__ );
wfProfileIn( __METHOD__.'-setup' );
@@ -1612,7 +1703,7 @@ class Parser {
$line = $a->current(); # Workaround for broken ArrayIterator::next() that returns "void"
$s = substr( $s, 1 );
- $useLinkPrefixExtension = $wgContLang->linkPrefixExtension();
+ $useLinkPrefixExtension = $this->getTargetLanguage()->linkPrefixExtension();
$e2 = null;
if ( $useLinkPrefixExtension ) {
# Match the end of a line for a word that's not followed by whitespace,
@@ -1638,8 +1729,9 @@ class Parser {
$prefix = '';
}
- if ( $wgContLang->hasVariants() ) {
- $selflink = $wgContLang->autoConvertToAllVariants( $this->mTitle->getPrefixedText() );
+ if ( $this->getConverterLanguage()->hasVariants() ) {
+ $selflink = $this->getConverterLanguage()->autoConvertToAllVariants(
+ $this->mTitle->getPrefixedText() );
} else {
$selflink = array( $this->mTitle->getPrefixedText() );
}
@@ -1807,6 +1899,7 @@ class Parser {
# Link not escaped by : , create the various objects
if ( $noforce ) {
+ global $wgContLang;
# Interwikis
wfProfileIn( __METHOD__."-interwiki" );
@@ -1856,7 +1949,7 @@ class Parser {
}
$sortkey = Sanitizer::decodeCharReferences( $sortkey );
$sortkey = str_replace( "\n", '', $sortkey );
- $sortkey = $wgContLang->convertCategoryKey( $sortkey );
+ $sortkey = $this->getConverterLanguage()->convertCategoryKey( $sortkey );
$this->mOutput->addCategory( $nt->getDBkey(), $sortkey );
/**
@@ -1883,11 +1976,12 @@ class Parser {
if ( $ns == NS_MEDIA ) {
wfProfileIn( __METHOD__."-media" );
# Give extensions a chance to select the file revision for us
- $time = $sha1 = $descQuery = false;
+ $options = array();
+ $descQuery = false;
wfRunHooks( 'BeforeParserFetchFileAndTitle',
- array( $this, $nt, &$time, &$sha1, &$descQuery ) );
+ array( $this, $nt, &$options, &$descQuery ) );
# Fetch and register the file (file title may be different via hooks)
- list( $file, $nt ) = $this->fetchFileAndTitle( $nt, $time, $sha1 );
+ list( $file, $nt ) = $this->fetchFileAndTitle( $nt, $options );
# Cloak with NOPARSE to avoid replacement in replaceExternalLinks
$s .= $prefix . $this->armorLinks(
Linker::makeMediaLinkFile( $nt, $file, $text ) ) . $trail;
@@ -1999,6 +2093,11 @@ class Parser {
* getCommon() returns the length of the longest common substring
* of both arguments, starting at the beginning of both.
* @private
+ *
+ * @param $st1 string
+ * @param $st2 string
+ *
+ * @return int
*/
function getCommon( $st1, $st2 ) {
$fl = strlen( $st1 );
@@ -2020,6 +2119,8 @@ class Parser {
* element appropriate to the prefix character passed into them.
* @private
*
+ * @param $char char
+ *
* @return string
*/
function openList( $char ) {
@@ -2283,10 +2384,10 @@ class Parser {
* Split up a string on ':', ignoring any occurences inside tags
* to prevent illegal overlapping.
*
- * @param $str String: the string to split
- * @param &$before String: set to everything before the ':'
- * @param &$after String: set to everything after the ':'
- * return String: the position of the ':', or false if none found
+ * @param $str String the string to split
+ * @param &$before String set to everything before the ':'
+ * @param &$after String set to everything after the ':'
+ * @return String the position of the ':', or false if none found
*/
function findColonNoLinks( $str, &$before, &$after ) {
wfProfileIn( __METHOD__ );
@@ -2451,11 +2552,22 @@ class Parser {
*
* @param $index integer
* @param $frame PPFrame
+ *
+ * @return string
*/
- function getVariableValue( $index, $frame=false ) {
+ function getVariableValue( $index, $frame = false ) {
global $wgContLang, $wgSitename, $wgServer;
global $wgArticlePath, $wgScriptPath, $wgStylePath;
+ if ( is_null( $this->mTitle ) ) {
+ // If no title set, bad things are going to happen
+ // later. Title should always be set since this
+ // should only be called in the middle of a parse
+ // operation (but the unit-tests do funky stuff)
+ throw new MWException( __METHOD__ . ' Should only be '
+ . ' called while parsing (no title set)' );
+ }
+
/**
* Some of these require message or data lookups and can be
* expensive to check many times.
@@ -2490,48 +2602,50 @@ class Parser {
date_default_timezone_set( $oldtz );
}
+ $pageLang = $this->getFunctionLang();
+
switch ( $index ) {
case 'currentmonth':
- $value = $wgContLang->formatNum( gmdate( 'm', $ts ) );
+ $value = $pageLang->formatNum( gmdate( 'm', $ts ) );
break;
case 'currentmonth1':
- $value = $wgContLang->formatNum( gmdate( 'n', $ts ) );
+ $value = $pageLang->formatNum( gmdate( 'n', $ts ) );
break;
case 'currentmonthname':
- $value = $wgContLang->getMonthName( gmdate( 'n', $ts ) );
+ $value = $pageLang->getMonthName( gmdate( 'n', $ts ) );
break;
case 'currentmonthnamegen':
- $value = $wgContLang->getMonthNameGen( gmdate( 'n', $ts ) );
+ $value = $pageLang->getMonthNameGen( gmdate( 'n', $ts ) );
break;
case 'currentmonthabbrev':
- $value = $wgContLang->getMonthAbbreviation( gmdate( 'n', $ts ) );
+ $value = $pageLang->getMonthAbbreviation( gmdate( 'n', $ts ) );
break;
case 'currentday':
- $value = $wgContLang->formatNum( gmdate( 'j', $ts ) );
+ $value = $pageLang->formatNum( gmdate( 'j', $ts ) );
break;
case 'currentday2':
- $value = $wgContLang->formatNum( gmdate( 'd', $ts ) );
+ $value = $pageLang->formatNum( gmdate( 'd', $ts ) );
break;
case 'localmonth':
- $value = $wgContLang->formatNum( $localMonth );
+ $value = $pageLang->formatNum( $localMonth );
break;
case 'localmonth1':
- $value = $wgContLang->formatNum( $localMonth1 );
+ $value = $pageLang->formatNum( $localMonth1 );
break;
case 'localmonthname':
- $value = $wgContLang->getMonthName( $localMonthName );
+ $value = $pageLang->getMonthName( $localMonthName );
break;
case 'localmonthnamegen':
- $value = $wgContLang->getMonthNameGen( $localMonthName );
+ $value = $pageLang->getMonthNameGen( $localMonthName );
break;
case 'localmonthabbrev':
- $value = $wgContLang->getMonthAbbreviation( $localMonthName );
+ $value = $pageLang->getMonthAbbreviation( $localMonthName );
break;
case 'localday':
- $value = $wgContLang->formatNum( $localDay );
+ $value = $pageLang->formatNum( $localDay );
break;
case 'localday2':
- $value = $wgContLang->formatNum( $localDay2 );
+ $value = $pageLang->formatNum( $localDay2 );
break;
case 'pagename':
$value = wfEscapeWikiText( $this->mTitle->getText() );
@@ -2656,68 +2770,68 @@ class Parser {
$value = ( wfUrlencode( $this->mTitle->getSubjectNsText() ) );
break;
case 'currentdayname':
- $value = $wgContLang->getWeekdayName( gmdate( 'w', $ts ) + 1 );
+ $value = $pageLang->getWeekdayName( gmdate( 'w', $ts ) + 1 );
break;
case 'currentyear':
- $value = $wgContLang->formatNum( gmdate( 'Y', $ts ), true );
+ $value = $pageLang->formatNum( gmdate( 'Y', $ts ), true );
break;
case 'currenttime':
- $value = $wgContLang->time( wfTimestamp( TS_MW, $ts ), false, false );
+ $value = $pageLang->time( wfTimestamp( TS_MW, $ts ), false, false );
break;
case 'currenthour':
- $value = $wgContLang->formatNum( gmdate( 'H', $ts ), true );
+ $value = $pageLang->formatNum( gmdate( 'H', $ts ), true );
break;
case 'currentweek':
# @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
# int to remove the padding
- $value = $wgContLang->formatNum( (int)gmdate( 'W', $ts ) );
+ $value = $pageLang->formatNum( (int)gmdate( 'W', $ts ) );
break;
case 'currentdow':
- $value = $wgContLang->formatNum( gmdate( 'w', $ts ) );
+ $value = $pageLang->formatNum( gmdate( 'w', $ts ) );
break;
case 'localdayname':
- $value = $wgContLang->getWeekdayName( $localDayOfWeek + 1 );
+ $value = $pageLang->getWeekdayName( $localDayOfWeek + 1 );
break;
case 'localyear':
- $value = $wgContLang->formatNum( $localYear, true );
+ $value = $pageLang->formatNum( $localYear, true );
break;
case 'localtime':
- $value = $wgContLang->time( $localTimestamp, false, false );
+ $value = $pageLang->time( $localTimestamp, false, false );
break;
case 'localhour':
- $value = $wgContLang->formatNum( $localHour, true );
+ $value = $pageLang->formatNum( $localHour, true );
break;
case 'localweek':
# @bug 4594 PHP5 has it zero padded, PHP4 does not, cast to
# int to remove the padding
- $value = $wgContLang->formatNum( (int)$localWeek );
+ $value = $pageLang->formatNum( (int)$localWeek );
break;
case 'localdow':
- $value = $wgContLang->formatNum( $localDayOfWeek );
+ $value = $pageLang->formatNum( $localDayOfWeek );
break;
case 'numberofarticles':
- $value = $wgContLang->formatNum( SiteStats::articles() );
+ $value = $pageLang->formatNum( SiteStats::articles() );
break;
case 'numberoffiles':
- $value = $wgContLang->formatNum( SiteStats::images() );
+ $value = $pageLang->formatNum( SiteStats::images() );
break;
case 'numberofusers':
- $value = $wgContLang->formatNum( SiteStats::users() );
+ $value = $pageLang->formatNum( SiteStats::users() );
break;
case 'numberofactiveusers':
- $value = $wgContLang->formatNum( SiteStats::activeUsers() );
+ $value = $pageLang->formatNum( SiteStats::activeUsers() );
break;
case 'numberofpages':
- $value = $wgContLang->formatNum( SiteStats::pages() );
+ $value = $pageLang->formatNum( SiteStats::pages() );
break;
case 'numberofadmins':
- $value = $wgContLang->formatNum( SiteStats::numberingroup( 'sysop' ) );
+ $value = $pageLang->formatNum( SiteStats::numberingroup( 'sysop' ) );
break;
case 'numberofedits':
- $value = $wgContLang->formatNum( SiteStats::edits() );
+ $value = $pageLang->formatNum( SiteStats::edits() );
break;
case 'numberofviews':
- $value = $wgContLang->formatNum( SiteStats::views() );
+ $value = $pageLang->formatNum( SiteStats::views() );
break;
case 'currenttimestamp':
$value = wfTimestamp( TS_MW, $ts );
@@ -2742,7 +2856,7 @@ class Parser {
case 'stylepath':
return $wgStylePath;
case 'directionmark':
- return $wgContLang->getDirMark();
+ return $pageLang->getDirMark();
case 'contentlanguage':
global $wgLanguageCode;
return $wgLanguageCode;
@@ -2755,8 +2869,9 @@ class Parser {
}
}
- if ( $index )
+ if ( $index ) {
$this->mVarCache[$index] = $value;
+ }
return $value;
}
@@ -2808,6 +2923,8 @@ class Parser {
/**
* Return a three-element array: leading whitespace, string contents, trailing whitespace
*
+ * @param $s string
+ *
* @return array
*/
public static function splitWhitespace( $s ) {
@@ -2833,11 +2950,11 @@ class Parser {
* self::OT_PREPROCESS: templates but not extension tags
* self::OT_HTML: all templates and extension tags
*
- * @param $text String: the text to transform
+ * @param $text String the text to transform
* @param $frame PPFrame Object describing the arguments passed to the template.
* Arguments may also be provided as an associative array, as was the usual case before MW1.12.
* Providing arguments this way may be useful for extensions wishing to perform variable replacement explicitly.
- * @param $argsOnly Boolean: only do argument (triple-brace) expansion, not double-brace expansion
+ * @param $argsOnly Boolean only do argument (triple-brace) expansion, not double-brace expansion
* @private
*
* @return string
@@ -2867,6 +2984,8 @@ class Parser {
/**
* Clean up argument array - refactored in 1.9 so parserfunctions can use it, too.
*
+ * @param $args array
+ *
* @return array
*/
static function createAssocArgs( $args ) {
@@ -2929,7 +3048,7 @@ class Parser {
* @private
*/
function braceSubstitution( $piece, $frame ) {
- global $wgContLang, $wgNonincludableNamespaces;
+ global $wgNonincludableNamespaces, $wgContLang;
wfProfileIn( __METHOD__ );
wfProfileIn( __METHOD__.'-setup' );
@@ -2957,7 +3076,8 @@ class Parser {
# @todo FIXME: If piece['parts'] is null then the call to getLength() below won't work b/c this $args isn't an object
$args = ( null == $piece['parts'] ) ? array() : $piece['parts'];
wfProfileOut( __METHOD__.'-setup' );
- wfProfileIn( __METHOD__."-title-$originalTitle" );
+
+ $titleProfileIn = null; // profile templates
# SUBST
wfProfileIn( __METHOD__.'-modifiers' );
@@ -3039,6 +3159,7 @@ class Parser {
}
}
if ( $function ) {
+ wfProfileIn( __METHOD__ . '-pfunc-' . $function );
list( $callback, $flags ) = $this->mFunctionHooks[$function];
$initialArgs = array( &$this );
$funcArgs = array( trim( substr( $part1, $colonPos + 1 ) ) );
@@ -3060,6 +3181,7 @@ class Parser {
# Workaround for PHP bug 35229 and similar
if ( !is_callable( $callback ) ) {
+ wfProfileOut( __METHOD__ . '-pfunc-' . $function );
wfProfileOut( __METHOD__ . '-pfunc' );
wfProfileOut( __METHOD__ );
throw new MWException( "Tag hook for $function is not callable\n" );
@@ -3085,6 +3207,7 @@ class Parser {
$text = $this->preprocessToDom( $text, $preprocessFlags );
$isChildObj = true;
}
+ wfProfileOut( __METHOD__ . '-pfunc-' . $function );
}
}
wfProfileOut( __METHOD__ . '-pfunc' );
@@ -3104,8 +3227,8 @@ class Parser {
if ( $title ) {
$titleText = $title->getPrefixedText();
# Check for language variants if the template is not found
- if ( $wgContLang->hasVariants() && $title->getArticleID() == 0 ) {
- $wgContLang->findVariantLink( $part1, $title, true );
+ if ( $this->getConverterLanguage()->hasVariants() && $title->getArticleID() == 0 ) {
+ $this->getConverterLanguage()->findVariantLink( $part1, $title, true );
}
# Do recursion depth check
$limit = $this->mOptions->getMaxTemplateDepth();
@@ -3120,14 +3243,37 @@ class Parser {
# Load from database
if ( !$found && $title ) {
+ $titleProfileIn = __METHOD__ . "-title-" . $title->getDBKey();
+ wfProfileIn( $titleProfileIn ); // template in
wfProfileIn( __METHOD__ . '-loadtpl' );
if ( !$title->isExternal() ) {
- if ( $title->getNamespace() == NS_SPECIAL
+ if ( $title->isSpecialPage()
&& $this->mOptions->getAllowSpecialInclusion()
&& $this->ot['html'] )
{
- $text = SpecialPageFactory::capturePath( $title );
- if ( is_string( $text ) ) {
+ // Pass the template arguments as URL parameters.
+ // "uselang" will have no effect since the Language object
+ // is forced to the one defined in ParserOptions.
+ $pageArgs = array();
+ for ( $i = 0; $i < $args->getLength(); $i++ ) {
+ $bits = $args->item( $i )->splitArg();
+ if ( strval( $bits['index'] ) === '' ) {
+ $name = trim( $frame->expand( $bits['name'], PPFrame::STRIP_COMMENTS ) );
+ $value = trim( $frame->expand( $bits['value'] ) );
+ $pageArgs[$name] = $value;
+ }
+ }
+
+ // Create a new context to execute the special page
+ $context = new RequestContext;
+ $context->setTitle( $title );
+ $context->setRequest( new FauxRequest( $pageArgs ) );
+ $context->setUser( $this->getUser() );
+ $context->setLanguage( $this->mOptions->getUserLangObj() );
+ $ret = SpecialPageFactory::capturePath( $title, $context );
+ if ( $ret ) {
+ $text = $context->getOutput()->getHTML();
+ $this->mOutput->addOutputPageMetadata( $context->getOutput() );
$found = true;
$isHTML = true;
$this->disableCache();
@@ -3176,7 +3322,9 @@ class Parser {
# Recover the source wikitext and return it
if ( !$found ) {
$text = $frame->virtualBracketedImplode( '{{', '|', '}}', $titleWithSpaces, $args );
- wfProfileOut( __METHOD__."-title-$originalTitle" );
+ if ( $titleProfileIn ) {
+ wfProfileOut( $titleProfileIn ); // template out
+ }
wfProfileOut( __METHOD__ );
return array( 'object' => $text );
}
@@ -3206,6 +3354,10 @@ class Parser {
$isLocalObj = false;
}
+ if ( $titleProfileIn ) {
+ wfProfileOut( $titleProfileIn ); // template out
+ }
+
# Replace raw HTML by a placeholder
# Add a blank line preceding, to prevent it from mucking up
# immediately preceding headings
@@ -3245,7 +3397,6 @@ class Parser {
$ret = array( 'text' => $text );
}
- wfProfileOut( __METHOD__."-title-$originalTitle" );
wfProfileOut( __METHOD__ );
return $ret;
}
@@ -3254,6 +3405,8 @@ class Parser {
* Get the semi-parsed DOM representation of a template with a given title,
* and its redirect destination title. Cached.
*
+ * @param $title Title
+ *
* @return array
*/
function getTemplateDom( $title ) {
@@ -3320,6 +3473,9 @@ class Parser {
* Static function to get a template
* Can be overridden via ParserOptions::setTemplateCallback().
*
+ * @parma $title Title
+ * @param $parser Parser
+ *
* @return array
*/
static function statelessFetchTemplate( $title, $parser = false ) {
@@ -3394,30 +3550,30 @@ class Parser {
/**
* Fetch a file and its title and register a reference to it.
+ * If 'broken' is a key in $options then the file will appear as a broken thumbnail.
* @param Title $title
- * @param string $time MW timestamp
- * @param string $sha1 base 36 SHA-1
- * @return mixed File or false
+ * @param Array $options Array of options to RepoGroup::findFile
+ * @return File|false
*/
- function fetchFile( $title, $time = false, $sha1 = false ) {
- $res = $this->fetchFileAndTitle( $title, $time, $sha1 );
+ function fetchFile( $title, $options = array() ) {
+ $res = $this->fetchFileAndTitle( $title, $options );
return $res[0];
}
/**
* Fetch a file and its title and register a reference to it.
+ * If 'broken' is a key in $options then the file will appear as a broken thumbnail.
* @param Title $title
- * @param string $time MW timestamp
- * @param string $sha1 base 36 SHA-1
+ * @param Array $options Array of options to RepoGroup::findFile
* @return Array ( File or false, Title of file )
*/
- function fetchFileAndTitle( $title, $time = false, $sha1 = false ) {
- if ( $time === '0' ) {
+ function fetchFileAndTitle( $title, $options = array() ) {
+ if ( isset( $options['broken'] ) ) {
$file = false; // broken thumbnail forced by hook
- } elseif ( $sha1 ) { // get by (sha1,timestamp)
- $file = RepoGroup::singleton()->findFileFromKey( $sha1, array( 'time' => $time ) );
+ } elseif ( isset( $options['sha1'] ) ) { // get by (sha1,timestamp)
+ $file = RepoGroup::singleton()->findFileFromKey( $options['sha1'], $options );
} else { // get by (name,timestamp)
- $file = wfFindFile( $title, array( 'time' => $time ) );
+ $file = wfFindFile( $title, $options );
}
$time = $file ? $file->getTimestamp() : false;
$sha1 = $file ? $file->getSha1() : false;
@@ -3660,6 +3816,10 @@ class Parser {
/**
* Strip double-underscore items like __NOGALLERY__ and __NOTOC__
* Fills $this->mDoubleUnderscores, returns the modified text
+ *
+ * @param $text string
+ *
+ * @return string
*/
function doDoubleUnderscore( $text ) {
wfProfileIn( __METHOD__ );
@@ -3719,12 +3879,16 @@ class Parser {
* @param $msg String: message key
* @return Boolean: whether the addition was successful
*/
- protected function addTrackingCategory( $msg ) {
+ public function addTrackingCategory( $msg ) {
if ( $this->mTitle->getNamespace() === NS_SPECIAL ) {
wfDebug( __METHOD__.": Not adding tracking category $msg to special page!\n" );
return false;
}
- $cat = wfMsgForContent( $msg );
+ // Important to parse with correct title (bug 31469)
+ $cat = wfMessage( $msg )
+ ->title( $this->getTitle() )
+ ->inContentLanguage()
+ ->text();
# Allow tracking categories to be disabled by setting them to "-"
if ( $cat === '-' ) {
@@ -3761,8 +3925,9 @@ class Parser {
# Inhibit editsection links if requested in the page
if ( isset( $this->mDoubleUnderscores['noeditsection'] ) ) {
- $showEditLink = 0;
+ $maybeShowEditLink = $showEditLink = false;
} else {
+ $maybeShowEditLink = true; /* Actual presence will depend on ParserOptions option */
$showEditLink = $this->mOptions->getEditSection();
}
if ( $showEditLink ) {
@@ -3894,24 +4059,28 @@ class Parser {
if ( $dot ) {
$numbering .= '.';
}
- $numbering .= $this->getFunctionLang()->formatNum( $sublevelCount[$i] );
+ $numbering .= $this->getTargetLanguage()->formatNum( $sublevelCount[$i] );
$dot = 1;
}
}
# The safe header is a version of the header text safe to use for links
- # Avoid insertion of weird stuff like <math> by expanding the relevant sections
- $safeHeadline = $this->mStripState->unstripBoth( $headline );
# Remove link placeholders by the link text.
# <!--LINK number-->
# turns into
# link text with suffix
- $safeHeadline = $this->replaceLinkHoldersText( $safeHeadline );
+ # Do this before unstrip since link text can contain strip markers
+ $safeHeadline = $this->replaceLinkHoldersText( $headline );
- # Strip out HTML (other than plain <sup> and <sub>: bug 8393)
+ # Avoid insertion of weird stuff like <math> by expanding the relevant sections
+ $safeHeadline = $this->mStripState->unstripBoth( $safeHeadline );
+
+ # Strip out HTML (first regex removes any tag not allowed)
+ # Allowed tags are <sup> and <sub> (bug 8393), <i> (bug 26375) and <b> (r105284)
+ # We strip any parameter from accepted tags (second regex)
$tocline = preg_replace(
- array( '#<(?!/?(sup|sub)).*?'.'>#', '#<(/?(sup|sub)).*?'.'>#' ),
+ array( '#<(?!/?(sup|sub|i|b)(?: [^>]*)?>).*?'.'>#', '#<(/?(sup|sub|i|b))(?: .*?)?'.'>#' ),
array( '', '<$1>' ),
$safeHeadline
);
@@ -4017,7 +4186,7 @@ class Parser {
);
# give headline the correct <h#> tag
- if ( $sectionIndex !== false ) {
+ if ( $maybeShowEditLink && $sectionIndex !== false ) {
// Output edit section links as markers with styles that can be customized by skins
if ( $isTemplate ) {
# Put a T flag in the section identifier, to indicate to extractSections()
@@ -4061,7 +4230,7 @@ class Parser {
if ( $prevtoclevel > 0 && $prevtoclevel < $wgMaxTocLevel ) {
$toc .= Linker::tocUnindent( $prevtoclevel - 1 );
}
- $toc = Linker::tocList( $toc, $this->mOptions->getUserLang() );
+ $toc = Linker::tocList( $toc, $this->mOptions->getUserLangObj() );
$this->mOutput->setTOCHTML( $toc );
}
@@ -4070,30 +4239,42 @@ class Parser {
}
# split up and insert constructed headlines
-
$blocks = preg_split( '/<H[1-6].*?' . '>.*?<\/H[1-6]>/i', $text );
$i = 0;
+ // build an array of document sections
+ $sections = array();
foreach ( $blocks as $block ) {
- if ( $showEditLink && $headlineCount > 0 && $i == 0 && $block !== "\n" ) {
- # This is the [edit] link that appears for the top block of text when
- # section editing is enabled
-
- # Disabled because it broke block formatting
- # For example, a bullet point in the top line
- # $full .= $sk->editSectionLink(0);
- }
- $full .= $block;
- if ( $enoughToc && !$i && $isMain && !$this->mForceTocPosition ) {
- # Top anchor now in skin
- $full = $full.$toc;
+ // $head is zero-based, sections aren't.
+ if ( empty( $head[$i - 1] ) ) {
+ $sections[$i] = $block;
+ } else {
+ $sections[$i] = $head[$i - 1] . $block;
}
- if ( !empty( $head[$i] ) ) {
- $full .= $head[$i];
- }
+ /**
+ * Send a hook, one per section.
+ * The idea here is to be able to make section-level DIVs, but to do so in a
+ * lower-impact, more correct way than r50769
+ *
+ * $this : caller
+ * $section : the section number
+ * &$sectionContent : ref to the content of the section
+ * $showEditLinks : boolean describing whether this section has an edit link
+ */
+ wfRunHooks( 'ParserSectionCreate', array( $this, $i, &$sections[$i], $showEditLink ) );
+
$i++;
}
+
+ if ( $enoughToc && $isMain && !$this->mForceTocPosition ) {
+ // append the TOC at the beginning
+ // Top anchor now in skin
+ $sections[0] = $sections[0] . $toc . "\n";
+ }
+
+ $full .= join( '', $sections );
+
if ( $this->mForceTocPosition ) {
return str_replace( '<!--MWTOC-->', $toc, $full );
} else {
@@ -4133,6 +4314,11 @@ class Parser {
/**
* Pre-save transform helper function
* @private
+ *
+ * @param $text string
+ * @param $user User
+ *
+ * @return string
*/
function pstPass2( $text, $user ) {
global $wgContLang, $wgLocaltimezone;
@@ -4188,9 +4374,9 @@ class Parser {
$tc = "[$wgLegalTitleChars]";
$nc = '[ _0-9A-Za-z\x80-\xff-]'; # Namespaces can use non-ascii!
- $p1 = "/\[\[(:?$nc+:|:|)($tc+?)( \\($tc+\\))\\|]]/"; # [[ns:page (context)|]]
- $p4 = "/\[\[(:?$nc+:|:|)($tc+?)(($tc+))\\|]]/"; # [[ns:page(context)|]]
- $p3 = "/\[\[(:?$nc+:|:|)($tc+?)( \\($tc+\\)|)(, $tc+|)\\|]]/"; # [[ns:page (context), context|]]
+ $p1 = "/\[\[(:?$nc+:|:|)($tc+?)( ?\\($tc+\\))\\|]]/"; # [[ns:page (context)|]]
+ $p4 = "/\[\[(:?$nc+:|:|)($tc+?)( ?($tc+))\\|]]/"; # [[ns:page(context)|]]
+ $p3 = "/\[\[(:?$nc+:|:|)($tc+?)( ?\\($tc+\\)|)(, $tc+|)\\|]]/"; # [[ns:page (context), context|]]
$p2 = "/\[\[\\|($tc+)]]/"; # [[|page]]
# try $p1 first, to turn "[[A, B (C)|]]" into "[[A, B (C)|A, B]]"
@@ -4224,8 +4410,8 @@ class Parser {
* as it may have changed if it's the $wgParser.
*
* @param $user User
- * @param $nickname String: nickname to use or false to use user's default nickname
- * @param $fancySig Boolean: whether the nicknname is the complete signature
+ * @param $nickname String|bool nickname to use or false to use user's default nickname
+ * @param $fancySig Boolean|null whether the nicknname is the complete signature
* or null to use default value
* @return string
*/
@@ -4260,7 +4446,7 @@ class Parser {
}
# Make sure nickname doesnt get a sig in a sig
- $nickname = $this->cleanSigInSig( $nickname );
+ $nickname = self::cleanSigInSig( $nickname );
# If we're still here, make it a link to the user page
$userText = wfEscapeWikiText( $username );
@@ -4287,16 +4473,13 @@ class Parser {
* 2) Substitute all transclusions
*
* @param $text String
- * @param $parsing Whether we're cleaning (preferences save) or parsing
+ * @param $parsing bool Whether we're cleaning (preferences save) or parsing
* @return String: signature text
*/
- function cleanSig( $text, $parsing = false ) {
+ public function cleanSig( $text, $parsing = false ) {
if ( !$parsing ) {
global $wgTitle;
- $this->mOptions = new ParserOptions;
- $this->clearState();
- $this->setTitle( $wgTitle );
- $this->setOutputType = self::OT_PREPROCESS;
+ $this->startParse( $wgTitle, new ParserOptions, self::OT_PREPROCESS, true );
}
# Option to disable this feature
@@ -4311,7 +4494,7 @@ class Parser {
$substText = '{{' . $substWord->getSynonym( 0 );
$text = preg_replace( $substRegex, $substText, $text );
- $text = $this->cleanSigInSig( $text );
+ $text = self::cleanSigInSig( $text );
$dom = $this->preprocessToDom( $text );
$frame = $this->getPreprocessor()->newFrame();
$text = $frame->expand( $dom );
@@ -4329,7 +4512,7 @@ class Parser {
* @param $text String
* @return String: signature text with /~{3,5}/ removed
*/
- function cleanSigInSig( $text ) {
+ public static function cleanSigInSig( $text ) {
$text = preg_replace( '/~{3,5}/', '', $text );
return $text;
}
@@ -4337,11 +4520,22 @@ class Parser {
/**
* Set up some variables which are usually set up in parse()
* so that an external function can call some class members with confidence
+ *
+ * @param $title Title|null
+ * @param $options ParserOptions
+ * @param $outputType
+ * @param $clearState bool
*/
public function startExternalParse( Title $title = null, ParserOptions $options, $outputType, $clearState = true ) {
$this->startParse( $title, $options, $outputType, $clearState );
}
+ /**
+ * @param $title Title|null
+ * @param $options ParserOptions
+ * @param $outputType
+ * @param $clearState bool
+ */
private function startParse( Title $title = null, ParserOptions $options, $outputType, $clearState = true ) {
$this->setTitle( $title );
$this->mOptions = $options;
@@ -4450,6 +4644,7 @@ class Parser {
*/
function clearTagHooks() {
$this->mTagHooks = array();
+ $this->mFunctionTagHooks = array();
$this->mStripList = $this->mDefaultStripList;
}
@@ -4559,7 +4754,11 @@ class Parser {
* @todo FIXME: Update documentation. makeLinkObj() is deprecated.
* Replace <!--LINK--> link placeholders with actual links, in the buffer
* Placeholders created in Skin::makeLinkObj()
- * Returns an array of link CSS classes, indexed by PDBK.
+ *
+ * @param $text string
+ * @param $options int
+ *
+ * @return array of link CSS classes, indexed by PDBK.
*/
function replaceLinkHolders( &$text, $options = 0 ) {
return $this->mLinkHolders->replace( $text );
@@ -4586,7 +4785,7 @@ class Parser {
* 'A tree'.
*
* @param string $text
- * @param array $param
+ * @param array $params
* @return string HTML
*/
function renderImageGallery( $text, $params ) {
@@ -4671,6 +4870,10 @@ class Parser {
return $ig->toHTML();
}
+ /**
+ * @param $handler
+ * @return array
+ */
function getImageParams( $handler ) {
if ( $handler ) {
$handlerClass = get_class( $handler );
@@ -4716,7 +4919,7 @@ class Parser {
*
* @param $title Title
* @param $options String
- * @param $holders LinkHolderArray
+ * @param $holders LinkHolderArray|false
* @return string HTML
*/
function makeImage( $title, $options, $holders = false ) {
@@ -4748,11 +4951,12 @@ class Parser {
$parts = StringUtils::explode( "|", $options );
# Give extensions a chance to select the file revision for us
- $time = $sha1 = $descQuery = false;
+ $options = array();
+ $descQuery = false;
wfRunHooks( 'BeforeParserFetchFileAndTitle',
- array( $this, $title, &$time, &$sha1, &$descQuery ) );
+ array( $this, $title, &$options, &$descQuery ) );
# Fetch and register the file (file title may be different via hooks)
- list( $file, $title ) = $this->fetchFileAndTitle( $title, $time, $sha1 );
+ list( $file, $title ) = $this->fetchFileAndTitle( $title, $options );
# Get parameter map
$handler = $file ? $file->getHandler() : false;
@@ -4820,7 +5024,7 @@ class Parser {
$value = true;
$validated = true;
} elseif ( preg_match( "/^$prots/", $value ) ) {
- if ( preg_match( "/^($prots)$chars+$/", $value, $m ) ) {
+ if ( preg_match( "/^($prots)$chars+$/u", $value, $m ) ) {
$paramName = 'link-url';
$this->mOutput->addExternalLink( $value );
if ( $this->mOptions->getExternalLinkTarget() ) {
@@ -4912,6 +5116,7 @@ class Parser {
wfRunHooks( 'ParserMakeImageParams', array( $title, $file, &$params ) );
# Linker does the rest
+ $time = isset( $options['time'] ) ? $options['time'] : false;
$ret = Linker::makeImageLink2( $title, $file, $params['frame'], $params['handler'],
$time, $descQuery, $this->mOptions->getThumbSize() );
@@ -4953,6 +5158,10 @@ class Parser {
*/
function disableCache() {
wfDebug( "Parser output marked as uncacheable.\n" );
+ if ( !$this->mOutput ) {
+ throw new MWException( __METHOD__ .
+ " can only be called when actually parsing something" );
+ }
$this->mOutput->setCacheTime( -1 ); // old style, for compatibility
$this->mOutput->updateCacheExpiry( 0 ); // new style, for consistency
}
@@ -4977,7 +5186,7 @@ class Parser {
* @return array
*/
function getTags() {
- return array_merge( array_keys( $this->mTransparentTagHooks ), array_keys( $this->mTagHooks ) );
+ return array_merge( array_keys( $this->mTransparentTagHooks ), array_keys( $this->mTagHooks ), array_keys( $this->mFunctionTagHooks ) );
}
/**
@@ -4985,6 +5194,10 @@ class Parser {
*
* Transparent tag hooks are like regular XML-style tag hooks, except they
* operate late in the transformation sequence, on HTML instead of wikitext.
+ *
+ * @param $text string
+ *
+ * @return string
*/
function replaceTransparentTags( $text ) {
$matches = array();
@@ -5026,7 +5239,7 @@ class Parser {
* not found, $mode=get will return $newtext, and $mode=replace will return $text.
*
* Section 0 is always considered to exist, even if it only contains the empty
- * string. If $text is the empty string and section 0 is replaced, $newText is
+ * string. If $text is the empty string and section 0 is replaced, $newText is
* returned.
*
* @param $mode String: one of "get" or "replace"
@@ -5161,7 +5374,7 @@ class Parser {
/**
* This function returns $oldtext after the content of the section
- * specified by $section has been replaced with $text. If the target
+ * specified by $section has been replaced with $text. If the target
* section does not exist, $oldtext is returned unchanged.
*
* @param $oldtext String: former text of the article
@@ -5287,6 +5500,10 @@ class Parser {
* Try to guess the section anchor name based on a wikitext fragment
* presumably extracted from a heading, for example "Header" from
* "== Header ==".
+ *
+ * @param $text string
+ *
+ * @return string
*/
public function guessSectionNameFromWikiText( $text ) {
# Strip out wikitext links(they break the anchor)
@@ -5346,6 +5563,11 @@ class Parser {
/**
* strip/replaceVariables/unstrip for preprocessor regression testing
*
+ * @param $text string
+ * @param $title Title
+ * @param $options ParserOptions
+ * @param $outputType int
+ *
* @return string
*/
function testSrvus( $text, Title $title, ParserOptions $options, $outputType = self::OT_HTML ) {
@@ -5357,10 +5579,22 @@ class Parser {
return $text;
}
+ /**
+ * @param $text string
+ * @param $title Title
+ * @param $options ParserOptions
+ * @return string
+ */
function testPst( $text, Title $title, ParserOptions $options ) {
return $this->preSaveTransform( $text, $title, $options->getUser(), $options );
}
+ /**
+ * @param $text
+ * @param $title Title
+ * @param $options ParserOptions
+ * @return string
+ */
function testPreprocess( $text, Title $title, ParserOptions $options ) {
return $this->testSrvus( $text, $title, $options, self::OT_PREPROCESS );
}
@@ -5376,6 +5610,9 @@ class Parser {
* two strings will be replaced with the value returned by the callback in
* each case.
*
+ * @param $s string
+ * @param $callback
+ *
* @return string
*/
function markerSkipCallback( $s, $callback ) {
@@ -5424,6 +5661,8 @@ class Parser {
* unserializeHalfParsedText(). The text can then be safely incorporated into
* the return value of a parser hook.
*
+ * @param $text string
+ *
* @return array
*/
function serializeHalfParsedText( $text ) {
diff --git a/includes/parser/ParserCache.php b/includes/parser/ParserCache.php
index dcbf7a4d..8b043290 100644
--- a/includes/parser/ParserCache.php
+++ b/includes/parser/ParserCache.php
@@ -15,6 +15,8 @@ class ParserCache {
/**
* Get an instance of this object
+ *
+ * @return ParserCache
*/
public static function singleton() {
static $instance;
@@ -78,7 +80,7 @@ class ParserCache {
*/
function getETag( $article, $popts ) {
return 'W/"' . $this->getParserOutputKey( $article,
- $popts->optionsHash( ParserOptions::legacyOptions() ) ) .
+ $popts->optionsHash( ParserOptions::legacyOptions(), $article->getTitle() ) ) .
"--" . $article->getTouched() . '"';
}
@@ -98,6 +100,8 @@ class ParserCache {
* It would be preferable to have this code in get()
* instead of having Article looking in our internals.
*
+ * @todo Document parameter $useOutdated
+ *
* @param $article Article
* @param $popts ParserOptions
*/
@@ -128,7 +132,7 @@ class ParserCache {
$usedOptions = ParserOptions::legacyOptions();
}
- return $this->getParserOutputKey( $article, $popts->optionsHash( $usedOptions ) );
+ return $this->getParserOutputKey( $article, $popts->optionsHash( $usedOptions, $article->getTitle() ) );
}
/**
@@ -156,6 +160,7 @@ class ParserCache {
$parserOutputKey = $this->getKey( $article, $popts, $useOutdated );
if ( $parserOutputKey === false ) {
+ wfIncrStats( 'pcache_miss_absent' );
wfProfileOut( __METHOD__ );
return false;
}
@@ -163,18 +168,19 @@ class ParserCache {
$value = $this->mMemc->get( $parserOutputKey );
if ( self::try116cache && !$value && strpos( $value, '*' ) !== -1 ) {
wfDebug( "New format parser cache miss.\n" );
- $parserOutputKey = $this->getParserOutputKey( $article, $popts->optionsHash( ParserOptions::legacyOptions() ) );
+ $parserOutputKey = $this->getParserOutputKey( $article,
+ $popts->optionsHash( ParserOptions::legacyOptions(), $article->getTitle() ) );
$value = $this->mMemc->get( $parserOutputKey );
}
if ( !$value ) {
- wfDebug( "Parser cache miss.\n" );
+ wfDebug( "ParserOutput cache miss.\n" );
wfIncrStats( "pcache_miss_absent" );
wfProfileOut( __METHOD__ );
return false;
}
- wfDebug( "Found.\n" );
-
+ wfDebug( "ParserOutput cache found.\n" );
+
// The edit section preference may not be the appropiate one in
// the ParserOutput, as we are not storing it in the parsercache
// key. Force it here. See bug 31445.
@@ -197,7 +203,6 @@ class ParserCache {
* @param $parserOutput ParserOutput
* @param $article Article
* @param $popts ParserOptions
- * @return void
*/
public function save( $parserOutput, $article, $popts ) {
$expire = $parserOutput->getCacheExpiry();
@@ -215,10 +220,10 @@ class ParserCache {
$optionsKey->setContainsOldMagic( $parserOutput->containsOldMagic() );
$parserOutputKey = $this->getParserOutputKey( $article,
- $popts->optionsHash( $optionsKey->mUsedOptions ) );
+ $popts->optionsHash( $optionsKey->mUsedOptions, $article->getTitle() ) );
// Save the timestamp so that we don't have to load the revision row on view
- $parserOutput->mTimestamp = $article->getTimestamp();
+ $parserOutput->setTimestamp( $article->getTimestamp() );
$parserOutput->mText .= "\n<!-- Saved in parser cache with key $parserOutputKey and timestamp $now -->\n";
wfDebug( "Saved in parser cache with key $parserOutputKey and timestamp $now\n" );
diff --git a/includes/parser/ParserOptions.php b/includes/parser/ParserOptions.php
index 07752768..57d3a7eb 100644
--- a/includes/parser/ParserOptions.php
+++ b/includes/parser/ParserOptions.php
@@ -1,58 +1,188 @@
<?php
/**
- * Options for the PHP parser
+ * \brief Options for the PHP parser
*
* @file
* @ingroup Parser
*/
/**
- * Set options of the Parser
- * @todo document
+ * \brief Set options of the Parser
+ *
+ * All member variables are supposed to be private in theory, although in practise this is not the case.
+ *
* @ingroup Parser
*/
class ParserOptions {
- # All variables are supposed to be private in theory, although in practise this is not the case.
- var $mUseDynamicDates; # Use DateFormatter to format dates
- var $mInterwikiMagic; # Interlanguage links are removed and returned in an array
- var $mAllowExternalImages; # Allow external images inline
- var $mAllowExternalImagesFrom; # If not, any exception?
- var $mEnableImageWhitelist; # If not or it doesn't match, should we check an on-wiki whitelist?
- var $mDateFormat = null; # Date format index
- var $mEditSection = true; # Create "edit section" links
- var $mAllowSpecialInclusion; # Allow inclusion of special pages
- var $mTidy = false; # Ask for tidy cleanup
- var $mInterfaceMessage = false; # Which lang to call for PLURAL and GRAMMAR
- var $mTargetLanguage = null; # Overrides above setting with arbitrary language
- var $mMaxIncludeSize; # Maximum size of template expansions, in bytes
- var $mMaxPPNodeCount; # Maximum number of nodes touched by PPFrame::expand()
- var $mMaxPPExpandDepth; # Maximum recursion depth in PPFrame::expand()
- var $mMaxTemplateDepth; # Maximum recursion depth for templates within templates
- var $mRemoveComments = true; # Remove HTML comments. ONLY APPLIES TO PREPROCESS OPERATIONS
- var $mTemplateCallback = # Callback for template fetching
+
+ /**
+ * Use DateFormatter to format dates
+ */
+ var $mUseDynamicDates;
+
+ /**
+ * Interlanguage links are removed and returned in an array
+ */
+ var $mInterwikiMagic;
+
+ /**
+ * Allow external images inline?
+ */
+ var $mAllowExternalImages;
+
+ /**
+ * If not, any exception?
+ */
+ var $mAllowExternalImagesFrom;
+
+ /**
+ * If not or it doesn't match, should we check an on-wiki whitelist?
+ */
+ var $mEnableImageWhitelist;
+
+ /**
+ * Date format index
+ */
+ var $mDateFormat = null;
+
+ /**
+ * Create "edit section" links?
+ */
+ var $mEditSection = true;
+
+ /**
+ * Allow inclusion of special pages?
+ */
+ var $mAllowSpecialInclusion;
+
+ /**
+ * Use tidy to cleanup output HTML?
+ */
+ var $mTidy = false;
+
+ /**
+ * Which lang to call for PLURAL and GRAMMAR
+ */
+ var $mInterfaceMessage = false;
+
+ /**
+ * Overrides $mInterfaceMessage with arbitrary language
+ */
+ var $mTargetLanguage = null;
+
+ /**
+ * Maximum size of template expansions, in bytes
+ */
+ var $mMaxIncludeSize;
+
+ /**
+ * Maximum number of nodes touched by PPFrame::expand()
+ */
+ var $mMaxPPNodeCount;
+
+ /**
+ * Maximum recursion depth in PPFrame::expand()
+ */
+ var $mMaxPPExpandDepth;
+
+ /**
+ * Maximum recursion depth for templates within templates
+ */
+ var $mMaxTemplateDepth;
+
+ /**
+ * Remove HTML comments. ONLY APPLIES TO PREPROCESS OPERATIONS
+ */
+ var $mRemoveComments = true;
+
+ /**
+ * Callback for template fetching. Used as first argument to call_user_func().
+ */
+ var $mTemplateCallback =
array( 'Parser', 'statelessFetchTemplate' );
- var $mEnableLimitReport = false; # Enable limit report in an HTML comment on output
- var $mTimestamp; # Timestamp used for {{CURRENTDAY}} etc.
- var $mExternalLinkTarget; # Target attribute for external links
- var $mCleanSignatures; #
- var $mPreSaveTransform = true; # Transform wiki markup when saving the page.
+
+ /**
+ * Enable limit report in an HTML comment on output
+ */
+ var $mEnableLimitReport = false;
+
+ /**
+ * Timestamp used for {{CURRENTDAY}} etc.
+ */
+ var $mTimestamp;
+
+ /**
+ * Target attribute for external links
+ */
+ var $mExternalLinkTarget;
+
+ /**
+ * Clean up signature texts?
+ *
+ * 1) Strip ~~~, ~~~~ and ~~~~~ out of signatures
+ * 2) Substitute all transclusions
+ */
+ var $mCleanSignatures;
+
+ /**
+ * Transform wiki markup when saving the page?
+ */
+ var $mPreSaveTransform = true;
- var $mNumberHeadings; # Automatically number headings
- var $mMath; # User math preference (as integer)
- var $mThumbSize; # Thumb size preferred by the user.
- private $mStubThreshold; # Maximum article size of an article to be marked as "stub"
- var $mUserLang; # Language code of the User language.
+ /**
+ * Automatically number headings?
+ */
+ var $mNumberHeadings;
+
+ /**
+ * User math preference (as integer). Not used (1.19)
+ */
+ var $mMath;
+
+ /**
+ * Thumb size preferred by the user.
+ */
+ var $mThumbSize;
+
+ /**
+ * Maximum article size of an article to be marked as "stub"
+ */
+ private $mStubThreshold;
+
+ /**
+ * Language object of the User language.
+ */
+ var $mUserLang;
/**
- * @var User
+ * @var User
+ * Stored user object
+ */
+ var $mUser;
+
+ /**
+ * Parsing the page for a "preview" operation?
+ */
+ var $mIsPreview = false;
+
+ /**
+ * Parsing the page for a "preview" operation on a single section?
+ */
+ var $mIsSectionPreview = false;
+
+ /**
+ * Parsing the printable version of the page?
*/
- var $mUser; # Stored user object
- var $mIsPreview = false; # Parsing the page for a "preview" operation
- var $mIsSectionPreview = false; # Parsing the page for a "preview" operation on a single section
- var $mIsPrintable = false; # Parsing the printable version of the page
+ var $mIsPrintable = false;
- var $mExtraKey = ''; # Extra key that should be present in the caching key.
+ /**
+ * Extra key that should be present in the caching key.
+ */
+ var $mExtraKey = '';
+ /**
+ * Function to be called when an option is accessed.
+ */
protected $onAccessCallback = null;
function getUseDynamicDates() { return $this->mUseDynamicDates; }
@@ -96,7 +226,7 @@ class ParserOptions {
* @deprecated since 1.18 Use Linker::* instead
*/
function getSkin( $title = null ) {
- wfDeprecated( __METHOD__ );
+ wfDeprecated( __METHOD__, '1.18' );
return new DummyLinker;
}
@@ -119,12 +249,23 @@ class ParserOptions {
* You shouldn't use this. Really. $parser->getFunctionLang() is all you need.
* Using this fragments the cache and is discouraged. Yes, {{int: }} uses this,
* producing inconsistent tables (Bug 14404).
+ *
+ * @return Language object
+ * @since 1.19
+ */
+ function getUserLangObj() {
+ $this->optionUsed( 'userlang' );
+ return $this->mUserLang;
+ }
+
+ /**
+ * Same as getUserLangObj() but returns a string instead.
+ *
* @return String Language code
* @since 1.17
*/
function getUserLang() {
- $this->optionUsed( 'userlang' );
- return $this->mUserLang;
+ return $this->getUserLangObj()->getCode();
}
function setUseDynamicDates( $x ) { return wfSetVar( $this->mUseDynamicDates, $x ); }
@@ -137,7 +278,9 @@ class ParserOptions {
function setNumberHeadings( $x ) { return wfSetVar( $this->mNumberHeadings, $x ); }
function setAllowSpecialInclusion( $x ) { return wfSetVar( $this->mAllowSpecialInclusion, $x ); }
function setTidy( $x ) { return wfSetVar( $this->mTidy, $x ); }
- function setSkin( $x ) { $this->mSkin = $x; }
+
+ /** @deprecated in 1.19; will be removed in 1.20 */
+ function setSkin( $x ) { wfDeprecated( __METHOD__, '1.19' ); }
function setInterfaceMessage( $x ) { return wfSetVar( $this->mInterfaceMessage, $x ); }
function setTargetLanguage( $x ) { return wfSetVar( $this->mTargetLanguage, $x, true ); }
function setMaxIncludeSize( $x ) { return wfSetVar( $this->mMaxIncludeSize, $x ); }
@@ -151,8 +294,8 @@ class ParserOptions {
function setExternalLinkTarget( $x ) { return wfSetVar( $this->mExternalLinkTarget, $x ); }
function setMath( $x ) { return wfSetVar( $this->mMath, $x ); }
function setUserLang( $x ) {
- if ( $x instanceof Language ) {
- $x = $x->getCode();
+ if ( is_string( $x ) ) {
+ $x = Language::factory( $x );
}
return wfSetVar( $this->mUserLang, $x );
}
@@ -171,41 +314,75 @@ class ParserOptions {
$this->mExtraKey .= '!' . $key;
}
- function __construct( $user = null ) {
- $this->initialiseFromUser( $user );
+ /**
+ * Constructor
+ * @param $user User object
+ * @param $lang Language object
+ */
+ function __construct( $user = null, $lang = null ) {
+ if ( $user === null ) {
+ global $wgUser;
+ if ( $wgUser === null ) {
+ $user = new User;
+ } else {
+ $user = $wgUser;
+ }
+ }
+ if ( $lang === null ) {
+ global $wgLang;
+ if ( !StubObject::isRealObject( $wgLang ) ) {
+ $wgLang->_unstub();
+ }
+ $lang = $wgLang;
+ }
+ $this->initialiseFromUser( $user, $lang );
}
/**
- * Get parser options
+ * Get a ParserOptions object from a given user.
+ * Language will be taken from $wgLang.
*
* @param $user User object
* @return ParserOptions object
*/
- static function newFromUser( $user ) {
+ public static function newFromUser( $user ) {
return new ParserOptions( $user );
}
- /** Get user options */
- function initialiseFromUser( $userInput ) {
- global $wgUseDynamicDates, $wgInterwikiMagic, $wgAllowExternalImages;
- global $wgAllowExternalImagesFrom, $wgEnableImageWhitelist, $wgAllowSpecialInclusion, $wgMaxArticleSize;
- global $wgMaxPPNodeCount, $wgMaxTemplateDepth, $wgMaxPPExpandDepth, $wgCleanSignatures;
- global $wgExternalLinkTarget, $wgLang;
+ /**
+ * Get a ParserOptions object from a given user and language
+ *
+ * @param $user User object
+ * @param $lang Language object
+ * @return ParserOptions object
+ */
+ public static function newFromUserAndLang( User $user, Language $lang ) {
+ return new ParserOptions( $user, $lang );
+ }
- wfProfileIn( __METHOD__ );
+ /**
+ * Get a ParserOptions object from a IContextSource object
+ *
+ * @param $context IContextSource object
+ * @return ParserOptions object
+ */
+ public static function newFromContext( IContextSource $context ) {
+ return new ParserOptions( $context->getUser(), $context->getLanguage() );
+ }
- if ( !$userInput ) {
- global $wgUser;
- if ( isset( $wgUser ) ) {
- $user = $wgUser;
- } else {
- $user = new User;
- }
- } else {
- $user =& $userInput;
- }
+ /**
+ * Get user options
+ *
+ * @param $user User object
+ * @param $lang Language object
+ */
+ private function initialiseFromUser( $user, $lang ) {
+ global $wgUseDynamicDates, $wgInterwikiMagic, $wgAllowExternalImages,
+ $wgAllowExternalImagesFrom, $wgEnableImageWhitelist, $wgAllowSpecialInclusion,
+ $wgMaxArticleSize, $wgMaxPPNodeCount, $wgMaxTemplateDepth, $wgMaxPPExpandDepth,
+ $wgCleanSignatures, $wgExternalLinkTarget;
- $this->mUser = $user;
+ wfProfileIn( __METHOD__ );
$this->mUseDynamicDates = $wgUseDynamicDates;
$this->mInterwikiMagic = $wgInterwikiMagic;
@@ -220,11 +397,12 @@ class ParserOptions {
$this->mCleanSignatures = $wgCleanSignatures;
$this->mExternalLinkTarget = $wgExternalLinkTarget;
+ $this->mUser = $user;
$this->mNumberHeadings = $user->getOption( 'numberheadings' );
$this->mMath = $user->getOption( 'math' );
$this->mThumbSize = $user->getOption( 'thumbsize' );
$this->mStubThreshold = $user->getStubThreshold();
- $this->mUserLang = $wgLang->getCode();
+ $this->mUserLang = $lang;
wfProfileOut( __METHOD__ );
}
@@ -274,10 +452,12 @@ class ParserOptions {
* settings.
*
* @since 1.17
- * @return \string Page rendering hash
+ * @param $forOptions Array
+ * @param $title Title: used to get the content language of the page (since r97636)
+ * @return string Page rendering hash
*/
- public function optionsHash( $forOptions ) {
- global $wgContLang, $wgRenderHashAppend;
+ public function optionsHash( $forOptions, $title = null ) {
+ global $wgRenderHashAppend;
$confstr = '';
@@ -308,7 +488,7 @@ class ParserOptions {
}
if ( in_array( 'userlang', $forOptions ) ) {
- $confstr .= '!' . $this->mUserLang;
+ $confstr .= '!' . $this->mUserLang->getCode();
} else {
$confstr .= '!*';
}
@@ -321,7 +501,12 @@ class ParserOptions {
// add in language specific options, if any
// @todo FIXME: This is just a way of retrieving the url/user preferred variant
- $confstr .= $wgContLang->getExtraHashOptions();
+ if( !is_null( $title ) ) {
+ $confstr .= $title->getPageLanguage()->getExtraHashOptions();
+ } else {
+ global $wgContLang;
+ $confstr .= $wgContLang->getExtraHashOptions();
+ }
$confstr .= $wgRenderHashAppend;
diff --git a/includes/parser/ParserOutput.php b/includes/parser/ParserOutput.php
index 403b6625..2d99a3b5 100644
--- a/includes/parser/ParserOutput.php
+++ b/includes/parser/ParserOutput.php
@@ -5,7 +5,7 @@
* @file
* @ingroup Parser
*/
-
+
/**
* @todo document
* @ingroup Parser
@@ -16,7 +16,7 @@ class CacheTime {
$mCacheTime = '', # Time when this object was generated, or -1 for uncacheable. Used in ParserCache.
$mCacheExpiry = null, # Seconds after which the object should expire, use 0 for uncachable. Used in ParserCache.
$mContainsOldMagic; # Boolean variable indicating if the input contained variables like {{CURRENTDAY}}
-
+
function getCacheTime() { return $this->mCacheTime; }
function containsOldMagic() { return $this->mContainsOldMagic; }
@@ -30,17 +30,17 @@ class CacheTime {
*/
function setCacheTime( $t ) { return wfSetVar( $this->mCacheTime, $t ); }
- /**
+ /**
* Sets the number of seconds after which this object should expire.
* This value is used with the ParserCache.
- * If called with a value greater than the value provided at any previous call,
+ * If called with a value greater than the value provided at any previous call,
* the new call has no effect. The value returned by getCacheExpiry is smaller
- * or equal to the smallest number that was provided as an argument to
+ * or equal to the smallest number that was provided as an argument to
* updateCacheExpiry().
*
* @param $seconds number
*/
- function updateCacheExpiry( $seconds ) {
+ function updateCacheExpiry( $seconds ) {
$seconds = (int)$seconds;
if ( $this->mCacheExpiry === null || $this->mCacheExpiry > $seconds ) {
@@ -52,23 +52,23 @@ class CacheTime {
$this->mCacheTime = -1;
}
}
-
+
/**
* Returns the number of seconds after which this object should expire.
* This method is used by ParserCache to determine how long the ParserOutput can be cached.
* The timestamp of expiry can be calculated by adding getCacheExpiry() to getCacheTime().
- * The value returned by getCacheExpiry is smaller or equal to the smallest number
+ * The value returned by getCacheExpiry is smaller or equal to the smallest number
* that was provided to a call of updateCacheExpiry(), and smaller or equal to the
* value of $wgParserCacheExpireTime.
*/
- function getCacheExpiry() {
+ function getCacheExpiry() {
global $wgParserCacheExpireTime;
if ( $this->mCacheTime < 0 ) {
return 0;
} // old-style marker for "not cachable"
- $expire = $this->mCacheExpiry;
+ $expire = $this->mCacheExpiry;
if ( $expire === null ) {
$expire = $wgParserCacheExpireTime;
@@ -78,7 +78,7 @@ class CacheTime {
if( $this->containsOldMagic() ) { //compatibility hack
$expire = min( $expire, 3600 ); # 1 hour
- }
+ }
if ( $expire <= 0 ) {
return 0; // not cachable
@@ -90,7 +90,7 @@ class CacheTime {
/**
* @return bool
*/
- function isCacheable() {
+ function isCacheable() {
return $this->getCacheExpiry() > 0;
}
@@ -105,14 +105,14 @@ class CacheTime {
public function expired( $touched ) {
global $wgCacheEpoch;
return !$this->isCacheable() || // parser says it's uncacheable
- $this->getCacheTime() < $touched ||
- $this->getCacheTime() <= $wgCacheEpoch ||
- $this->getCacheTime() < wfTimestamp( TS_MW, time() - $this->getCacheExpiry() ) || // expiry period has passed
- !isset( $this->mVersion ) ||
- version_compare( $this->mVersion, Parser::VERSION, "lt" );
- }
+ $this->getCacheTime() < $touched ||
+ $this->getCacheTime() <= $wgCacheEpoch ||
+ $this->getCacheTime() < wfTimestamp( TS_MW, time() - $this->getCacheExpiry() ) || // expiry period has passed
+ !isset( $this->mVersion ) ||
+ version_compare( $this->mVersion, Parser::VERSION, "lt" );
+ }
}
-
+
class ParserOutput extends CacheTime {
var $mText, # The output text
$mLanguageLinks, # List of the full text of language links, in the order they appear
@@ -122,7 +122,7 @@ class ParserOutput extends CacheTime {
$mTemplates = array(), # 2-D map of NS/DBK to ID for the template references. ID=zero for broken.
$mTemplateIds = array(), # 2-D map of NS/DBK to rev ID for the template references. ID=zero for broken.
$mImages = array(), # DB keys of the images used, in the array key only
- $mImageTimeKeys = array(), # DB keys of the images used mapped to sha1 and MW timestamp
+ $mFileSearchOptions = array(), # DB keys of the images used mapped to sha1 and MW timestamp
$mExternalLinks = array(), # External link URLs, in the key only
$mInterwikiLinks = array(), # 2-D map of prefix/DBK (in keys only) for the inline interwiki links in the document.
$mNewSection = false, # Show a new section link?
@@ -138,8 +138,9 @@ class ParserOutput extends CacheTime {
$mSections = array(), # Table of contents
$mEditSectionTokens = false, # prefix/suffix markers if edit sections were output as tokens
$mProperties = array(), # Name/value pairs to be cached in the DB
- $mTOCHTML = ''; # HTML of the TOC
- private $mIndexPolicy = ''; # 'index' or 'noindex'? Any other value will result in no change.
+ $mTOCHTML = '', # HTML of the TOC
+ $mTimestamp; # Timestamp of the revision
+ private $mIndexPolicy = ''; # 'index' or 'noindex'? Any other value will result in no change.
private $mAccessedOptions = array(); # List of ParserOptions (stored in the keys)
const EDITSECTION_REGEX = '#<(?:mw:)?editsection page="(.*?)" section="(.*?)"(?:/>|>(.*?)(</(?:mw:)?editsection>))#';
@@ -158,12 +159,10 @@ class ParserOutput extends CacheTime {
if ( $this->mEditSectionTokens ) {
return preg_replace_callback( ParserOutput::EDITSECTION_REGEX,
array( &$this, 'replaceEditSectionLinksCallback' ), $this->mText );
- } else {
- return preg_replace( ParserOutput::EDITSECTION_REGEX, '', $this->mText );
}
- return $this->mText;
+ return preg_replace( ParserOutput::EDITSECTION_REGEX, '', $this->mText );
}
-
+
/**
* callback used by getText to replace editsection tokens
* @private
@@ -195,7 +194,7 @@ class ParserOutput extends CacheTime {
function &getTemplates() { return $this->mTemplates; }
function &getTemplateIds() { return $this->mTemplateIds; }
function &getImages() { return $this->mImages; }
- function &getImageTimeKeys() { return $this->mImageTimeKeys; }
+ function &getFileSearchOptions() { return $this->mFileSearchOptions; }
function &getExternalLinks() { return $this->mExternalLinks; }
function getNoGallery() { return $this->mNoGallery; }
function getHeadItems() { return $this->mHeadItems; }
@@ -207,6 +206,7 @@ class ParserOutput extends CacheTime {
function getWarnings() { return array_keys( $this->mWarnings ); }
function getIndexPolicy() { return $this->mIndexPolicy; }
function getTOCHTML() { return $this->mTOCHTML; }
+ function getTimestamp() { return $this->mTimestamp; }
function setText( $text ) { return wfSetVar( $this->mText, $text ); }
function setLanguageLinks( $ll ) { return wfSetVar( $this->mLanguageLinks, $ll ); }
@@ -217,6 +217,7 @@ class ParserOutput extends CacheTime {
function setEditSectionTokens( $t ) { return wfSetVar( $this->mEditSectionTokens, $t ); }
function setIndexPolicy( $policy ) { return wfSetVar( $this->mIndexPolicy, $policy ); }
function setTOCHTML( $tochtml ) { return wfSetVar( $this->mTOCHTML, $tochtml ); }
+ function setTimestamp( $timestamp ) { return wfSetVar( $this->mTimestamp, $timestamp ); }
function addCategory( $c, $sort ) { $this->mCategories[$c] = $sort; }
function addLanguageLink( $t ) { $this->mLanguageLinks[] = $t; }
@@ -243,7 +244,7 @@ class ParserOutput extends CacheTime {
# We don't register links pointing to our own server, unless... :-)
global $wgServer, $wgRegisterInternalExternals;
if( $wgRegisterInternalExternals or stripos($url,$wgServer.'/')!==0)
- $this->mExternalLinks[$url] = 1;
+ $this->mExternalLinks[$url] = 1;
}
/**
@@ -284,13 +285,13 @@ class ParserOutput extends CacheTime {
* Register a file dependency for this output
* @param $name string Title dbKey
* @param $timestamp string MW timestamp of file creation (or false if non-existing)
- * @param $sha string base 36 SHA-1 of file (or false if non-existing)
+ * @param $sha1 string base 36 SHA-1 of file (or false if non-existing)
* @return void
*/
function addImage( $name, $timestamp = null, $sha1 = null ) {
$this->mImages[$name] = 1;
if ( $timestamp !== null && $sha1 !== null ) {
- $this->mImageTimeKeys[$name] = array( 'time' => $timestamp, 'sha1' => $sha1 );
+ $this->mFileSearchOptions[$name] = array( 'time' => $timestamp, 'sha1' => $sha1 );
}
}
@@ -313,7 +314,7 @@ class ParserOutput extends CacheTime {
}
$this->mTemplateIds[$ns][$dbk] = $rev_id; // For versioning
}
-
+
/**
* @param $title Title object, must be an interwiki link
* @throws MWException if given invalid input
@@ -341,7 +342,7 @@ class ParserOutput extends CacheTime {
$this->mHeadItems[] = $section;
}
}
-
+
public function addModules( $modules ) {
$this->mModules = array_merge( $this->mModules, (array) $modules );
}
@@ -359,6 +360,20 @@ class ParserOutput extends CacheTime {
}
/**
+ * Copy items from the OutputPage object into this one
+ *
+ * @param $out OutputPage object
+ */
+ public function addOutputPageMetadata( OutputPage $out ) {
+ $this->addModules( $out->getModules() );
+ $this->addModuleScripts( $out->getModuleScripts() );
+ $this->addModuleStyles( $out->getModuleStyles() );
+ $this->addModuleMessages( $out->getModuleMessages() );
+
+ $this->mHeadItems = array_merge( $this->mHeadItems, $out->getHeadItemsArray() );
+ }
+
+ /**
* Override the title to be used for display
* -- this is assumed to have been validated
* (check equal normalisation, etc.)
@@ -411,10 +426,10 @@ class ParserOutput extends CacheTime {
}
return $this->mProperties;
}
-
-
+
+
/**
- * Returns the options from its ParserOptions which have been taken
+ * Returns the options from its ParserOptions which have been taken
* into account to produce this output or false if not available.
* @return mixed Array
*/
@@ -424,7 +439,7 @@ class ParserOutput extends CacheTime {
}
return array_keys( $this->mAccessedOptions );
}
-
+
/**
* Callback passed by the Parser to the ParserOptions to keep track of which options are used.
* @access private
diff --git a/includes/parser/Preprocessor.php b/includes/parser/Preprocessor.php
index d6328aa7..ae088fdb 100644
--- a/includes/parser/Preprocessor.php
+++ b/includes/parser/Preprocessor.php
@@ -27,7 +27,7 @@ interface Preprocessor {
* Create a new custom frame for programmatic use of parameter replacement as used in some extensions
*
* @param $args array
- *
+ *
* @return PPFrame
*/
function newCustomFrame( $args );
@@ -44,7 +44,7 @@ interface Preprocessor {
*
* @param $text
* @param $flags
- *
+ *
* @return PPNode
*/
function preprocessToObj( $text, $flags = 0 );
@@ -138,6 +138,13 @@ interface PPFrame {
* Return true if the frame is a template frame
*/
function isTemplate();
+
+ /**
+ * Get a title of frame
+ *
+ * @return Title
+ */
+ function getTitle();
}
/**
diff --git a/includes/parser/Preprocessor_DOM.php b/includes/parser/Preprocessor_DOM.php
index 755563a0..066589f6 100644
--- a/includes/parser/Preprocessor_DOM.php
+++ b/includes/parser/Preprocessor_DOM.php
@@ -81,7 +81,7 @@ class Preprocessor_DOM implements Preprocessor {
*/
function memCheck() {
if ( $this->memoryLimit === false ) {
- return;
+ return true;
}
$usage = memory_get_usage();
if ( $usage > $this->memoryLimit * 0.9 ) {
@@ -543,7 +543,7 @@ class Preprocessor_DOM implements Preprocessor {
'open' => $curChar,
'close' => $rule['end'],
'count' => $count,
- 'lineStart' => ($i == 0 || $text[$i-1] == "\n"),
+ 'lineStart' => ($i > 0 && $text[$i-1] == "\n"),
);
$stack->push( $piece );
@@ -957,8 +957,7 @@ class PPFrame_DOM implements PPFrame {
return $root;
}
- if ( ++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount() )
- {
+ if ( ++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount() ) {
return '<span class="error">Node-count limit exceeded</span>';
}
@@ -1340,6 +1339,15 @@ class PPFrame_DOM implements PPFrame {
function isTemplate() {
return false;
}
+
+ /**
+ * Get a title of frame
+ *
+ * @return Title
+ */
+ function getTitle() {
+ return $this->title;
+ }
}
/**
diff --git a/includes/parser/Preprocessor_Hash.php b/includes/parser/Preprocessor_Hash.php
index c2d7d3d8..2934181a 100644
--- a/includes/parser/Preprocessor_Hash.php
+++ b/includes/parser/Preprocessor_Hash.php
@@ -498,7 +498,7 @@ class Preprocessor_Hash implements Preprocessor {
'open' => $curChar,
'close' => $rule['end'],
'count' => $count,
- 'lineStart' => ($i == 0 || $text[$i-1] == "\n"),
+ 'lineStart' => ($i > 0 && $text[$i-1] == "\n"),
);
$stack->push( $piece );
@@ -1268,6 +1268,15 @@ class PPFrame_Hash implements PPFrame {
function isTemplate() {
return false;
}
+
+ /**
+ * Get a title of frame
+ *
+ * @return Title
+ */
+ function getTitle() {
+ return $this->title;
+ }
}
/**
diff --git a/includes/parser/Preprocessor_HipHop.hphp b/includes/parser/Preprocessor_HipHop.hphp
index dc404f7c..f5af0154 100644
--- a/includes/parser/Preprocessor_HipHop.hphp
+++ b/includes/parser/Preprocessor_HipHop.hphp
@@ -260,8 +260,8 @@ class Preprocessor_HipHop implements Preprocessor {
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;
@@ -362,7 +362,7 @@ class Preprocessor_HipHop implements Preprocessor {
}
$tagStartPos = $i;
- $inner = $close = '';
+ $close = '';
if ( $text[$tagEndPos-1] === '/' ) {
// Short end tag
$attrEnd = $tagEndPos - 1;
@@ -1008,7 +1008,6 @@ class PPFrame_HipHop implements PPFrame {
*/
var $depth;
-
/**
* Construct a new preprocessor frame.
* @param $preprocessor Preprocessor: the parent preprocessor
@@ -1426,6 +1425,15 @@ class PPFrame_HipHop implements PPFrame {
function isTemplate() {
return false;
}
+
+ /**
+ * Get a title of frame
+ *
+ * @return Title
+ */
+ function getTitle() {
+ return $this->title;
+ }
}
/**
diff --git a/includes/parser/StripState.php b/includes/parser/StripState.php
index a6eff70e..7ad80fa1 100644
--- a/includes/parser/StripState.php
+++ b/includes/parser/StripState.php
@@ -11,6 +11,9 @@ class StripState {
protected $tempType, $tempMergePrefix;
+ /**
+ * @param $prefix string
+ */
function __construct( $prefix ) {
$this->prefix = $prefix;
$this->data = array(
@@ -170,6 +173,10 @@ class StripState {
return $texts;
}
+ /**
+ * @param $m
+ * @return string
+ */
protected function mergeCallback( $m ) {
$key = $m[1];
return "{$this->prefix}{$this->tempMergePrefix}-$key" . Parser::MARKER_SUFFIX;
diff --git a/includes/parser/Tidy.php b/includes/parser/Tidy.php
index 3a6d3e9c..2b98f01d 100644
--- a/includes/parser/Tidy.php
+++ b/includes/parser/Tidy.php
@@ -11,7 +11,7 @@
* we may create a real postprocessor or something that will replace this.
* It's called wrapper because for now it basically takes over MWTidy::tidy's task
* of wrapping the text in a xhtml block
- *
+ *
* This re-uses some of the parser's UNIQ tricks, though some of it is private so it's
* duplicated. Perhaps we should create an abstract marker hiding class.
*/
@@ -40,7 +40,7 @@ class MWTidyWrapper {
$this->mUniqPrefix = "\x7fUNIQ" .
dechex( mt_rand( 0, 0x7fffffff ) ) . dechex( mt_rand( 0, 0x7fffffff ) );
$this->mMarkerIndex = 0;
-
+
$wrappedtext = preg_replace_callback( ParserOutput::EDITSECTION_REGEX,
array( &$this, 'replaceEditSectionLinksCallback' ), $text );
@@ -126,7 +126,7 @@ class MWTidy {
*/
public static function checkErrors( $text, &$errorStr = null ) {
global $wgTidyInternal;
-
+
$retval = 0;
if( $wgTidyInternal ) {
$errorStr = self::execInternalTidy( $text, true, $retval );
@@ -166,7 +166,7 @@ class MWTidy {
2 => array( 'file', wfGetNull(), 'a' )
);
}
-
+
$readpipe = $stderr ? 2 : 1;
$pipes = array();
@@ -217,7 +217,7 @@ class MWTidy {
if ( !MWInit::classExists( 'tidy' ) ) {
wfWarn( "Unable to load internal tidy class." );
$retval = -1;
-
+
wfProfileOut( __METHOD__ );
return null;
}
@@ -245,7 +245,7 @@ class MWTidy {
"\n-->";
}
}
-
+
wfProfileOut( __METHOD__ );
return $cleansource;
}