Merge "Selenium: replace UserLoginPage with BlankPage where possible"
[lhc/web/wiklou.git] / includes / Revision / RevisionStoreCacheRecord.php
1 <?php
2 /**
3 * A RevisionStoreRecord loaded from the cache.
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
23 namespace MediaWiki\Revision;
24
25 use MediaWiki\User\UserIdentity;
26 use MediaWiki\User\UserIdentityValue;
27 use CommentStoreComment;
28 use InvalidArgumentException;
29 use Title;
30 use User;
31
32 /**
33 * A cached RevisionStoreRecord. Ensures that changes performed "behind the back"
34 * of the cache do not cause the revision record to deliver stale data.
35 *
36 * @since 1.33
37 */
38 class RevisionStoreCacheRecord extends RevisionStoreRecord {
39
40 /**
41 * @var callable
42 */
43 private $mCallback;
44
45 /**
46 * @note Avoid calling this constructor directly. Use the appropriate methods
47 * in RevisionStore instead.
48 *
49 * @param callable $callback Callback for loading data. Signature: function ( $id ): object
50 * @param Title $title The title of the page this Revision is associated with.
51 * @param UserIdentity $user
52 * @param CommentStoreComment $comment
53 * @param object $row A row from the revision table. Use RevisionStore::getQueryInfo() to build
54 * a query that yields the required fields.
55 * @param RevisionSlots $slots The slots of this revision.
56 * @param bool|string $dbDomain DB domain of the relevant wiki or false for the current one.
57 */
58 function __construct(
59 $callback,
60 Title $title,
61 UserIdentity $user,
62 CommentStoreComment $comment,
63 $row,
64 RevisionSlots $slots,
65 $dbDomain = false
66 ) {
67 parent::__construct( $title, $user, $comment, $row, $slots, $dbDomain );
68 $this->mCallback = $callback;
69 }
70
71 /**
72 * Overridden to ensure that we return a fresh value and not a cached one.
73 *
74 * @return int
75 */
76 public function getVisibility() {
77 if ( $this->mCallback ) {
78 $this->loadFreshRow();
79 }
80 return parent::getVisibility();
81 }
82
83 /**
84 * Overridden to ensure that we return a fresh value and not a cached one.
85 *
86 * @param int $audience
87 * @param User|null $user
88 *
89 * @return UserIdentity The identity of the revision author, null if access is forbidden.
90 */
91 public function getUser( $audience = self::FOR_PUBLIC, User $user = null ) {
92 if ( $this->mCallback ) {
93 $this->loadFreshRow();
94 }
95 return parent::getUser( $audience, $user );
96 }
97
98 /**
99 * Load a fresh row from the database to ensure we return updated information
100
101 * @throws RevisionAccessException if the row could not be loaded
102 */
103 private function loadFreshRow() {
104 $freshRow = call_user_func( $this->mCallback, $this->mId );
105
106 // Set to null to ensure we do not make unnecessary queries for subsequent getter calls,
107 // and to allow the closure to be freed.
108 $this->mCallback = null;
109
110 if ( $freshRow ) {
111 $this->mDeleted = intval( $freshRow->rev_deleted );
112
113 try {
114 $this->mUser = User::newFromAnyId(
115 $freshRow->rev_user ?? null,
116 $freshRow->rev_user_text ?? null,
117 $freshRow->rev_actor ?? null
118 );
119 } catch ( InvalidArgumentException $ex ) {
120 wfWarn(
121 __METHOD__
122 . ': '
123 . $this->mTitle->getPrefixedDBkey()
124 . ': '
125 . $ex->getMessage()
126 );
127 $this->mUser = new UserIdentityValue( 0, 'Unknown user', 0 );
128 }
129 } else {
130 throw new RevisionAccessException(
131 'Unable to load fresh row for rev_id: ' . $this->mId
132 );
133 }
134 }
135
136 }