Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
13 / 13
CRAP
100.00% covered (success)
100.00%
1 / 1
EditResult
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
13 / 13
15
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 newFromArray
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 getNewestRevertedRevisionId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getOldestRevertedRevisionId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getUndidRevId
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getOriginalRevisionId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isNew
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isRevert
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getRevertMethod
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isExactRevert
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isNullEdit
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRevertTags
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 jsonSerialize
100.00% covered (success)
100.00%
11 / 11
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 JsonSerializable;
24
25/**
26 * Object for storing information about the effects of an edit.
27 *
28 * This object should be constructed by an EditResultBuilder with relevant information filled in
29 * during the process of saving the revision by the PageUpdater. You can use it to extract
30 * information about whether the edit was a revert and which edits were reverted.
31 *
32 * @since 1.35
33 * @author Ostrzyciel
34 */
35class EditResult implements JsonSerializable {
36
37    // revert methods
38    public const REVERT_UNDO = 1;
39    public const REVERT_ROLLBACK = 2;
40    public const REVERT_MANUAL = 3;
41
42    private const SERIALIZATION_FORMAT_VERSION = '1';
43
44    /** @var bool */
45    private $isNew;
46
47    /** @var bool|int */
48    private $originalRevisionId;
49
50    /** @var int|null */
51    private $revertMethod;
52
53    /** @var int|null */
54    private $newestRevertedRevId;
55
56    /** @var int|null */
57    private $oldestRevertedRevId;
58
59    /** @var bool */
60    private $isExactRevert;
61
62    /** @var bool */
63    private $isNullEdit;
64
65    /** @var string[] */
66    private $revertTags;
67
68    /**
69     * @param bool $isNew
70     * @param bool|int $originalRevisionId
71     * @param int|null $revertMethod
72     * @param int|null $oldestReverted
73     * @param int|null $newestReverted
74     * @param bool $isExactRevert
75     * @param bool $isNullEdit
76     * @param string[] $revertTags
77     *
78     * @internal Use EditResultBuilder for constructing EditResults.
79     */
80    public function __construct(
81        bool $isNew,
82        $originalRevisionId,
83        ?int $revertMethod,
84        ?int $oldestReverted,
85        ?int $newestReverted,
86        bool $isExactRevert,
87        bool $isNullEdit,
88        array $revertTags
89    ) {
90        $this->isNew = $isNew;
91        $this->originalRevisionId = $originalRevisionId;
92        $this->revertMethod = $revertMethod;
93        $this->oldestRevertedRevId = $oldestReverted;
94        $this->newestRevertedRevId = $newestReverted;
95        $this->isExactRevert = $isExactRevert;
96        $this->isNullEdit = $isNullEdit;
97        $this->revertTags = $revertTags;
98    }
99
100    /**
101     * Recreate the EditResult object from its array representation.
102     *
103     * This must ONLY be used for deserializing EditResult objects serialized using
104     * EditResult::jsonSerialize(). The structure of the array may change without prior
105     * notice.
106     *
107     * Any changes to the format are guaranteed to be backwards-compatible, so this
108     * method will work fine with old serialized EditResults.
109     *
110     * For constructing EditResult objects from scratch use EditResultBuilder.
111     *
112     * @see EditResult::jsonSerialize()
113     *
114     * @param array $a
115     * @phpcs:ignore Generic.Files.LineLength
116     * @phan-param array{isNew:bool,originalRevisionId:bool|int,revertMethod:int|null,newestRevertedRevId:int|null,oldestRevertedRevId:int|null,isExactRevert:bool,isNullEdit:bool,revertTags:string[],version:string} $a
117     *
118     * @return EditResult
119     *
120     * @since 1.36
121     */
122    public static function newFromArray( array $a ) {
123        return new self(
124            $a['isNew'],
125            $a['originalRevisionId'],
126            $a['revertMethod'],
127            $a['oldestRevertedRevId'],
128            $a['newestRevertedRevId'],
129            $a['isExactRevert'],
130            $a['isNullEdit'],
131            $a['revertTags']
132        );
133    }
134
135    /**
136     * Returns the ID of the most recent revision that was reverted by this edit.
137     * The same as getOldestRevertedRevisionId if only a single revision was
138     * reverted. Returns null if the edit was not a revert.
139     *
140     * @see EditResult::isRevert() for information on how a revert is defined
141     *
142     * @return int|null
143     */
144    public function getNewestRevertedRevisionId(): ?int {
145        return $this->newestRevertedRevId;
146    }
147
148    /**
149     * Returns the ID of the oldest revision that was reverted by this edit.
150     * The same as getOldestRevertedRevisionId if only a single revision was
151     * reverted. Returns null if the edit was not a revert.
152     *
153     * @see EditResult::isRevert() for information on how a revert is defined
154     *
155     * @return int|null
156     */
157    public function getOldestRevertedRevisionId(): ?int {
158        return $this->oldestRevertedRevId;
159    }
160
161    /**
162     * If the edit was an undo, returns the oldest revision that was undone.
163     * Method kept for compatibility reasons.
164     *
165     * @return int
166     */
167    public function getUndidRevId(): int {
168        if ( $this->getRevertMethod() !== self::REVERT_UNDO ) {
169            return 0;
170        }
171        return $this->getOldestRevertedRevisionId() ?? 0;
172    }
173
174    /**
175     * Returns the ID of an earlier revision that is being repeated or restored.
176     *
177     * The original revision's content should match the new revision exactly.
178     *
179     * @return bool|int The original revision id, or false if no earlier revision is known to be
180     * repeated or restored.
181     * The old PageUpdater::getOriginalRevisionId() returned false in such cases. This value would
182     * be then passed on to extensions through hooks, so it may be wise to keep compatibility with
183     * the old behavior.
184     */
185    public function getOriginalRevisionId() {
186        return $this->originalRevisionId;
187    }
188
189    /**
190     * Whether the edit created a new page
191     *
192     * @return bool
193     */
194    public function isNew(): bool {
195        return $this->isNew;
196    }
197
198    /**
199     * Whether the edit was a revert, not necessarily exact.
200     *
201     * An edit is considered a revert if it either:
202     * - Restores the page to an exact previous state (rollbacks, manual reverts and some undos).
203     *   E.g. for edits A B C D, edits C and D are reverted.
204     * - Undoes some edits made previously, not necessarily restoring the page to an exact
205     *   previous state (undo). It is guaranteed that the revert was a "clean" result of a
206     *   three-way merge and no additional changes were made by the reverting user.
207     *   E.g. for edits A B C D, edits B and C are reverted.
208     *
209     * To check whether the edit was an exact revert, please use the isExactRevert() method.
210     * The getRevertMethod() will provide additional information about which kind of revert
211     * was made.
212     *
213     * @return bool
214     */
215    public function isRevert(): bool {
216        return !$this->isNew() && $this->getOldestRevertedRevisionId();
217    }
218
219    /**
220     * Returns the revert method that was used to perform the edit, if any changes were reverted.
221     * Returns null if the edit was not a revert.
222     *
223     * Possible values: REVERT_UNDO, REVERT_ROLLBACK, REVERT_MANUAL
224     *
225     * @see EditResult::isRevert()
226     *
227     * @return int|null
228     */
229    public function getRevertMethod(): ?int {
230        return $this->revertMethod;
231    }
232
233    /**
234     * Whether the edit was an exact revert,
235     * i.e. the contents of the revert revision and restored revision match
236     *
237     * @return bool
238     */
239    public function isExactRevert(): bool {
240        return $this->isExactRevert;
241    }
242
243    /**
244     * An edit is a null edit if the original revision is equal to the parent revision,
245     * i.e. no changes were made.
246     *
247     * @return bool
248     */
249    public function isNullEdit(): bool {
250        return $this->isNullEdit;
251    }
252
253    /**
254     * Returns an array of revert-related tags that were applied automatically to this edit.
255     *
256     * @return string[]
257     */
258    public function getRevertTags(): array {
259        return $this->revertTags;
260    }
261
262    /**
263     * Returns an array representing the EditResult object.
264     *
265     * @see EditResult::newFromArray()
266     *
267     * @return array
268     * @phpcs:ignore Generic.Files.LineLength
269     * @phan-return array{isNew:bool,originalRevisionId:bool|int,revertMethod:int|null,newestRevertedRevId:int|null,oldestRevertedRevId:int|null,isExactRevert:bool,isNullEdit:bool,revertTags:string[],version:string}
270     *
271     * @since 1.36
272     */
273    public function jsonSerialize(): array {
274        return [
275            'isNew' => $this->isNew,
276            'originalRevisionId' => $this->originalRevisionId,
277            'revertMethod' => $this->revertMethod,
278            'newestRevertedRevId' => $this->newestRevertedRevId,
279            'oldestRevertedRevId' => $this->oldestRevertedRevId,
280            'isExactRevert' => $this->isExactRevert,
281            'isNullEdit' => $this->isNullEdit,
282            'revertTags' => $this->revertTags,
283            'version' => self::SERIALIZATION_FORMAT_VERSION
284        ];
285    }
286}