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