Manual:Revision.php/Migration

For extensions that make widespread use of Revision objects, switching to RevisionRecord may be challenging. Here are some suggestions:

Constructors
To replace uses of the static constructors (Revision::newFrom*) while still having Revision objects, as a stop-gap measure use "new Revision( $revisionRecord )" with the RevisionRecord constructed using the RevisionLookup service. The RevisionLookup methods either return a RevisionRecord or null, and the corresponding Revision methods returned either a Revision or null:

Instead of $revision = Revision::newFromId( $id, $flags ); use $revisionRecord = MediaWikiServices::getInstance->getRevisionLookup->getRevisionById( $ids, $flags ); $revision = $revisionRecord ? new Revision( $revisionRecord ) : null;

For the other static constructors, simply replace `getRevisionById` with the corresponding RevisionLookup method (note that the loadFrom* methods accept different parameters than the newFrom*):

Revision::newFromTitle -> RevisionLookup::getRevisionByTitle Revision::newFromPageId -> RevisionLookup::getRevisionByPageId Revision::loadFromPageId -> RevisionLookup::loadRevisionFromPageId Revision::loadFromTitle -> RevisionLookup::loadRevisionFromTitle Revision::loadFromTimestamp -> RevisionLookup::loadRevisionFromTimestamp

The RevisionFactory service is used to replace Revision::newFromArchiveRow and Revision::newFromRow; use RevisionFactory::newRevisionFromArchiveRow, and ::newRevisionFromRow (if you were calling ::newFromRow with an array, the replacement would be RevisionFactory::newMutableRevisionFromArray, but that method is also hard deprecated - you should construct a MutableRevisionRecord instead).

For direct uses of `new Revision`, either existing ones or those added now, see the notes below regarding the final removal of constructing Revision objects.

Fetching relative revisions
Revision::getNext was replaced by RevisionLookup::getNextRevision, which returns a RevisionRecord object. Likewise, Revision::getPrevious was replaced by RevisionLookup::getPreviousRevision.

Revision text
Revision::getRevisionText returns a string of the text content for a specific revision (based on a row from the database). To replace it, get the RevisionRecord object corresponding to the row, and then use RevisionRecord::getContent( SlotRecord::MAIN ) to get the relevant content object, and if there is such an object, use Content::serialize to get the text.

Revision::compressRevisionText can be replaced with SqlBlobStore::compressData. Revision::decompressRevisionText can be replaced with SqlBlobStore::decompressData.

Other static methods
The following static methods are replaced by non-static methods:

Revision::getQueryInfo -> RevisionStore::getQueryInfo Revision::getArchiveQueryInfo -> RevisionStore::getArchiveQueryInfo

Revision::getParentLengths -> RevisionStore::getRevisionSizes Revision::getTimestampFromId -> RevisionStore::getTimestampFromId (NOTE: the Revision method's first parameter was a Title object that was ignored, the RevisionStore method does not accept a Title, and only needs the relevant id and any query flags)

Revision::countByPageId -> RevisionStore::countRevisionsByPageId Revision::countByTitle -> RevisionStore::countRevisionsByTitle

Revision::userWasLastToEdit -> RevisionStore::userWasLastToEdit (NOTE: the Revision method's first parameter was an int or an IDatabase object, the RevisionStore method requires an IDatabase object. ADDITIONALLY, the RevisionStore method is itself soft deprecated)

userCan
Revision::userCanBitfield can be replaced with RevisionRecord::userCanBitfield, and Revision::userCan can be replaced with RevisionRecord::userCanBitfield with the bitfield being the int returned from RevisionRecord::getVisibility. NOTE that both of the Revision methods fell back to $wgUser if no user object was passed; RevisionRecord::userCanBitfield requires that a User be provided.

Corresponding Revision and RevisionRecord methods
The following Revision methods can be replaced with idential RevisionRecord methods (though some have different names). Once a Revision object is available in a relevant class or function, I suggest immediately retrieving the corresponding RevisionRecord via Revision::getRevisionRecord and slowly making use of the RevisionRecord instead of the Revision:

Revision::getId -> RevisionRecord::getId Revision::getParentId -> RevisionRecord::getParentId Revision::getPage -> RevisionRecord::getPageId Revision::isMinor -> RevisionRecord::isMinor Revision::isDeleted -> RevisionRecord::isDeleted Revision::getVisibility -> RevisionRecord::getVisibility Revision::getTimestamp -> RevisionRecord::getTimestamp Revision::isCurrent -> RevisionRecord::isCurrent

The following Revision methods can be replaced with identical RevisionRecord methods, BUT, while the Revision methods returned null if the value was unknown, the RevisionRecord methods throw RevisionAccessException exceptions:

Revision::getSize -> RevisionRecord::getSize Revision::getSha1 -> RevisionRecord::getSha1

Revision::getTitle returned the relevant Title object for the revision. Its replacement, RevisionRecord::getPageAsLinkTarget, returns a LinkTarget instead of a Title. If a full Title object is needed, use Title::newFromLinkTarget.

Audience-based information
The Revision class had multiple methods that accepted a specific audience for which a value (the editing user's id and username, the edit summary used, or the revision content) was based on whether the audience could view the information (since it may have been revision deleted).

The constants used for specifying an audience are identical in the Revision and RevisionRecord classes:


 * Revision::FOR_PUBLIC -> RevisionRecord::FOR_PUBLIC
 * Revision::FOR_THIS_USER -> RevisionRecord::FOR_THIS_USER
 * Revision::RAW -> RevisionRecord::RAW

Replacements:


 * Revision::getUser returned the editing user's id, or 0, and Revision::getUserText returned the editing user's username, or an empty string. These were replaced with RevisionRecord::getUser, which returns a UserIdentity object if the audience specifid can view the information, or null otherwise. To get the user's id, use UserIdentity::getId, and for the username, use UserIdentity::getName.
 * Revision::getComment returned the revision's edit summary (as a string), or null. It was replaced with RevisionRecord::getComment, HOWEVER instead of a string, RevisionRecord::getComment returns a CommentStoreComment.
 * Revision::getContent returned the content of the revision's main slot, or null. It was replaced with RevisionRecord::getContent, HOWEVER the RevisionRecord method requires that the slot for which the content should be retrieved be specified. Use SlotRecord::MAIN to match the behaviour of the Revision method. FURTHERMORE, the RevisionRecord method can throw a RevisionAccessException, which the Revision method silently converted to null.

NOTE: When the audience specified for ::getUser, ::getUserText, ::getComment, or ::getContent was FOR_THIS_USER, and no second parameter was passed with the user, the Revision methods fell back to using $wgUser. The RevisionRecord methods have no such fallback, and will throw an InvalidArgumentException if attempting to use FOR_THIS_USER without passing a User.

Content handling
As part of the migration to multi-content revisions, there is no longer a single content model or format for a revision, but rather there are content models and formats for each slot.


 * Revision::getContentModel returned a string for the content model of the revision's main slot (SlotRecord::MAIN), either the model set or the default model. To replace it, get the RevisionRecord's main slot, and use SlotRecord::getModel. If the revision has no main slot, fall back to the default model for the title. Use the SlotRoleRegistry service to get the SlotRoleHandler for the relevant role (in this case SlotRecord::MAIN), and then use SlotRoleHandler::getDefaultModel.
 * Revision::getContentFormat returned a string for the content format of the revision's main slot, or falls back to the default format for the content model. To replace it, get the RevisionRecord's main slot, and use SlotRecord::getFormat. If the revision has no main slot, or if SlotRecord::getFormat returns null, fall back to the default format for the content model using ContentHandler::getDefaultFormat with the relevant ContentHandler.
 * Revision::getContentHandler returned the relevant ContentHandler object. Use ContentHandlerFactory::getContentHandler with the relevant content model as a replacement.

Setting revision information

 * Revision::setId was only supported if the Revision object corresponded to a MutableRevisionRecord. It can be replaced with MutableRevisionRecord::setId.
 * Revision::setUserIdAndName was only supported if the Revision object corresponded to a MutableRevisionRecord. It can be replaced with MutableRevisionRecord::setUser; NOTE that the MutableRevisionRecord method requires a UserIdentity, rather than a user id and name.
 * Revision::setTitle was not supported, and threw an exception if it was called with a different title than the one already set for the revision.

Misc
Revision::getTextId -> use SlotRecord::getContentAddress for retrieving an actual content address, or RevisionRecord::hasSameContent to compare content Revision::isUnpatrolled -> RevisionStore::getRcIdIfUnpatrolled Revision::getRecentChange -> RevisionStore::getRecentChange Revision::getSerializedData -> use SlotRecord::getContent for retrieving a content object, and Content::serialize for the serialized form Revision::insertOn -> RevisionStore::insertRevisionOn Revision::base36Sha1 -> SlotRecord::base36Sha1 Revision::newNullRevision -> RevisionStore::newNullRevision Revision::newKnownCurrent -> RevisionLookup::getKnownCurrentRevision

Constructing
For uses of `new Revision`, there are multiple relevant replacements:

If the Revision was constructed with a RevisionRecord, just use that RevisionRecord directly

If the Revision was constructed with an array, use a MutableRevisionRecord - construct it manually and set the various fields. NOTE: If neither the `user` nor `user_text` fields were set, the Revision class fell back to using $wgUser; MutableRevisionRecord includes no such fallback, and if no user is provided the object simply won't have a user set.

If the Revision was constructed with an object, use RevisionFactory::newRevisionFromRow

Hooks
A number of hooks that included Revision objects as parameters were hard deprecated in 1.35 and then removed in 1.37. The following is a basic guide to replacing the use of such hooks, with a focus on handling RevisionRecord objects instead of Revision objects. Note that the replacement hooks can include other changes as well as the conversion from Revision to RevisionRecord, and the code examples below simply convert back everything else where possible (eg casting a UserIdentity back to a User object) - this will add technical debt and should be properly addressed by actually updating the code in the hook handler to use the new objects. For the sake of simplicity, the code below assumes that all relevant  statements are already included. The hook replacement snippets do not include actually updating using the Revision objects to use RevisionRecord, see the notes above for how to do that.

The hooks UndeleteShowRevision and UserRetrieveNewTalks were removed without replacement.