Deprecate ContentHandler::makeParserOptions()
[lhc/web/wiklou.git] / includes / page / WikiPage.php
index a85518f..7cc25bd 100644 (file)
@@ -202,7 +202,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @param object|string|int $type
         * @return mixed
         */
-       private static function convertSelectType( $type ) {
+       protected static function convertSelectType( $type ) {
                switch ( $type ) {
                        case 'fromdb':
                                return self::READ_NORMAL;
@@ -768,7 +768,7 @@ class WikiPage implements Page, IDBAccessObject {
         *   Revision::FOR_PUBLIC       to be displayed to all users
         *   Revision::FOR_THIS_USER    to be displayed to $wgUser
         *   Revision::RAW              get the text regardless of permissions
-        * @param User $user User object to check for, only if FOR_THIS_USER is passed
+        * @param User|null $user User object to check for, only if FOR_THIS_USER is passed
         *   to the $audience parameter
         * @return Content|null The content of the current revision
         *
@@ -808,7 +808,7 @@ class WikiPage implements Page, IDBAccessObject {
         *   Revision::FOR_PUBLIC       to be displayed to all users
         *   Revision::FOR_THIS_USER    to be displayed to the given user
         *   Revision::RAW              get the text regardless of permissions
-        * @param User $user User object to check for, only if FOR_THIS_USER is passed
+        * @param User|null $user User object to check for, only if FOR_THIS_USER is passed
         *   to the $audience parameter
         * @return int User ID for the user that made the last article revision
         */
@@ -827,7 +827,7 @@ class WikiPage implements Page, IDBAccessObject {
         *   Revision::FOR_PUBLIC       to be displayed to all users
         *   Revision::FOR_THIS_USER    to be displayed to the given user
         *   Revision::RAW              get the text regardless of permissions
-        * @param User $user User object to check for, only if FOR_THIS_USER is passed
+        * @param User|null $user User object to check for, only if FOR_THIS_USER is passed
         *   to the $audience parameter
         * @return User|null
         */
@@ -846,7 +846,7 @@ class WikiPage implements Page, IDBAccessObject {
         *   Revision::FOR_PUBLIC       to be displayed to all users
         *   Revision::FOR_THIS_USER    to be displayed to the given user
         *   Revision::RAW              get the text regardless of permissions
-        * @param User $user User object to check for, only if FOR_THIS_USER is passed
+        * @param User|null $user User object to check for, only if FOR_THIS_USER is passed
         *   to the $audience parameter
         * @return string Username of the user that made the last article revision
         */
@@ -864,7 +864,7 @@ class WikiPage implements Page, IDBAccessObject {
         *   Revision::FOR_PUBLIC       to be displayed to all users
         *   Revision::FOR_THIS_USER    to be displayed to the given user
         *   Revision::RAW              get the text regardless of permissions
-        * @param User $user User object to check for, only if FOR_THIS_USER is passed
+        * @param User|null $user User object to check for, only if FOR_THIS_USER is passed
         *   to the $audience parameter
         * @return string Comment stored for the last article revision
         */
@@ -1306,10 +1306,10 @@ class WikiPage implements Page, IDBAccessObject {
         * @param IDatabase $dbw
         * @param Revision $revision For ID number, and text used to set
         *   length and redirect status fields
-        * @param int $lastRevision If given, will not overwrite the page field
+        * @param int|null $lastRevision If given, will not overwrite the page field
         *   when different from the currently set value.
         *   Giving 0 indicates the new page flag should be set on.
-        * @param bool $lastRevIsRedirect If given, will optimize adding and
+        * @param bool|null $lastRevIsRedirect If given, will optimize adding and
         *   removing rows in redirect table.
         * @return bool Success; false if the page row was missing or page_latest changed
         */
@@ -1664,13 +1664,17 @@ class WikiPage implements Page, IDBAccessObject {
         *        to perform updates, if the edit was already saved.
         * @param RevisionSlotsUpdate|null $forUpdate The new content to be saved by the edit (pre PST),
         *        if the edit was not yet saved.
+        * @param bool $forEdit Only re-use if the cached DerivedPageDataUpdater has the current
+        *       revision as the edit's parent revision. This ensures that the same
+        *       DerivedPageDataUpdater cannot be re-used for two consecutive edits.
         *
         * @return DerivedPageDataUpdater
         */
        private function getDerivedDataUpdater(
                User $forUser = null,
                RevisionRecord $forRevision = null,
-               RevisionSlotsUpdate $forUpdate = null
+               RevisionSlotsUpdate $forUpdate = null,
+               $forEdit = false
        ) {
                if ( !$forRevision && !$forUpdate ) {
                        // NOTE: can't re-use an existing derivedDataUpdater if we don't know what the caller is
@@ -1693,7 +1697,8 @@ class WikiPage implements Page, IDBAccessObject {
                        && !$this->derivedDataUpdater->isReusableFor(
                                $forUser,
                                $forRevision,
-                               $forUpdate
+                               $forUpdate,
+                               $forEdit ? $this->getLatest() : null
                        )
                ) {
                        $this->derivedDataUpdater = null;
@@ -1716,16 +1721,18 @@ class WikiPage implements Page, IDBAccessObject {
         * @since 1.32
         *
         * @param User $user
+        * @param RevisionSlotsUpdate|null $forUpdate If given, allows any cached ParserOutput
+        *        that may already have been returned via getDerivedDataUpdater to be re-used.
         *
         * @return PageUpdater
         */
-       public function newPageUpdater( User $user ) {
+       public function newPageUpdater( User $user, RevisionSlotsUpdate $forUpdate = null ) {
                global $wgAjaxEditStash, $wgUseAutomaticEditSummaries, $wgPageCreationLog;
 
                $pageUpdater = new PageUpdater(
                        $user,
                        $this, // NOTE: eventually, PageUpdater should not know about WikiPage
-                       $this->getDerivedDataUpdater( $user ),
+                       $this->getDerivedDataUpdater( $user, null, $forUpdate, true ),
                        $this->getDBLoadBalancer(),
                        $this->getRevisionStore()
                );
@@ -1774,8 +1781,8 @@ class WikiPage implements Page, IDBAccessObject {
         * restores or repeats. The new revision is expected to have the exact same content as
         * the given original revision. This is used with rollbacks and with dummy "null" revisions
         * which are created to record things like page moves.
-        * @param User $user The user doing the edit
-        * @param string $serialFormat IGNORED.
+        * @param User|null $user The user doing the edit
+        * @param string|null $serialFormat IGNORED.
         * @param array|null $tags Change tags to apply to this edit
         * Callers are responsible for permission checks
         * (with ChangeTags::canAddTagsAccompanyingChange)
@@ -1820,10 +1827,13 @@ class WikiPage implements Page, IDBAccessObject {
                        $flags = ( $flags & ~EDIT_MINOR );
                }
 
+               $slotsUpdate = new RevisionSlotsUpdate();
+               $slotsUpdate->modifyContent( 'main', $content );
+
                // NOTE: while doEditContent() executes, callbacks to getDerivedDataUpdater and
                // prepareContentForEdit will generally use the DerivedPageDataUpdater that is also
                // used by this PageUpdater. However, there is no guarantee for this.
-               $updater = $this->newPageUpdater( $user );
+               $updater = $this->newPageUpdater( $user, $slotsUpdate );
                $updater->setContent( 'main', $content );
                $updater->setOriginalRevisionId( $originalRevId );
                $updater->setUndidRevisionId( $undidRevId );
@@ -1860,7 +1870,7 @@ class WikiPage implements Page, IDBAccessObject {
        /**
         * Get parser options suitable for rendering the primary article wikitext
         *
-        * @see ContentHandler::makeParserOptions
+        * @see ParserOptions::newCanonical
         *
         * @param IContextSource|User|string $context One of the following:
         *        - IContextSource: Use the User and the Language of the provided
@@ -1872,7 +1882,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @return ParserOptions
         */
        public function makeParserOptions( $context ) {
-               $options = $this->getContentHandler()->makeParserOptions( $context );
+               $options = ParserOptions::newCanonical( $context );
 
                if ( $this->getTitle()->isConversionTable() ) {
                        // @todo ConversionTable should become a separate content model, so
@@ -1935,7 +1945,7 @@ class WikiPage implements Page, IDBAccessObject {
                $updater = $this->getDerivedDataUpdater( $user, $revision, $slots );
 
                if ( !$updater->isUpdatePrepared() ) {
-                       $updater->prepareContent( $user, $slots, [], $useCache );
+                       $updater->prepareContent( $user, $slots, $useCache );
 
                        if ( $revision ) {
                                $updater->prepareUpdate( $revision );
@@ -1987,7 +1997,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @param int &$cascade Set to false if cascading protection isn't allowed.
         * @param string $reason
         * @param User $user The user updating the restrictions
-        * @param string|string[] $tags Change tags to add to the pages and protection log entries
+        * @param string|string[]|null $tags Change tags to add to the pages and protection log entries
         *   ($user should be able to add the specified tags before this is called)
         * @return Status Status object; if action is taken, $status->value is the log_id of the
         *   protection log entry.
@@ -2403,10 +2413,10 @@ class WikiPage implements Page, IDBAccessObject {
         * @param string $reason Delete reason for deletion log
         * @param bool $suppress Suppress all revisions and log the deletion in
         *        the suppression log instead of the deletion log
-        * @param int $u1 Unused
-        * @param bool $u2 Unused
+        * @param int|null $u1 Unused
+        * @param bool|null $u2 Unused
         * @param array|string &$error Array of errors to append to
-        * @param User $user The deleting user
+        * @param User|null $user The deleting user
         * @return bool True if successful
         */
        public function doDeleteArticle(
@@ -2425,10 +2435,10 @@ class WikiPage implements Page, IDBAccessObject {
         * @param string $reason Delete reason for deletion log
         * @param bool $suppress Suppress all revisions and log the deletion in
         *   the suppression log instead of the deletion log
-        * @param int $u1 Unused
-        * @param bool $u2 Unused
+        * @param int|null $u1 Unused
+        * @param bool|null $u2 Unused
         * @param array|string &$error Array of errors to append to
-        * @param User $deleter The deleting user
+        * @param User|null $deleter The deleting user
         * @param array $tags Tags to apply to the deletion action
         * @param string $logsubtype
         * @return Status Status object; if successful, $status->value is the log_id of the
@@ -2527,30 +2537,30 @@ class WikiPage implements Page, IDBAccessObject {
                // Note array_intersect() preserves keys from the first arg, and we're
                // assuming $revQuery has `revision` primary and isn't using subtables
                // for anything we care about.
-               $tablesFlat = [];
-               array_walk_recursive(
-                       $revQuery['tables'],
-                       function ( $a ) use ( &$tablesFlat ) {
-                               $tablesFlat[] = $a;
-                       }
-               );
-
-               $res = $dbw->select(
+               $dbw->lockForUpdate(
                        array_intersect(
-                               $tablesFlat,
+                               $revQuery['tables'],
                                [ 'revision', 'revision_comment_temp', 'revision_actor_temp' ]
                        ),
-                       '1',
                        [ 'rev_page' => $id ],
                        __METHOD__,
-                       'FOR UPDATE',
+                       [],
                        $revQuery['joins']
                );
-               foreach ( $res as $row ) {
-                       // Fetch all rows in case the DB needs that to properly lock them.
+
+               // If SCHEMA_COMPAT_WRITE_OLD is set, also select all extra fields we still write,
+               // so we can copy it to the archive table.
+               // We know the fields exist, otherwise SCHEMA_COMPAT_WRITE_OLD could not function.
+               if ( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
+                       $revQuery['fields'][] = 'rev_text_id';
+
+                       if ( $wgContentHandlerUseDB ) {
+                               $revQuery['fields'][] = 'rev_content_model';
+                               $revQuery['fields'][] = 'rev_content_format';
+                       }
                }
 
-               // Get all of the page revisions
+                       // Get all of the page revisions
                $res = $dbw->select(
                        $revQuery['tables'],
                        $revQuery['fields'],
@@ -2592,17 +2602,15 @@ class WikiPage implements Page, IDBAccessObject {
                        ] + $commentStore->insert( $dbw, 'ar_comment', $comment )
                                + $actorMigration->getInsertValues( $dbw, 'ar_user', $user );
 
-                       if ( $wgMultiContentRevisionSchemaMigrationStage < MIGRATION_NEW ) {
+                       if ( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
                                $rowInsert['ar_text_id'] = $row->rev_text_id;
-                       }
 
-                       if (
-                               $wgContentHandlerUseDB &&
-                               $wgMultiContentRevisionSchemaMigrationStage <= MIGRATION_WRITE_BOTH
-                       ) {
-                               $rowInsert['ar_content_model'] = $row->rev_content_model;
-                               $rowInsert['ar_content_format'] = $row->rev_content_format;
+                               if ( $wgContentHandlerUseDB ) {
+                                       $rowInsert['ar_content_model'] = $row->rev_content_model;
+                                       $rowInsert['ar_content_format'] = $row->rev_content_format;
+                               }
                        }
+
                        $rowsInsert[] = $rowInsert;
                        $revids[] = $row->rev_id;
 
@@ -2828,7 +2836,7 @@ class WikiPage implements Page, IDBAccessObject {
         * Callers are responsible for permission checks
         * (with ChangeTags::canAddTagsAccompanyingChange)
         *
-        * @return array
+        * @return array An array of error messages, as returned by Status::getErrorsArray()
         */
        public function commitRollback( $fromP, $summary, $bot,
                &$resultDetails, User $guser, $tags = null
@@ -2841,43 +2849,44 @@ class WikiPage implements Page, IDBAccessObject {
                        return [ [ 'readonlytext' ] ];
                }
 
-               // Get the last editor
-               $current = $this->getRevision();
+               // Begin revision creation cycle by creating a PageUpdater.
+               // If the page is changed concurrently after grabParentRevision(), the rollback will fail.
+               $updater = $this->newPageUpdater( $guser );
+               $current = $updater->grabParentRevision();
+
                if ( is_null( $current ) ) {
                        // Something wrong... no page?
                        return [ [ 'notanarticle' ] ];
                }
 
+               $currentEditorForPublic = $current->getUser( RevisionRecord::FOR_PUBLIC );
+               $legacyCurrent = new Revision( $current );
                $from = str_replace( '_', ' ', $fromP );
+
                // User name given should match up with the top revision.
-               // If the user was deleted then $from should be empty.
-               if ( $from != $current->getUserText() ) {
-                       $resultDetails = [ 'current' => $current ];
+               // If the revision's user is not visible, then $from should be empty.
+               if ( $from !== ( $currentEditorForPublic ? $currentEditorForPublic->getName() : '' ) ) {
+                       $resultDetails = [ 'current' => $legacyCurrent ];
                        return [ [ 'alreadyrolled',
                                htmlspecialchars( $this->mTitle->getPrefixedText() ),
                                htmlspecialchars( $fromP ),
-                               htmlspecialchars( $current->getUserText() )
+                               htmlspecialchars( $currentEditorForPublic ? $currentEditorForPublic->getName() : '' )
                        ] ];
                }
 
                // Get the last edit not by this person...
                // Note: these may not be public values
-               $userId = intval( $current->getUser( Revision::RAW ) );
-               $userName = $current->getUserText( Revision::RAW );
-               if ( $userId ) {
-                       $user = User::newFromId( $userId );
-                       $user->setName( $userName );
-               } else {
-                       $user = User::newFromName( $current->getUserText( Revision::RAW ), false );
-               }
-
-               $actorWhere = ActorMigration::newMigration()->getWhere( $dbw, 'rev_user', $user );
+               $actorWhere = ActorMigration::newMigration()->getWhere(
+                       $dbw,
+                       'rev_user',
+                       $current->getUser( RevisionRecord::RAW )
+               );
 
                $s = $dbw->selectRow(
                        [ 'revision' ] + $actorWhere['tables'],
                        [ 'rev_id', 'rev_timestamp', 'rev_deleted' ],
                        [
-                               'rev_page' => $current->getPage(),
+                               'rev_page' => $current->getPageId(),
                                'NOT(' . $actorWhere['conds'] . ')',
                        ],
                        __METHOD__,
@@ -2890,28 +2899,36 @@ class WikiPage implements Page, IDBAccessObject {
                if ( $s === false ) {
                        // No one else ever edited this page
                        return [ [ 'cantrollback' ] ];
-               } elseif ( $s->rev_deleted & Revision::DELETED_TEXT
-                       || $s->rev_deleted & Revision::DELETED_USER
+               } elseif ( $s->rev_deleted & RevisionRecord::DELETED_TEXT
+                       || $s->rev_deleted & RevisionRecord::DELETED_USER
                ) {
                        // Only admins can see this text
                        return [ [ 'notvisiblerev' ] ];
                }
 
                // Generate the edit summary if necessary
-               $target = Revision::newFromId( $s->rev_id, Revision::READ_LATEST );
+               $target = $this->getRevisionStore()->getRevisionById(
+                       $s->rev_id,
+                       RevisionStore::READ_LATEST
+               );
                if ( empty( $summary ) ) {
-                       if ( $from == '' ) { // no public user name
+                       if ( !$currentEditorForPublic ) { // no public user name
                                $summary = wfMessage( 'revertpage-nouser' );
                        } else {
                                $summary = wfMessage( 'revertpage' );
                        }
                }
+               $legacyTarget = new Revision( $target );
+               $targetEditorForPublic = $target->getUser( RevisionRecord::FOR_PUBLIC );
 
                // Allow the custom summary to use the same args as the default message
                $args = [
-                       $target->getUserText(), $from, $s->rev_id,
+                       $targetEditorForPublic ? $targetEditorForPublic->getName() : null,
+                       $currentEditorForPublic ? $currentEditorForPublic->getName() : null,
+                       $s->rev_id,
                        $wgContLang->timeanddate( wfTimestamp( TS_MW, $s->rev_timestamp ) ),
-                       $current->getId(), $wgContLang->timeanddate( $current->getTimestamp() )
+                       $current->getId(),
+                       $wgContLang->timeanddate( $current->getTimestamp() )
                ];
                if ( $summary instanceof Message ) {
                        $summary = $summary->params( $args )->inContentLanguage()->text();
@@ -2933,22 +2950,45 @@ class WikiPage implements Page, IDBAccessObject {
                        $flags |= EDIT_FORCE_BOT;
                }
 
-               $targetContent = $target->getContent();
-               $changingContentModel = $targetContent->getModel() !== $current->getContentModel();
+               // TODO: MCR: also log model changes in other slots, in case that becomes possible!
+               $currentContent = $current->getContent( 'main' );
+               $targetContent = $target->getContent( 'main' );
+               $changingContentModel = $targetContent->getModel() !== $currentContent->getModel();
 
                if ( in_array( 'mw-rollback', ChangeTags::getSoftwareTags() ) ) {
                        $tags[] = 'mw-rollback';
                }
 
-               // Actually store the edit
-               $status = $this->doEditContent(
-                       $targetContent,
-                       $summary,
-                       $flags,
-                       $target->getId(),
-                       $guser,
-                       null,
-                       $tags
+               // Build rollback revision:
+               // Restore old content
+               // TODO: MCR: test this once we can store multiple slots
+               foreach ( $target->getSlots()->getSlots() as $slot ) {
+                       $updater->inheritSlot( $slot );
+               }
+
+               // Remove extra slots
+               // TODO: MCR: test this once we can store multiple slots
+               foreach ( $current->getSlotRoles() as $role ) {
+                       if ( !$target->hasSlot( $role ) ) {
+                               $updater->removeSlot( $role );
+                       }
+               }
+
+               $updater->setOriginalRevisionId( $target->getId() );
+               // Do not call setUndidRevisionId(), that causes an extra "mw-undo" tag to be added (T190374)
+               $updater->addTags( $tags );
+
+               // TODO: this logic should not be in the storage layer, it's here for compatibility
+               // with 1.31 behavior. Applying the 'autopatrol' right should be done in the same
+               // place the 'bot' right is handled, which is currently in EditPage::attemptSave.
+               if ( $wgUseRCPatrol && $this->getTitle()->userCan( 'autopatrol', $guser ) ) {
+                       $updater->setRcPatrolStatus( RecentChange::PRC_AUTOPATROLLED );
+               }
+
+               // Actually store the rollback
+               $rev = $updater->saveRevision(
+                       CommentStoreComment::newUnsavedComment( $summary ),
+                       $flags
                );
 
                // Set patrolling and bot flag on the edits, which gets rollbacked.
@@ -2965,10 +3005,15 @@ class WikiPage implements Page, IDBAccessObject {
                }
 
                if ( count( $set ) ) {
-                       $actorWhere = ActorMigration::newMigration()->getWhere( $dbw, 'rc_user', $user, false );
+                       $actorWhere = ActorMigration::newMigration()->getWhere(
+                               $dbw,
+                               'rc_user',
+                               $current->getUser( RevisionRecord::RAW ),
+                               false
+                       );
                        $dbw->update( 'recentchanges', $set,
                                [ /* WHERE */
-                                       'rc_cur_id' => $current->getPage(),
+                                       'rc_cur_id' => $current->getPageId(),
                                        'rc_timestamp > ' . $dbw->addQuotes( $s->rev_timestamp ),
                                        $actorWhere['conds'], // No tables/joins are needed for rc_user
                                ],
@@ -2976,18 +3021,17 @@ class WikiPage implements Page, IDBAccessObject {
                        );
                }
 
-               if ( !$status->isOK() ) {
-                       return $status->getErrorsArray();
+               if ( !$updater->wasSuccessful() ) {
+                       return $updater->getStatus()->getErrorsArray();
                }
 
-               // raise error, when the edit is an edit without a new version
-               $statusRev = $status->value['revision'] ?? null;
-               if ( !( $statusRev instanceof Revision ) ) {
-                       $resultDetails = [ 'current' => $current ];
+               // Report if the edit was not created because it did not change the content.
+               if ( $updater->isUnchanged() ) {
+                       $resultDetails = [ 'current' => $legacyCurrent ];
                        return [ [ 'alreadyrolled',
                                        htmlspecialchars( $this->mTitle->getPrefixedText() ),
                                        htmlspecialchars( $fromP ),
-                                       htmlspecialchars( $current->getUserText() )
+                                       htmlspecialchars( $targetEditorForPublic ? $targetEditorForPublic->getName() : '' )
                        ] ];
                }
 
@@ -2999,7 +3043,7 @@ class WikiPage implements Page, IDBAccessObject {
                        $log->setTarget( $this->mTitle );
                        $log->setComment( $summary );
                        $log->setParameters( [
-                               '4::oldmodel' => $current->getContentModel(),
+                               '4::oldmodel' => $currentContent->getModel(),
                                '5::newmodel' => $targetContent->getModel(),
                        ] );
 
@@ -3007,18 +3051,19 @@ class WikiPage implements Page, IDBAccessObject {
                        $log->publish( $logId );
                }
 
-               $revId = $statusRev->getId();
+               $revId = $rev->getId();
 
-               Hooks::run( 'ArticleRollbackComplete', [ $this, $guser, $target, $current ] );
+               Hooks::run( 'ArticleRollbackComplete', [ $this, $guser, $legacyTarget, $legacyCurrent ] );
 
                $resultDetails = [
                        'summary' => $summary,
-                       'current' => $current,
-                       'target' => $target,
+                       'current' => $legacyCurrent,
+                       'target' => $legacyTarget,
                        'newid' => $revId,
                        'tags' => $tags
                ];
 
+               // TODO: make this return a Status object and wrap $resultDetails in that.
                return [];
        }
 
@@ -3229,16 +3274,13 @@ class WikiPage implements Page, IDBAccessObject {
         */
        public function updateCategoryCounts( array $added, array $deleted, $id = 0 ) {
                $id = $id ?: $this->getId();
-               $ns = $this->getTitle()->getNamespace();
+               $type = MWNamespace::getCategoryLinkType( $this->getTitle()->getNamespace() );
 
                $addFields = [ 'cat_pages = cat_pages + 1' ];
                $removeFields = [ 'cat_pages = cat_pages - 1' ];
-               if ( $ns == NS_CATEGORY ) {
-                       $addFields[] = 'cat_subcats = cat_subcats + 1';
-                       $removeFields[] = 'cat_subcats = cat_subcats - 1';
-               } elseif ( $ns == NS_FILE ) {
-                       $addFields[] = 'cat_files = cat_files + 1';
-                       $removeFields[] = 'cat_files = cat_files - 1';
+               if ( $type !== 'page' ) {
+                       $addFields[] = "cat_{$type}s = cat_{$type}s + 1";
+                       $removeFields[] = "cat_{$type}s = cat_{$type}s - 1";
                }
 
                $dbw = wfGetDB( DB_MASTER );
@@ -3270,8 +3312,8 @@ class WikiPage implements Page, IDBAccessObject {
                                        $insertRows[] = [
                                                'cat_title'   => $cat,
                                                'cat_pages'   => 1,
-                                               'cat_subcats' => ( $ns == NS_CATEGORY ) ? 1 : 0,
-                                               'cat_files'   => ( $ns == NS_FILE ) ? 1 : 0,
+                                               'cat_subcats' => ( $type === 'subcat' ) ? 1 : 0,
+                                               'cat_files'   => ( $type === 'file' ) ? 1 : 0,
                                        ];
                                }
                                $dbw->upsert(