Revert "Log the reason why revision->getContent() returns null"
[lhc/web/wiklou.git] / includes / Revision.php
index ed0646a..510c1ee 100644 (file)
@@ -65,10 +65,14 @@ class Revision implements IDBAccessObject {
        }
 
        /**
+        * @param bool|string $wiki The ID of the target wiki database. Use false for the local wiki.
+        *
         * @return SqlBlobStore
         */
-       protected static function getBlobStore() {
-               $store = MediaWikiServices::getInstance()->getBlobStore();
+       protected static function getBlobStore( $wiki = false ) {
+               $store = MediaWikiServices::getInstance()
+                       ->getBlobStoreFactory()
+                       ->newSqlBlobStore( $wiki );
 
                if ( !$store instanceof SqlBlobStore ) {
                        throw new RuntimeException(
@@ -90,12 +94,11 @@ class Revision implements IDBAccessObject {
         *
         * @param int $id
         * @param int $flags (optional)
-        * @param Title $title (optional)
         * @return Revision|null
         */
-       public static function newFromId( $id, $flags = 0, Title $title = null ) {
-               $rec = self::getRevisionStore()->getRevisionById( $id, $flags, $title );
-               return $rec === null ? null : new Revision( $rec, $flags, $title );
+       public static function newFromId( $id, $flags = 0 ) {
+               $rec = self::getRevisionStore()->getRevisionById( $id, $flags );
+               return $rec === null ? null : new Revision( $rec, $flags );
        }
 
        /**
@@ -142,12 +145,11 @@ class Revision implements IDBAccessObject {
         *
         * @param object $row
         * @param array $overrides
-        * @param Title $title (optional)
         *
         * @throws MWException
         * @return Revision
         */
-       public static function newFromArchiveRow( $row, $overrides = [], Title $title = null ) {
+       public static function newFromArchiveRow( $row, $overrides = [] ) {
                /**
                 * MCR Migration: https://phabricator.wikimedia.org/T183564
                 * This method used to overwrite attributes, then passed to Revision::__construct
@@ -159,6 +161,29 @@ class Revision implements IDBAccessObject {
                        unset( $overrides['page'] );
                }
 
+               /**
+                * We require a Title for both the Revision object and the RevisionRecord.
+                * Below is duplicated logic from RevisionStore::newRevisionFromArchiveRow
+                * to fetch a title in order pass it into the Revision object.
+                */
+               $title = null;
+               if ( isset( $overrides['title'] ) ) {
+                       if ( !( $overrides['title'] instanceof Title ) ) {
+                               throw new MWException( 'title field override must contain a Title object.' );
+                       }
+
+                       $title = $overrides['title'];
+               }
+               if ( $title !== null ) {
+                       if ( isset( $row->ar_namespace ) && isset( $row->ar_title ) ) {
+                               $title = Title::makeTitle( $row->ar_namespace, $row->ar_title );
+                       } else {
+                               throw new InvalidArgumentException(
+                                       'A Title or ar_namespace and ar_title must be given'
+                               );
+                       }
+               }
+
                $rec = self::getRevisionStore()->newRevisionFromArchiveRow( $row, 0, $title, $overrides );
                return new Revision( $rec, self::READ_NORMAL, $title );
        }
@@ -168,8 +193,9 @@ class Revision implements IDBAccessObject {
         *
         * MCR migration note: replaced by RevisionStore::newRevisionFromRow(). Note that
         * newFromRow() also accepts arrays, while newRevisionFromRow() does not. Instead,
-        * a MutableRevisionRecord should be constructed directly. RevisionStore::newRevisionFromArray()
-        * can be used as a temporary replacement, but should be avoided.
+        * a MutableRevisionRecord should be constructed directly.
+        * RevisionStore::newMutableRevisionFromArray() can be used as a temporary replacement,
+        * but should be avoided.
         *
         * @param object|array $row
         * @return Revision
@@ -239,7 +265,8 @@ class Revision implements IDBAccessObject {
         * WARNING: Timestamps may in some circumstances not be unique,
         * so this isn't the best key to use.
         *
-        * @deprecated since 1.31, use RevisionStore::loadRevisionFromTimestamp() instead.
+        * @deprecated since 1.31, use RevisionStore::getRevisionByTimestamp()
+        *   or RevisionStore::loadRevisionFromTimestamp() instead.
         *
         * @param IDatabase $db
         * @param Title $title
@@ -247,7 +274,6 @@ class Revision implements IDBAccessObject {
         * @return Revision|null
         */
        public static function loadFromTimestamp( $db, $title, $timestamp ) {
-               // XXX: replace loadRevisionFromTimestamp by getRevisionByTimestamp?
                $rec = self::getRevisionStore()->loadRevisionFromTimestamp( $db, $title, $timestamp );
                return $rec === null ? null : new Revision( $rec );
        }
@@ -436,6 +462,9 @@ class Revision implements IDBAccessObject {
 
        /**
         * Do a batched query to get the parent revision lengths
+        *
+        * @deprecated in 1.31, use RevisionStore::getRevisionSizes instead.
+        *
         * @param IDatabase $db
         * @param array $revIds
         * @return array
@@ -457,6 +486,8 @@ class Revision implements IDBAccessObject {
                if ( $row instanceof RevisionRecord ) {
                        $this->mRecord = $row;
                } elseif ( is_array( $row ) ) {
+                       // If no user is specified, fall back to using the global user object, to stay
+                       // compatible with pre-1.31 behavior.
                        if ( !isset( $row['user'] ) && !isset( $row['user_text'] ) ) {
                                $row['user'] = $wgUser;
                        }
@@ -464,13 +495,13 @@ class Revision implements IDBAccessObject {
                        $this->mRecord = self::getRevisionStore()->newMutableRevisionFromArray(
                                $row,
                                $queryFlags,
-                               $title
+                               $this->ensureTitle( $row, $queryFlags, $title )
                        );
                } elseif ( is_object( $row ) ) {
                        $this->mRecord = self::getRevisionStore()->newRevisionFromRow(
                                $row,
                                $queryFlags,
-                               $title
+                               $this->ensureTitle( $row, $queryFlags, $title )
                        );
                } else {
                        throw new InvalidArgumentException(
@@ -479,6 +510,51 @@ class Revision implements IDBAccessObject {
                }
        }
 
+       /**
+        * Make sure we have *some* Title object for use by the constructor.
+        * For B/C, the constructor shouldn't fail even for a bad page ID or bad revision ID.
+        *
+        * @param array|object $row
+        * @param int $queryFlags
+        * @param Title|null $title
+        *
+        * @return Title $title if not null, or a Title constructed from information in $row.
+        */
+       private function ensureTitle( $row, $queryFlags, $title = null ) {
+               if ( $title ) {
+                       return $title;
+               }
+
+               if ( is_array( $row ) ) {
+                       if ( isset( $row['title'] ) ) {
+                               if ( !( $row['title'] instanceof Title ) ) {
+                                       throw new MWException( 'title field must contain a Title object.' );
+                               }
+
+                               return $row['title'];
+                       }
+
+                       $pageId = isset( $row['page'] ) ? $row['page'] : 0;
+                       $revId = isset( $row['id'] ) ? $row['id'] : 0;
+               } else {
+                       $pageId = isset( $row->rev_page ) ? $row->rev_page : 0;
+                       $revId = isset( $row->rev_id ) ? $row->rev_id : 0;
+               }
+
+               try {
+                       $title = self::getRevisionStore()->getTitle( $pageId, $revId, $queryFlags );
+               } catch ( RevisionAccessException $ex ) {
+                       // construct a dummy title!
+                       wfLogWarning( __METHOD__ . ': ' . $ex->getMessage() );
+
+                       // NOTE: this Title will only be used inside RevisionRecord
+                       $title = Title::makeTitleSafe( NS_SPECIAL, "Badtitle/ID=$pageId" );
+                       $title->resetArticleID( $pageId );
+               }
+
+               return $title;
+       }
+
        /**
         * @return RevisionRecord
         */
@@ -577,20 +653,27 @@ class Revision implements IDBAccessObject {
        /**
         * Returns the length of the text in this revision, or null if unknown.
         *
-        * @return int
+        * @return int|null
         */
        public function getSize() {
-               return $this->mRecord->getSize();
+               try {
+                       return $this->mRecord->getSize();
+               } catch ( RevisionAccessException $ex ) {
+                       return null;
+               }
        }
 
        /**
         * Returns the base36 sha1 of the content in this revision, or null if unknown.
         *
-        * @return string
+        * @return string|null
         */
        public function getSha1() {
-               // XXX: we may want to drop all the hashing logic, it's not worth the overhead.
-               return $this->mRecord->getSha1();
+               try {
+                       return $this->mRecord->getSha1();
+               } catch ( RevisionAccessException $ex ) {
+                       return null;
+               }
        }
 
        /**
@@ -748,7 +831,7 @@ class Revision implements IDBAccessObject {
         * @return int Rcid of the unpatrolled row, zero if there isn't one
         */
        public function isUnpatrolled() {
-               return self::getRevisionStore()->isUnpatrolled( $this->mRecord );
+               return self::getRevisionStore()->getRcIdIfUnpatrolled( $this->mRecord );
        }
 
        /**
@@ -892,10 +975,9 @@ class Revision implements IDBAccessObject {
         * @return Revision|null
         */
        public function getPrevious() {
-               $rec = self::getRevisionStore()->getPreviousRevision( $this->mRecord, $this->getTitle() );
-               return $rec === null
-                       ? null
-                       : new Revision( $rec, self::READ_NORMAL, $this->getTitle() );
+               $title = $this->getTitle();
+               $rec = self::getRevisionStore()->getPreviousRevision( $this->mRecord, $title );
+               return $rec === null ? null : new Revision( $rec, self::READ_NORMAL, $title );
        }
 
        /**
@@ -904,10 +986,9 @@ class Revision implements IDBAccessObject {
         * @return Revision|null
         */
        public function getNext() {
-               $rec = self::getRevisionStore()->getNextRevision( $this->mRecord, $this->getTitle() );
-               return $rec === null
-                       ? null
-                       : new Revision( $rec, self::READ_NORMAL, $this->getTitle() );
+               $title = $this->getTitle();
+               $rec = self::getRevisionStore()->getNextRevision( $this->mRecord, $title );
+               return $rec === null ? null : new Revision( $rec, self::READ_NORMAL, $title );
        }
 
        /**
@@ -942,7 +1023,7 @@ class Revision implements IDBAccessObject {
 
                $cacheKey = isset( $row->old_id ) ? ( 'tt:' . $row->old_id ) : null;
 
-               return self::getBlobStore()->expandBlob( $text, $flags, $cacheKey );
+               return self::getBlobStore( $wiki )->expandBlob( $text, $flags, $cacheKey );
        }
 
        /**
@@ -1036,7 +1117,11 @@ class Revision implements IDBAccessObject {
 
                $comment = CommentStoreComment::newUnsavedComment( $summary, null );
 
-               $title = Title::newFromID( $pageId );
+               $title = Title::newFromID( $pageId, Title::GAID_FOR_UPDATE );
+               if ( $title === null ) {
+                       return null;
+               }
+
                $rec = self::getRevisionStore()->newNullRevision( $dbw, $title, $comment, $minor, $user );
 
                return new Revision( $rec );
@@ -1158,6 +1243,10 @@ class Revision implements IDBAccessObject {
                        ? $pageIdOrTitle
                        : Title::newFromID( $pageIdOrTitle );
 
+               if ( !$title ) {
+                       return false;
+               }
+
                $record = self::getRevisionStore()->getKnownCurrentRevision( $title, $revId );
                return $record ? new Revision( $record ) : false;
        }