Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
EditResultCache
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
4 / 4
7
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 set
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 get
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
4
 makeKey
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\Storage;
8
9use MediaWiki\Config\ServiceOptions;
10use MediaWiki\Json\FormatJson;
11use MediaWiki\MainConfigNames;
12use Wikimedia\ObjectCache\BagOStuff;
13use Wikimedia\Rdbms\IConnectionProvider;
14
15/**
16 * Class allowing easy storage and retrieval of EditResults associated with revisions.
17 *
18 * EditResults are stored in the main object stash and (depending on wiki's configuration)
19 * in revert change tags. This class stores the relevant data in the main stash. When
20 * asked to retrieve an EditResult for an edit and the requested key is not present in the
21 * main stash, the class will attempt to retrieve the EditResult from revert tags.
22 *
23 * @internal Used by RevertedTagUpdateManager
24 * @since 1.36
25 * @author Ostrzyciel
26 */
27class EditResultCache {
28
29    public const CONSTRUCTOR_OPTIONS = [
30        MainConfigNames::RCMaxAge,
31    ];
32
33    private const CACHE_KEY_PREFIX = 'EditResult';
34
35    /** @var BagOStuff */
36    private $mainObjectStash;
37
38    /** @var IConnectionProvider */
39    private $dbProvider;
40
41    /** @var ServiceOptions */
42    private $options;
43
44    /**
45     * @param BagOStuff $mainObjectStash Main object stash, see
46     *  MediaWikiServices::getMainObjectStash()
47     * @param IConnectionProvider $dbProvider
48     * @param ServiceOptions $options
49     */
50    public function __construct(
51        BagOStuff $mainObjectStash,
52        IConnectionProvider $dbProvider,
53        ServiceOptions $options
54    ) {
55        $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
56
57        $this->mainObjectStash = $mainObjectStash;
58        $this->dbProvider = $dbProvider;
59        $this->options = $options;
60    }
61
62    /**
63     * Store the EditResult in the main object stash.
64     *
65     * @param int $revisionId
66     * @param EditResult $editResult
67     *
68     * @return bool Success
69     */
70    public function set( int $revisionId, EditResult $editResult ): bool {
71        return $this->mainObjectStash->set(
72            $this->makeKey( $revisionId ),
73            FormatJson::encode( $editResult ),
74            // Patrol flags are not stored for longer than $wgRCMaxAge
75            $this->options->get( MainConfigNames::RCMaxAge ),
76            BagOStuff::WRITE_BACKGROUND
77        );
78    }
79
80    /**
81     * Get an EditResult for the given revision ID.
82     *
83     * Will first attempt to get the EditResult from the main stash. If this fails, it
84     * will try to retrieve the EditResult from revert change tags of this revision.
85     *
86     * @param int $revisionId
87     *
88     * @return EditResult|null Returns null on failure
89     */
90    public function get( int $revisionId ): ?EditResult {
91        $result = $this->mainObjectStash->get( $this->makeKey( $revisionId ) );
92
93        // not found in stash, try change tags
94        if ( !$result ) {
95            $result = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder()
96                ->select( 'ct_params' )
97                ->from( 'change_tag' )
98                ->join( 'change_tag_def', null, 'ctd_id = ct_tag_id' )
99                ->where( [
100                    'ct_rev_id' => $revisionId,
101                    'ctd_name' => [ 'mw-rollback', 'mw-undo', 'mw-manual-revert' ]
102                ] )
103                ->caller( __METHOD__ )->fetchField();
104        }
105
106        if ( !$result ) {
107            return null;
108        }
109
110        $decoded = FormatJson::decode( $result, true );
111        return $decoded ? EditResult::newFromArray( $decoded ) : null;
112    }
113
114    /**
115     * Generates a cache key for the given revision ID.
116     *
117     * @param int $revisionId
118     *
119     * @return string
120     */
121    private function makeKey( int $revisionId ): string {
122        return $this->mainObjectStash->makeKey( self::CACHE_KEY_PREFIX, $revisionId );
123    }
124}