Merge "Simplify PHP by using ?? and ?:"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 11 Jul 2018 09:09:18 +0000 (09:09 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 11 Jul 2018 09:09:18 +0000 (09:09 +0000)
1  2 
includes/Block.php
includes/CommentStore.php
includes/EditPage.php
includes/Storage/RevisionStore.php
includes/api/ApiQueryAllDeletedRevisions.php
includes/api/ApiQueryImageInfo.php
includes/db/CloneDatabase.php
includes/logging/LogEntry.php

diff --combined includes/Block.php
@@@ -299,7 -299,7 +299,7 @@@ class Block 
         *     1) A block directly on the given user or IP
         *     2) A rangeblock encompassing the given IP (smallest first)
         *     3) An autoblock on the given IP
 -       * @param User|string $vagueTarget Also search for blocks affecting this target.  Doesn't
 +       * @param User|string|null $vagueTarget Also search for blocks affecting this target.  Doesn't
         *     make any sense to use TYPE_AUTO / TYPE_ID here. Leave blank to skip IP lookups.
         * @throws MWException
         * @return bool Whether a relevant block was found
        /**
         * Get a set of SQL conditions which will select rangeblocks encompassing a given range
         * @param string $start Hexadecimal IP representation
 -       * @param string $end Hexadecimal IP representation, or null to use $start = $end
 +       * @param string|null $end Hexadecimal IP representation, or null to use $start = $end
         * @return string
         */
        public static function getRangeCond( $start, $end = null ) {
         * Insert a block into the block table. Will fail if there is a conflicting
         * block (same name and options) already in the database.
         *
 -       * @param IDatabase $dbw If you have one available
 +       * @param IDatabase|null $dbw If you have one available
         * @return bool|array False on failure, assoc array on success:
         *      ('id' => block ID, 'autoIds' => array of autoblock IDs)
         */
         *     Calling this with a user, IP address or range will not select autoblocks, and will
         *     only select a block where the targets match exactly (so looking for blocks on
         *     1.2.3.4 will not select 1.2.0.0/16 or even 1.2.3.4/32)
 -       * @param string|User|int $vagueTarget As above, but we will search for *any* block which
 +       * @param string|User|int|null $vagueTarget As above, but we will search for *any* block which
         *     affects that target (so for an IP address, get ranges containing that IP; and also
         *     get any relevant autoblocks). Leave empty or blank to skip IP-based lookups.
         * @param bool $fromMaster Whether to use the DB_MASTER database
                        $reason,
                        $context->getRequest()->getIP(),
                        $this->getByName(),
-                       $systemBlockType !== null ? $systemBlockType : $this->getId(),
+                       $systemBlockType ?? $this->getId(),
                        $lang->formatExpiry( $this->mExpiry ),
                        (string)$intended,
                        $lang->userTimeAndDate( $this->mTimestamp, $context->getUser() ),
@@@ -138,7 -138,7 +138,7 @@@ class CommentStore 
         * @return string
         */
        private function getKey( $methodKey = null ) {
-               $key = $this->key !== null ? $this->key : $methodKey;
+               $key = $this->key ?? $methodKey;
                if ( $key === null ) {
                        // @codeCoverageIgnoreStart
                        throw new InvalidArgumentException( '$key should not be null' );
         *
         * @since 1.30
         * @since 1.31 Method signature changed, $key parameter added (with deprecated back compat)
 -       * @param string $key A key such as "rev_comment" identifying the comment
 +       * @param string|null $key A key such as "rev_comment" identifying the comment
         *  field being fetched.
         * @return string[] to include in the `$vars` to `IDatabase->select()`. All
         *  fields are aliased, so `+` is safe to use.
         *
         * @since 1.30
         * @since 1.31 Method signature changed, $key parameter added (with deprecated back compat)
 -       * @param string $key A key such as "rev_comment" identifying the comment
 +       * @param string|null $key A key such as "rev_comment" identifying the comment
         *  field being fetched.
         * @return array With three keys:
         *   - tables: (string[]) to include in the `$table` to `IDatabase->select()`
         * @since 1.31 Method signature changed, $key parameter added (with deprecated back compat)
         * @param string $key A key such as "rev_comment" identifying the comment
         *  field being fetched.
 -       * @param object|array $row Result row.
 +       * @param object|array|null $row Result row.
         * @param bool $fallback If true, fall back as well as possible instead of throwing an exception.
         * @return CommentStoreComment
         */
         * @param IDatabase $db Database handle to use for lookup
         * @param string $key A key such as "rev_comment" identifying the comment
         *  field being fetched.
 -       * @param object|array $row Result row.
 +       * @param object|array|null $row Result row.
         * @param bool $fallback If true, fall back as well as possible instead of throwing an exception.
         * @return CommentStoreComment
         */
                $comment = CommentStoreComment::newUnsavedComment( $comment, $data );
  
                # Truncate comment in a Unicode-sensitive manner
 -              $comment->text = $this->lang->truncate( $comment->text, self::MAX_COMMENT_LENGTH );
 -              if ( mb_strlen( $comment->text, 'UTF-8' ) > self::COMMENT_CHARACTER_LIMIT ) {
 -                      $ellipsis = wfMessage( 'ellipsis' )->inLanguage( $this->lang )->escaped();
 -                      if ( mb_strlen( $ellipsis ) >= self::COMMENT_CHARACTER_LIMIT ) {
 -                              // WTF?
 -                              $ellipsis = '...';
 -                      }
 -                      $maxLength = self::COMMENT_CHARACTER_LIMIT - mb_strlen( $ellipsis, 'UTF-8' );
 -                      $comment->text = mb_substr( $comment->text, 0, $maxLength, 'UTF-8' ) . $ellipsis;
 -              }
 +              $comment->text = $this->lang->truncateForVisual( $comment->text, self::COMMENT_CHARACTER_LIMIT );
  
                if ( $this->stage > MIGRATION_OLD && !$comment->id ) {
                        $dbData = $comment->data;
                $comment = $this->createComment( $dbw, $comment, $data );
  
                if ( $this->stage <= MIGRATION_WRITE_BOTH ) {
 -                      $fields[$key] = $this->lang->truncate( $comment->text, 255 );
 +                      $fields[$key] = $this->lang->truncateForDatabase( $comment->text, 255 );
                }
  
                if ( $this->stage >= MIGRATION_WRITE_BOTH ) {
         * @param IDatabase $dbw Database handle to insert on
         * @param string $key A key such as "rev_comment" identifying the comment
         *  field being fetched.
 -       * @param string|Message|CommentStoreComment $comment As for `self::createComment()`
 +       * @param string|Message|CommentStoreComment|null $comment As for `self::createComment()`
         * @param array|null $data As for `self::createComment()`
         * @return array Fields for the insert or update
         */
         * @param IDatabase $dbw Database handle to insert on
         * @param string $key A key such as "rev_comment" identifying the comment
         *  field being fetched.
 -       * @param string|Message|CommentStoreComment $comment As for `self::createComment()`
 +       * @param string|Message|CommentStoreComment|null $comment As for `self::createComment()`
         * @param array|null $data As for `self::createComment()`
         * @return array Two values:
         *  - array Fields for the insert or update
diff --combined includes/EditPage.php
@@@ -948,7 -948,12 +948,7 @@@ class EditPage 
                        } else {
                                // If we receive the last parameter of the request, we can fairly
                                // claim the POST request has not been truncated.
 -
 -                              // TODO: softened the check for cutover.  Once we determine
 -                              // that it is safe, we should complete the transition by
 -                              // removing the "edittime" clause.
 -                              $this->incompleteForm = ( !$request->getVal( 'wpUltimateParam' )
 -                                      && is_null( $this->edittime ) );
 +                              $this->incompleteForm = !$request->getVal( 'wpUltimateParam' );
                        }
                        if ( $this->incompleteForm ) {
                                # If the form is incomplete, force to preview.
@@@ -3193,8 -3198,8 +3193,8 @@@ ERROR
         * Builds a standard summary input with a label.
         *
         * @param string $summary The value of the summary input
 -       * @param string $labelText The html to place inside the label
 -       * @param array $inputAttrs Array of attrs to use on the input
 +       * @param string|null $labelText The html to place inside the label
 +       * @param array|null $inputAttrs Array of attrs to use on the input
         *
         * @return OOUI\FieldLayout OOUI FieldLayout with Label and Input
         */
         * The $textoverride method can be used by subclasses overriding showContentForm
         * to pass back to this method.
         *
 -       * @param array $customAttribs Array of html attributes to use in the textarea
 -       * @param string $textoverride Optional text to override $this->textarea1 with
 +       * @param array|null $customAttribs Array of html attributes to use in the textarea
 +       * @param string|null $textoverride Optional text to override $this->textarea1 with
         */
        protected function showTextbox1( $customAttribs = null, $textoverride = null ) {
                if ( $this->wasDeletedSinceLastEdit() && $this->formtype == 'save' ) {
                }
  
                $this->showTextbox(
-                       $textoverride !== null ? $textoverride : $this->textbox1,
+                       $textoverride ?? $this->textbox1,
                        'wpTextbox1',
                        $attribs
                );
         * Shows a bulletin board style toolbar for common editing functions.
         * It can be disabled in the user preferences.
         *
 -       * @param Title $title Title object for the page being edited (optional)
 +       * @param Title|null $title Title object for the page being edited (optional)
         * @return string
         */
        public static function getEditToolbar( $title = null ) {
@@@ -122,7 -122,7 +122,7 @@@ class RevisionStor
         */
        private $slotRoleStore;
  
 -      /** @var int One of the MIGRATION_* constants */
 +      /** @var int An appropriate combination of SCHEMA_COMPAT_XXX flags. */
        private $mcrMigrationStage;
  
        /**
         * @param CommentStore $commentStore
         * @param NameTableStore $contentModelStore
         * @param NameTableStore $slotRoleStore
 -       * @param int $migrationStage
 +       * @param int $mcrMigrationStage An appropriate combination of SCHEMA_COMPAT_XXX flags
         * @param ActorMigration $actorMigration
         * @param bool|string $wikiId
 +       *
 +       * @throws MWException if $mcrMigrationStage or $wikiId is invalid.
         */
        public function __construct(
                LoadBalancer $loadBalancer,
                CommentStore $commentStore,
                NameTableStore $contentModelStore,
                NameTableStore $slotRoleStore,
 -              $migrationStage,
 +              $mcrMigrationStage,
                ActorMigration $actorMigration,
                $wikiId = false
        ) {
                Assert::parameterType( 'string|boolean', $wikiId, '$wikiId' );
 -              Assert::parameterType( 'integer', $migrationStage, '$migrationStage' );
 +              Assert::parameterType( 'integer', $mcrMigrationStage, '$mcrMigrationStage' );
 +              Assert::parameter(
 +                      ( $mcrMigrationStage & SCHEMA_COMPAT_READ_BOTH ) !== SCHEMA_COMPAT_READ_BOTH,
 +                      '$mcrMigrationStage',
 +                      'Reading from the old and the new schema at the same time is not supported.'
 +              );
 +              Assert::parameter(
 +                      ( $mcrMigrationStage & SCHEMA_COMPAT_READ_BOTH ) !== 0,
 +                      '$mcrMigrationStage',
 +                      'Reading needs to be enabled for the old or the new schema.'
 +              );
 +              Assert::parameter(
 +                      ( $mcrMigrationStage & SCHEMA_COMPAT_WRITE_BOTH ) !== 0,
 +                      '$mcrMigrationStage',
 +                      'Writing needs to be enabled for the old or the new schema.'
 +              );
 +              Assert::parameter(
 +                      ( $mcrMigrationStage & SCHEMA_COMPAT_READ_OLD ) === 0
 +                      || ( $mcrMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) !== 0,
 +                      '$mcrMigrationStage',
 +                      'Cannot read the old schema when not also writing it.'
 +              );
 +              Assert::parameter(
 +                      ( $mcrMigrationStage & SCHEMA_COMPAT_READ_NEW ) === 0
 +                      || ( $mcrMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) !== 0,
 +                      '$mcrMigrationStage',
 +                      'Cannot read the new schema when not also writing it.'
 +              );
  
                $this->loadBalancer = $loadBalancer;
                $this->blobStore = $blobStore;
                $this->commentStore = $commentStore;
                $this->contentModelStore = $contentModelStore;
                $this->slotRoleStore = $slotRoleStore;
 -              $this->mcrMigrationStage = $migrationStage;
 +              $this->mcrMigrationStage = $mcrMigrationStage;
                $this->actorMigration = $actorMigration;
                $this->wikiId = $wikiId;
                $this->logger = new NullLogger();
        }
  
 +      /**
 +       * @param int $flags A combination of SCHEMA_COMPAT_XXX flags.
 +       * @return bool True if all the given flags were set in the $mcrMigrationStage
 +       *         parameter passed to the constructor.
 +       */
 +      private function hasMcrSchemaFlags( $flags ) {
 +              return ( $this->mcrMigrationStage & $flags ) === $flags;
 +      }
 +
        public function setLogger( LoggerInterface $logger ) {
                $this->logger = $logger;
        }
         * @throws MWException
         */
        public function setContentHandlerUseDB( $contentHandlerUseDB ) {
 -              if ( !$contentHandlerUseDB && $this->mcrMigrationStage > MIGRATION_OLD ) {
 -                      throw new MWException(
 -                              'Content model must be stored in the database for multi content revision migration.'
 -                      );
 +              if ( $this->hasMcrSchemaFlags( SCHEMA_COMPAT_WRITE_NEW )
 +                      || $this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_NEW )
 +              ) {
 +                      if ( !$contentHandlerUseDB ) {
 +                              throw new MWException(
 +                                      'Content model must be stored in the database for multi content revision migration.'
 +                              );
 +                      }
                }
                $this->contentHandlerUseDB = $contentHandlerUseDB;
        }
                        );
                }
  
 -              // While inserting into the old schema make sure only the main slot is allowed.
 -              // TODO: support extra slots in MIGRATION_WRITE_BOTH mode!
 -              if ( $this->mcrMigrationStage <= MIGRATION_WRITE_BOTH && $slotRoles !== [ 'main' ] ) {
 +              // If we are not writing into the new schema, we can't support extra slots.
 +              if ( !$this->hasMcrSchemaFlags( SCHEMA_COMPAT_WRITE_NEW ) && $slotRoles !== [ 'main' ] ) {
 +                      throw new InvalidArgumentException(
 +                              'Only the main slot is supported when not writing to the MCR enabled schema!'
 +                      );
 +              }
 +
 +              // As long as we are not reading from the new schema, we don't want to write extra slots.
 +              if ( !$this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_NEW ) && $slotRoles !== [ 'main' ] ) {
                        throw new InvalidArgumentException(
 -                              'Only the main slot is supported with MCR migration mode <= MIGRATION_WRITE_BOTH!'
 +                              'Only the main slot is supported when not reading from the MCR enabled schema!'
                        );
                }
  
                );
  
                // Trigger exception if the main slot is missing.
 -              // Technically, this could go away with MIGRATION_NEW: while
 +              // Technically, this could go away after MCR migration: while
                // calling code may require a main slot to exist, RevisionStore
                // really should not know or care about that requirement.
                $rev->getSlot( 'main', RevisionRecord::RAW );
                                $newSlots[$role] = $slot;
  
                                // Write the main slot's text ID to the revision table for backwards compatibility
 -                              if ( $slot->getRole() === 'main' && $this->mcrMigrationStage <= MIGRATION_WRITE_BOTH ) {
 +                              if ( $slot->getRole() === 'main'
 +                                      && $this->hasMcrSchemaFlags( SCHEMA_COMPAT_WRITE_OLD )
 +                              ) {
                                        $blobAddress = $slot->getAddress();
                                        $this->updateRevisionTextId( $dbw, $revisionId, $blobAddress );
                                }
                }
  
                // Write the main slot's text ID to the revision table for backwards compatibility
 -              if ( $protoSlot->getRole() === 'main' && $this->mcrMigrationStage <= MIGRATION_WRITE_BOTH ) {
 +              if ( $protoSlot->getRole() === 'main'
 +                      && $this->hasMcrSchemaFlags( SCHEMA_COMPAT_WRITE_OLD )
 +              ) {
                        $this->updateRevisionTextId( $dbw, $revisionId, $blobAddress );
                }
  
 -              if ( $this->mcrMigrationStage >= MIGRATION_WRITE_BOTH ) {
 +              if ( $this->hasMcrSchemaFlags( SCHEMA_COMPAT_WRITE_NEW ) ) {
                        if ( $protoSlot->hasContentId() ) {
                                $contentId = $protoSlot->getContentId();
                        } else {
                        $revisionRow['rev_id'] = $rev->getId();
                }
  
 -              if ( $this->mcrMigrationStage <= MIGRATION_WRITE_BOTH ) {
 -                      // In non MCR more this IF section will relate to the main slot
 +              if ( $this->hasMcrSchemaFlags( SCHEMA_COMPAT_WRITE_OLD ) ) {
 +                      // In non MCR mode this IF section will relate to the main slot
                        $mainSlot = $rev->getSlot( 'main' );
                        $model = $mainSlot->getModel();
                        $format = $mainSlot->getFormat();
                        return null;
                }
  
 -              // Fetch the actual revision row, without locking all extra tables.
 -              $oldRevision = $this->loadRevisionFromId( $dbw, $pageLatest );
 +              // Fetch the actual revision row from master, without locking all extra tables.
 +              $oldRevision = $this->loadRevisionFromConds(
 +                      $dbw,
 +                      [ 'rev_id' => intval( $pageLatest ) ],
 +                      self::READ_LATEST,
 +                      $title
 +              );
  
                // Construct the new revision
                $timestamp = wfTimestampNow(); // TODO: use a callback, so we can override it for testing.
                $blobFlags = null;
  
                if ( is_object( $row ) ) {
 -                      if ( $this->mcrMigrationStage >= MIGRATION_NEW ) {
 +                      if ( $this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_NEW ) ) {
                                // Don't emulate from a row when using the new schema.
                                // Emulating from an array is still OK.
                                throw new LogicException( 'Can\'t emulate the main slot when using MCR schema.' );
                                if ( !property_exists( $row, 'old_flags' ) ) {
                                        throw new InvalidArgumentException( 'old_flags was not set in $row' );
                                }
-                               $blobFlags = ( $row->old_flags === null ) ? '' : $row->old_flags;
+                               $blobFlags = $row->old_flags ?? '';
                        }
  
                        $mainSlotRow->slot_revision_id = intval( $row->rev_id );
                $queryFlags,
                Title $title
        ) {
 -              if ( $this->mcrMigrationStage < MIGRATION_NEW ) {
 -                      // TODO: in MIGRATION_WRITE_BOTH, we could use the old and the new method:
 -                      // e.g. call emulateMainSlot_1_29() if loadSlotRecords() fails.
 -
 +              if ( !$this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_NEW ) ) {
                        $mainSlot = $this->emulateMainSlot_1_29( $revisionRow, $queryFlags, $title );
                        $slots = new RevisionSlots( [ 'main' => $mainSlot ] );
                } else {
                }
  
                if ( !empty( $fields['text_id'] ) ) {
 -                      if ( $this->mcrMigrationStage >= MIGRATION_NEW ) {
 -                              throw new MWException( "Cannot use text_id field with MCR schema" );
 +                      if ( !$this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_OLD ) ) {
 +                              throw new MWException( "The text_id field is only available in the pre-MCR schema" );
                        }
  
                        if ( !empty( $fields['content'] ) ) {
         *
         * @param array $conditions
         * @param int $flags (optional)
 -       * @param Title $title
 +       * @param Title|null $title
         *
         * @return RevisionRecord|null
         */
         * @param IDatabase $db
         * @param array $conditions
         * @param int $flags (optional)
 -       * @param Title $title
 +       * @param Title|null $title
         *
         * @return RevisionRecord|null
         */
        /**
         * Finds the ID of a content row for a given revision and slot role.
         * This can be used to re-use content rows even while the content ID
 -       * is still missing from SlotRecords, in MIGRATION_WRITE_BOTH mode.
 +       * is still missing from SlotRecords, when writing to both the old and
 +       * the new schema during MCR schema migration.
         *
         * @todo remove after MCR schema migration is complete.
         *
         * @return int|null
         */
        private function findSlotContentId( IDatabase $db, $revId, $role ) {
 -              if ( $this->mcrMigrationStage < MIGRATION_WRITE_BOTH ) {
 +              if ( !$this->hasMcrSchemaFlags( SCHEMA_COMPAT_WRITE_NEW ) ) {
                        return null;
                }
  
         *  - 'page': Join with the page table, and select fields to identify the page
         *  - 'user': Join with the user table, and select the user name
         *  - 'text': Join with the text table, and select fields to load page text. This
 -       *    option is deprecated in MW 1.32 with MCR migration stage MIGRATION_WRITE_BOTH,
 -       *    and disallowed with MIGRATION_MEW.
 +       *    option is deprecated in MW 1.32 when the MCR migration flag SCHEMA_COMPAT_WRITE_NEW
 +       *    is set, and disallowed when SCHEMA_COMPAT_READ_OLD is not set.
         *
         * @return array With three keys:
         *  - tables: (string[]) to include in the `$table` to `IDatabase->select()`
                $ret['fields'] = array_merge( $ret['fields'], $actorQuery['fields'] );
                $ret['joins'] = array_merge( $ret['joins'], $actorQuery['joins'] );
  
 -              if ( $this->mcrMigrationStage < MIGRATION_NEW ) {
 +              if ( $this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_OLD ) ) {
                        $ret['fields'][] = 'rev_text_id';
  
                        if ( $this->contentHandlerUseDB ) {
                }
  
                if ( in_array( 'text', $options, true ) ) {
 -                      if ( $this->mcrMigrationStage === MIGRATION_NEW ) {
 +                      if ( !$this->hasMcrSchemaFlags( SCHEMA_COMPAT_WRITE_OLD ) ) {
                                throw new InvalidArgumentException( 'text table can no longer be joined directly' );
 -                      } elseif ( $this->mcrMigrationStage >= MIGRATION_WRITE_BOTH ) {
 +                      } elseif ( !$this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_OLD ) ) {
 +                              // NOTE: even when this class is set to not read from the old schema, callers
 +                              // should still be able to join against the text table, as long as we are still
 +                              // writing the old schema for compatibility.
                                wfDeprecated( __METHOD__ . ' with `text` option', '1.32' );
                        }
  
                        'joins'  => [],
                ];
  
 -              if ( $this->mcrMigrationStage < MIGRATION_NEW ) {
 +              if ( $this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_OLD ) ) {
                        $db = $this->getDBConnectionRef( DB_REPLICA );
                        $ret['tables']['slots'] = 'revision';
  
                                        $ret['fields']['model_name'] = 'NULL';
                                }
                        }
 -
 -                      // XXX: in MIGRATION_WRITE_BOTH mode, emulate *and* select - using a UNION?
 -                      // See Anomie's idea at <https://gerrit.wikimedia.org/r/c/416465/
 -                      // 8..10/includes/Storage/RevisionStore.php#2113>
                } else {
                        $ret['tables'][] = 'slots';
                        $ret['tables'][] = 'slot_roles';
                        'joins' => $commentQuery['joins'] + $actorQuery['joins'],
                ];
  
 -              if ( $this->mcrMigrationStage < MIGRATION_NEW ) {
 +              if ( $this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_OLD ) ) {
                        $ret['fields'][] = 'ar_text_id';
  
                        if ( $this->contentHandlerUseDB ) {
         * MCR migration note: this replaces Revision::getPrevious
         *
         * @param RevisionRecord $rev
 -       * @param Title $title if known (optional)
 +       * @param Title|null $title if known (optional)
         *
         * @return RevisionRecord|null
         */
         * MCR migration note: this replaces Revision::getNext
         *
         * @param RevisionRecord $rev
 -       * @param Title $title if known (optional)
 +       * @param Title|null $title if known (optional)
         *
         * @return RevisionRecord|null
         */
@@@ -35,7 -35,7 +35,7 @@@ class ApiQueryAllDeletedRevisions exten
        }
  
        /**
 -       * @param ApiPageSet $resultPageSet
 +       * @param ApiPageSet|null $resultPageSet
         * @return void
         */
        protected function run( ApiPageSet $resultPageSet = null ) {
                $miser_ns = null;
  
                if ( $mode == 'all' ) {
-                       if ( $params['namespace'] !== null ) {
-                               $namespaces = $params['namespace'];
-                       } else {
-                               $namespaces = MWNamespace::getValidNamespaces();
-                       }
+                       $namespaces = $params['namespace'] ?? MWNamespace::getValidNamespaces();
                        $this->addWhereFld( 'ar_namespace', $namespaces );
  
                        // For from/to/prefix, we have to consider the potential
@@@ -127,7 -127,7 +127,7 @@@ class ApiQueryImageInfo extends ApiQuer
                                        if ( count( $pageIds[NS_FILE] ) == 1 ) {
                                                // See the 'the user is screwed' comment below
                                                $this->setContinueEnumParameter( 'start',
-                                                       $start !== null ? $start : wfTimestamp( TS_ISO_8601, $img->getTimestamp() )
+                                                       $start ?? wfTimestamp( TS_ISO_8601, $img->getTimestamp() )
                                                );
                                        } else {
                                                $this->setContinueEnumParameter( 'continue',
                                                // thing again. When the violating queries have been
                                                // out-continued, the result will get through
                                                $this->setContinueEnumParameter( 'start',
-                                                       $start !== null ? $start : wfTimestamp( TS_ISO_8601, $img->getTimestamp() )
+                                                       $start ?? wfTimestamp( TS_ISO_8601, $img->getTimestamp() )
                                                );
                                        } else {
                                                $this->setContinueEnumParameter( 'continue',
         * @param File $file
         * @param array $prop Array of properties to get (in the keys)
         * @param ApiResult $result
 -       * @param array $thumbParams Containing 'width' and 'height' items, or null
 +       * @param array|null $thumbParams Containing 'width' and 'height' items, or null
         * @param array|bool|string $opts Options for data fetching.
         *   This is an array consisting of the keys:
         *    'version': The metadata version for the metadata option
@@@ -46,7 -46,7 +46,7 @@@ class CloneDatabase 
         * @param IMaintainableDatabase $db A database subclass
         * @param array $tablesToClone An array of tables to clone, unprefixed
         * @param string $newTablePrefix Prefix to assign to the tables
 -       * @param string $oldTablePrefix Prefix on current tables, if not $wgDBprefix
 +       * @param string|null $oldTablePrefix Prefix on current tables, if not $wgDBprefix
         * @param bool $dropCurrentTables
         */
        public function __construct( IMaintainableDatabase $db, array $tablesToClone,
@@@ -55,7 -55,7 +55,7 @@@
                $this->db = $db;
                $this->tablesToClone = $tablesToClone;
                $this->newTablePrefix = $newTablePrefix;
-               $this->oldTablePrefix = $oldTablePrefix !== null ? $oldTablePrefix : $this->db->tablePrefix();
+               $this->oldTablePrefix = $oldTablePrefix ?? $this->db->tablePrefix();
                $this->dropCurrentTables = $dropCurrentTables;
        }
  
                        # works correctly across DB engines, we need to change the pre-
                        # fix back and forth so tableName() works right.
  
 -                      self::changePrefix( $this->oldTablePrefix );
 +                      $this->db->tablePrefix( $this->oldTablePrefix );
                        $oldTableName = $this->db->tableName( $tbl, 'raw' );
  
 -                      self::changePrefix( $this->newTablePrefix );
 +                      $this->db->tablePrefix( $this->newTablePrefix );
                        $newTableName = $this->db->tableName( $tbl, 'raw' );
  
                        // Postgres: Temp tables are automatically deleted upon end of session
         */
        public function destroy( $dropTables = false ) {
                if ( $dropTables ) {
 -                      self::changePrefix( $this->newTablePrefix );
 +                      $this->db->tablePrefix( $this->newTablePrefix );
                        foreach ( $this->tablesToClone as $tbl ) {
                                $this->db->dropTable( $tbl );
                        }
                }
 -              self::changePrefix( $this->oldTablePrefix );
 +              $this->db->tablePrefix( $this->oldTablePrefix );
        }
  
        /**
@@@ -625,7 -625,7 +625,7 @@@ class ManualLogEntry extends LogEntryBa
        /**
         * Insert the entry into the `logging` table.
         *
 -       * @param IDatabase $dbw
 +       * @param IDatabase|null $dbw
         * @return int ID of the log entry
         * @throws MWException
         */
        }
  
        public function getTimestamp() {
-               $ts = $this->timestamp !== null ? $this->timestamp : wfTimestampNow();
+               $ts = $this->timestamp ?? wfTimestampNow();
  
                return wfTimestamp( TS_MW, $ts );
        }