Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
37 / 37
100.00% covered (success)
100.00%
11 / 11
CRAP
100.00% covered (success)
100.00%
1 / 1
RevisionStoreRecord
100.00% covered (success)
100.00%
37 / 37
100.00% covered (success)
100.00%
11 / 11
22
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
8
 isCurrent
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isDeleted
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 userCan
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 getId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSize
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getSha1
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getUser
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getComment
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTimestamp
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isReadyForInsertion
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * A RevisionRecord representing an existing revision persisted in the revision table.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 */
22
23namespace MediaWiki\Revision;
24
25use InvalidArgumentException;
26use MediaWiki\CommentStore\CommentStoreComment;
27use MediaWiki\Page\PageIdentity;
28use MediaWiki\Permissions\Authority;
29use MediaWiki\User\UserIdentity;
30use MediaWiki\Utils\MWTimestamp;
31
32/**
33 * A RevisionRecord representing an existing revision persisted in the revision table.
34 * RevisionStoreRecord has no optional fields, getters will never return null.
35 *
36 * @since 1.31
37 * @since 1.32 Renamed from MediaWiki\Storage\RevisionStoreRecord
38 */
39class RevisionStoreRecord extends RevisionRecord {
40
41    /** @var bool */
42    protected $mCurrent = false;
43
44    /**
45     * @note Avoid calling this constructor directly. Use the appropriate methods
46     * in RevisionStore instead.
47     *
48     * @param PageIdentity $page The page this RevisionRecord is associated with.
49     * @param UserIdentity $user
50     * @param CommentStoreComment $comment
51     * @param \stdClass $row A row from the revision table. Use RevisionStore::getQueryInfo() to build
52     *        a query that yields the required fields.
53     * @param RevisionSlots $slots The slots of this revision.
54     * @param false|string $wikiId Relevant wiki id or self::LOCAL for the current one.
55     */
56    public function __construct(
57        PageIdentity $page,
58        UserIdentity $user,
59        CommentStoreComment $comment,
60        \stdClass $row,
61        RevisionSlots $slots,
62        $wikiId = self::LOCAL
63    ) {
64        parent::__construct( $page, $slots, $wikiId );
65        $this->mId = intval( $row->rev_id );
66        $this->mPageId = intval( $row->rev_page );
67        $this->mComment = $comment;
68
69        // Don't use MWTimestamp::convert, instead let any detailed exception from MWTimestamp
70        // bubble up (T254210)
71        $timestamp = ( new MWTimestamp( $row->rev_timestamp ) )->getTimestamp( TS_MW );
72
73        $this->mUser = $user;
74        $this->mMinorEdit = boolval( $row->rev_minor_edit );
75        $this->mTimestamp = $timestamp;
76        $this->mDeleted = intval( $row->rev_deleted );
77
78        // NOTE: rev_parent_id = 0 indicates that there is no parent revision, while null
79        // indicates that the parent revision is unknown. As per MW 1.31, the database schema
80        // allows rev_parent_id to be NULL.
81        $this->mParentId = isset( $row->rev_parent_id ) ? intval( $row->rev_parent_id ) : null;
82        $this->mSize = isset( $row->rev_len ) ? intval( $row->rev_len ) : null;
83        $this->mSha1 = !empty( $row->rev_sha1 ) ? $row->rev_sha1 : null;
84
85        // NOTE: we must not call $this->mTitle->getLatestRevID() here, since the state of
86        // page_latest may be in limbo during revision creation. In that case, calling
87        // $this->mTitle->getLatestRevID() would cause a bad value to be cached in the Title
88        // object. During page creation, that bad value would be 0.
89        if ( isset( $row->page_latest ) ) {
90            $this->mCurrent = ( $row->rev_id == $row->page_latest );
91        }
92
93        $pageIdBasedOnPage = $this->getArticleId( $this->mPage );
94        if ( $this->mPageId && $pageIdBasedOnPage && $this->mPageId !== $pageIdBasedOnPage ) {
95            throw new InvalidArgumentException(
96                'The given page (' . $this->mPage . ')' .
97                ' does not belong to page ID ' . $this->mPageId .
98                ' but actually belongs to ' . $this->getArticleId( $this->mPage )
99            );
100        }
101    }
102
103    /**
104     * @inheritDoc
105     */
106    public function isCurrent() {
107        return $this->mCurrent;
108    }
109
110    /**
111     * MCR migration note: this replaced Revision::isDeleted
112     *
113     * @param int $field One of DELETED_* bitfield constants
114     *
115     * @return bool
116     */
117    public function isDeleted( $field ) {
118        if ( $this->isCurrent() && $field === self::DELETED_TEXT ) {
119            // Current revisions of pages cannot have the content hidden. Skipping this
120            // check is very useful for Parser as it fetches templates using newKnownCurrent().
121            // Calling getVisibility() in that case triggers a verification database query.
122            return false; // no need to check
123        }
124
125        return parent::isDeleted( $field );
126    }
127
128    public function userCan( $field, Authority $performer ) {
129        if ( $this->isCurrent() && $field === self::DELETED_TEXT ) {
130            // Current revisions of pages cannot have the content hidden. Skipping this
131            // check is very useful for Parser as it fetches templates using newKnownCurrent().
132            // Calling getVisibility() in that case triggers a verification database query.
133            return true; // no need to check
134        }
135
136        return parent::userCan( $field, $performer );
137    }
138
139    /**
140     * @param string|false $wikiId The wiki ID expected by the caller.
141     * @return int|null The revision id, never null.
142     */
143    public function getId( $wikiId = self::LOCAL ) {
144        // overwritten just to add a guarantee to the contract
145        return parent::getId( $wikiId );
146    }
147
148    /**
149     * @throws RevisionAccessException if the size was unknown and could not be calculated.
150     * @return int The nominal revision size, never null. May be computed on the fly.
151     */
152    public function getSize() {
153        // If length is null, calculate and remember it (potentially SLOW!).
154        // This is for compatibility with old database rows that don't have the field set.
155        $this->mSize ??= $this->mSlots->computeSize();
156
157        return $this->mSize;
158    }
159
160    /**
161     * @throws RevisionAccessException if the hash was unknown and could not be calculated.
162     * @return string The revision hash, never null. May be computed on the fly.
163     */
164    public function getSha1() {
165        // If hash is null, calculate it and remember (potentially SLOW!)
166        // This is for compatibility with old database rows that don't have the field set.
167        $this->mSha1 ??= $this->mSlots->computeSha1();
168
169        return $this->mSha1;
170    }
171
172    /**
173     * @param int $audience
174     * @param Authority|null $performer
175     *
176     * @return UserIdentity The identity of the revision author, null if access is forbidden.
177     */
178    public function getUser( $audience = self::FOR_PUBLIC, Authority $performer = null ) {
179        // overwritten just to add a guarantee to the contract
180        return parent::getUser( $audience, $performer );
181    }
182
183    /**
184     * @param int $audience
185     * @param Authority|null $performer
186     *
187     * @return CommentStoreComment The revision comment, null if access is forbidden.
188     */
189    public function getComment( $audience = self::FOR_PUBLIC, Authority $performer = null ) {
190        // overwritten just to add a guarantee to the contract
191        return parent::getComment( $audience, $performer );
192    }
193
194    /**
195     * @return string timestamp, never null
196     */
197    public function getTimestamp() {
198        // overwritten just to add a guarantee to the contract
199        return parent::getTimestamp();
200    }
201
202    /**
203     * @see RevisionStore::isComplete
204     *
205     * @return bool always true.
206     */
207    public function isReadyForInsertion() {
208        return true;
209    }
210
211}