Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
76.47% covered (warning)
76.47%
13 / 17
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
RevisionStoreCacheRecord
76.47% covered (warning)
76.47%
13 / 17
75.00% covered (warning)
75.00%
3 / 4
8.83
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getVisibility
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getUser
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 loadFreshRow
55.56% covered (warning)
55.56%
5 / 9
0.00% covered (danger)
0.00%
0 / 1
3.79
1<?php
2/**
3 * A RevisionStoreRecord loaded from the cache.
4 *
5 * @license GPL-2.0-or-later
6 * @file
7 */
8
9namespace MediaWiki\Revision;
10
11use MediaWiki\CommentStore\CommentStoreComment;
12use MediaWiki\Page\PageIdentity;
13use MediaWiki\Permissions\Authority;
14use MediaWiki\User\UserIdentity;
15
16/**
17 * A cached RevisionStoreRecord.  Ensures that changes performed "behind the back"
18 * of the cache do not cause the revision record to deliver stale data.
19 *
20 * @internal
21 * @since 1.33
22 */
23class RevisionStoreCacheRecord extends RevisionStoreRecord {
24
25    /**
26     * @var null|callable ( int $revId ): [ int $rev_deleted, UserIdentity $user ]
27     */
28    private $mCallback;
29
30    /**
31     * @note Avoid calling this constructor directly. Use the appropriate methods
32     * in RevisionStore instead.
33     *
34     * @param callable $callback Callback for loading data.
35     *        Signature: function ( int $revId ): [ int $rev_deleted, UserIdentity $user ]
36     * @param PageIdentity $page The page this RevisionRecord is associated with.
37     * @param UserIdentity $user
38     * @param CommentStoreComment $comment
39     * @param \stdClass $row A row from the revision table. Use RevisionStore::getQueryInfo() to build
40     *        a query that yields the required fields.
41     * @param RevisionSlots $slots The slots of this revision.
42     * @param false|string $wikiID Relevant wiki id or self::LOCAL for the current one.
43     */
44    public function __construct(
45        callable $callback,
46        PageIdentity $page,
47        UserIdentity $user,
48        CommentStoreComment $comment,
49        $row,
50        RevisionSlots $slots,
51        $wikiID = self::LOCAL
52    ) {
53        parent::__construct( $page, $user, $comment, $row, $slots, $wikiID );
54        $this->mCallback = $callback;
55    }
56
57    /**
58     * Overridden to ensure that we return a fresh value and not a cached one.
59     *
60     * @return int
61     */
62    public function getVisibility() {
63        if ( $this->mCallback ) {
64            $this->loadFreshRow();
65        }
66        return parent::getVisibility();
67    }
68
69    /**
70     * Overridden to ensure that we return a fresh value and not a cached one.
71     *
72     * @param int $audience
73     * @param Authority|null $performer
74     *
75     * @return UserIdentity The identity of the revision author, null if access is forbidden.
76     */
77    public function getUser( $audience = self::FOR_PUBLIC, ?Authority $performer = null ) {
78        if ( $this->mCallback ) {
79            $this->loadFreshRow();
80        }
81        return parent::getUser( $audience, $performer );
82    }
83
84    /**
85     * Load a fresh row from the database to ensure we return updated information
86     *
87     * @throws RevisionAccessException if the row could not be loaded
88     */
89    private function loadFreshRow() {
90        [ $freshRevDeleted, $freshUser ] = ( $this->mCallback )( $this->mId );
91
92        // Set to null to ensure we do not make unnecessary queries for subsequent getter calls,
93        // and to allow the closure to be freed.
94        $this->mCallback = null;
95
96        if ( $freshRevDeleted !== null && $freshUser !== null ) {
97            $this->mDeleted = intval( $freshRevDeleted );
98            $this->mUser = $freshUser;
99        } else {
100            throw new RevisionAccessException(
101                'Unable to load fresh row for rev_id: {rev_id}',
102                [ 'rev_id' => $this->mId ]
103            );
104        }
105    }
106
107}