Merge "Delete autoload.ide.php"
[lhc/web/wiklou.git] / includes / parser / Parser.php
index fd5ccb8..96bf892 100644 (file)
@@ -51,9 +51,6 @@ use Wikimedia\ScopedCallback;
  * - Parser::getPreloadText()
  *     removes <noinclude> sections and <includeonly> tags
  *
- * Globals used:
- *    object: $wgContLang
- *
  * @warning $wgUser or $wgTitle or $wgRequest or $wgLang. Keep them away!
  *
  * @par Settings:
@@ -153,6 +150,9 @@ class Parser {
        public $mImageParams = [];
        public $mImageParamsMagicArray = [];
        public $mMarkerIndex = 0;
+       /**
+        * @var bool Whether firstCallInit still needs to be called
+        */
        public $mFirstCall = true;
 
        # Initialised by initialiseVariables()
@@ -260,12 +260,28 @@ class Parser {
         */
        protected $mLinkRenderer;
 
+       /** @var MagicWordFactory */
+       private $magicWordFactory;
+
+       /** @var Language */
+       private $contLang;
+
+       /** @var ParserFactory */
+       private $factory;
+
        /**
-        * @param array $conf
+        * @param array $conf See $wgParserConf documentation
+        * @param MagicWordFactory|null $magicWordFactory
+        * @param Language|null $contLang Content language
+        * @param ParserFactory|null $factory
+        * @param string|null $urlProtocols As returned from wfUrlProtocols()
         */
-       public function __construct( $conf = [] ) {
+       public function __construct(
+               array $conf = [], MagicWordFactory $magicWordFactory = null, Language $contLang = null,
+               ParserFactory $factory = null, $urlProtocols = null
+       ) {
                $this->mConf = $conf;
-               $this->mUrlProtocols = wfUrlProtocols();
+               $this->mUrlProtocols = $urlProtocols ?? wfUrlProtocols();
                $this->mExtLinkBracketedRegex = '/\[(((?i)' . $this->mUrlProtocols . ')' .
                        self::EXT_LINK_ADDR .
                        self::EXT_LINK_URL_CLASS . '*)\p{Zs}*([^\]\\x00-\\x08\\x0a-\\x1F\\x{FFFD}]*?)\]/Su';
@@ -284,6 +300,13 @@ class Parser {
                        $this->mPreprocessorClass = Preprocessor_Hash::class;
                }
                wfDebug( __CLASS__ . ": using preprocessor: {$this->mPreprocessorClass}\n" );
+
+               $this->magicWordFactory = $magicWordFactory ??
+                       MediaWikiServices::getInstance()->getMagicWordFactory();
+
+               $this->contLang = $contLang ?? MediaWikiServices::getInstance()->getContentLanguage();
+
+               $this->factory = $factory ?? MediaWikiServices::getInstance()->getParserFactory();
        }
 
        /**
@@ -345,9 +368,7 @@ class Parser {
         * @private
         */
        public function clearState() {
-               if ( $this->mFirstCall ) {
-                       $this->firstCallInit();
-               }
+               $this->firstCallInit();
                $this->mOutput = new ParserOutput;
                $this->mOptions->registerWatcher( [ $this->mOutput, 'recordOption' ] );
                $this->mAutonumber = 0;
@@ -948,6 +969,26 @@ class Parser {
                return $this->mLinkRenderer;
        }
 
+       /**
+        * Get the MagicWordFactory that this Parser is using
+        *
+        * @since 1.32
+        * @return MagicWordFactory
+        */
+       public function getMagicWordFactory() {
+               return $this->magicWordFactory;
+       }
+
+       /**
+        * Get the content language that this Parser is using
+        *
+        * @since 1.32
+        * @return Language
+        */
+       public function getContentLanguage() {
+               return $this->contLang;
+       }
+
        /**
         * Replaces all occurrences of HTML-style comments and the given tags
         * in the text with a random marker and returns the next text. The output
@@ -2127,8 +2168,7 @@ class Parser {
                if ( $useLinkPrefixExtension ) {
                        # Match the end of a line for a word that's not followed by whitespace,
                        # e.g. in the case of 'The Arab al[[Razi]]', 'al' will be matched
-                       global $wgContLang;
-                       $charset = $wgContLang->linkPrefixCharset();
+                       $charset = $this->contLang->linkPrefixCharset();
                        $e2 = "/^((?>.*[^$charset]|))(.+)$/sDu";
                }
 
@@ -2482,14 +2522,14 @@ class Parser {
         *
         * @private
         *
-        * @param string $index Magic variable identifier as mapped in MagicWord::$mVariableIDs
+        * @param string $index Magic variable identifier as mapped in MagicWordFactory::$mVariableIDs
         * @param bool|PPFrame $frame
         *
         * @throws MWException
         * @return string
         */
        public function getVariableValue( $index, $frame = false ) {
-               global $wgContLang, $wgSitename, $wgServer, $wgServerName;
+               global $wgSitename, $wgServer, $wgServerName;
                global $wgArticlePath, $wgScriptPath, $wgStylePath;
 
                if ( is_null( $this->mTitle ) ) {
@@ -2683,10 +2723,11 @@ class Parser {
                                $value = $this->getRevisionSize();
                                break;
                        case 'namespace':
-                               $value = str_replace( '_', ' ', $wgContLang->getNsText( $this->mTitle->getNamespace() ) );
+                               $value = str_replace( '_', ' ',
+                                       $this->contLang->getNsText( $this->mTitle->getNamespace() ) );
                                break;
                        case 'namespacee':
-                               $value = wfUrlencode( $wgContLang->getNsText( $this->mTitle->getNamespace() ) );
+                               $value = wfUrlencode( $this->contLang->getNsText( $this->mTitle->getNamespace() ) );
                                break;
                        case 'namespacenumber':
                                $value = $this->mTitle->getNamespace();
@@ -2829,15 +2870,13 @@ class Parser {
         * @return string
         */
        private function getRevisionTimestampSubstring( $start, $len, $mtts, $variable ) {
-               global $wgContLang;
-
                # Get the timezone-adjusted timestamp to be used for this revision
                $resNow = substr( $this->getRevisionTimestamp(), $start, $len );
                # Possibly set vary-revision if there is not yet an associated revision
                if ( !$this->getRevisionObject() ) {
                        # Get the timezone-adjusted timestamp $mtts seconds in the future
                        $resThen = substr(
-                               $wgContLang->userAdjust( wfTimestamp( TS_MW, time() + $mtts ), '' ),
+                               $this->contLang->userAdjust( wfTimestamp( TS_MW, time() + $mtts ), '' ),
                                $start,
                                $len
                        );
@@ -2859,11 +2898,11 @@ class Parser {
         * @private
         */
        public function initialiseVariables() {
-               $variableIDs = MagicWord::getVariableIDs();
-               $substIDs = MagicWord::getSubstIDs();
+               $variableIDs = $this->magicWordFactory->getVariableIDs();
+               $substIDs = $this->magicWordFactory->getSubstIDs();
 
-               $this->mVariables = new MagicWordArray( $variableIDs );
-               $this->mSubstWords = new MagicWordArray( $substIDs );
+               $this->mVariables = $this->magicWordFactory->newArray( $variableIDs );
+               $this->mSubstWords = $this->magicWordFactory->newArray( $substIDs );
        }
 
        /**
@@ -3098,8 +3137,9 @@ class Parser {
                        $id = $this->mVariables->matchStartToEnd( $part1 );
                        if ( $id !== false ) {
                                $text = $this->getVariableValue( $id, $frame );
-                               if ( MagicWord::getCacheTTL( $id ) > -1 ) {
-                                       $this->mOutput->updateCacheExpiry( MagicWord::getCacheTTL( $id ) );
+                               if ( $this->magicWordFactory->getCacheTTL( $id ) > -1 ) {
+                                       $this->mOutput->updateCacheExpiry(
+                                               $this->magicWordFactory->getCacheTTL( $id ) );
                                }
                                $found = true;
                        }
@@ -3108,17 +3148,17 @@ class Parser {
                # MSG, MSGNW and RAW
                if ( !$found ) {
                        # Check for MSGNW:
-                       $mwMsgnw = MagicWord::get( 'msgnw' );
+                       $mwMsgnw = $this->magicWordFactory->get( 'msgnw' );
                        if ( $mwMsgnw->matchStartAndRemove( $part1 ) ) {
                                $nowiki = true;
                        } else {
                                # Remove obsolete MSG:
-                               $mwMsg = MagicWord::get( 'msg' );
+                               $mwMsg = $this->magicWordFactory->get( 'msg' );
                                $mwMsg->matchStartAndRemove( $part1 );
                        }
 
                        # Check for RAW:
-                       $mwRaw = MagicWord::get( 'raw' );
+                       $mwRaw = $this->magicWordFactory->get( 'raw' );
                        if ( $mwRaw->matchStartAndRemove( $part1 ) ) {
                                $forceRawInterwiki = true;
                        }
@@ -3383,14 +3423,12 @@ class Parser {
         * @return array
         */
        public function callParserFunction( $frame, $function, array $args = [] ) {
-               global $wgContLang;
-
                # Case sensitive functions
                if ( isset( $this->mFunctionSynonyms[1][$function] ) ) {
                        $function = $this->mFunctionSynonyms[1][$function];
                } else {
                        # Case insensitive functions
-                       $function = $wgContLang->lc( $function );
+                       $function = $this->contLang->lc( $function );
                        if ( isset( $this->mFunctionSynonyms[0][$function] ) ) {
                                $function = $this->mFunctionSynonyms[0][$function];
                        } else {
@@ -3655,8 +3693,8 @@ class Parser {
                                        break;
                                }
                        } elseif ( $title->getNamespace() == NS_MEDIAWIKI ) {
-                               global $wgContLang;
-                               $message = wfMessage( $wgContLang->lcfirst( $title->getText() ) )->inContentLanguage();
+                               $message = wfMessage( MediaWikiServices::getInstance()->getContentLanguage()->
+                                       lcfirst( $title->getText() ) )->inContentLanguage();
                                if ( !$message->exists() ) {
                                        $text = false;
                                        break;
@@ -3985,7 +4023,7 @@ class Parser {
         */
        public function doDoubleUnderscore( $text ) {
                # The position of __TOC__ needs to be recorded
-               $mw = MagicWord::get( 'toc' );
+               $mw = $this->magicWordFactory->get( 'toc' );
                if ( $mw->match( $text ) ) {
                        $this->mShowToc = true;
                        $this->mForceTocPosition = true;
@@ -3998,7 +4036,7 @@ class Parser {
                }
 
                # Now match and remove the rest of them
-               $mwa = MagicWord::getDoubleUnderscoreArray();
+               $mwa = $this->magicWordFactory->getDoubleUnderscoreArray();
                $this->mDoubleUnderscores = $mwa->matchAndRemove( $text );
 
                if ( isset( $this->mDoubleUnderscores['nogallery'] ) ) {
@@ -4216,6 +4254,13 @@ class Parser {
                        # Avoid insertion of weird stuff like <math> by expanding the relevant sections
                        $safeHeadline = $this->mStripState->unstripBoth( $safeHeadline );
 
+                       # Remove any <style> or <script> tags (T198618)
+                       $safeHeadline = preg_replace(
+                               '#<(style|script)(?: [^>]*[^>/])?>.*?</\1>#is',
+                               '',
+                               $safeHeadline
+                       );
+
                        # Strip out HTML (first regex removes any tag not allowed)
                        # Allowed tags are:
                        # * <sup> and <sub> (T10393)
@@ -4486,19 +4531,15 @@ class Parser {
         * @return string
         */
        private function pstPass2( $text, $user ) {
-               global $wgContLang;
-
-               # Note: This is the timestamp saved as hardcoded wikitext to
-               # the database, we use $wgContLang here in order to give
-               # everyone the same signature and use the default one rather
-               # than the one selected in each user's preferences.
-               # (see also T14815)
+               # Note: This is the timestamp saved as hardcoded wikitext to the database, we use
+               # $this->contLang here in order to give everyone the same signature and use the default one
+               # rather than the one selected in each user's preferences.  (see also T14815)
                $ts = $this->mOptions->getTimestamp();
                $timestamp = MWTimestamp::getLocalInstance( $ts );
                $ts = $timestamp->format( 'YmdHis' );
                $tzMsg = $timestamp->getTimezoneMessage()->inContentLanguage()->text();
 
-               $d = $wgContLang->timeanddate( $ts, false, false ) . " ($tzMsg)";
+               $d = $this->contLang->timeanddate( $ts, false, false ) . " ($tzMsg)";
 
                # Variable replacement
                # Because mOutputType is OT_WIKI, this will only process {{subst:xxx}} type tags
@@ -4642,7 +4683,7 @@ class Parser {
 
                # @todo FIXME: Regex doesn't respect extension tags or nowiki
                #  => Move this logic to braceSubstitution()
-               $substWord = MagicWord::get( 'subst' );
+               $substWord = $this->magicWordFactory->get( 'subst' );
                $substRegex = '/\{\{(?!(?:' . $substWord->getBaseRegex() . '))/x' . $substWord->getRegexCase();
                $substText = '{{' . $substWord->getSynonym( 0 );
 
@@ -4849,13 +4890,11 @@ class Parser {
         * @return string|callable The old callback function for this name, if any
         */
        public function setFunctionHook( $id, callable $callback, $flags = 0 ) {
-               global $wgContLang;
-
                $oldVal = isset( $this->mFunctionHooks[$id] ) ? $this->mFunctionHooks[$id][0] : null;
                $this->mFunctionHooks[$id] = [ $callback, $flags ];
 
                # Add to function cache
-               $mw = MagicWord::get( $id );
+               $mw = $this->magicWordFactory->get( $id );
                if ( !$mw ) {
                        throw new MWException( __METHOD__ . '() expecting a magic word identifier.' );
                }
@@ -4866,7 +4905,7 @@ class Parser {
                foreach ( $synonyms as $syn ) {
                        # Case
                        if ( !$sensitive ) {
-                               $syn = $wgContLang->lc( $syn );
+                               $syn = $this->contLang->lc( $syn );
                        }
                        # Add leading hash
                        if ( !( $flags & self::SFH_NO_HASH ) ) {
@@ -5039,7 +5078,7 @@ class Parser {
                                unset( $paramMap['img_width'] );
                        }
 
-                       $mwArray = new MagicWordArray( array_keys( $paramMap ) );
+                       $mwArray = $this->magicWordFactory->newArray( array_keys( $paramMap ) );
 
                        $label = '';
                        $alt = '';
@@ -5158,7 +5197,8 @@ class Parser {
                                }
                        }
                        $this->mImageParams[$handlerClass] = $paramMap;
-                       $this->mImageParamsMagicArray[$handlerClass] = new MagicWordArray( array_keys( $paramMap ) );
+                       $this->mImageParamsMagicArray[$handlerClass] =
+                               $this->magicWordFactory->newArray( array_keys( $paramMap ) );
                }
                return [ $this->mImageParams[$handlerClass], $this->mImageParamsMagicArray[$handlerClass] ];
        }
@@ -5701,8 +5741,6 @@ class Parser {
         */
        public function getRevisionTimestamp() {
                if ( is_null( $this->mRevisionTimestamp ) ) {
-                       global $wgContLang;
-
                        $revObject = $this->getRevisionObject();
                        $timestamp = $revObject ? $revObject->getTimestamp() : wfTimestampNow();
 
@@ -5711,8 +5749,7 @@ class Parser {
                        # Since this value will be saved into the parser cache, served
                        # to other users, and potentially even used inside links and such,
                        # it needs to be consistent for all visitors.
-                       $this->mRevisionTimestamp = $wgContLang->userAdjust( $timestamp, '' );
-
+                       $this->mRevisionTimestamp = $this->contLang->userAdjust( $timestamp, '' );
                }
                return $this->mRevisionTimestamp;
        }
@@ -6167,9 +6204,8 @@ class Parser {
         * @return Parser A parser object that is not parsing anything
         */
        public function getFreshParser() {
-               global $wgParserConf;
                if ( $this->mInParse ) {
-                       return new $wgParserConf['class']( $wgParserConf );
+                       return $this->factory->create();
                } else {
                        return $this;
                }