From d9022f63880ce039446fba8364f68e656b7bf4cb Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Thu, 3 May 2012 13:01:35 +0200 Subject: Update to MediaWiki 1.19.0 --- includes/parser/CoreParserFunctions.php | 42 ++- includes/parser/LinkHolderArray.php | 6 +- includes/parser/Parser.php | 549 ++++++++++++++++++++++--------- includes/parser/ParserCache.php | 23 +- includes/parser/ParserOptions.php | 327 ++++++++++++++---- includes/parser/ParserOutput.php | 87 +++-- includes/parser/Preprocessor.php | 11 +- includes/parser/Preprocessor_DOM.php | 16 +- includes/parser/Preprocessor_Hash.php | 11 +- includes/parser/Preprocessor_HipHop.hphp | 16 +- includes/parser/StripState.php | 7 + includes/parser/Tidy.php | 12 +- 12 files changed, 807 insertions(+), 300 deletions(-) (limited to 'includes/parser') 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( '' . wfMsgForContent( 'duplicate-defaultsort', htmlspecialchars( $old ), htmlspecialchars( $text ) ) . '' ); + } } // 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 sections, and 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 \\2', + '/(.) (?=\\?|:|;|!|%|\\302\\273)/' => '\\1 ', # french spaces, Guillemet-right '/(\\302\\253) /' => '\\1 ', '/ (!\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 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 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 ); } } @@ -503,11 +512,33 @@ class Parser { return $text; } + /** + * 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) * * , 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,21 +695,41 @@ 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' ); } return $this->mTitle->getPageLanguage(); } + /** + * 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'ISBN $isbn"; } 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 by expanding the relevant sections - $safeHeadline = $this->mStripState->unstripBoth( $headline ); # Remove link placeholders by the link text. # # 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 and : bug 8393) + # Avoid insertion of weird stuff like by expanding the relevant sections + $safeHeadline = $this->mStripState->unstripBoth( $safeHeadline ); + + # Strip out HTML (first regex removes any tag not allowed) + # Allowed tags are and (bug 8393), (bug 26375) and (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 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]>/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( '', $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 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\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 @@ 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="(.*?)"(?:/>|>(.*?)())#'; @@ -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 ); } @@ -358,6 +359,20 @@ class ParserOutput extends CacheTime { $this->mModuleMessages = array_merge( $this->mModuleMessages, (array)$modules ); } + /** + * 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 @@ -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 'Node-count limit exceeded'; } @@ -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 - if ( $enableOnlyinclude - && substr( $text, $i, strlen( '' ) ) === '' ) + if ( $enableOnlyinclude + && substr( $text, $i, strlen( '' ) ) === '' ) { $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; } -- cgit v1.2.2