Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
EditResultCache
100.00% covered (success)
100.00%
25 / 25
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%
5 / 5
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 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21namespace MediaWiki\Storage;
22
23use MediaWiki\Config\ServiceOptions;
24use MediaWiki\Json\FormatJson;
25use MediaWiki\MainConfigNames;
26use Wikimedia\ObjectCache\BagOStuff;
27use Wikimedia\Rdbms\IConnectionProvider;
28
29/**
30 * Class allowing easy storage and retrieval of EditResults associated with revisions.
31 *
32 * EditResults are stored in the main object stash and (depending on wiki's configuration)
33 * in revert change tags. This class stores the relevant data in the main stash. When
34 * asked to retrieve an EditResult for an edit and the requested key is not present in the
35 * main stash, the class will attempt to retrieve the EditResult from revert tags.
36 *
37 * @internal Used by RevertedTagUpdateManager
38 * @since 1.36
39 * @author Ostrzyciel
40 */
41class EditResultCache {
42
43    public const CONSTRUCTOR_OPTIONS = [
44        MainConfigNames::RCMaxAge,
45    ];
46
47    private const CACHE_KEY_PREFIX = 'EditResult';
48
49    /** @var BagOStuff */
50    private $mainObjectStash;
51
52    /** @var IConnectionProvider */
53    private $dbProvider;
54
55    /** @var ServiceOptions */
56    private $options;
57
58    /**
59     * @param BagOStuff $mainObjectStash Main object stash, see
60     *  MediaWikiServices::getMainObjectStash()
61     * @param IConnectionProvider $dbProvider
62     * @param ServiceOptions $options
63     */
64    public function __construct(
65        BagOStuff $mainObjectStash,
66        IConnectionProvider $dbProvider,
67        ServiceOptions $options
68    ) {
69        $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
70
71        $this->mainObjectStash = $mainObjectStash;
72        $this->dbProvider = $dbProvider;
73        $this->options = $options;
74    }
75
76    /**
77     * Store the EditResult in the main object stash.
78     *
79     * @param int $revisionId
80     * @param EditResult $editResult
81     *
82     * @return bool Success
83     */
84    public function set( int $revisionId, EditResult $editResult ): bool {
85        return $this->mainObjectStash->set(
86            $this->makeKey( $revisionId ),
87            FormatJson::encode( $editResult ),
88            // Patrol flags are not stored for longer than $wgRCMaxAge
89            $this->options->get( MainConfigNames::RCMaxAge )
90        );
91    }
92
93    /**
94     * Get an EditResult for the given revision ID.
95     *
96     * Will first attempt to get the EditResult from the main stash. If this fails, it
97     * will try to retrieve the EditResult from revert change tags of this revision.
98     *
99     * @param int $revisionId
100     *
101     * @return EditResult|null Returns null on failure
102     */
103    public function get( int $revisionId ): ?EditResult {
104        $result = $this->mainObjectStash->get( $this->makeKey( $revisionId ) );
105
106        // not found in stash, try change tags
107        if ( !$result ) {
108            $result = $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder()
109                ->select( 'ct_params' )
110                ->from( 'change_tag' )
111                ->join( 'change_tag_def', null, 'ctd_id = ct_tag_id' )
112                ->where( [
113                    'ct_rev_id' => $revisionId,
114                    'ctd_name' => [ 'mw-rollback', 'mw-undo', 'mw-manual-revert' ]
115                ] )
116                ->caller( __METHOD__ )->fetchField();
117        }
118
119        if ( !$result ) {
120            return null;
121        }
122
123        $decoded = FormatJson::decode( $result, true );
124        return $decoded ? EditResult::newFromArray( $decoded ) : null;
125    }
126
127    /**
128     * Generates a cache key for the given revision ID.
129     *
130     * @param int $revisionId
131     *
132     * @return string
133     */
134    private function makeKey( int $revisionId ): string {
135        return $this->mainObjectStash->makeKey( self::CACHE_KEY_PREFIX, $revisionId );
136    }
137}