Merge "Make Title::getFirstRevision() ignore the rev_timestamp index"
[lhc/web/wiklou.git] / includes / page / WikiPage.php
index f760cd1..f2f5781 100644 (file)
@@ -22,6 +22,8 @@
 
 use \MediaWiki\Logger\LoggerFactory;
 use \MediaWiki\MediaWikiServices;
+use Wikimedia\Rdbms\FakeResultWrapper;
+use Wikimedia\Rdbms\IDatabase;
 
 /**
  * Class representing a MediaWiki article and history.
@@ -83,9 +85,10 @@ class WikiPage implements Page, IDBAccessObject {
         */
        protected $mLinksUpdated = '19700101000000';
 
-       const PURGE_CDN_CACHE = 1; // purge CDN cache for page variant URLs
-       const PURGE_CLUSTER_PCACHE = 2; // purge parser cache in the local datacenter
-       const PURGE_GLOBAL_PCACHE = 4; // set page_touched to clear parser cache in all datacenters
+       /** @deprecated since 1.29. Added in 1.28 for partial purging, no longer used. */
+       const PURGE_CDN_CACHE = 1;
+       const PURGE_CLUSTER_PCACHE = 2;
+       const PURGE_GLOBAL_PCACHE = 4;
        const PURGE_ALL = 7;
 
        /**
@@ -151,7 +154,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @return WikiPage|null
         */
        public static function newFromID( $id, $from = 'fromdb' ) {
-               // page id's are never 0 or negative, see bug 61166
+               // page ids are never 0 or negative, see T63166
                if ( $id < 1 ) {
                        return null;
                }
@@ -257,7 +260,7 @@ class WikiPage implements Page, IDBAccessObject {
                $this->mTimestamp = '';
                $this->mIsRedirect = false;
                $this->mLatest = false;
-               // Bug 57026: do not clear mPreparedEdit since prepareTextForEdit() already checks
+               // T59026: do not clear mPreparedEdit since prepareTextForEdit() already checks
                // the requested rev ID and content against the cached one for equality. For most
                // content types, the output should not change during the lifetime of this cache.
                // Clearing it can cause extra parses on edit for no reason.
@@ -323,7 +326,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                $row = $dbr->selectRow( 'page', $fields, $conditions, __METHOD__, $options );
 
-               Hooks::run( 'ArticlePageDataAfter', [ &$this, &$row ] );
+               Hooks::run( 'ArticlePageDataAfter', [ &$wikiPage, &$row ] );
 
                return $row;
        }
@@ -424,7 +427,7 @@ class WikiPage implements Page, IDBAccessObject {
                        $this->mLinksUpdated = wfTimestampOrNull( TS_MW, $data->page_links_updated );
                        $this->mIsRedirect = intval( $data->page_is_redirect );
                        $this->mLatest = intval( $data->page_latest );
-                       // Bug 37225: $latest may no longer match the cached latest Revision object.
+                       // T39225: $latest may no longer match the cached latest Revision object.
                        // Double-check the ID of any cached latest Revision object for consistency.
                        if ( $this->mLastRevision && $this->mLastRevision->getId() != $this->mLatest ) {
                                $this->mLastRevision = null;
@@ -582,16 +585,15 @@ class WikiPage implements Page, IDBAccessObject {
                $row = null;
                while ( $continue ) {
                        $row = $db->selectRow(
-                               [ 'page', 'revision' ],
+                               [ 'revision' ],
                                $revSelectFields,
                                [
-                                       'page_namespace' => $this->mTitle->getNamespace(),
-                                       'page_title' => $this->mTitle->getDBkey(),
-                                       'rev_page = page_id'
+                                       'rev_page' => $this->getId()
                                ],
                                __METHOD__,
                                [
-                                       'ORDER BY' => 'rev_timestamp ASC'
+                                       'ORDER BY' => 'rev_timestamp ASC',
+                                       'IGNORE INDEX' => 'rev_timestamp'
                                ]
                        );
 
@@ -621,7 +623,7 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                if ( $this->mDataLoadedFrom == self::READ_LOCKING ) {
-                       // Bug 37225: if session S1 loads the page row FOR UPDATE, the result always
+                       // T39225: if session S1 loads the page row FOR UPDATE, the result always
                        // includes the latest changes committed. This is true even within REPEATABLE-READ
                        // transactions, where S1 normally only sees changes committed before the first S1
                        // SELECT. Thus we need S1 to also gets the revision row FOR UPDATE; otherwise, it
@@ -1120,10 +1122,11 @@ class WikiPage implements Page, IDBAccessObject {
 
        /**
         * Perform the actions of a page purging
-        * @param integer $flags Bitfield of WikiPage::PURGE_* constants
         * @return bool
+        * @note In 1.28 (and only 1.28), this took a $flags parameter that
+        *  controlled how much purging was done.
         */
-       public function doPurge( $flags = self::PURGE_ALL ) {
+       public function doPurge() {
                // Avoid PHP 7.1 warning of passing $this by reference
                $wikiPage = $this;
 
@@ -1131,30 +1134,15 @@ class WikiPage implements Page, IDBAccessObject {
                        return false;
                }
 
-               if ( ( $flags & self::PURGE_GLOBAL_PCACHE ) == self::PURGE_GLOBAL_PCACHE ) {
-                       // Set page_touched in the database to invalidate all DC caches
-                       $this->mTitle->invalidateCache();
-               } elseif ( ( $flags & self::PURGE_CLUSTER_PCACHE ) == self::PURGE_CLUSTER_PCACHE ) {
-                       // Delete the parser options key in the local cluster to invalidate the DC cache
-                       ParserCache::singleton()->deleteOptionsKey( $this );
-                       // Avoid sending HTTP 304s in ViewAction to the client who just issued the purge
-                       $cache = ObjectCache::getLocalClusterInstance();
-                       $cache->set(
-                               $cache->makeKey( 'page', 'last-dc-purge', $this->getId() ),
-                               wfTimestamp( TS_MW ),
-                               $cache::TTL_HOUR
-                       );
-               }
+               $this->mTitle->invalidateCache();
 
-               if ( ( $flags & self::PURGE_CDN_CACHE ) == self::PURGE_CDN_CACHE ) {
-                       // Clear any HTML file cache
-                       HTMLFileCache::clearFileCache( $this->getTitle() );
-                       // Send purge after any page_touched above update was committed
-                       DeferredUpdates::addUpdate(
-                               new CdnCacheUpdate( $this->mTitle->getCdnUrls() ),
-                               DeferredUpdates::PRESEND
-                       );
-               }
+               // Clear file cache
+               HTMLFileCache::clearFileCache( $this->getTitle() );
+               // Send purge after above page_touched update was committed
+               DeferredUpdates::addUpdate(
+                       new CdnCacheUpdate( $this->mTitle->getCdnUrls() ),
+                       DeferredUpdates::PRESEND
+               );
 
                if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
                        $messageCache = MessageCache::singleton();
@@ -1169,11 +1157,11 @@ class WikiPage implements Page, IDBAccessObject {
         *
         * @return string|bool TS_MW timestamp or false
         * @since 1.28
+        * @deprecated since 1.29. It will always return false.
         */
        public function getLastPurgeTimestamp() {
-               $cache = ObjectCache::getLocalClusterInstance();
-
-               return $cache->get( $cache->makeKey( 'page', 'last-dc-purge', $this->getId() ) );
+               wfDeprecated( __METHOD__, '1.29' );
+               return false;
        }
 
        /**
@@ -1462,7 +1450,7 @@ class WikiPage implements Page, IDBAccessObject {
                                        $this->getContentHandler()->getModelID() );
                        }
 
-                       // Bug 30711: always use current version when adding a new section
+                       // T32711: always use current version when adding a new section
                        if ( is_null( $baseRevId ) || $sectionId === 'new' ) {
                                $oldContent = $this->getContent();
                        } else {
@@ -1697,7 +1685,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                        return $status;
                } elseif ( !$oldContent ) {
-                       // Sanity check for bug 37225
+                       // Sanity check for T39225
                        throw new MWException( "Could not find text for current revision {$oldid}." );
                }
 
@@ -1785,7 +1773,7 @@ class WikiPage implements Page, IDBAccessObject {
                        $dbw->endAtomic( __METHOD__ );
                        $this->mTimestamp = $now;
                } else {
-                       // Bug 32948: revision ID must be set to page {{REVISIONID}} and
+                       // T34948: revision ID must be set to page {{REVISIONID}} and
                        // related variables correctly. Likewise for {{REVISIONUSER}} (T135261).
                        $revision->setId( $this->getLatest() );
                        $revision->setUserIdAndName(
@@ -2025,7 +2013,7 @@ class WikiPage implements Page, IDBAccessObject {
                $user = is_null( $user ) ? $wgUser : $user;
                // XXX: check $user->getId() here???
 
-               // Use a sane default for $serialFormat, see bug 57026
+               // Use a sane default for $serialFormat, see T59026
                if ( $serialFormat === null ) {
                        $serialFormat = $content->getContentHandler()->getDefaultFormat();
                }
@@ -2088,8 +2076,12 @@ class WikiPage implements Page, IDBAccessObject {
                                );
                        } else {
                                // Try to avoid a second parse if {{REVISIONID}} is used
-                               $edit->popts->setSpeculativeRevIdCallback( function () {
-                                       return 1 + (int)wfGetDB( DB_MASTER )->selectField(
+                               $dbIndex = ( $this->mDataLoadedFrom & self::READ_LATEST ) === self::READ_LATEST
+                                       ? DB_MASTER // use the best possible guess
+                                       : DB_REPLICA; // T154554
+
+                               $edit->popts->setSpeculativeRevIdCallback( function () use ( $dbIndex ) {
+                                       return 1 + (int)wfGetDB( $dbIndex )->selectField(
                                                'revision',
                                                'MAX(rev_id)',
                                                [],
@@ -2193,7 +2185,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                // Update the links tables and other secondary data
                if ( $content ) {
-                       $recursive = $options['changed']; // bug 50785
+                       $recursive = $options['changed']; // T52785
                        $updates = $content->getSecondaryDataUpdates(
                                $this->getTitle(), null, $recursive, $editInfo->output
                        );
@@ -2295,7 +2287,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                if ( $options['created'] ) {
                        self::onArticleCreate( $this->mTitle );
-               } elseif ( $options['changed'] ) { // bug 50785
+               } elseif ( $options['changed'] ) { // T52785
                        self::onArticleEdit( $this->mTitle, $revision );
                }
 
@@ -2909,7 +2901,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                $dbw->onTransactionPreCommitOrIdle(
                        function () use ( $dbw, $logEntry, $logid ) {
-                               // Bug 56776: avoid deadlocks (especially from FileDeleteForm)
+                               // T58776: avoid deadlocks (especially from FileDeleteForm)
                                $logEntry->publish( $logid );
                        },
                        __METHOD__
@@ -3189,7 +3181,7 @@ class WikiPage implements Page, IDBAccessObject {
                );
 
                // Set patrolling and bot flag on the edits, which gets rollbacked.
-               // This is done even on edit failure to have patrolling in that case (bug 62157).
+               // This is done even on edit failure to have patrolling in that case (T64157).
                $set = [];
                if ( $bot && $guser->isAllowed( 'markbotedits' ) ) {
                        // Mark all reverted edits as bot
@@ -3648,4 +3640,15 @@ class WikiPage implements Page, IDBAccessObject {
        public function getSourceURL() {
                return $this->getTitle()->getCanonicalURL();
        }
+
+       /*
+        * @param WANObjectCache $cache
+        * @return string[]
+        * @since 1.28
+        */
+       public function getMutableCacheKeys( WANObjectCache $cache ) {
+               $linkCache = MediaWikiServices::getInstance()->getLinkCache();
+
+               return $linkCache->getMutableCacheKeys( $cache, $this->getTitle()->getTitleValue() );
+       }
 }