Merge "Allow fragments in link= parameter in <gallery> tags."
[lhc/web/wiklou.git] / includes / parser / Parser.php
index 2066580..61fd9cc 100644 (file)
@@ -119,203 +119,102 @@ class Parser {
        const TOC_START = '<mw:toc>';
        const TOC_END = '</mw:toc>';
 
-       # Persistent
+       # Persistent:
+       var $mTagHooks = array();
+       var $mTransparentTagHooks = array();
+       var $mFunctionHooks = array();
+       var $mFunctionSynonyms = array( 0 => array(), 1 => array() );
+       var $mFunctionTagHooks = array();
+       var $mStripList = array();
+       var $mDefaultStripList = array();
+       var $mVarCache = array();
+       var $mImageParams = array();
+       var $mImageParamsMagicArray = array();
+       var $mMarkerIndex = 0;
+       var $mFirstCall = true;
 
-       /** @var array */
-       public $mTagHooks = array();
-
-       /** @var array */
-       public $mTransparentTagHooks = array();
-
-       /** @var array */
-       public $mFunctionHooks = array();
-
-       /** @var array */
-       protected $mFunctionSynonyms = array( 0 => array(), 1 => array() );
-
-       /** @var array */
-       protected $mFunctionTagHooks = array();
-
-       /** @var array */
-       protected $mStripList = array();
+       # Initialised by initialiseVariables()
 
        /**
-        * @var array
-        * @todo Unused?
+        * @var MagicWordArray
         */
-       private $mDefaultStripList = array();
+       var $mVariables;
 
-       /** @var array */
-       protected $mVarCache = array();
-
-       /** @var array */
-       protected $mImageParams = array();
-
-       /** @var array */
-       protected $mImageParamsMagicArray = array();
-
-       /** @var int */
-       public $mMarkerIndex = 0;
-
-       /** @var bool */
-       protected $mFirstCall = true;
-
-       # Initialised by initialiseVariables()
-
-       /** @var MagicWordArray */
-       public $mVariables;
-
-       /** @var MagicWordArray */
-       protected $mSubstWords;
-
-       # Initialised in constructor
-
-       /** @var array */
-       protected $mConf;
-
-       /** @var Parser */
-       public $mPreprocessor;
-
-       /** @var string */
-       protected $mExtLinkBracketedRegex;
-
-       /** @var string */
-       protected $mUrlProtocols;
+       /**
+        * @var MagicWordArray
+        */
+       var $mSubstWords;
+       var $mConf, $mPreprocessor, $mExtLinkBracketedRegex, $mUrlProtocols; # Initialised in constructor
 
        # Cleared with clearState():
-
-       /** @var ParserOutput */
-       public $mOutput;
-
-       /** @var int */
-       protected $mAutonumber;
-
-       /** @var bool */
-       protected $mDTopen;
-
-       /** @var StripState */
-       public $mStripState;
-
        /**
-        * @var array
-        * @todo Unused?
+        * @var ParserOutput
         */
-       private $mIncludeCount;
+       var $mOutput;
+       var $mAutonumber, $mDTopen;
 
        /**
-        * @var bool
-        * @todo Unused?
+        * @var StripState
         */
-       private $mArgStack;
-
-       /** @var string */
-       protected $mLastSection;
-
-       /** @var bool */
-       protected $mInPre;
-
-       /** @var LinkHolderArray */
-       protected $mLinkHolders;
-
-       /** @var int */
-       protected $mLinkID;
-
-       /** @var array */
-       protected $mIncludeSizes;
-
-       /** @var int */
-       public $mPPNodeCount;
-
-       /** @var int */
-       public $mGeneratedPPNodeCount;
-
-       /** @var int */
-       public $mHighestExpansionDepth;
+       var $mStripState;
 
-       /** @var bool|string */
-       protected $mDefaultSort;
-
-       /** @var array Empty-frame expansion cache */
-       protected $mTplExpandCache;
-
-       /** @var array */
-       protected $mTplRedirCache;
-
-       /** @var array */
-       protected $mTplDomCache;
-
-       /** @var array */
-       public $mHeadings;
-
-       /** @var array */
-       protected $mDoubleUnderscores;
-
-       /** @var int Number of expensive parser function calls */
-       protected $mExpensiveFunctionCount;
-
-       /** @var bool */
-       protected $mShowToc;
+       var $mIncludeCount, $mArgStack, $mLastSection, $mInPre;
+       /**
+        * @var LinkHolderArray
+        */
+       var $mLinkHolders;
 
-       /** @var bool */
-       protected $mForceTocPosition;
+       var $mLinkID;
+       var $mIncludeSizes, $mPPNodeCount, $mGeneratedPPNodeCount, $mHighestExpansionDepth;
+       var $mDefaultSort;
+       var $mTplRedirCache, $mTplDomCache, $mHeadings, $mDoubleUnderscores;
+       var $mExpensiveFunctionCount; # number of expensive parser function calls
+       var $mShowToc, $mForceTocPosition;
 
-       /** @var User User object; only used when doing pre-save transform */
-       protected $mUser;
+       /**
+        * @var User
+        */
+       var $mUser; # User object; only used when doing pre-save transform
 
        # Temporary
        # These are variables reset at least once per parse regardless of $clearState
 
-       /** @var ParserOptions */
-       public $mOptions;
-
-       /** @var Title Title context, used for self-link rendering and similar things */
-       public $mTitle;
-
-       /** @var array Shortcut alias, see setOutputType() */
-       public $ot;
-
-       /** @var string The timestamp of the specified revision ID */
-       public $mRevisionTimestamp;
-
-       /** @var string */
-       public $mUniqPrefix;
-
        /**
-        * @var boolean Recursive call protection.
-        * This variable should be treated as if it were private.
+        * @var ParserOptions
         */
-       public $mInParse = false;
-
-       /** @var int Output type, one of the OT_xxx constants */
-       protected $mOutputType;
-
-       /** @var Revision The revision object of the specified revision ID */
-       protected $mRevisionObject;
-
-       /** @var int ID to display in {{REVISIONID}} tags */
-       protected $mRevisionId;
-
-       /** @var string User to display in {{REVISIONUSER}} tag */
-       protected $mRevisionUser;
+       var $mOptions;
 
-       /** @var int Size to display in {{REVISIONSIZE}} variable */
-       protected $mRevisionSize;
+       /**
+        * @var Title
+        */
+       var $mTitle;        # Title context, used for self-link rendering and similar things
+       var $mOutputType;   # Output type, one of the OT_xxx constants
+       var $ot;            # Shortcut alias, see setOutputType()
+       var $mRevisionObject; # The revision object of the specified revision ID
+       var $mRevisionId;   # ID to display in {{REVISIONID}} tags
+       var $mRevisionTimestamp; # The timestamp of the specified revision ID
+       var $mRevisionUser; # User to display in {{REVISIONUSER}} tag
+       var $mRevisionSize; # Size to display in {{REVISIONSIZE}} variable
+       var $mRevIdForTs;   # The revision ID which was used to fetch the timestamp
+       var $mInputSize = false; # For {{PAGESIZE}} on current page.
 
-       /** @var bool|int For {{PAGESIZE}} on current page. */
-       protected $mInputSize = false;
+       /**
+        * @var string
+        */
+       var $mUniqPrefix;
 
        /**
         * @var array Array with the language name of each language link (i.e. the
         * interwiki prefix) in the key, value arbitrary. Used to avoid sending
         * duplicate language links to the ParserOutput.
         */
-       protected $mLangLinkLanguages;
+       var $mLangLinkLanguages;
 
        /**
-        * @var int The revision ID which was used to fetch the timestamp
-        * @todo Unused?
+        * @var boolean Recursive call protection.
+        * This variable should be treated as if it were private.
         */
-       private $mRevIdForTs;
+       public $mInParse = false;
 
        /**
         * @param array $conf
@@ -421,7 +320,7 @@ class Parser {
                $this->mStripState = new StripState( $this->mUniqPrefix );
 
                # Clear these on every parse, bug 4549
-               $this->mTplExpandCache = $this->mTplRedirCache = $this->mTplDomCache = array();
+               $this->mTplRedirCache = $this->mTplDomCache = array();
 
                $this->mShowToc = true;
                $this->mForceTocPosition = false;
@@ -718,13 +617,15 @@ class Parser {
        /**
         * Expand templates and variables in the text, producing valid, static wikitext.
         * Also removes comments.
+        * Do not call this function recursively.
         * @param string $text
         * @param Title $title
         * @param ParserOptions $options
         * @param int|null $revid
+        * @param bool|PPFrame $frame
         * @return mixed|string
         */
-       function preprocess( $text, Title $title = null, ParserOptions $options, $revid = null ) {
+       function preprocess( $text, Title $title = null, ParserOptions $options, $revid = null, $frame = false ) {
                wfProfileIn( __METHOD__ );
                $magicScopeVariable = $this->lock();
                $this->startParse( $title, $options, self::OT_PREPROCESS, true );
@@ -733,7 +634,7 @@ class Parser {
                }
                wfRunHooks( 'ParserBeforeStrip', array( &$this, &$text, &$this->mStripState ) );
                wfRunHooks( 'ParserAfterStrip', array( &$this, &$text, &$this->mStripState ) );
-               $text = $this->replaceVariables( $text );
+               $text = $this->replaceVariables( $text, $frame );
                $text = $this->mStripState->unstripBoth( $text );
                wfProfileOut( __METHOD__ );
                return $text;
@@ -1975,6 +1876,7 @@ class Parser {
         * @private
         */
        function replaceInternalLinks2( &$s ) {
+               global $wgExtraInterlanguageLinkPrefixes;
                wfProfileIn( __METHOD__ );
 
                wfProfileIn( __METHOD__ . '-setup' );
@@ -2195,11 +2097,12 @@ class Parser {
                        if ( $noforce ) {
                                # Interwikis
                                wfProfileIn( __METHOD__ . "-interwiki" );
-                               if ( $iw && $this->mOptions->getInterwikiMagic()
-                                       && $nottalk && Language::fetchLanguageName( $iw, null, 'mw' )
+                               if (
+                                       $iw && $this->mOptions->getInterwikiMagic() && $nottalk && (
+                                               Language::fetchLanguageName( $iw, null, 'mw' ) ||
+                                               in_array( $iw, $wgExtraInterlanguageLinkPrefixes )
+                                       )
                                ) {
-                                       // XXX: the above check prevents links to sites with identifiers that are not language codes
-
                                        # Bug 24502: filter duplicates
                                        if ( !isset( $this->mLangLinkLanguages[$iw] ) ) {
                                                $this->mLangLinkLanguages[$iw] = true;
@@ -2427,13 +2330,13 @@ class Parser {
                $result = $this->closeParagraph();
 
                if ( '*' === $char ) {
-                       $result .= "<ul>\n<li>";
+                       $result .= "<ul><li>";
                } elseif ( '#' === $char ) {
-                       $result .= "<ol>\n<li>";
+                       $result .= "<ol><li>";
                } elseif ( ':' === $char ) {
-                       $result .= "<dl>\n<dd>";
+                       $result .= "<dl><dd>";
                } elseif ( ';' === $char ) {
-                       $result .= "<dl>\n<dt>";
+                       $result .= "<dl><dt>";
                        $this->mDTopen = true;
                } else {
                        $result = '<!-- ERR 1 -->';
@@ -2477,20 +2380,20 @@ class Parser {
         */
        function closeList( $char ) {
                if ( '*' === $char ) {
-                       $text = "</li>\n</ul>";
+                       $text = "</li></ul>";
                } elseif ( '#' === $char ) {
-                       $text = "</li>\n</ol>";
+                       $text = "</li></ol>";
                } elseif ( ':' === $char ) {
                        if ( $this->mDTopen ) {
                                $this->mDTopen = false;
-                               $text = "</dt>\n</dl>";
+                               $text = "</dt></dl>";
                        } else {
-                               $text = "</dd>\n</dl>";
+                               $text = "</dd></dl>";
                        }
                } else {
                        return '<!-- ERR 3 -->';
                }
-               return $text . "\n";
+               return $text;
        }
        /**#@-*/
 
@@ -2588,6 +2491,9 @@ class Parser {
                                }
 
                                # Open prefixes where appropriate.
+                               if (  $lastPrefix && $prefixLength > $commonPrefixLength ) {
+                                       $output .= "\n";
+                               }
                                while ( $prefixLength > $commonPrefixLength ) {
                                        $char = substr( $prefix, $commonPrefixLength, 1 );
                                        $output .= $this->openList( $char );
@@ -2601,6 +2507,9 @@ class Parser {
                                        }
                                        ++$commonPrefixLength;
                                }
+                               if ( !$prefixLength && $lastPrefix ) {
+                                       $output .= "\n";
+                               }
                                $lastPrefix = $prefix2;
                        }
 
@@ -2682,12 +2591,18 @@ class Parser {
                                $this->mInPre = false;
                        }
                        if ( $paragraphStack === false ) {
-                               $output .= $t . "\n";
+                               $output .= $t;
+                               if ( $prefixLength === 0 ) {
+                                       $output .= "\n";
+                               }
                        }
                }
                while ( $prefixLength ) {
                        $output .= $this->closeList( $prefix2[$prefixLength - 1] );
                        --$prefixLength;
+                       if ( !$prefixLength ) {
+                               $output .= "\n";
+                       }
                }
                if ( $this->mLastSection != '' ) {
                        $output .= '</' . $this->mLastSection . '>';
@@ -3662,12 +3577,7 @@ class Parser {
                                $text = $newFrame->expand( $text, PPFrame::RECOVER_ORIG );
                        } elseif ( $titleText !== false && $newFrame->isEmpty() ) {
                                # Expansion is eligible for the empty-frame cache
-                               if ( isset( $this->mTplExpandCache[$titleText] ) ) {
-                                       $text = $this->mTplExpandCache[$titleText];
-                               } else {
-                                       $text = $newFrame->expand( $text );
-                                       $this->mTplExpandCache[$titleText] = $text;
-                               }
+                               $text = $newFrame->cachedExpand( $titleText, $text );
                        } else {
                                # Uncached expansion
                                $text = $newFrame->expand( $text );
@@ -5429,7 +5339,7 @@ class Parser {
                                                        } else {
                                                                $localLinkTitle = Title::newFromText( $linkValue );
                                                                if ( $localLinkTitle !== null ) {
-                                                                       $link = $localLinkTitle->getLocalURL();
+                                                                       $link = $localLinkTitle->getLinkURL();
                                                                }
                                                        }
                                                        break;
@@ -5827,7 +5737,7 @@ class Parser {
         * External callers should use the getSection and replaceSection methods.
         *
         * @param string $text Page wikitext
-        * @param string $section A section identifier string of the form:
+        * @param string|number $sectionId A section identifier string of the form:
         *   "<flag1> - <flag2> - ... - <section number>"
         *
         * Currently the only recognised flag is "T", which means the target section number
@@ -5849,7 +5759,7 @@ class Parser {
         * @return string For "get", the extracted section text.
         *   for "replace", the whole page with the section replaced.
         */
-       private function extractSections( $text, $section, $mode, $newText = '' ) {
+       private function extractSections( $text, $sectionId, $mode, $newText = '' ) {
                global $wgTitle; # not generally used but removes an ugly failure mode
 
                $magicScopeVariable = $this->lock();
@@ -5859,7 +5769,7 @@ class Parser {
 
                # Process section extraction flags
                $flags = 0;
-               $sectionParts = explode( '-', $section );
+               $sectionParts = explode( '-', $sectionId );
                $sectionIndex = array_pop( $sectionParts );
                foreach ( $sectionParts as $part ) {
                        if ( $part === 'T' ) {
@@ -5968,12 +5878,14 @@ class Parser {
         * If a section contains subsections, these are also returned.
         *
         * @param string $text Text to look in
-        * @param string $section Section identifier
-        * @param string $deftext Default to return if section is not found
+        * @param string|number $sectionId Section identifier as a number or string
+        * (e.g. 0, 1 or 'T-1').
+        * @param string $defaultText Default to return if section is not found
+        *
         * @return string Text of the requested section
         */
-       public function getSection( $text, $section, $deftext = '' ) {
-               return $this->extractSections( $text, $section, "get", $deftext );
+       public function getSection( $text, $sectionId, $defaultText = '' ) {
+               return $this->extractSections( $text, $sectionId, 'get', $defaultText );
        }
 
        /**
@@ -5981,13 +5893,15 @@ class Parser {
         * specified by $section has been replaced with $text. If the target
         * section does not exist, $oldtext is returned unchanged.
         *
-        * @param string $oldtext Former text of the article
-        * @param int $section Section identifier
-        * @param string $text Replacing text
+        * @param string $oldText Former text of the article
+        * @param string|number $sectionId Section identifier as a number or string
+        * (e.g. 0, 1 or 'T-1').
+        * @param string $newText Replacing text
+        *
         * @return string Modified text
         */
-       public function replaceSection( $oldtext, $section, $text ) {
-               return $this->extractSections( $oldtext, $section, "replace", $text );
+       public function replaceSection( $oldText, $sectionId, $newText ) {
+               return $this->extractSections( $oldText, $sectionId, 'replace', $newText );
        }
 
        /**
@@ -6400,4 +6314,25 @@ class Parser {
 
                return $recursiveCheck;
        }
+
+       /**
+        * Strip outer <p></p> tag from the HTML source of a single paragraph.
+        *
+        * Returns original HTML if the <p/> tag has any attributes, if there's no wrapping <p/> tag,
+        * or if there is more than one <p/> tag in the input HTML.
+        *
+        * @param string $html
+        * @return string
+        * @since 1.24
+        */
+       public static function stripOuterParagraph( $html ) {
+               $m = array();
+               if ( preg_match( '/^<p>(.*)\n?<\/p>\n?$/sU', $html, $m ) ) {
+                       if ( strpos( $m[1], '</p>' ) === false ) {
+                               $html = $m[1];
+                       }
+               }
+
+               return $html;
+       }
 }