Deprecate Parser::areSubpagesAllowed() / Parser::maybeDoSubpageLink()
[lhc/web/wiklou.git] / includes / parser / Parser.php
index ccc46bb..c5dd811 100644 (file)
@@ -162,7 +162,7 @@ class Parser {
         */
        public $mFirstCall = true;
 
-       # Initialised by initialiseVariables()
+       # Initialised by initializeVariables()
 
        /**
         * @var MagicWordArray
@@ -228,7 +228,11 @@ class Parser {
        public $mOptions;
 
        /**
-        * @var Title
+        * Since 1.34, leaving `mTitle` uninitialized or setting `mTitle` to
+        * `null` is deprecated.
+        *
+        * @internal
+        * @var Title|null
         */
        public $mTitle;        # Title context, used for self-link rendering and similar things
        public $mOutputType;   # Output type, one of the OT_xxx constants
@@ -465,7 +469,7 @@ class Parser {
 
                CoreParserFunctions::register( $this );
                CoreTagHooks::register( $this );
-               $this->initialiseVariables();
+               $this->initializeVariables();
 
                // Avoid PHP 7.1 warning from passing $this by reference
                $parser = $this;
@@ -759,7 +763,7 @@ class Parser {
 
                if ( $this->mGeneratedPPNodeCount > $this->mOptions->getMaxGeneratedPPNodeCount() / 10 ) {
                        wfDebugLog( 'generated-pp-node-count', $this->mGeneratedPPNodeCount . ' ' .
-                               $this->getTitle()->getPrefixedDBkey() );
+                               $this->mTitle->getPrefixedDBkey() );
                }
                return $text;
        }
@@ -924,9 +928,14 @@ class Parser {
        /**
         * Accessor for the Title object
         *
-        * @return Title
+        * Since 1.34, leaving `mTitle` uninitialized as `null` is deprecated.
+        *
+        * @return Title|null
         */
-       public function getTitle() : Title {
+       public function getTitle() : ?Title {
+               if ( $this->mTitle === null ) {
+                       wfDeprecated( 'Parser title should never be null', '1.34' );
+               }
                return $this->mTitle;
        }
 
@@ -934,9 +943,9 @@ class Parser {
         * Accessor/mutator for the Title object
         *
         * @param Title|null $x Title object or null to just get the current one
-        * @return Title
+        * @return Title|null
         */
-       public function Title( Title $x = null ) : Title {
+       public function Title( Title $x = null ) : ?Title {
                return wfSetVar( $this->mTitle, $x );
        }
 
@@ -1022,6 +1031,7 @@ class Parser {
         *
         * @since 1.19
         *
+        * @throws MWException
         * @return Language
         */
        public function getTargetLanguage() {
@@ -1031,9 +1041,11 @@ class Parser {
                        return $target;
                } elseif ( $this->mOptions->getInterfaceMessage() ) {
                        return $this->mOptions->getUserLangObj();
+               } elseif ( is_null( $this->mTitle ) ) {
+                       throw new MWException( __METHOD__ . ': $this->mTitle is null' );
                }
 
-               return $this->getTitle()->getPageLanguage();
+               return $this->mTitle->getPageLanguage();
        }
 
        /**
@@ -1221,13 +1233,25 @@ class Parser {
        }
 
        /**
-        * parse the wiki syntax used to render tables
+        * Parse the wiki syntax used to render tables.
         *
         * @private
         * @param string $text
         * @return string
+        * @deprecated since 1.34; should not be used outside parser class.
         */
        public function doTableStuff( $text ) {
+               wfDeprecated( __METHOD__, '1.34' );
+               return $this->handleTables( $text );
+       }
+
+       /**
+        * Parse the wiki syntax used to render tables.
+        *
+        * @param string $text
+        * @return string
+        */
+       private function handleTables( $text ) {
                $lines = StringUtils::explode( "\n", $text );
                $out = '';
                $td_history = []; # Is currently a td tag open?
@@ -1479,23 +1503,23 @@ class Parser {
                # properly; putting them before other transformations should keep
                # exciting things like link expansions from showing up in surprising
                # places.
-               $text = $this->doTableStuff( $text );
+               $text = $this->handleTables( $text );
 
                $text = preg_replace( '/(^|\n)-----*/', '\\1<hr />', $text );
 
-               $text = $this->doDoubleUnderscore( $text );
+               $text = $this->handleDoubleUnderscore( $text );
 
-               $text = $this->doHeadings( $text );
-               $text = $this->replaceInternalLinks( $text );
-               $text = $this->doAllQuotes( $text );
-               $text = $this->replaceExternalLinks( $text );
+               $text = $this->handleHeadings( $text );
+               $text = $this->handleInternalLinks( $text );
+               $text = $this->handleAllQuotes( $text );
+               $text = $this->handleExternalLinks( $text );
 
-               # replaceInternalLinks may sometimes leave behind
-               # absolute URLs, which have to be masked to hide them from replaceExternalLinks
+               # handleInternalLinks may sometimes leave behind
+               # absolute URLs, which have to be masked to hide them from handleExternalLinks
                $text = str_replace( self::MARKER_PREFIX . 'NOPARSE', '', $text );
 
-               $text = $this->doMagicLinks( $text );
-               $text = $this->formatHeadings( $text, $origText, $isMain );
+               $text = $this->handleMagicLinks( $text );
+               $text = $this->finalizeHeadings( $text, $origText, $isMain );
 
                return $text;
        }
@@ -1601,12 +1625,26 @@ class Parser {
         *
         * DML
         * @private
+        * @param string $text
+        * @return string
+        * @deprecated since 1.34; should not be used outside parser class.
+        */
+       public function doMagicLinks( $text ) {
+               wfDeprecated( __METHOD__, '1.34' );
+               return $this->handleMagicLinks( $text );
+       }
+
+       /**
+        * Replace special strings like "ISBN xxx" and "RFC xxx" with
+        * magic external links.
+        *
+        * DML
         *
         * @param string $text
         *
         * @return string
         */
-       public function doMagicLinks( $text ) {
+       private function handleMagicLinks( $text ) {
                $prots = wfUrlProtocolsWithoutProtRel();
                $urlChar = self::EXT_LINK_URL_CLASS;
                $addr = self::EXT_LINK_ADDR;
@@ -1673,14 +1711,7 @@ class Parser {
                        }
                        $url = wfMessage( $urlmsg, $id )->inContentLanguage()->text();
                        $this->addTrackingCategory( $trackingCat );
-                       return Linker::makeExternalLink(
-                               $url,
-                               "{$keyword} {$id}",
-                               true,
-                               $cssClass,
-                               [],
-                               $this->getTitle()
-                       );
+                       return Linker::makeExternalLink( $url, "{$keyword} {$id}", true, $cssClass, [], $this->mTitle );
                } elseif ( isset( $m[6] ) && $m[6] !== ''
                        && $this->mOptions->getMagicISBNLinks()
                ) {
@@ -1775,7 +1806,7 @@ class Parser {
                        $text = Linker::makeExternalLink( $url,
                                $this->getTargetLanguage()->getConverter()->markNoConversion( $url ),
                                true, 'free',
-                               $this->getExternalLinkAttribs( $url ), $this->getTitle() );
+                               $this->getExternalLinkAttribs( $url ), $this->mTitle );
                        # Register it in the output object...
                        $this->mOutput->addExternalLink( $url );
                }
@@ -1783,15 +1814,25 @@ class Parser {
        }
 
        /**
-        * Parse headers and return html
+        * Parse headers and return html.
         *
         * @private
-        *
         * @param string $text
-        *
         * @return string
+        * @deprecated since 1.34; should not be used outside parser class.
         */
        public function doHeadings( $text ) {
+               wfDeprecated( __METHOD__, '1.34' );
+               return $this->handleHeadings( $text );
+       }
+
+       /**
+        * Parse headers and return html
+        *
+        * @param string $text
+        * @return string
+        */
+       private function handleHeadings( $text ) {
                for ( $i = 6; $i >= 1; --$i ) {
                        $h = str_repeat( '=', $i );
                        // Trim non-newline whitespace from headings
@@ -1808,8 +1849,21 @@ class Parser {
         * @param string $text
         *
         * @return string The altered text
+        * @deprecated since 1.34; should not be used outside parser class.
         */
        public function doAllQuotes( $text ) {
+               wfDeprecated( __METHOD__, '1.34' );
+               return $this->handleAllQuotes( $text );
+       }
+
+       /**
+        * Replace single quotes with HTML markup
+        *
+        * @param string $text
+        *
+        * @return string The altered text
+        */
+       private function handleAllQuotes( $text ) {
                $outtext = '';
                $lines = StringUtils::explode( "\n", $text );
                foreach ( $lines as $line ) {
@@ -1825,6 +1879,7 @@ class Parser {
         * @param string $text
         *
         * @return string
+        * @internal
         */
        public function doQuotes( $text ) {
                $arr = preg_split( "/(''+)/", $text, -1, PREG_SPLIT_DELIM_CAPTURE );
@@ -2013,6 +2068,21 @@ class Parser {
         * @return string
         */
        public function replaceExternalLinks( $text ) {
+               wfDeprecated( __METHOD__, '1.34' );
+               return $this->handleExternalLinks( $text );
+       }
+
+       /**
+        * Replace external links (REL)
+        *
+        * Note: this is all very hackish and the order of execution matters a lot.
+        * Make sure to run tests/parser/parserTests.php if you change this code.
+        *
+        * @param string $text
+        * @throws MWException
+        * @return string
+        */
+       private function handleExternalLinks( $text ) {
                $bits = preg_split( $this->mExtLinkBracketedRegex, $text, -1, PREG_SPLIT_DELIM_CAPTURE );
                // @phan-suppress-next-line PhanTypeComparisonFromArray See phan issue #3161
                if ( $bits === false ) {
@@ -2073,7 +2143,7 @@ class Parser {
                        # Funny characters like รถ aren't valid in URLs anyway
                        # This was changed in August 2004
                        $s .= Linker::makeExternalLink( $url, $text, false, $linktype,
-                               $this->getExternalLinkAttribs( $url ), $this->getTitle() ) . $dtrail . $trail;
+                               $this->getExternalLinkAttribs( $url ), $this->mTitle ) . $dtrail . $trail;
 
                        # Register link in the output object.
                        $this->mOutput->addExternalLink( $url );
@@ -2086,6 +2156,7 @@ class Parser {
         * Get the rel attribute for a particular external link.
         *
         * @since 1.21
+        * @internal
         * @param string|bool $url Optional URL, to extract the domain from for rel =>
         *   nofollow if appropriate
         * @param LinkTarget|null $title Optional LinkTarget, for wgNoFollowNsExceptions lookups
@@ -2108,13 +2179,14 @@ class Parser {
         * (depending on configuration, namespace, and the URL's domain) and/or a
         * target attribute (depending on configuration).
         *
+        * @internal
         * @param string $url URL to extract the domain from for rel =>
         *   nofollow if appropriate
         * @return array Associative array of HTML attributes
         */
        public function getExternalLinkAttribs( $url ) {
                $attribs = [];
-               $rel = self::getExternalLinkRel( $url, $this->getTitle() );
+               $rel = self::getExternalLinkRel( $url, $this->mTitle );
 
                $target = $this->mOptions->getExternalLinkTarget();
                if ( $target ) {
@@ -2139,6 +2211,7 @@ class Parser {
         * This generally follows the syntax defined in RFC 3986, with special
         * consideration for HTTP query strings.
         *
+        * @internal
         * @param string $url
         * @return string
         */
@@ -2276,25 +2349,51 @@ class Parser {
        /**
         * Process [[ ]] wikilinks
         *
-        * @param string $s
+        * @param string $text
         *
         * @return string Processed text
         *
         * @private
+        * @deprecated since 1.34; should not be used outside parser class.
         */
-       public function replaceInternalLinks( $s ) {
-               $this->mLinkHolders->merge( $this->replaceInternalLinks2( $s ) );
-               return $s;
+       public function replaceInternalLinks( $text ) {
+               wfDeprecated( __METHOD__, '1.34' );
+               return $this->handleInternalLinks( $text );
+       }
+
+       /**
+        * Process [[ ]] wikilinks
+        *
+        * @param string $text
+        *
+        * @return string Processed text
+        */
+       private function handleInternalLinks( $text ) {
+               $this->mLinkHolders->merge( $this->handleInternalLinks2( $text ) );
+               return $text;
        }
 
        /**
         * Process [[ ]] wikilinks (RIL)
-        * @param string &$s
+        * @param string &$text
+        * @throws MWException
         * @return LinkHolderArray
         *
         * @private
+        * @deprecated since 1.34; should not be used outside parser class.
+        */
+       public function replaceInternalLinks2( &$text ) {
+               wfDeprecated( __METHOD__, '1.34' );
+               return $this->handleInternalLinks2( $text );
+       }
+
+       /**
+        * Process [[ ]] wikilinks (RIL)
+        * @param string &$s
+        * @throws MWException
+        * @return LinkHolderArray
         */
-       public function replaceInternalLinks2( &$s ) {
+       private function handleInternalLinks2( &$s ) {
                static $tc = false, $e1, $e1_img;
                # the % is needed to support urlencoded titles as well
                if ( !$tc ) {
@@ -2315,7 +2414,10 @@ class Parser {
                $line = $a->current(); # Workaround for broken ArrayIterator::next() that returns "void"
                $s = substr( $s, 1 );
 
-               $nottalk = !$this->getTitle()->isTalkPage();
+               if ( is_null( $this->mTitle ) ) {
+                       throw new MWException( __METHOD__ . ": \$this->mTitle is null\n" );
+               }
+               $nottalk = !$this->mTitle->isTalkPage();
 
                $useLinkPrefixExtension = $this->getTargetLanguage()->linkPrefixExtension();
                $e2 = null;
@@ -2334,7 +2436,10 @@ class Parser {
                        $prefix = '';
                }
 
-               $useSubpages = $this->areSubpagesAllowed();
+               # Some namespaces don't allow subpages
+               $useSubpages = $this->nsInfo->hasSubpages(
+                       $this->mTitle->getNamespace()
+               );
 
                # Loop for each link
                for ( ; $line !== false && $line !== null; $a->next(), $line = $a->current() ) {
@@ -2373,7 +2478,7 @@ class Parser {
                                        && substr( $m[3], 0, 1 ) === ']'
                                        && strpos( $text, '[' ) !== false
                                ) {
-                                       $text .= ']'; # so that replaceExternalLinks($text) works later
+                                       $text .= ']'; # so that handleExternalLinks($text) works later
                                        $m[3] = substr( $m[3], 1 );
                                }
                                # fix up urlencoded title texts
@@ -2407,7 +2512,9 @@ class Parser {
 
                        # Make subpage if necessary
                        if ( $useSubpages ) {
-                               $link = $this->maybeDoSubpageLink( $origLink, $text );
+                               $link = Linker::normalizeSubpageLink(
+                                       $this->mTitle, $origLink, $text
+                               );
                        } else {
                                $link = $origLink;
                        }
@@ -2459,7 +2566,7 @@ class Parser {
                                        if ( !$found ) {
                                                # we couldn't find the end of this imageLink, so output it raw
                                                # but don't ignore what might be perfectly normal links in the text we've examined
-                                               $holders->merge( $this->replaceInternalLinks2( $text ) );
+                                               $holders->merge( $this->handleInternalLinks2( $text ) );
                                                $s .= "{$prefix}[[$link|$text";
                                                # note: no $trail, because without an end, there *is* no trail
                                                continue;
@@ -2509,7 +2616,7 @@ class Parser {
                                }
 
                                if ( $ns == NS_FILE ) {
-                                       if ( !$this->badFileLookup->isBadFile( $nt->getDBkey(), $this->getTitle() ) ) {
+                                       if ( !$this->badFileLookup->isBadFile( $nt->getDBkey(), $this->mTitle ) ) {
                                                if ( $wasblank ) {
                                                        # if no parameters were passed, $text
                                                        # becomes something like "File:Foo.png",
@@ -2520,10 +2627,10 @@ class Parser {
                                                        # recursively parse links inside the image caption
                                                        # actually, this will parse them in any other parameters, too,
                                                        # but it might be hard to fix that, and it doesn't matter ATM
-                                                       $text = $this->replaceExternalLinks( $text );
-                                                       $holders->merge( $this->replaceInternalLinks2( $text ) );
+                                                       $text = $this->handleExternalLinks( $text );
+                                                       $holders->merge( $this->handleInternalLinks2( $text ) );
                                                }
-                                               # cloak any absolute URLs inside the image markup, so replaceExternalLinks() won't touch them
+                                               # cloak any absolute URLs inside the image markup, so handleExternalLinks() won't touch them
                                                $s .= $prefix . $this->armorLinks(
                                                        $this->makeImage( $nt, $text, $holders ) ) . $trail;
                                                continue;
@@ -2551,7 +2658,7 @@ class Parser {
                        # Self-link checking. For some languages, variants of the title are checked in
                        # LinkHolderArray::doVariants() to allow batching the existence checks necessary
                        # for linking to a different variant.
-                       if ( $ns != NS_SPECIAL && $nt->equals( $this->getTitle() ) && !$nt->hasFragment() ) {
+                       if ( $ns != NS_SPECIAL && $nt->equals( $this->mTitle ) && !$nt->hasFragment() ) {
                                $s .= $prefix . Linker::makeSelfLinkObj( $nt, $text, '', $trail );
                                continue;
                        }
@@ -2566,7 +2673,7 @@ class Parser {
                                        [ $this, $nt, &$options, &$descQuery ] );
                                # Fetch and register the file (file title may be different via hooks)
                                list( $file, $nt ) = $this->fetchFileAndTitle( $nt, $options );
-                               # Cloak with NOPARSE to avoid replacement in replaceExternalLinks
+                               # Cloak with NOPARSE to avoid replacement in handleExternalLinks
                                $s .= $prefix . $this->armorLinks(
                                        Linker::makeMediaLinkFile( $nt, $file, $text ) ) . $trail;
                                continue;
@@ -2632,10 +2739,12 @@ class Parser {
        /**
         * Return true if subpage links should be expanded on this page.
         * @return bool
+        * @deprecated since 1.34; should not be used outside parser class.
         */
        public function areSubpagesAllowed() {
                # Some namespaces don't allow subpages
-               return $this->nsInfo->hasSubpages( $this->getTitle()->getNamespace() );
+               wfDeprecated( __METHOD__, '1.34' );
+               return $this->nsInfo->hasSubpages( $this->mTitle->getNamespace() );
        }
 
        /**
@@ -2645,9 +2754,11 @@ class Parser {
         * @param string &$text The link text, modified as necessary
         * @return string The full name of the link
         * @private
+        * @deprecated since 1.34; should not be used outside parser class.
         */
        public function maybeDoSubpageLink( $target, &$text ) {
-               return Linker::normalizeSubpageLink( $this->getTitle(), $target, $text );
+               wfDeprecated( __METHOD__, '1.34' );
+               return Linker::normalizeSubpageLink( $this->mTitle, $target, $text );
        }
 
        /**
@@ -2670,9 +2781,36 @@ class Parser {
         * @param string $index Magic variable identifier as mapped in MagicWordFactory::$mVariableIDs
         * @param bool|PPFrame $frame
         *
+        * @throws MWException
         * @return string
+        * @deprecated since 1.34; should not be used outside parser class.
         */
        public function getVariableValue( $index, $frame = false ) {
+               wfDeprecated( __METHOD__, '1.34' );
+               return $this->expandMagicVariable( $index, $frame );
+       }
+
+       /**
+        * Return value of a magic variable (like PAGENAME)
+        *
+        * @param string $index Magic variable identifier as mapped in MagicWordFactory::$mVariableIDs
+        * @param bool|PPFrame $frame
+        *
+        * @throws MWException
+        * @return string
+        */
+       private function expandMagicVariable( $index, $frame = false ) {
+               // XXX This function should be moved out of Parser class for
+               // reuse by Parsoid/etc.
+               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)' );
+               }
+
                // Avoid PHP 7.1 warning from passing $this by reference
                $parser = $this;
 
@@ -2739,72 +2877,72 @@ class Parser {
                                $value = $pageLang->formatNum( MWTimestamp::getLocalInstance( $ts )->format( 'd' ), true );
                                break;
                        case 'pagename':
-                               $value = wfEscapeWikiText( $this->getTitle()->getText() );
+                               $value = wfEscapeWikiText( $this->mTitle->getText() );
                                break;
                        case 'pagenamee':
-                               $value = wfEscapeWikiText( $this->getTitle()->getPartialURL() );
+                               $value = wfEscapeWikiText( $this->mTitle->getPartialURL() );
                                break;
                        case 'fullpagename':
-                               $value = wfEscapeWikiText( $this->getTitle()->getPrefixedText() );
+                               $value = wfEscapeWikiText( $this->mTitle->getPrefixedText() );
                                break;
                        case 'fullpagenamee':
-                               $value = wfEscapeWikiText( $this->getTitle()->getPrefixedURL() );
+                               $value = wfEscapeWikiText( $this->mTitle->getPrefixedURL() );
                                break;
                        case 'subpagename':
-                               $value = wfEscapeWikiText( $this->getTitle()->getSubpageText() );
+                               $value = wfEscapeWikiText( $this->mTitle->getSubpageText() );
                                break;
                        case 'subpagenamee':
-                               $value = wfEscapeWikiText( $this->getTitle()->getSubpageUrlForm() );
+                               $value = wfEscapeWikiText( $this->mTitle->getSubpageUrlForm() );
                                break;
                        case 'rootpagename':
-                               $value = wfEscapeWikiText( $this->getTitle()->getRootText() );
+                               $value = wfEscapeWikiText( $this->mTitle->getRootText() );
                                break;
                        case 'rootpagenamee':
                                $value = wfEscapeWikiText( wfUrlencode( str_replace(
                                        ' ',
                                        '_',
-                                       $this->getTitle()->getRootText()
+                                       $this->mTitle->getRootText()
                                ) ) );
                                break;
                        case 'basepagename':
-                               $value = wfEscapeWikiText( $this->getTitle()->getBaseText() );
+                               $value = wfEscapeWikiText( $this->mTitle->getBaseText() );
                                break;
                        case 'basepagenamee':
                                $value = wfEscapeWikiText( wfUrlencode( str_replace(
                                        ' ',
                                        '_',
-                                       $this->getTitle()->getBaseText()
+                                       $this->mTitle->getBaseText()
                                ) ) );
                                break;
                        case 'talkpagename':
-                               if ( $this->getTitle()->canHaveTalkPage() ) {
-                                       $talkPage = $this->getTitle()->getTalkPage();
+                               if ( $this->mTitle->canHaveTalkPage() ) {
+                                       $talkPage = $this->mTitle->getTalkPage();
                                        $value = wfEscapeWikiText( $talkPage->getPrefixedText() );
                                } else {
                                        $value = '';
                                }
                                break;
                        case 'talkpagenamee':
-                               if ( $this->getTitle()->canHaveTalkPage() ) {
-                                       $talkPage = $this->getTitle()->getTalkPage();
+                               if ( $this->mTitle->canHaveTalkPage() ) {
+                                       $talkPage = $this->mTitle->getTalkPage();
                                        $value = wfEscapeWikiText( $talkPage->getPrefixedURL() );
                                } else {
                                        $value = '';
                                }
                                break;
                        case 'subjectpagename':
-                               $subjPage = $this->getTitle()->getSubjectPage();
+                               $subjPage = $this->mTitle->getSubjectPage();
                                $value = wfEscapeWikiText( $subjPage->getPrefixedText() );
                                break;
                        case 'subjectpagenamee':
-                               $subjPage = $this->getTitle()->getSubjectPage();
+                               $subjPage = $this->mTitle->getSubjectPage();
                                $value = wfEscapeWikiText( $subjPage->getPrefixedURL() );
                                break;
                        case 'pageid': // requested in T25427
                                # Inform the edit saving system that getting the canonical output
                                # after page insertion requires a parse that used that exact page ID
                                $this->setOutputFlag( 'vary-page-id', '{{PAGEID}} used' );
-                               $value = $this->getTitle()->getArticleID();
+                               $value = $this->mTitle->getArticleID();
                                if ( !$value ) {
                                        $value = $this->mOptions->getSpeculativePageId();
                                        if ( $value ) {
@@ -2817,7 +2955,7 @@ class Parser {
                                        $this->svcOptions->get( 'MiserMode' ) &&
                                        !$this->mOptions->getInterfaceMessage() &&
                                        // @TODO: disallow this word on all namespaces
-                                       $this->nsInfo->isContent( $this->getTitle()->getNamespace() )
+                                       $this->nsInfo->isContent( $this->mTitle->getNamespace() )
                                ) {
                                        // Use a stub result instead of the actual revision ID in order to avoid
                                        // double parses on page save but still allow preview detection (T137900)
@@ -2873,29 +3011,27 @@ class Parser {
                                break;
                        case 'namespace':
                                $value = str_replace( '_', ' ',
-                                       $this->contLang->getNsText( $this->getTitle()->getNamespace() ) );
+                                       $this->contLang->getNsText( $this->mTitle->getNamespace() ) );
                                break;
                        case 'namespacee':
-                               $value = wfUrlencode( $this->contLang->getNsText( $this->getTitle()->getNamespace() ) );
+                               $value = wfUrlencode( $this->contLang->getNsText( $this->mTitle->getNamespace() ) );
                                break;
                        case 'namespacenumber':
-                               $value = $this->getTitle()->getNamespace();
+                               $value = $this->mTitle->getNamespace();
                                break;
                        case 'talkspace':
-                               $value = $this->getTitle()->canHaveTalkPage()
-                                       ? str_replace( '_', ' ', $this->getTitle()->getTalkNsText() )
+                               $value = $this->mTitle->canHaveTalkPage()
+                                       ? str_replace( '_', ' ', $this->mTitle->getTalkNsText() )
                                        : '';
                                break;
                        case 'talkspacee':
-                               $value = $this->getTitle()->canHaveTalkPage()
-                                       ? wfUrlencode( $this->getTitle()->getTalkNsText() )
-                                       : '';
+                               $value = $this->mTitle->canHaveTalkPage() ? wfUrlencode( $this->mTitle->getTalkNsText() ) : '';
                                break;
                        case 'subjectspace':
-                               $value = str_replace( '_', ' ', $this->getTitle()->getSubjectNsText() );
+                               $value = str_replace( '_', ' ', $this->mTitle->getSubjectNsText() );
                                break;
                        case 'subjectspacee':
-                               $value = ( wfUrlencode( $this->getTitle()->getSubjectNsText() ) );
+                               $value = ( wfUrlencode( $this->mTitle->getSubjectNsText() ) );
                                break;
                        case 'currentdayname':
                                $value = $pageLang->getWeekdayName( (int)MWTimestamp::getInstance( $ts )->format( 'w' ) + 1 );
@@ -3048,8 +3184,18 @@ class Parser {
         * initialise the magic variables (like CURRENTMONTHNAME) and substitution modifiers
         *
         * @private
+        * @deprecated since 1.34; should not be used outside parser class.
         */
        public function initialiseVariables() {
+               wfDeprecated( __METHOD__, '1.34' );
+               $this->initializeVariables();
+       }
+
+       /**
+        * Initialize the magic variables (like CURRENTMONTHNAME) and
+        * substitution modifiers.
+        */
+       private function initializeVariables() {
                $variableIDs = $this->magicWordFactory->getVariableIDs();
                $substIDs = $this->magicWordFactory->getSubstIDs();
 
@@ -3224,6 +3370,7 @@ class Parser {
         * @param PPFrame $frame The current frame, contains template arguments
         * @throws Exception
         * @return string|array The text of the template
+        * @internal
         */
        public function braceSubstitution( $piece, $frame ) {
                // Flags
@@ -3290,7 +3437,7 @@ class Parser {
                if ( !$found && $args->getLength() == 0 ) {
                        $id = $this->mVariables->matchStartToEnd( $part1 );
                        if ( $id !== false ) {
-                               $text = $this->getVariableValue( $id, $frame );
+                               $text = $this->expandMagicVariable( $id, $frame );
                                if ( $this->magicWordFactory->getCacheTTL( $id ) > -1 ) {
                                        $this->mOutput->updateCacheExpiry(
                                                $this->magicWordFactory->getCacheTTL( $id ) );
@@ -3366,10 +3513,12 @@ class Parser {
                        $ns = NS_TEMPLATE;
                        # Split the title into page and subpage
                        $subpage = '';
-                       $relative = $this->maybeDoSubpageLink( $part1, $subpage );
+                       $relative = Linker::normalizeSubpageLink(
+                               $this->mTitle, $part1, $subpage
+                       );
                        if ( $part1 !== $relative ) {
                                $part1 = $relative;
-                               $ns = $this->getTitle()->getNamespace();
+                               $ns = $this->mTitle->getNamespace();
                        }
                        $title = Title::newFromText( $part1, $ns );
                        if ( $title ) {
@@ -3574,6 +3723,7 @@ class Parser {
         * @param PPFrame $frame The current frame, contains template arguments
         * @param string $function Function name
         * @param array $args Arguments to the function
+        * @throws MWException
         * @return array
         */
        public function callParserFunction( $frame, $function, array $args = [] ) {
@@ -3999,7 +4149,7 @@ class Parser {
 
        /**
         * Triple brace replacement -- used for template arguments
-        * @private
+        * @internal
         *
         * @param array $piece
         * @param PPFrame $frame
@@ -4057,6 +4207,7 @@ class Parser {
         *
         * @throws MWException
         * @return string
+        * @internal
         */
        public function extensionSubstitution( $params, $frame ) {
                static $errorStr = '<span class="error">';
@@ -4184,10 +4335,22 @@ class Parser {
         * Fills $this->mDoubleUnderscores, returns the modified text
         *
         * @param string $text
-        *
         * @return string
+        * @deprecated since 1.34; should not be used outside parser class.
         */
        public function doDoubleUnderscore( $text ) {
+               wfDeprecated( __METHOD__, '1.34' );
+               return $this->handleDoubleUnderscore( $text );
+       }
+
+       /**
+        * Strip double-underscore items like __NOGALLERY__ and __NOTOC__
+        * Fills $this->mDoubleUnderscores, returns the modified text
+        *
+        * @param string $text
+        * @return string
+        */
+       private function handleDoubleUnderscore( $text ) {
                # The position of __TOC__ needs to be recorded
                $mw = $this->magicWordFactory->get( 'toc' );
                if ( $mw->match( $text ) ) {
@@ -4212,17 +4375,17 @@ class Parser {
                        $this->mShowToc = false;
                }
                if ( isset( $this->mDoubleUnderscores['hiddencat'] )
-                       && $this->getTitle()->getNamespace() == NS_CATEGORY
+                       && $this->mTitle->getNamespace() == NS_CATEGORY
                ) {
                        $this->addTrackingCategory( 'hidden-category-category' );
                }
                # (T10068) Allow control over whether robots index a page.
                # __INDEX__ always overrides __NOINDEX__, see T16899
-               if ( isset( $this->mDoubleUnderscores['noindex'] ) && $this->getTitle()->canUseNoindex() ) {
+               if ( isset( $this->mDoubleUnderscores['noindex'] ) && $this->mTitle->canUseNoindex() ) {
                        $this->mOutput->setIndexPolicy( 'noindex' );
                        $this->addTrackingCategory( 'noindex-category' );
                }
-               if ( isset( $this->mDoubleUnderscores['index'] ) && $this->getTitle()->canUseNoindex() ) {
+               if ( isset( $this->mDoubleUnderscores['index'] ) && $this->mTitle->canUseNoindex() ) {
                        $this->mOutput->setIndexPolicy( 'index' );
                        $this->addTrackingCategory( 'index-category' );
                }
@@ -4241,7 +4404,7 @@ class Parser {
         * @return bool Whether the addition was successful
         */
        public function addTrackingCategory( $msg ) {
-               return $this->mOutput->addTrackingCategory( $msg, $this->getTitle() );
+               return $this->mOutput->addTrackingCategory( $msg, $this->mTitle );
        }
 
        /**
@@ -4259,8 +4422,29 @@ class Parser {
         * @param bool $isMain
         * @return mixed|string
         * @private
+        * @deprecated since 1.34; should not be used outside parser class.
         */
        public function formatHeadings( $text, $origText, $isMain = true ) {
+               wfDeprecated( __METHOD__, '1.34' );
+               return $this->finalizeHeadings( $text, $origText, $isMain );
+       }
+
+       /**
+        * This function accomplishes several tasks:
+        * 1) Auto-number headings if that option is enabled
+        * 2) Add an [edit] link to sections for users who have enabled the option and can edit the page
+        * 3) Add a Table of contents on the top for users who have enabled the option
+        * 4) Auto-anchor headings
+        *
+        * It loops through all headlines, collects the necessary data, then splits up the
+        * string and re-inserts the newly formatted headlines.
+        *
+        * @param string $text
+        * @param string $origText Original, untouched wikitext
+        * @param bool $isMain
+        * @return mixed|string
+        */
+       private function finalizeHeadings( $text, $origText, $isMain = true ) {
                # Inhibit editsection links if requested in the page
                if ( isset( $this->mDoubleUnderscores['noeditsection'] ) ) {
                        $maybeShowEditLink = false;
@@ -4270,7 +4454,7 @@ class Parser {
 
                # Get all headlines for numbering them and adding funky stuff like [edit]
                # links - this is for later, but we need the number of headlines right now
-               # NOTE: white space in headings have been trimmed in doHeadings. They shouldn't
+               # NOTE: white space in headings have been trimmed in handleHeadings. They shouldn't
                # be trimmed here since whitespace in HTML headings is significant.
                $matches = [];
                $numMatches = preg_match_all(
@@ -4319,7 +4503,7 @@ class Parser {
                $toclevel = 0;
                $prevtoclevel = 0;
                $markerRegex = self::MARKER_PREFIX . "-h-(\d+)-" . self::MARKER_SUFFIX;
-               $baseTitleText = $this->getTitle()->getPrefixedDBkey();
+               $baseTitleText = $this->mTitle->getPrefixedDBkey();
                $oldType = $this->mOutputType;
                $this->setOutputType( self::OT_WIKI );
                $frame = $this->getPreprocessor()->newFrame();
@@ -4552,7 +4736,7 @@ class Parser {
                                        $editsectionSection = "T-$sectionIndex";
                                        $editsectionContent = null;
                                } else {
-                                       $editsectionPage = $this->getTitle()->getPrefixedText();
+                                       $editsectionPage = $this->mTitle->getPrefixedText();
                                        $editsectionSection = $sectionIndex;
                                        $editsectionContent = $headlineHint;
                                }
@@ -4743,7 +4927,7 @@ class Parser {
                $text = preg_replace( $p4, '[[\\1\\2\\3|\\2]]', $text );
                $text = preg_replace( $p3, '[[\\1\\2\\3\\4|\\2]]', $text );
 
-               $t = $this->getTitle()->getText();
+               $t = $this->mTitle->getText();
                $m = [];
                if ( preg_match( "/^($nc+:|)$tc+?( \\($tc+\\))$/", $t, $m ) ) {
                        $text = preg_replace( $p2, "[[$m[1]\\1$m[2]|\\1]]", $text );
@@ -5170,7 +5354,7 @@ class Parser {
                        $ig = ImageGalleryBase::factory( false );
                }
 
-               $ig->setContextTitle( $this->getTitle() );
+               $ig->setContextTitle( $this->mTitle );
                $ig->setShowBytes( false );
                $ig->setShowDimensions( false );
                $ig->setShowFilename( false );
@@ -5628,7 +5812,7 @@ class Parser {
        protected function stripAltText( $caption, $holders ) {
                # Strip bad stuff out of the title (tooltip).  We can't just use
                # replaceLinkHoldersText() here, because if this function is called
-               # from replaceInternalLinks2(), mLinkHolders won't be up-to-date.
+               # from handleInternalLinks2(), mLinkHolders won't be up-to-date.
                if ( $holders ) {
                        $tooltip = $holders->replaceText( $caption );
                } else {
@@ -6232,9 +6416,27 @@ class Parser {
         * @param int $outputType
         *
         * @return string
+        * @deprecated since 1.34; should not be used outside parser class.
         */
        public function testSrvus( $text, Title $title, ParserOptions $options,
                $outputType = self::OT_HTML
+       ) {
+               wfDeprecated( __METHOD__, '1.34' );
+               return $this->fuzzTestSrvus( $text, $title, $options, $outputType );
+       }
+
+       /**
+        * Strip/replaceVariables/unstrip for preprocessor regression testing
+        *
+        * @param string $text
+        * @param Title $title
+        * @param ParserOptions $options
+        * @param int $outputType
+        *
+        * @return string
+        */
+       private function fuzzTestSrvus( $text, Title $title, ParserOptions $options,
+               $outputType = self::OT_HTML
        ) {
                $magicScopeVariable = $this->lock();
                $this->startParse( $title, $options, $outputType, true );
@@ -6250,8 +6452,20 @@ class Parser {
         * @param Title $title
         * @param ParserOptions $options
         * @return string
+        * @deprecated since 1.34; should not be used outside parser class.
         */
        public function testPst( $text, Title $title, ParserOptions $options ) {
+               wfDeprecated( __METHOD__, '1.34' );
+               return $this->fuzzTestPst( $text, $title, $options );
+       }
+
+       /**
+        * @param string $text
+        * @param Title $title
+        * @param ParserOptions $options
+        * @return string
+        */
+       private function fuzzTestPst( $text, Title $title, ParserOptions $options ) {
                return $this->preSaveTransform( $text, $title, $options->getUser(), $options );
        }
 
@@ -6260,9 +6474,21 @@ class Parser {
         * @param Title $title
         * @param ParserOptions $options
         * @return string
+        * @deprecated since 1.34; should not be used outside parser class.
         */
        public function testPreprocess( $text, Title $title, ParserOptions $options ) {
-               return $this->testSrvus( $text, $title, $options, self::OT_PREPROCESS );
+               wfDeprecated( __METHOD__, '1.34' );
+               return $this->fuzzTestPreprocess( $text, $title, $options );
+       }
+
+       /**
+        * @param string $text
+        * @param Title $title
+        * @param ParserOptions $options
+        * @return string
+        */
+       private function fuzzTestPreprocess( $text, Title $title, ParserOptions $options ) {
+               return $this->fuzzTestSrvus( $text, $title, $options, self::OT_PREPROCESS );
        }
 
        /**
@@ -6501,7 +6727,7 @@ class Parser {
         */
        protected function setOutputFlag( $flag, $reason ) {
                $this->mOutput->setFlag( $flag );
-               $name = $this->getTitle()->getPrefixedText();
+               $name = $this->mTitle->getPrefixedText();
                $this->logger->debug( __METHOD__ . ": set $flag flag on '$name'; $reason" );
        }
 }