Merge "OutputPage: Change outdated comment to point to mediawiki.org page."
[lhc/web/wiklou.git] / includes / Title.php
index e4f18fa..f69f1a4 100644 (file)
@@ -246,6 +246,8 @@ class Title implements LinkTarget, IDBAccessObject {
         * unless $forceClone is "clone". If $forceClone is "clone" and the given TitleValue
         * is already a Title instance, that instance is copied using the clone operator.
         *
+        * @deprecated since 1.34, use newFromLinkTarget or castFromLinkTarget
+        *
         * @param TitleValue $titleValue Assumed to be safe.
         * @param string $forceClone set to NEW_CLONE to ensure a fresh instance is returned.
         *
@@ -283,6 +285,17 @@ class Title implements LinkTarget, IDBAccessObject {
                );
        }
 
+       /**
+        * Same as newFromLinkTarget, but if passed null, returns null.
+        *
+        * @param LinkTarget|null $linkTarget Assumed to be safe (if not null).
+        *
+        * @return Title|null
+        */
+       public static function castFromLinkTarget( $linkTarget ) {
+               return $linkTarget ? self::newFromLinkTarget( $linkTarget ) : null;
+       }
+
        /**
         * Create a new Title from text, such as what one would find in a link. De-
         * codes any HTML entities in the text.
@@ -605,7 +618,7 @@ class Title implements LinkTarget, IDBAccessObject {
                // NOTE: ideally, this would just call makeTitle() and then isValid(),
                // but presently, that means more overhead on a potential performance hotspot.
 
-               if ( !MWNamespace::exists( $ns ) ) {
+               if ( !MediaWikiServices::getInstance()->getNamespaceInfo()->exists( $ns ) ) {
                        return null;
                }
 
@@ -623,15 +636,33 @@ class Title implements LinkTarget, IDBAccessObject {
        /**
         * Create a new Title for the Main Page
         *
+        * This uses the 'mainpage' interface message, which could be specified in
+        * `$wgForceUIMsgAsContentMsg`. If that is the case, then calling this method
+        * will use the user language, which would involve initialising the session
+        * via `RequestContext::getMain()->getLanguage()`. For session-less endpoints,
+        * be sure to pass in a MessageLocalizer (such as your own RequestContext,
+        * or ResourceloaderContext) to prevent an error.
+        *
         * @note The Title instance returned by this method is not guaranteed to be a fresh instance.
         * It may instead be a cached instance created previously, with references to it remaining
         * elsewhere.
         *
-        * @return Title The new object
+        * @param MessageLocalizer|null $localizer An optional context to use (since 1.34)
+        * @return Title
         */
-       public static function newMainPage() {
-               $title = self::newFromText( wfMessage( 'mainpage' )->inContentLanguage()->text() );
-               // Don't give fatal errors if the message is broken
+       public static function newMainPage( MessageLocalizer $localizer = null ) {
+               if ( $localizer ) {
+                       $msg = $localizer->msg( 'mainpage' );
+               } else {
+                       $msg = wfMessage( 'mainpage' );
+               }
+
+               $title = self::newFromText( $msg->inContentLanguage()->text() );
+
+               // Every page renders at least one link to the Main Page (e.g. sidebar).
+               // If the localised value is invalid, don't produce fatal errors that
+               // would make the wiki inaccessible (and hard to fix the invalid message).
+               // Gracefully fallback...
                if ( !$title ) {
                        $title = self::newFromText( 'Main Page' );
                }
@@ -789,7 +820,8 @@ class Title implements LinkTarget, IDBAccessObject {
                $canonicalNamespace = false
        ) {
                if ( $canonicalNamespace ) {
-                       $namespace = MWNamespace::getCanonicalName( $ns );
+                       $namespace = MediaWikiServices::getInstance()->getNamespaceInfo()->
+                               getCanonicalName( $ns );
                } else {
                        $namespace = MediaWikiServices::getInstance()->getContentLanguage()->getNsText( $ns );
                }
@@ -831,13 +863,13 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return bool
         */
        public function isValid() {
-               if ( !MWNamespace::exists( $this->mNamespace ) ) {
+               $services = MediaWikiServices::getInstance();
+               if ( !$services->getNamespaceInfo()->exists( $this->mNamespace ) ) {
                        return false;
                }
 
                try {
-                       $parser = MediaWikiServices::getInstance()->getTitleParser();
-                       $parser->parseTitle( $this->mDbkeyform, $this->mNamespace );
+                       $services->getTitleParser()->parseTitle( $this->mDbkeyform, $this->mNamespace );
                        return true;
                } catch ( MalformedTitleException $ex ) {
                        return false;
@@ -1055,7 +1087,8 @@ class Title implements LinkTarget, IDBAccessObject {
                if ( $this->isExternal() ) {
                        // This probably shouldn't even happen, except for interwiki transclusion.
                        // If possible, use the canonical name for the foreign namespace.
-                       $nsText = MWNamespace::getCanonicalName( $this->mNamespace );
+                       $nsText = MediaWikiServices::getInstance()->getNamespaceInfo()->
+                               getCanonicalName( $this->mNamespace );
                        if ( $nsText !== false ) {
                                return $nsText;
                        }
@@ -1076,8 +1109,9 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return string Namespace text
         */
        public function getSubjectNsText() {
-               return MediaWikiServices::getInstance()->getContentLanguage()->
-                       getNsText( MWNamespace::getSubject( $this->mNamespace ) );
+               $services = MediaWikiServices::getInstance();
+               return $services->getContentLanguage()->
+                       getNsText( $services->getNamespaceInfo()->getSubject( $this->mNamespace ) );
        }
 
        /**
@@ -1086,20 +1120,22 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return string Namespace text
         */
        public function getTalkNsText() {
-               return MediaWikiServices::getInstance()->getContentLanguage()->
-                       getNsText( MWNamespace::getTalk( $this->mNamespace ) );
+               $services = MediaWikiServices::getInstance();
+               return $services->getContentLanguage()->
+                       getNsText( $services->getNamespaceInfo()->getTalk( $this->mNamespace ) );
        }
 
        /**
         * Can this title have a corresponding talk page?
         *
-        * @see MWNamespace::hasTalkNamespace
+        * @see NamespaceInfo::hasTalkNamespace
         * @since 1.30
         *
         * @return bool True if this title either is a talk page or can have a talk page associated.
         */
        public function canHaveTalkPage() {
-               return MWNamespace::hasTalkNamespace( $this->mNamespace );
+               return MediaWikiServices::getInstance()->getNamespaceInfo()->
+                       hasTalkNamespace( $this->mNamespace );
        }
 
        /**
@@ -1117,7 +1153,8 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return bool
         */
        public function isWatchable() {
-               return !$this->isExternal() && MWNamespace::isWatchable( $this->mNamespace );
+               return !$this->isExternal() && MediaWikiServices::getInstance()->getNamespaceInfo()->
+                       isWatchable( $this->mNamespace );
        }
 
        /**
@@ -1178,7 +1215,8 @@ class Title implements LinkTarget, IDBAccessObject {
         * @since 1.19
         */
        public function inNamespace( $ns ) {
-               return MWNamespace::equals( $this->mNamespace, $ns );
+               return MediaWikiServices::getInstance()->getNamespaceInfo()->
+                       equals( $this->mNamespace, $ns );
        }
 
        /**
@@ -1217,7 +1255,8 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return bool
         */
        public function hasSubjectNamespace( $ns ) {
-               return MWNamespace::subjectEquals( $this->mNamespace, $ns );
+               return MediaWikiServices::getInstance()->getNamespaceInfo()->
+                       subjectEquals( $this->mNamespace, $ns );
        }
 
        /**
@@ -1228,7 +1267,8 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return bool
         */
        public function isContentPage() {
-               return MWNamespace::isContent( $this->mNamespace );
+               return MediaWikiServices::getInstance()->getNamespaceInfo()->
+                       isContent( $this->mNamespace );
        }
 
        /**
@@ -1238,7 +1278,10 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return bool
         */
        public function isMovable() {
-               if ( !MWNamespace::isMovable( $this->mNamespace ) || $this->isExternal() ) {
+               if (
+                       !MediaWikiServices::getInstance()->getNamespaceInfo()->
+                               isMovable( $this->mNamespace ) || $this->isExternal()
+               ) {
                        // Interwiki title or immovable namespace. Hooks don't get to override here
                        return false;
                }
@@ -1268,7 +1311,8 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return bool
         */
        public function isSubpage() {
-               return MWNamespace::hasSubpages( $this->mNamespace )
+               return MediaWikiServices::getInstance()->getNamespaceInfo()->
+                       hasSubpages( $this->mNamespace )
                        ? strpos( $this->getText(), '/' ) !== false
                        : false;
        }
@@ -1464,16 +1508,19 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return bool
         */
        public function isTalkPage() {
-               return MWNamespace::isTalk( $this->mNamespace );
+               return MediaWikiServices::getInstance()->getNamespaceInfo()->
+                       isTalk( $this->mNamespace );
        }
 
        /**
         * Get a Title object associated with the talk page of this article
         *
+        * @deprecated since 1.34, use NamespaceInfo::getTalkPage
         * @return Title The object for the talk page
         */
        public function getTalkPage() {
-               return self::makeTitle( MWNamespace::getTalk( $this->mNamespace ), $this->mDbkeyform );
+               return self::castFromLinkTarget(
+                       MediaWikiServices::getInstance()->getNamespaceInfo()->getTalkPage( $this ) );
        }
 
        /**
@@ -1497,37 +1544,26 @@ class Title implements LinkTarget, IDBAccessObject {
         * Get a title object associated with the subject page of this
         * talk page
         *
+        * @deprecated since 1.34, use NamespaceInfo::getSubjectPage
         * @return Title The object for the subject page
         */
        public function getSubjectPage() {
-               // Is this the same title?
-               $subjectNS = MWNamespace::getSubject( $this->mNamespace );
-               if ( $this->mNamespace == $subjectNS ) {
-                       return $this;
-               }
-               return self::makeTitle( $subjectNS, $this->mDbkeyform );
+               return self::castFromLinkTarget(
+                       MediaWikiServices::getInstance()->getNamespaceInfo()->getSubjectPage( $this ) );
        }
 
        /**
         * Get the other title for this page, if this is a subject page
         * get the talk page, if it is a subject page get the talk page
         *
+        * @deprecated since 1.34, use NamespaceInfo::getAssociatedPage
         * @since 1.25
         * @throws MWException If the page doesn't have an other page
         * @return Title
         */
        public function getOtherPage() {
-               if ( $this->isSpecialPage() ) {
-                       throw new MWException( 'Special pages cannot have other pages' );
-               }
-               if ( $this->isTalkPage() ) {
-                       return $this->getSubjectPage();
-               } else {
-                       if ( !$this->canHaveTalkPage() ) {
-                               throw new MWException( "{$this->getPrefixedText()} does not have an other page" );
-                       }
-                       return $this->getTalkPage();
-               }
+               return self::castFromLinkTarget(
+                       MediaWikiServices::getInstance()->getNamespaceInfo()->getAssociatedPage( $this ) );
        }
 
        /**
@@ -1702,7 +1738,10 @@ class Title implements LinkTarget, IDBAccessObject {
         * @since 1.20
         */
        public function getRootText() {
-               if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
+               if (
+                       !MediaWikiServices::getInstance()->getNamespaceInfo()->
+                               hasSubpages( $this->mNamespace )
+               ) {
                        return $this->getText();
                }
 
@@ -1738,7 +1777,10 @@ class Title implements LinkTarget, IDBAccessObject {
         */
        public function getBaseText() {
                $text = $this->getText();
-               if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
+               if (
+                       !MediaWikiServices::getInstance()->getNamespaceInfo()->
+                               hasSubpages( $this->mNamespace )
+               ) {
                        return $text;
                }
 
@@ -1779,7 +1821,10 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return string Subpage name
         */
        public function getSubpageText() {
-               if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
+               if (
+                       !MediaWikiServices::getInstance()->getNamespaceInfo()->
+                               hasSubpages( $this->mNamespace )
+               ) {
                        return $this->mTextform;
                }
                $parts = explode( '/', $this->mTextform );
@@ -1934,7 +1979,7 @@ class Title implements LinkTarget, IDBAccessObject {
         *
         * @param string|string[] $query An optional query string,
         *   not used for interwiki links. Can be specified as an associative array as well,
-        *   e.g., array( 'action' => 'edit' ) (keys and values will be URL-escaped).
+        *   e.g., [ 'action' => 'edit' ] (keys and values will be URL-escaped).
         *   Some query patterns will trigger various shorturl path replacements.
         * @param string|string[]|bool $query2 An optional secondary query array. This one MUST
         *   be an array. If a string is passed it will be interpreted as a deprecated
@@ -2065,8 +2110,8 @@ class Title implements LinkTarget, IDBAccessObject {
         * protocol-relative, the URL will be expanded to http://
         *
         * @see self::getLocalURL for the arguments.
-        * @param string $query
-        * @param string|bool $query2
+        * @param string|string[] $query
+        * @param string|bool $query2 Deprecated
         * @return string The URL
         */
        public function getInternalURL( $query = '', $query2 = false ) {
@@ -2088,8 +2133,8 @@ class Title implements LinkTarget, IDBAccessObject {
         * NOTE: Unlike getInternalURL(), the canonical URL includes the fragment
         *
         * @see self::getLocalURL for the arguments.
-        * @param string $query
-        * @param string|bool $query2
+        * @param string|string[] $query
+        * @param string|bool $query2 Deprecated
         * @return string The URL
         * @since 1.18
         */
@@ -2190,7 +2235,7 @@ class Title implements LinkTarget, IDBAccessObject {
         * @throws Exception
         *
         * @deprecated since 1.33,
-        * use MediaWikiServices::getInstance()->getPermissionManager()->getUserPermissionsErrors()
+        * use MediaWikiServices::getInstance()->getPermissionManager()->getPermissionErrors()
         *
         */
        public function getUserPermissionsErrors(
@@ -2211,7 +2256,7 @@ class Title implements LinkTarget, IDBAccessObject {
         * Add the resulting error code to the errors array
         *
         * @param array $errors List of current errors
-        * @param array $result Result of errors
+        * @param array|string|MessageSpecifier|false $result Result of errors
         *
         * @return array List of errors
         */
@@ -2732,7 +2777,7 @@ class Title implements LinkTarget, IDBAccessObject {
                $id = $this->getArticleID();
                if ( $id ) {
                        $fname = __METHOD__;
-                       $loadRestrictionsFromDb = function ( Database $dbr ) use ( $fname, $id ) {
+                       $loadRestrictionsFromDb = function ( IDatabase $dbr ) use ( $fname, $id ) {
                                return iterator_to_array(
                                        $dbr->select(
                                                'page_restrictions',
@@ -2846,7 +2891,10 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return bool
         */
        public function hasSubpages() {
-               if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
+               if (
+                       !MediaWikiServices::getInstance()->getNamespaceInfo()->
+                               hasSubpages( $this->mNamespace )
+               ) {
                        # Duh
                        return false;
                }
@@ -2874,7 +2922,10 @@ class Title implements LinkTarget, IDBAccessObject {
         *  doesn't allow subpages
         */
        public function getSubpages( $limit = -1 ) {
-               if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
+               if (
+                       !MediaWikiServices::getInstance()->getNamespaceInfo()->
+                               hasSubpages( $this->mNamespace )
+               ) {
                        return [];
                }
 
@@ -3108,7 +3159,8 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return string Containing capitalized title
         */
        public static function capitalize( $text, $ns = NS_MAIN ) {
-               if ( MWNamespace::isCapitalized( $ns ) ) {
+               $services = MediaWikiServices::getInstance();
+               if ( $services->getNamespaceInfo()->isCapitalized( $ns ) ) {
                        return MediaWikiServices::getInstance()->getContentLanguage()->ucfirst( $text );
                } else {
                        return $text;
@@ -3377,6 +3429,8 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return array|bool True on success, getUserPermissionsErrors()-like array on failure
         */
        public function isValidMoveOperation( &$nt, $auth = true, $reason = '' ) {
+               wfDeprecated( __METHOD__, '1.25' );
+
                global $wgUser;
 
                if ( !( $nt instanceof Title ) ) {
@@ -3413,20 +3467,13 @@ class Title implements LinkTarget, IDBAccessObject {
        public function moveTo( &$nt, $auth = true, $reason = '', $createRedirect = true,
                array $changeTags = []
        ) {
+               wfDeprecated( __METHOD__, '1.25' );
+
                global $wgUser;
-               $err = $this->isValidMoveOperation( $nt, $auth, $reason );
-               if ( is_array( $err ) ) {
-                       // Auto-block user's IP if the account was "hard" blocked
-                       $wgUser->spreadAnyEditBlock();
-                       return $err;
-               }
-               // Check suppressredirect permission
-               if ( $auth && !$wgUser->isAllowed( 'suppressredirect' ) ) {
-                       $createRedirect = true;
-               }
 
                $mp = new MovePage( $this, $nt );
-               $status = $mp->move( $wgUser, $reason, $createRedirect, $changeTags );
+               $method = $auth ? 'moveIfAllowed' : 'move';
+               $status = $mp->$method( $wgUser, $reason, $createRedirect, $changeTags );
                if ( $status->isOK() ) {
                        return true;
                } else {
@@ -3437,6 +3484,7 @@ class Title implements LinkTarget, IDBAccessObject {
        /**
         * Move this page's subpages to be subpages of $nt
         *
+        * @deprecated since 1.34, use MovePage instead
         * @param Title $nt Move target
         * @param bool $auth Whether $wgUser's permissions should be checked
         * @param string $reason The reason for the move
@@ -3451,65 +3499,24 @@ class Title implements LinkTarget, IDBAccessObject {
        public function moveSubpages( $nt, $auth = true, $reason = '', $createRedirect = true,
                array $changeTags = []
        ) {
-               global $wgMaximumMovedPages;
-               // Check permissions
-               if ( !$this->userCan( 'move-subpages' ) ) {
-                       return [
-                               [ 'cant-move-subpages' ],
-                       ];
-               }
-               // Do the source and target namespaces support subpages?
-               if ( !MWNamespace::hasSubpages( $this->mNamespace ) ) {
-                       return [
-                               [ 'namespace-nosubpages', MWNamespace::getCanonicalName( $this->mNamespace ) ],
-                       ];
-               }
-               if ( !MWNamespace::hasSubpages( $nt->getNamespace() ) ) {
-                       return [
-                               [ 'namespace-nosubpages', MWNamespace::getCanonicalName( $nt->getNamespace() ) ],
-                       ];
-               }
+               wfDeprecated( __METHOD__, '1.34' );
 
-               $subpages = $this->getSubpages( $wgMaximumMovedPages + 1 );
-               $retval = [];
-               $count = 0;
-               foreach ( $subpages as $oldSubpage ) {
-                       $count++;
-                       if ( $count > $wgMaximumMovedPages ) {
-                               $retval[$oldSubpage->getPrefixedText()] = [
-                                       [ 'movepage-max-pages', $wgMaximumMovedPages ],
-                               ];
-                               break;
-                       }
+               global $wgUser;
 
-                       // We don't know whether this function was called before
-                       // or after moving the root page, so check both
-                       // $this and $nt
-                       if ( $oldSubpage->getArticleID() == $this->getArticleID()
-                               || $oldSubpage->getArticleID() == $nt->getArticleID()
-                       ) {
-                               // When moving a page to a subpage of itself,
-                               // don't move it twice
-                               continue;
-                       }
-                       $newPageName = preg_replace(
-                                       '#^' . preg_quote( $this->mDbkeyform, '#' ) . '#',
-                                       StringUtils::escapeRegexReplacement( $nt->getDBkey() ), # T23234
-                                       $oldSubpage->getDBkey() );
-                       if ( $oldSubpage->isTalkPage() ) {
-                               $newNs = $nt->getTalkPage()->getNamespace();
-                       } else {
-                               $newNs = $nt->getSubjectPage()->getNamespace();
-                       }
-                       # T16385: we need makeTitleSafe because the new page names may
-                       # be longer than 255 characters.
-                       $newSubpage = self::makeTitleSafe( $newNs, $newPageName );
+               $mp = new MovePage( $this, $nt );
+               $method = $auth ? 'moveSubpagesIfAllowed' : 'moveSubpages';
+               $result = $mp->$method( $wgUser, $reason, $createRedirect, $changeTags );
 
-                       $success = $oldSubpage->moveTo( $newSubpage, $auth, $reason, $createRedirect, $changeTags );
-                       if ( $success === true ) {
-                               $retval[$oldSubpage->getPrefixedText()] = $newSubpage->getPrefixedText();
+               if ( !$result->isOk() ) {
+                       return $result->getErrorsArray();
+               }
+
+               $retval = [];
+               foreach ( $result->getValue() as $key => $status ) {
+                       if ( $status->isOK() ) {
+                               $retval[$key] = $status->getValue();
                        } else {
-                               $retval[$oldSubpage->getPrefixedText()] = $success;
+                               $retval[$key] = $status->getErrorsArray();
                        }
                }
                return $retval;
@@ -3573,9 +3580,12 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return bool
         */
        public function isValidMoveTarget( $nt ) {
+               wfDeprecated( __METHOD__, '1.25' );
+
                # Is it an existing file?
                if ( $nt->getNamespace() == NS_FILE ) {
-                       $file = wfLocalFile( $nt );
+                       $file = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()
+                               ->newFile( $nt );
                        $file->load( File::READ_LATEST );
                        if ( $file->exists() ) {
                                wfDebug( __METHOD__ . ": file exists\n" );
@@ -3699,57 +3709,25 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return int|bool New revision ID, or false if none exists
         */
        private function getRelativeRevisionID( $revId, $flags, $dir ) {
-               $revId = (int)$revId;
-               if ( $dir === 'next' ) {
-                       $op = '>';
-                       $sort = 'ASC';
-               } elseif ( $dir === 'prev' ) {
-                       $op = '<';
-                       $sort = 'DESC';
-               } else {
-                       throw new InvalidArgumentException( '$dir must be "next" or "prev"' );
-               }
-
-               if ( $flags & self::GAID_FOR_UPDATE ) {
-                       $db = wfGetDB( DB_MASTER );
-               } else {
-                       $db = wfGetDB( DB_REPLICA, 'contributions' );
-               }
-
-               // Intentionally not caring if the specified revision belongs to this
-               // page. We only care about the timestamp.
-               $ts = $db->selectField( 'revision', 'rev_timestamp', [ 'rev_id' => $revId ], __METHOD__ );
-               if ( $ts === false ) {
-                       $ts = $db->selectField( 'archive', 'ar_timestamp', [ 'ar_rev_id' => $revId ], __METHOD__ );
-                       if ( $ts === false ) {
-                               // Or should this throw an InvalidArgumentException or something?
-                               return false;
-                       }
+               $rl = MediaWikiServices::getInstance()->getRevisionLookup();
+               $rlFlags = $flags === self::GAID_FOR_UPDATE ? IDBAccessObject::READ_LATEST : 0;
+               $rev = $rl->getRevisionById( $revId, $rlFlags );
+               if ( !$rev ) {
+                       return false;
                }
-               $ts = $db->addQuotes( $ts );
-
-               $revId = $db->selectField( 'revision', 'rev_id',
-                       [
-                               'rev_page' => $this->getArticleID( $flags ),
-                               "rev_timestamp $op $ts OR (rev_timestamp = $ts AND rev_id $op $revId)"
-                       ],
-                       __METHOD__,
-                       [
-                               'ORDER BY' => "rev_timestamp $sort, rev_id $sort",
-                               'IGNORE INDEX' => 'rev_timestamp', // Probably needed for T159319
-                       ]
-               );
-
-               if ( $revId === false ) {
+               $oldRev = $dir === 'next'
+                       ? $rl->getNextRevision( $rev, $rlFlags )
+                       : $rl->getPreviousRevision( $rev, $rlFlags );
+               if ( !$oldRev ) {
                        return false;
-               } else {
-                       return intval( $revId );
                }
+               return $oldRev->getId();
        }
 
        /**
         * Get the revision ID of the previous revision
         *
+        * @deprecated since 1.34, use RevisionLookup::getPreviousRevision
         * @param int $revId Revision ID. Get the revision that was before this one.
         * @param int $flags Title::GAID_FOR_UPDATE
         * @return int|bool Old revision ID, or false if none exists
@@ -3761,6 +3739,7 @@ class Title implements LinkTarget, IDBAccessObject {
        /**
         * Get the revision ID of the next revision
         *
+        * @deprecated since 1.34, use RevisionLookup::getNextRevision
         * @param int $revId Revision ID. Get the revision that was after this one.
         * @param int $flags Title::GAID_FOR_UPDATE
         * @return int|bool Next revision ID, or false if none exists
@@ -4000,14 +3979,14 @@ class Title implements LinkTarget, IDBAccessObject {
        /**
         * Compare with another title.
         *
-        * @param Title $title
+        * @param LinkTarget $title
         * @return bool
         */
-       public function equals( Title $title ) {
+       public function equals( LinkTarget $title ) {
                // Note: === is necessary for proper matching of number-like titles.
-               return $this->mInterwiki === $title->mInterwiki
-                       && $this->mNamespace == $title->mNamespace
-                       && $this->mDbkeyform === $title->mDbkeyform;
+               return $this->mInterwiki === $title->getInterwiki()
+                       && $this->mNamespace == $title->getNamespace()
+                       && $this->mDbkeyform === $title->getDBkey();
        }
 
        /**
@@ -4078,15 +4057,15 @@ class Title implements LinkTarget, IDBAccessObject {
                        return true; // any interwiki link might be viewable, for all we know
                }
 
+               $services = MediaWikiServices::getInstance();
                switch ( $this->mNamespace ) {
                        case NS_MEDIA:
                        case NS_FILE:
                                // file exists, possibly in a foreign repo
-                               return (bool)wfFindFile( $this );
+                               return (bool)$services->getRepoGroup()->findFile( $this );
                        case NS_SPECIAL:
                                // valid special page
-                               return MediaWikiServices::getInstance()->getSpecialPageFactory()->
-                                       exists( $this->mDbkeyform );
+                               return $services->getSpecialPageFactory()->exists( $this->mDbkeyform );
                        case NS_MAIN:
                                // selflink, possibly with fragment
                                return $this->mDbkeyform == '';
@@ -4311,9 +4290,10 @@ class Title implements LinkTarget, IDBAccessObject {
         */
        public function getNamespaceKey( $prepend = 'nstab-' ) {
                // Gets the subject namespace of this title
-               $subjectNS = MWNamespace::getSubject( $this->mNamespace );
+               $nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
+               $subjectNS = $nsInfo->getSubject( $this->mNamespace );
                // Prefer canonical namespace name for HTML IDs
-               $namespaceKey = MWNamespace::getCanonicalName( $subjectNS );
+               $namespaceKey = $nsInfo->getCanonicalName( $subjectNS );
                if ( $namespaceKey === false ) {
                        // Fallback to localised text
                        $namespaceKey = $this->getSubjectNsText();
@@ -4409,7 +4389,8 @@ class Title implements LinkTarget, IDBAccessObject {
        public function canUseNoindex() {
                global $wgExemptFromUserRobotsControl;
 
-               $bannedNamespaces = $wgExemptFromUserRobotsControl ?? MWNamespace::getContentNamespaces();
+               $bannedNamespaces = $wgExemptFromUserRobotsControl ??
+                       MediaWikiServices::getInstance()->getNamespaceInfo()->getContentNamespaces();
 
                return !in_array( $this->mNamespace, $bannedNamespaces );
        }
@@ -4576,7 +4557,10 @@ class Title implements LinkTarget, IDBAccessObject {
                        }
                }
 
-               if ( MWNamespace::hasSubpages( $this->mNamespace ) ) {
+               if (
+                       MediaWikiServices::getInstance()->getNamespaceInfo()->
+                               hasSubpages( $this->mNamespace )
+               ) {
                        // Optional notice for page itself and any parent page
                        $editnotice_base = $editnotice_ns;
                        foreach ( explode( '/', $this->mDbkeyform ) as $part ) {