X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=blobdiff_plain;f=includes%2FTitle.php;h=6c15a062b095dd91f785803bbfc470ae8a5e3bf8;hp=547b28c0c38c7622c422da88be2494aaece08828;hb=82bd6b026ef6ecb7f2fe15acaa40c608680dfff1;hpb=f9fbcfd93905d4a9909bead84d3cf4364f4e5254 diff --git a/includes/Title.php b/includes/Title.php index 547b28c0c3..6c15a062b0 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -23,7 +23,7 @@ */ use MediaWiki\Permissions\PermissionManager; -use MediaWiki\Storage\RevisionRecord; +use MediaWiki\Revision\RevisionRecord; use Wikimedia\Assert\Assert; use Wikimedia\Rdbms\Database; use Wikimedia\Rdbms\IDatabase; @@ -51,10 +51,11 @@ class Title implements LinkTarget, IDBAccessObject { const CACHE_MAX = 1000; /** - * Used to be GAID_FOR_UPDATE define. Used with getArticleID() and friends - * to use the master DB + * Used to be GAID_FOR_UPDATE define(). Used with getArticleID() and friends + * to use the master DB and inject it into link cache. + * @deprecated since 1.34, use Title::READ_LATEST instead. */ - const GAID_FOR_UPDATE = 1; + const GAID_FOR_UPDATE = 512; /** * Flag for use with factory methods like newFromLinkTarget() that have @@ -74,25 +75,18 @@ class Title implements LinkTarget, IDBAccessObject { /** @var string Text form (spaces not underscores) of the main part */ public $mTextform = ''; - /** @var string URL-encoded form of the main part */ public $mUrlform = ''; - /** @var string Main part with underscores */ public $mDbkeyform = ''; - /** @var string Database key with the initial letter in the case specified by the user */ protected $mUserCaseDBKey; - /** @var int Namespace index, i.e. one of the NS_xxxx constants */ public $mNamespace = NS_MAIN; - /** @var string Interwiki prefix */ public $mInterwiki = ''; - /** @var bool Was this Title created from a string with a local interwiki prefix? */ private $mLocalInterwiki = false; - /** @var string Title fragment (i.e. the bit after the #) */ public $mFragment = ''; @@ -467,16 +461,18 @@ class Title implements LinkTarget, IDBAccessObject { * Create a new Title from an article ID * * @param int $id The page_id corresponding to the Title to create - * @param int $flags Use Title::GAID_FOR_UPDATE to use master + * @param int $flags Bitfield of class READ_* constants * @return Title|null The new object, or null on an error */ public static function newFromID( $id, $flags = 0 ) { - $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_REPLICA ); - $row = $db->selectRow( + $flags |= ( $flags & self::GAID_FOR_UPDATE ) ? self::READ_LATEST : 0; // b/c + list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags ); + $row = wfGetDB( $index )->selectRow( 'page', self::getSelectFields(), [ 'page_id' => $id ], - __METHOD__ + __METHOD__, + $options ); if ( $row !== false ) { $title = self::newFromRow( $row ); @@ -545,10 +541,10 @@ class Title implements LinkTarget, IDBAccessObject { if ( isset( $row->page_latest ) ) { $this->mLatestID = (int)$row->page_latest; } - if ( !$this->mForcedContentModel && isset( $row->page_content_model ) ) { - $this->mContentModel = (string)$row->page_content_model; - } elseif ( !$this->mForcedContentModel ) { - $this->mContentModel = false; # initialized lazily in getContentModel() + if ( isset( $row->page_content_model ) ) { + $this->lazyFillContentModel( $row->page_content_model ); + } else { + $this->lazyFillContentModel( false ); // lazily-load getContentModel() } if ( isset( $row->page_lang ) ) { $this->mDbPageLanguage = (string)$row->page_lang; @@ -561,9 +557,7 @@ class Title implements LinkTarget, IDBAccessObject { $this->mLength = 0; $this->mRedirect = false; $this->mLatestID = 0; - if ( !$this->mForcedContentModel ) { - $this->mContentModel = false; # initialized lazily in getContentModel() - } + $this->lazyFillContentModel( false ); // lazily-load getContentModel() } } @@ -598,7 +592,6 @@ class Title implements LinkTarget, IDBAccessObject { $t->mArticleID = ( $ns >= 0 ) ? -1 : 0; $t->mUrlform = wfUrlencode( $t->mDbkeyform ); $t->mTextform = strtr( $title, '_', ' ' ); - $t->mContentModel = false; # initialized lazily in getContentModel() return $t; } @@ -676,7 +669,7 @@ class Title implements LinkTarget, IDBAccessObject { * Get the prefixed DB key associated with an ID * * @param int $id The page_id of the article - * @return Title|null An object representing the article, or null if no such article was found + * @return string|null An object representing the article, or null if no such article was found */ public static function nameOf( $id ) { $dbr = wfGetDB( DB_REPLICA ); @@ -691,8 +684,7 @@ class Title implements LinkTarget, IDBAccessObject { return null; } - $n = self::makeName( $s->page_namespace, $s->page_title ); - return $n; + return self::makeName( $s->page_namespace, $s->page_title ); } /** @@ -1051,21 +1043,31 @@ class Title implements LinkTarget, IDBAccessObject { * * @todo Deprecate this in favor of SlotRecord::getModel() * - * @param int $flags A bit field; may be Title::GAID_FOR_UPDATE to select for update + * @param int $flags Either a bitfield of class READ_* constants or GAID_FOR_UPDATE * @return string Content model id */ public function getContentModel( $flags = 0 ) { - if ( !$this->mForcedContentModel - && ( !$this->mContentModel || $flags === self::GAID_FOR_UPDATE ) - && $this->getArticleID( $flags ) + if ( $this->mForcedContentModel ) { + if ( !$this->mContentModel ) { + throw new RuntimeException( 'Got out of sync; an empty model is being forced' ); + } + // Content model is locked to the currently loaded one + return $this->mContentModel; + } + + if ( DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST ) ) { + $this->lazyFillContentModel( $this->loadFieldFromDB( 'page_content_model', $flags ) ); + } elseif ( + ( !$this->mContentModel || $flags & self::GAID_FOR_UPDATE ) && + $this->getArticleID( $flags ) ) { $linkCache = MediaWikiServices::getInstance()->getLinkCache(); $linkCache->addLinkObj( $this ); # in case we already had an article ID - $this->mContentModel = $linkCache->getGoodLinkFieldObj( $this, 'model' ); + $this->lazyFillContentModel( $linkCache->getGoodLinkFieldObj( $this, 'model' ) ); } if ( !$this->mContentModel ) { - $this->mContentModel = ContentHandler::getDefaultModelFor( $this ); + $this->lazyFillContentModel( ContentHandler::getDefaultModelFor( $this ) ); } return $this->mContentModel; @@ -1082,21 +1084,38 @@ class Title implements LinkTarget, IDBAccessObject { } /** - * Set a proposed content model for the page for permissions - * checking. This does not actually change the content model - * of a title! + * Set a proposed content model for the page for permissions checking + * + * This does not actually change the content model of a title in the DB. + * It only affects this particular Title instance. The content model is + * forced to remain this value until another setContentModel() call. * - * Additionally, you should make sure you've checked - * ContentHandler::canBeUsedOn() first. + * ContentHandler::canBeUsedOn() should be checked before calling this + * if there is any doubt regarding the applicability of the content model * * @since 1.28 * @param string $model CONTENT_MODEL_XXX constant */ public function setContentModel( $model ) { + if ( (string)$model === '' ) { + throw new InvalidArgumentException( "Missing CONTENT_MODEL_* constant" ); + } + $this->mContentModel = $model; $this->mForcedContentModel = true; } + /** + * If the content model field is not frozen then update it with a retreived value + * + * @param string|bool $model CONTENT_MODEL_XXX constant or false + */ + private function lazyFillContentModel( $model ) { + if ( !$this->mForcedContentModel ) { + $this->mContentModel = ( $model === false ) ? false : (string)$model; + } + } + /** * Get the namespace text * @@ -1542,14 +1561,32 @@ class Title implements LinkTarget, IDBAccessObject { * Get a Title object associated with the talk page of this article * * @deprecated since 1.34, use getTalkPageIfDefined() or NamespaceInfo::getTalkPage() - * with NamespaceInfo::canHaveTalkPage(). + * with NamespaceInfo::canHaveTalkPage(). Note that the new method will + * throw if asked for the talk page of a section-only link, or of an interwiki + * link. * @return Title The object for the talk page * @throws MWException if $target doesn't have talk pages, e.g. because it's in NS_SPECIAL * or because it's a relative link, or an interwiki link. */ public function getTalkPage() { - return self::castFromLinkTarget( - MediaWikiServices::getInstance()->getNamespaceInfo()->getTalkPage( $this ) ); + // NOTE: The equivalent code in NamespaceInfo is less lenient about producing invalid titles. + // Instead of failing on invalid titles, let's just log the issue for now. + // See the discussion on T227817. + + // Is this the same title? + $talkNS = MediaWikiServices::getInstance()->getNamespaceInfo()->getTalk( $this->mNamespace ); + if ( $this->mNamespace == $talkNS ) { + return $this; + } + + $title = self::makeTitle( $talkNS, $this->mDbkeyform ); + + $this->warnIfPageCannotExist( $title, __METHOD__ ); + + return $title; + // TODO: replace the above with the code below: + // return self::castFromLinkTarget( + // MediaWikiServices::getInstance()->getNamespaceInfo()->getTalkPage( $this ) ); } /** @@ -1577,8 +1614,51 @@ class Title implements LinkTarget, IDBAccessObject { * @return Title The object for the subject page */ public function getSubjectPage() { - return self::castFromLinkTarget( - MediaWikiServices::getInstance()->getNamespaceInfo()->getSubjectPage( $this ) ); + // Is this the same title? + $subjectNS = MediaWikiServices::getInstance()->getNamespaceInfo() + ->getSubject( $this->mNamespace ); + if ( $this->mNamespace == $subjectNS ) { + return $this; + } + // NOTE: The equivalent code in NamespaceInfo is less lenient about producing invalid titles. + // Instead of failing on invalid titles, let's just log the issue for now. + // See the discussion on T227817. + $title = self::makeTitle( $subjectNS, $this->mDbkeyform ); + + $this->warnIfPageCannotExist( $title, __METHOD__ ); + + return $title; + // TODO: replace the above with the code below: + // return self::castFromLinkTarget( + // MediaWikiServices::getInstance()->getNamespaceInfo()->getSubjectPage( $this ) ); + } + + /** + * @param Title $title + * @param string $method + * + * @return bool whether a warning was issued + */ + private function warnIfPageCannotExist( Title $title, $method ) { + if ( $this->getText() == '' ) { + wfLogWarning( + $method . ': called on empty title ' . $this->getFullText() . ', returning ' + . $title->getFullText() + ); + + return true; + } + + if ( $this->getInterwiki() !== '' ) { + wfLogWarning( + $method . ': called on interwiki title ' . $this->getFullText() . ', returning ' + . $title->getFullText() + ); + + return true; + } + + return false; } /** @@ -1591,8 +1671,23 @@ class Title implements LinkTarget, IDBAccessObject { * @return Title */ public function getOtherPage() { - return self::castFromLinkTarget( - MediaWikiServices::getInstance()->getNamespaceInfo()->getAssociatedPage( $this ) ); + // NOTE: Depend on the methods in this class instead of their equivalent in NamespaceInfo, + // until their semantics has become exactly the same. + // See the discussion on T227817. + 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(); + } + // TODO: replace the above with the code below: + // return self::castFromLinkTarget( + // MediaWikiServices::getInstance()->getNamespaceInfo()->getAssociatedPage( $this ) ); } /** @@ -1967,7 +2062,7 @@ class Title implements LinkTarget, IDBAccessObject { * * @see self::getLocalURL for the arguments. * @see wfExpandUrl - * @param string|string[] $query + * @param string|array $query * @param string|string[]|bool $query2 * @param string|int|null $proto Protocol type to use in URL * @return string The URL @@ -2028,7 +2123,7 @@ class Title implements LinkTarget, IDBAccessObject { * valid to link, locally, to the current Title. * @see self::newFromText to produce a Title object. * - * @param string|string[] $query An optional query string, + * @param string|array $query An optional query string, * not used for interwiki links. Can be specified as an associative array as well, * e.g., [ 'action' => 'edit' ] (keys and values will be URL-escaped). * Some query patterns will trigger various shorturl path replacements. @@ -2042,7 +2137,7 @@ class Title implements LinkTarget, IDBAccessObject { * @return string String of the URL. */ public function getLocalURL( $query = '', $query2 = false ) { - global $wgArticlePath, $wgScript, $wgServer, $wgRequest; + global $wgArticlePath, $wgScript, $wgServer, $wgRequest, $wgMainPageIsDomainRoot; $query = self::fixUrlQueryArgs( $query, $query2 ); @@ -2068,16 +2163,18 @@ class Title implements LinkTarget, IDBAccessObject { $url = false; $matches = []; - if ( !empty( $wgActionPaths ) + $articlePaths = PathRouter::getActionPaths( $wgActionPaths, $wgArticlePath ); + + if ( $articlePaths && preg_match( '/^(.*&|)action=([^&]*)(&(.*)|)$/', $query, $matches ) ) { $action = urldecode( $matches[2] ); - if ( isset( $wgActionPaths[$action] ) ) { + if ( isset( $articlePaths[$action] ) ) { $query = $matches[1]; if ( isset( $matches[4] ) ) { $query .= $matches[4]; } - $url = str_replace( '$1', $dbkey, $wgActionPaths[$action] ); + $url = str_replace( '$1', $dbkey, $articlePaths[$action] ); if ( $query != '' ) { $url = wfAppendQuery( $url, $query ); } @@ -2117,6 +2214,11 @@ class Title implements LinkTarget, IDBAccessObject { $url = $wgServer . $url; } } + + if ( $wgMainPageIsDomainRoot && $this->isMainPage() && $query === '' ) { + return '/'; + } + // Avoid PHP 7.1 warning from passing $this by reference $titleRef = $this; Hooks::run( 'GetLocalURL', [ &$titleRef, &$url, $query ] ); @@ -2161,7 +2263,7 @@ class Title implements LinkTarget, IDBAccessObject { * protocol-relative, the URL will be expanded to http:// * * @see self::getLocalURL for the arguments. - * @param string|string[] $query + * @param string|array $query * @param string|bool $query2 Deprecated * @return string The URL */ @@ -2184,7 +2286,7 @@ class Title implements LinkTarget, IDBAccessObject { * NOTE: Unlike getInternalURL(), the canonical URL includes the fragment * * @see self::getLocalURL for the arguments. - * @param string|string[] $query + * @param string|array $query * @param string|bool $query2 Deprecated * @return string The URL * @since 1.18 @@ -2796,10 +2898,7 @@ class Title implements LinkTarget, IDBAccessObject { return; } - // TODO: should probably pass $flags into getArticleID, but it seems hacky - // to mix READ_LATEST and GAID_FOR_UPDATE, even if they have the same value. - // Maybe deprecate GAID_FOR_UPDATE now that we implement IDBAccessObject? - $id = $this->getArticleID(); + $id = $this->getArticleID( $flags ); if ( $id ) { $fname = __METHOD__; $loadRestrictionsFromDb = function ( IDatabase $dbr ) use ( $fname, $id ) { @@ -3023,24 +3122,28 @@ class Title implements LinkTarget, IDBAccessObject { * Get the article ID for this Title from the link cache, * adding it if necessary * - * @param int $flags A bit field; may be Title::GAID_FOR_UPDATE to select - * for update + * @param int $flags Either a bitfield of class READ_* constants or GAID_FOR_UPDATE * @return int The ID */ public function getArticleID( $flags = 0 ) { if ( $this->mNamespace < 0 ) { $this->mArticleID = 0; + return $this->mArticleID; } + $linkCache = MediaWikiServices::getInstance()->getLinkCache(); if ( $flags & self::GAID_FOR_UPDATE ) { $oldUpdate = $linkCache->forUpdate( true ); $linkCache->clearLink( $this ); $this->mArticleID = $linkCache->addLinkObj( $this ); $linkCache->forUpdate( $oldUpdate ); + } elseif ( DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST ) ) { + $this->mArticleID = (int)$this->loadFieldFromDB( 'page_id', $flags ); } elseif ( $this->mArticleID == -1 ) { $this->mArticleID = $linkCache->addLinkObj( $this ); } + return $this->mArticleID; } @@ -3048,33 +3151,27 @@ class Title implements LinkTarget, IDBAccessObject { * Is this an article that is a redirect page? * Uses link cache, adding it if necessary * - * @param int $flags A bit field; may be Title::GAID_FOR_UPDATE to select for update + * @param int $flags Either a bitfield of class READ_* constants or GAID_FOR_UPDATE * @return bool */ public function isRedirect( $flags = 0 ) { - if ( !is_null( $this->mRedirect ) ) { - return $this->mRedirect; - } - if ( !$this->getArticleID( $flags ) ) { - $this->mRedirect = false; - return $this->mRedirect; - } + if ( DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST ) ) { + $this->mRedirect = (bool)$this->loadFieldFromDB( 'page_is_redirect', $flags ); + } else { + if ( $this->mRedirect !== null ) { + return $this->mRedirect; + } elseif ( !$this->getArticleID( $flags ) ) { + $this->mRedirect = false; - $linkCache = MediaWikiServices::getInstance()->getLinkCache(); - $linkCache->addLinkObj( $this ); # in case we already had an article ID - $cached = $linkCache->getGoodLinkFieldObj( $this, 'redirect' ); - if ( $cached === null ) { - # Trust LinkCache's state over our own - # LinkCache is telling us that the page doesn't exist, despite there being cached - # data relating to an existing page in $this->mArticleID. Updaters should clear - # LinkCache as appropriate, or use $flags = Title::GAID_FOR_UPDATE. If that flag is - # set, then LinkCache will definitely be up to date here, since getArticleID() forces - # LinkCache to refresh its data from the master. - $this->mRedirect = false; - return $this->mRedirect; - } + return $this->mRedirect; + } - $this->mRedirect = (bool)$cached; + $linkCache = MediaWikiServices::getInstance()->getLinkCache(); + $linkCache->addLinkObj( $this ); // in case we already had an article ID + // Note that LinkCache returns null if it thinks the page does not exist; + // always trust the state of LinkCache over that of this Title instance. + $this->mRedirect = (bool)$linkCache->getGoodLinkFieldObj( $this, 'redirect' ); + } return $this->mRedirect; } @@ -3083,27 +3180,26 @@ class Title implements LinkTarget, IDBAccessObject { * What is the length of this page? * Uses link cache, adding it if necessary * - * @param int $flags A bit field; may be Title::GAID_FOR_UPDATE to select for update + * @param int $flags Either a bitfield of class READ_* constants or GAID_FOR_UPDATE * @return int */ public function getLength( $flags = 0 ) { - if ( $this->mLength != -1 ) { - return $this->mLength; - } - if ( !$this->getArticleID( $flags ) ) { - $this->mLength = 0; - return $this->mLength; - } - $linkCache = MediaWikiServices::getInstance()->getLinkCache(); - $linkCache->addLinkObj( $this ); # in case we already had an article ID - $cached = $linkCache->getGoodLinkFieldObj( $this, 'length' ); - if ( $cached === null ) { - # Trust LinkCache's state over our own, as for isRedirect() - $this->mLength = 0; - return $this->mLength; - } + if ( DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST ) ) { + $this->mLength = (int)$this->loadFieldFromDB( 'page_len', $flags ); + } else { + if ( $this->mLength != -1 ) { + return $this->mLength; + } elseif ( !$this->getArticleID( $flags ) ) { + $this->mLength = 0; + return $this->mLength; + } - $this->mLength = intval( $cached ); + $linkCache = MediaWikiServices::getInstance()->getLinkCache(); + $linkCache->addLinkObj( $this ); // in case we already had an article ID + // Note that LinkCache returns null if it thinks the page does not exist; + // always trust the state of LinkCache over that of this Title instance. + $this->mLength = (int)$linkCache->getGoodLinkFieldObj( $this, 'length' ); + } return $this->mLength; } @@ -3111,49 +3207,46 @@ class Title implements LinkTarget, IDBAccessObject { /** * What is the page_latest field for this page? * - * @param int $flags A bit field; may be Title::GAID_FOR_UPDATE to select for update + * @param int $flags Either a bitfield of class READ_* constants or GAID_FOR_UPDATE * @return int Int or 0 if the page doesn't exist */ public function getLatestRevID( $flags = 0 ) { - if ( !( $flags & self::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) { - return intval( $this->mLatestID ); - } - if ( !$this->getArticleID( $flags ) ) { - $this->mLatestID = 0; - return $this->mLatestID; - } - $linkCache = MediaWikiServices::getInstance()->getLinkCache(); - $linkCache->addLinkObj( $this ); # in case we already had an article ID - $cached = $linkCache->getGoodLinkFieldObj( $this, 'revision' ); - if ( $cached === null ) { - # Trust LinkCache's state over our own, as for isRedirect() - $this->mLatestID = 0; - return $this->mLatestID; - } + if ( DBAccessObjectUtils::hasFlags( $flags, self::READ_LATEST ) ) { + $this->mLatestID = (int)$this->loadFieldFromDB( 'page_latest', $flags ); + } else { + if ( $this->mLatestID !== false ) { + return (int)$this->mLatestID; + } elseif ( !$this->getArticleID( $flags ) ) { + $this->mLatestID = 0; + + return $this->mLatestID; + } - $this->mLatestID = intval( $cached ); + $linkCache = MediaWikiServices::getInstance()->getLinkCache(); + $linkCache->addLinkObj( $this ); // in case we already had an article ID + // Note that LinkCache returns null if it thinks the page does not exist; + // always trust the state of LinkCache over that of this Title instance. + $this->mLatestID = (int)$linkCache->getGoodLinkFieldObj( $this, 'revision' ); + } return $this->mLatestID; } /** - * This clears some fields in this object, and clears any associated - * keys in the "bad links" section of the link cache. + * Inject a page ID, reset DB-loaded fields, and clear the link cache for this title + * + * This can be called on page insertion to allow loading of the new page_id without + * having to create a new Title instance. Likewise with deletion. * - * - This is called from WikiPage::doEditContent() and WikiPage::insertOn() to allow - * loading of the new page_id. It's also called from - * WikiPage::doDeleteArticleReal() + * @note This overrides Title::setContentModel() * - * @param int $newid The new Article ID + * @param int|bool $id Page ID, 0 for non-existant, or false for "unknown" (lazy-load) */ - public function resetArticleID( $newid ) { - $linkCache = MediaWikiServices::getInstance()->getLinkCache(); - $linkCache->clearLink( $this ); - - if ( $newid === false ) { + public function resetArticleID( $id ) { + if ( $id === false ) { $this->mArticleID = -1; } else { - $this->mArticleID = intval( $newid ); + $this->mArticleID = (int)$id; } $this->mRestrictionsLoaded = false; $this->mRestrictions = []; @@ -3162,10 +3255,13 @@ class Title implements LinkTarget, IDBAccessObject { $this->mLength = -1; $this->mLatestID = false; $this->mContentModel = false; + $this->mForcedContentModel = false; $this->mEstimateRevisions = null; $this->mPageLanguage = null; $this->mDbPageLanguage = false; $this->mIsBigDeletion = null; + + MediaWikiServices::getInstance()->getLinkCache()->clearLink( $this ); } public static function clearCaches() { @@ -3499,6 +3595,7 @@ class Title implements LinkTarget, IDBAccessObject { $mp = MediaWikiServices::getInstance()->getMovePageFactory()->newMovePage( $this, $nt ); $method = $auth ? 'moveIfAllowed' : 'move'; + /** @var Status $status */ $status = $mp->$method( $wgUser, $reason, $createRedirect, $changeTags ); if ( $status->isOK() ) { return true; @@ -3531,6 +3628,7 @@ class Title implements LinkTarget, IDBAccessObject { $mp = new MovePage( $this, $nt ); $method = $auth ? 'moveSubpagesIfAllowed' : 'moveSubpages'; + /** @var Status $result */ $result = $mp->$method( $wgUser, $reason, $createRedirect, $changeTags ); if ( !$result->isOK() ) { @@ -3539,6 +3637,7 @@ class Title implements LinkTarget, IDBAccessObject { $retval = []; foreach ( $result->getValue() as $key => $status ) { + /** @var Status $status */ if ( $status->isOK() ) { $retval[$key] = $status->getValue(); } else { @@ -3549,8 +3648,9 @@ class Title implements LinkTarget, IDBAccessObject { } /** - * Checks if this page is just a one-rev redirect. - * Adds lock, so don't use just for light purposes. + * Locks the page row and check if this page is single revision redirect + * + * This updates the cached fields of this instance via Title::loadFromRow() * * @return bool */ @@ -3730,24 +3830,22 @@ class Title implements LinkTarget, IDBAccessObject { /** * Get next/previous revision ID relative to another revision ID * @param int $revId Revision ID. Get the revision that was before this one. - * @param int $flags Title::GAID_FOR_UPDATE + * @param int $flags Bitfield of class READ_* constants * @param string $dir 'next' or 'prev' * @return int|bool New revision ID, or false if none exists */ private function getRelativeRevisionID( $revId, $flags, $dir ) { $rl = MediaWikiServices::getInstance()->getRevisionLookup(); - $rlFlags = $flags === self::GAID_FOR_UPDATE ? IDBAccessObject::READ_LATEST : 0; - $rev = $rl->getRevisionById( $revId, $rlFlags ); + $rev = $rl->getRevisionById( $revId, $flags ); if ( !$rev ) { return false; } - $oldRev = $dir === 'next' - ? $rl->getNextRevision( $rev, $rlFlags ) - : $rl->getPreviousRevision( $rev, $rlFlags ); - if ( !$oldRev ) { - return false; - } - return $oldRev->getId(); + + $oldRev = ( $dir === 'next' ) + ? $rl->getNextRevision( $rev, $flags ) + : $rl->getPreviousRevision( $rev, $flags ); + + return $oldRev ? $oldRev->getId() : false; } /** @@ -3755,7 +3853,7 @@ class Title implements LinkTarget, IDBAccessObject { * * @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 + * @param int $flags Bitfield of class READ_* constants * @return int|bool Old revision ID, or false if none exists */ public function getPreviousRevisionID( $revId, $flags = 0 ) { @@ -3767,7 +3865,7 @@ class Title implements LinkTarget, IDBAccessObject { * * @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 + * @param int $flags Bitfield of class READ_* constants * @return int|bool Next revision ID, or false if none exists */ public function getNextRevisionID( $revId, $flags = 0 ) { @@ -3777,21 +3875,26 @@ class Title implements LinkTarget, IDBAccessObject { /** * Get the first revision of the page * - * @param int $flags Title::GAID_FOR_UPDATE + * @param int $flags Bitfield of class READ_* constants * @return Revision|null If page doesn't exist */ public function getFirstRevision( $flags = 0 ) { $pageId = $this->getArticleID( $flags ); if ( $pageId ) { - $db = ( $flags & self::GAID_FOR_UPDATE ) ? wfGetDB( DB_MASTER ) : wfGetDB( DB_REPLICA ); + $flags |= ( $flags & self::GAID_FOR_UPDATE ) ? self::READ_LATEST : 0; // b/c + list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags ); $revQuery = Revision::getQueryInfo(); - $row = $db->selectRow( $revQuery['tables'], $revQuery['fields'], + $row = wfGetDB( $index )->selectRow( + $revQuery['tables'], $revQuery['fields'], [ 'rev_page' => $pageId ], __METHOD__, - [ - 'ORDER BY' => 'rev_timestamp ASC, rev_id ASC', - 'IGNORE INDEX' => [ 'revision' => 'rev_timestamp' ], // See T159319 - ], + array_merge( + [ + 'ORDER BY' => 'rev_timestamp ASC, rev_id ASC', + 'IGNORE INDEX' => [ 'revision' => 'rev_timestamp' ], // See T159319 + ], + $options + ), $revQuery['joins'] ); if ( $row ) { @@ -3804,7 +3907,7 @@ class Title implements LinkTarget, IDBAccessObject { /** * Get the oldest revision timestamp of this page * - * @param int $flags Title::GAID_FOR_UPDATE + * @param int $flags Bitfield of class READ_* constants * @return string|null MW timestamp */ public function getEarliestRevTime( $flags = 0 ) { @@ -4035,8 +4138,7 @@ class Title implements LinkTarget, IDBAccessObject { * If you want to know if a title can be meaningfully viewed, you should * probably call the isKnown() method instead. * - * @param int $flags An optional bit field; may be Title::GAID_FOR_UPDATE to check - * from master/for update + * @param int $flags Either a bitfield of class READ_* constants or GAID_FOR_UPDATE * @return bool */ public function exists( $flags = 0 ) { @@ -4249,12 +4351,21 @@ class Title implements LinkTarget, IDBAccessObject { * on the number of links. Typically called on create and delete. */ public function touchLinks() { - DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this, 'pagelinks', 'page-touch' ) ); + $jobs = []; + $jobs[] = HTMLCacheUpdateJob::newForBacklinks( + $this, + 'pagelinks', + [ 'causeAction' => 'page-touch' ] + ); if ( $this->mNamespace == NS_CATEGORY ) { - DeferredUpdates::addUpdate( - new HTMLCacheUpdate( $this, 'categorylinks', 'category-touch' ) + $jobs[] = HTMLCacheUpdateJob::newForBacklinks( + $this, + 'categorylinks', + [ 'causeAction' => 'category-touch' ] ); } + + JobQueueGroup::singleton()->lazyPush( $jobs ); } /** @@ -4632,6 +4743,27 @@ class Title implements LinkTarget, IDBAccessObject { return $notices; } + /** + * @param int $flags Bitfield of class READ_* constants + * @return string|bool + */ + private function loadFieldFromDB( $field, $flags ) { + if ( !in_array( $field, self::getSelectFields(), true ) ) { + return false; // field does not exist + } + + $flags |= ( $flags & self::GAID_FOR_UPDATE ) ? self::READ_LATEST : 0; // b/c + list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags ); + + return wfGetDB( $index )->selectField( + 'page', + $field, + $this->pageCond(), + __METHOD__, + $options + ); + } + /** * @return array */