Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
EditResultCache
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
4 / 4
7
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
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    /**
36     * @param BagOStuff $mainObjectStash Main object stash, see
37     *  MediaWikiServices::getMainObjectStash()
38     * @param IConnectionProvider $dbProvider
39     * @param ServiceOptions $options
40     */
41    public function __construct(
42        private readonly BagOStuff $mainObjectStash,
43        private readonly IConnectionProvider $dbProvider,
44        private readonly ServiceOptions $options,
45    ) {
46        $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
47    }
48
49    /**
50     * Store the EditResult in the main object stash.
51     *
52     * @param int $revisionId
53     * @param EditResult $editResult
54     *
55     * @return bool Success
56     */
57    public function set( int $revisionId, EditResult $editResult ): bool {
58        return $this->mainObjectStash->set(
59            $this->makeKey( $revisionId ),
60            FormatJson::encode( $editResult ),
61            // Patrol flags are not stored for longer than $wgRCMaxAge
62            $this->options->get( MainConfigNames::RCMaxAge ),
63            BagOStuff::WRITE_BACKGROUND
64        );
65    }
66
67    /**
68     * Get an EditResult for the given revision ID.
69     *
70     * Will first attempt to get the EditResult from the main stash. If this fails, it
71     * will try to retrieve the EditResult from revert change tags of this revision.
72     *
73     * @param int $revisionId
74     *
75     * @return EditResult|null Returns null on failure
76     */
77    public function get( int $revisionId ): ?EditResult {
78        $result = $this->mainObjectStash->get( $this->makeKey( $revisionId ) );
79
80        // not found in stash, try change tags
81        if ( !$result ) {
82            $result = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder()
83                ->select( 'ct_params' )
84                ->from( 'change_tag' )
85                ->join( 'change_tag_def', null, 'ctd_id = ct_tag_id' )
86                ->where( [
87                    'ct_rev_id' => $revisionId,
88                    'ctd_name' => [ 'mw-rollback', 'mw-undo', 'mw-manual-revert' ]
89                ] )
90                ->caller( __METHOD__ )->fetchField();
91        }
92
93        if ( !$result ) {
94            return null;
95        }
96
97        $decoded = FormatJson::decode( $result, true );
98        return $decoded ? EditResult::newFromArray( $decoded ) : null;
99    }
100
101    /**
102     * Generates a cache key for the given revision ID.
103     *
104     * @param int $revisionId
105     *
106     * @return string
107     */
108    private function makeKey( int $revisionId ): string {
109        return $this->mainObjectStash->makeKey( self::CACHE_KEY_PREFIX, $revisionId );
110    }
111}