Add missing samp tags and closing kbd tag
[lhc/web/wiklou.git] / includes / deferred / LinksUpdate.php
index 9cd20fc..1f7f3b0 100644 (file)
  */
 
 /**
- * See docs/deferred.txt
+ * Class the manages updates of *_link tables as well as similar extension-managed tables
+ *
+ * @note: LinksUpdate is managed by DeferredUpdates::execute(). Do not run this in a transaction.
  *
- * @todo document (e.g. one-sentence top-level class description).
+ * See docs/deferred.txt
  */
 class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
        // @todo make members protected, but make sure extensions don't break
@@ -55,6 +57,9 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
        /** @var array Map of language codes to titles */
        public $mInterlangs;
 
+       /** @var array 2-D map of (prefix => DBK => 1) */
+       public $mInterwikis;
+
        /** @var array Map of arbitrary name to value */
        public $mProperties;
 
@@ -79,6 +84,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         */
        private $user;
 
+       const BATCH_SIZE = 500; // try to keep typical updates in a single transaction
+
        /**
         * Constructor
         *
@@ -88,7 +95,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @throws MWException
         */
        function __construct( Title $title, ParserOutput $parserOutput, $recursive = true ) {
-               parent::__construct( false ); // no implicit transaction
+               // Implicit transactions are disabled as they interfere with batching
+               parent::__construct( false );
 
                $this->mTitle = $title;
                $this->mId = $title->getArticleID( Title::GAID_FOR_UPDATE );
@@ -114,7 +122,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                # the way back to the skin, so either a skin API break would be required, or an
                # inefficient back-conversion.
                $ill = $parserOutput->getLanguageLinks();
-               $this->mInterlangs = array();
+               $this->mInterlangs = [];
                foreach ( $ill as $link ) {
                        list( $key, $title ) = explode( ':', $link, 2 );
                        $this->mInterlangs[$key] = $title;
@@ -133,21 +141,51 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
 
                $this->mRecursive = $recursive;
 
-               Hooks::run( 'LinksUpdateConstructed', array( &$this ) );
+               Hooks::run( 'LinksUpdateConstructed', [ &$this ] );
        }
 
        /**
         * Update link tables with outgoing links from an updated article
+        *
+        * @note: this is managed by DeferredUpdates::execute(). Do not run this in a transaction.
         */
        public function doUpdate() {
-               Hooks::run( 'LinksUpdate', array( &$this ) );
+               // Make sure all links update threads see the changes of each other.
+               // This handles the case when updates have to batched into several COMMITs.
+               $scopedLock = self::acquirePageLock( $this->mDb, $this->mId );
+
+               Hooks::run( 'LinksUpdate', [ &$this ] );
                $this->doIncrementalUpdate();
 
-               $this->mDb->onTransactionIdle( function() {
-                       Hooks::run( 'LinksUpdateComplete', array( &$this ) );
+               $this->mDb->onTransactionIdle( function() use ( &$scopedLock ) {
+                       Hooks::run( 'LinksUpdateComplete', [ &$this ] );
+                       // Release the lock *after* the final COMMIT for correctness
+                       ScopedCallback::consume( $scopedLock );
                } );
        }
 
+       /**
+        * Acquire a lock for performing link table updates for a page on a DB
+        *
+        * @param IDatabase $dbw
+        * @param integer $pageId
+        * @return ScopedCallback|null Returns null on failure
+        * @throws RuntimeException
+        * @since 1.27
+        */
+       public static function acquirePageLock( IDatabase $dbw, $pageId ) {
+               $scopedLock = $dbw->getScopedLockAndFlush(
+                       "LinksUpdate:pageid:$pageId",
+                       __METHOD__,
+                       15
+               );
+               if ( !$scopedLock ) {
+                       throw new RuntimeException( "Could not acquire lock on page #$pageId." );
+               }
+
+               return $scopedLock;
+       }
+
        protected function doIncrementalUpdate() {
                # Page links
                $existing = $this->getExistingLinks();
@@ -157,7 +195,6 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
 
                # Image links
                $existing = $this->getExistingImages();
-
                $imageDeletes = $this->getImageDeletions( $existing );
                $this->incrTableUpdate( 'imagelinks', 'il', $imageDeletes,
                        $this->getImageInsertions( $existing ) );
@@ -188,9 +225,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
 
                # Category links
                $existing = $this->getExistingCategories();
-
                $categoryDeletes = $this->getCategoryDeletions( $existing );
-
                $this->incrTableUpdate( 'categorylinks', 'cl', $categoryDeletes,
                        $this->getCategoryInsertions( $existing ) );
 
@@ -202,9 +237,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
 
                # Page properties
                $existing = $this->getExistingProperties();
-
                $propertiesDeletes = $this->getPropertyDeletions( $existing );
-
                $this->incrTableUpdate( 'page_props', 'pp', $propertiesDeletes,
                        $this->getPropertyInsertions( $existing ) );
 
@@ -242,9 +275,9 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                // should be implicitly protected as soon as possible, if applicable.
                // These jobs duplicate a subset of the above ones, but can run sooner.
                // Which ever runs first generally no-ops the other one.
-               $jobs = array();
+               $jobs = [];
                foreach ( $bc->getCascadeProtectedLinks() as $title ) {
-                       $jobs[] = RefreshLinksJob::newPrioritized( $title, array() );
+                       $jobs[] = RefreshLinksJob::newPrioritized( $title, [] );
                }
                JobQueueGroup::singleton()->push( $jobs );
        }
@@ -259,10 +292,10 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                if ( $title->getBacklinkCache()->hasLinks( $table ) ) {
                        $job = new RefreshLinksJob(
                                $title,
-                               array(
+                               [
                                        'table' => $table,
                                        'recursive' => true,
-                               ) + Job::newRootJobParams( // "overall" refresh links job info
+                               ] + Job::newRootJobParams( // "overall" refresh links job info
                                        "refreshlinks:{$table}:{$title->getPrefixedText()}"
                                )
                        );
@@ -304,45 +337,70 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $deletions
         * @param array $insertions Rows to insert
         */
-       function incrTableUpdate( $table, $prefix, $deletions, $insertions ) {
-               if ( $table == 'page_props' ) {
+       private function incrTableUpdate( $table, $prefix, $deletions, $insertions ) {
+               if ( $table === 'page_props' ) {
                        $fromField = 'pp_page';
                } else {
                        $fromField = "{$prefix}_from";
                }
-               $where = array( $fromField => $this->mId );
-               if ( $table == 'pagelinks' || $table == 'templatelinks' || $table == 'iwlinks' ) {
-                       if ( $table == 'iwlinks' ) {
-                               $baseKey = 'iwl_prefix';
-                       } else {
-                               $baseKey = "{$prefix}_namespace";
+
+               $deleteWheres = []; // list of WHERE clause arrays for each DB delete() call
+               if ( $table === 'pagelinks' || $table === 'templatelinks' || $table === 'iwlinks' ) {
+                       $baseKey =  ( $table === 'iwlinks' ) ? 'iwl_prefix' : "{$prefix}_namespace";
+
+                       $curBatchSize = 0;
+                       $curDeletionBatch = [];
+                       $deletionBatches = [];
+                       foreach ( $deletions as $ns => $dbKeys ) {
+                               foreach ( $dbKeys as $dbKey => $unused ) {
+                                       $curDeletionBatch[$ns][$dbKey] = 1;
+                                       if ( ++$curBatchSize >= self::BATCH_SIZE ) {
+                                               $deletionBatches[] = $curDeletionBatch;
+                                               $curDeletionBatch = [];
+                                               $curBatchSize = 0;
+                                       }
+                               }
                        }
-                       $clause = $this->mDb->makeWhereFrom2d( $deletions, $baseKey, "{$prefix}_title" );
-                       if ( $clause ) {
-                               $where[] = $clause;
-                       } else {
-                               $where = false;
+                       if ( $curDeletionBatch ) {
+                               $deletionBatches[] = $curDeletionBatch;
+                       }
+
+                       foreach ( $deletionBatches as $deletionBatch ) {
+                               $deleteWheres[] = [
+                                       $fromField => $this->mId,
+                                       $this->mDb->makeWhereFrom2d( $deletionBatch, $baseKey, "{$prefix}_title" )
+                               ];
                        }
                } else {
-                       if ( $table == 'langlinks' ) {
+                       if ( $table === 'langlinks' ) {
                                $toField = 'll_lang';
-                       } elseif ( $table == 'page_props' ) {
+                       } elseif ( $table === 'page_props' ) {
                                $toField = 'pp_propname';
                        } else {
                                $toField = $prefix . '_to';
                        }
-                       if ( count( $deletions ) ) {
-                               $where[$toField] = array_keys( $deletions );
-                       } else {
-                               $where = false;
+
+                       $deletionBatches = array_chunk( array_keys( $deletions ), self::BATCH_SIZE );
+                       foreach ( $deletionBatches as $deletionBatch ) {
+                               $deleteWheres[] = [ $fromField => $this->mId, $toField => $deletionBatch ];
                        }
                }
-               if ( $where ) {
-                       $this->mDb->delete( $table, $where, __METHOD__ );
+
+               foreach ( $deleteWheres as $deleteWhere ) {
+                       $this->mDb->delete( $table, $deleteWhere, __METHOD__ );
+                       $this->mDb->commit( __METHOD__, 'flush' );
+                       wfGetLBFactory()->waitForReplication( [ 'wiki' => $this->mDb->getWikiID() ] );
                }
+
+               $insertBatches = array_chunk( $insertions, self::BATCH_SIZE );
+               foreach ( $insertBatches as $insertBatch ) {
+                       $this->mDb->insert( $table, $insertBatch, __METHOD__, 'IGNORE' );
+                       $this->mDb->commit( __METHOD__, 'flush' );
+                       wfGetLBFactory()->waitForReplication( [ 'wiki' => $this->mDb->getWikiID() ] );
+               }
+
                if ( count( $insertions ) ) {
-                       $this->mDb->insert( $table, $insertions, __METHOD__, 'IGNORE' );
-                       Hooks::run( 'LinksUpdateAfterInsert', array( $this, $table, $insertions ) );
+                       Hooks::run( 'LinksUpdateAfterInsert', [ $this, $table, $insertions ] );
                }
        }
 
@@ -352,19 +410,19 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $existing
         * @return array
         */
-       private function getLinkInsertions( $existing = array() ) {
-               $arr = array();
+       private function getLinkInsertions( $existing = [] ) {
+               $arr = [];
                foreach ( $this->mLinks as $ns => $dbkeys ) {
                        $diffs = isset( $existing[$ns] )
                                ? array_diff_key( $dbkeys, $existing[$ns] )
                                : $dbkeys;
                        foreach ( $diffs as $dbk => $id ) {
-                               $arr[] = array(
+                               $arr[] = [
                                        'pl_from' => $this->mId,
                                        'pl_from_namespace' => $this->mTitle->getNamespace(),
                                        'pl_namespace' => $ns,
                                        'pl_title' => $dbk
-                               );
+                               ];
                        }
                }
 
@@ -376,17 +434,17 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $existing
         * @return array
         */
-       private function getTemplateInsertions( $existing = array() ) {
-               $arr = array();
+       private function getTemplateInsertions( $existing = [] ) {
+               $arr = [];
                foreach ( $this->mTemplates as $ns => $dbkeys ) {
                        $diffs = isset( $existing[$ns] ) ? array_diff_key( $dbkeys, $existing[$ns] ) : $dbkeys;
                        foreach ( $diffs as $dbk => $id ) {
-                               $arr[] = array(
+                               $arr[] = [
                                        'tl_from' => $this->mId,
                                        'tl_from_namespace' => $this->mTitle->getNamespace(),
                                        'tl_namespace' => $ns,
                                        'tl_title' => $dbk
-                               );
+                               ];
                        }
                }
 
@@ -399,15 +457,15 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $existing
         * @return array
         */
-       private function getImageInsertions( $existing = array() ) {
-               $arr = array();
+       private function getImageInsertions( $existing = [] ) {
+               $arr = [];
                $diffs = array_diff_key( $this->mImages, $existing );
                foreach ( $diffs as $iname => $dummy ) {
-                       $arr[] = array(
+                       $arr[] = [
                                'il_from' => $this->mId,
                                'il_from_namespace' => $this->mTitle->getNamespace(),
                                'il_to' => $iname
-                       );
+                       ];
                }
 
                return $arr;
@@ -418,17 +476,17 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $existing
         * @return array
         */
-       private function getExternalInsertions( $existing = array() ) {
-               $arr = array();
+       private function getExternalInsertions( $existing = [] ) {
+               $arr = [];
                $diffs = array_diff_key( $this->mExternals, $existing );
                foreach ( $diffs as $url => $dummy ) {
                        foreach ( wfMakeUrlIndexes( $url ) as $index ) {
-                               $arr[] = array(
+                               $arr[] = [
                                        'el_id' => $this->mDb->nextSequenceValue( 'externallinks_el_id_seq' ),
                                        'el_from' => $this->mId,
                                        'el_to' => $url,
                                        'el_index' => $index,
-                               );
+                               ];
                        }
                }
 
@@ -443,10 +501,10 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         *
         * @return array
         */
-       private function getCategoryInsertions( $existing = array() ) {
+       private function getCategoryInsertions( $existing = [] ) {
                global $wgContLang, $wgCategoryCollation;
                $diffs = array_diff_assoc( $this->mCategories, $existing );
-               $arr = array();
+               $arr = [];
                foreach ( $diffs as $name => $prefix ) {
                        $nt = Title::makeTitleSafe( NS_CATEGORY, $name );
                        $wgContLang->findVariantLink( $name, $nt, true );
@@ -466,7 +524,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                        $sortkey = Collation::singleton()->getSortKey(
                                $this->mTitle->getCategorySortkey( $prefix ) );
 
-                       $arr[] = array(
+                       $arr[] = [
                                'cl_from' => $this->mId,
                                'cl_to' => $name,
                                'cl_sortkey' => $sortkey,
@@ -474,7 +532,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                                'cl_sortkey_prefix' => $prefix,
                                'cl_collation' => $wgCategoryCollation,
                                'cl_type' => $type,
-                       );
+                       ];
                }
 
                return $arr;
@@ -487,15 +545,15 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         *
         * @return array
         */
-       private function getInterlangInsertions( $existing = array() ) {
+       private function getInterlangInsertions( $existing = [] ) {
                $diffs = array_diff_assoc( $this->mInterlangs, $existing );
-               $arr = array();
+               $arr = [];
                foreach ( $diffs as $lang => $title ) {
-                       $arr[] = array(
+                       $arr[] = [
                                'll_from' => $this->mId,
                                'll_lang' => $lang,
                                'll_title' => $title
-                       );
+                       ];
                }
 
                return $arr;
@@ -506,10 +564,10 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $existing
         * @return array
         */
-       function getPropertyInsertions( $existing = array() ) {
+       function getPropertyInsertions( $existing = [] ) {
                $diffs = array_diff_assoc( $this->mProperties, $existing );
 
-               $arr = array();
+               $arr = [];
                foreach ( array_keys( $diffs ) as $name ) {
                        $arr[] = $this->getPagePropRowData( $name );
                }
@@ -538,11 +596,11 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
 
                $value = $this->mProperties[$prop];
 
-               $row = array(
+               $row = [
                        'pp_page' => $this->mId,
                        'pp_propname' => $prop,
                        'pp_value' => $value,
-               );
+               ];
 
                if ( $wgPagePropsHaveSortkey ) {
                        $row['pp_sortkey'] = $this->getPropertySortKeyValue( $value );
@@ -577,19 +635,19 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @param array $existing
         * @return array
         */
-       private function getInterwikiInsertions( $existing = array() ) {
-               $arr = array();
+       private function getInterwikiInsertions( $existing = [] ) {
+               $arr = [];
                foreach ( $this->mInterwikis as $prefix => $dbkeys ) {
                        $diffs = isset( $existing[$prefix] )
                                ? array_diff_key( $dbkeys, $existing[$prefix] )
                                : $dbkeys;
 
                        foreach ( $diffs as $dbk => $id ) {
-                               $arr[] = array(
+                               $arr[] = [
                                        'iwl_from' => $this->mId,
                                        'iwl_prefix' => $prefix,
                                        'iwl_title' => $dbk
-                               );
+                               ];
                        }
                }
 
@@ -603,7 +661,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getLinkDeletions( $existing ) {
-               $del = array();
+               $del = [];
                foreach ( $existing as $ns => $dbkeys ) {
                        if ( isset( $this->mLinks[$ns] ) ) {
                                $del[$ns] = array_diff_key( $existing[$ns], $this->mLinks[$ns] );
@@ -622,7 +680,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getTemplateDeletions( $existing ) {
-               $del = array();
+               $del = [];
                foreach ( $existing as $ns => $dbkeys ) {
                        if ( isset( $this->mTemplates[$ns] ) ) {
                                $del[$ns] = array_diff_key( $existing[$ns], $this->mTemplates[$ns] );
@@ -690,7 +748,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getInterwikiDeletions( $existing ) {
-               $del = array();
+               $del = [];
                foreach ( $existing as $prefix => $dbkeys ) {
                        if ( isset( $this->mInterwikis[$prefix] ) ) {
                                $del[$prefix] = array_diff_key( $existing[$prefix], $this->mInterwikis[$prefix] );
@@ -708,12 +766,12 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingLinks() {
-               $res = $this->mDb->select( 'pagelinks', array( 'pl_namespace', 'pl_title' ),
-                       array( 'pl_from' => $this->mId ), __METHOD__, $this->mOptions );
-               $arr = array();
+               $res = $this->mDb->select( 'pagelinks', [ 'pl_namespace', 'pl_title' ],
+                       [ 'pl_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $arr = [];
                foreach ( $res as $row ) {
                        if ( !isset( $arr[$row->pl_namespace] ) ) {
-                               $arr[$row->pl_namespace] = array();
+                               $arr[$row->pl_namespace] = [];
                        }
                        $arr[$row->pl_namespace][$row->pl_title] = 1;
                }
@@ -727,12 +785,12 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingTemplates() {
-               $res = $this->mDb->select( 'templatelinks', array( 'tl_namespace', 'tl_title' ),
-                       array( 'tl_from' => $this->mId ), __METHOD__, $this->mOptions );
-               $arr = array();
+               $res = $this->mDb->select( 'templatelinks', [ 'tl_namespace', 'tl_title' ],
+                       [ 'tl_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $arr = [];
                foreach ( $res as $row ) {
                        if ( !isset( $arr[$row->tl_namespace] ) ) {
-                               $arr[$row->tl_namespace] = array();
+                               $arr[$row->tl_namespace] = [];
                        }
                        $arr[$row->tl_namespace][$row->tl_title] = 1;
                }
@@ -746,9 +804,9 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingImages() {
-               $res = $this->mDb->select( 'imagelinks', array( 'il_to' ),
-                       array( 'il_from' => $this->mId ), __METHOD__, $this->mOptions );
-               $arr = array();
+               $res = $this->mDb->select( 'imagelinks', [ 'il_to' ],
+                       [ 'il_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->il_to] = 1;
                }
@@ -762,9 +820,9 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingExternals() {
-               $res = $this->mDb->select( 'externallinks', array( 'el_to' ),
-                       array( 'el_from' => $this->mId ), __METHOD__, $this->mOptions );
-               $arr = array();
+               $res = $this->mDb->select( 'externallinks', [ 'el_to' ],
+                       [ 'el_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->el_to] = 1;
                }
@@ -778,9 +836,9 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingCategories() {
-               $res = $this->mDb->select( 'categorylinks', array( 'cl_to', 'cl_sortkey_prefix' ),
-                       array( 'cl_from' => $this->mId ), __METHOD__, $this->mOptions );
-               $arr = array();
+               $res = $this->mDb->select( 'categorylinks', [ 'cl_to', 'cl_sortkey_prefix' ],
+                       [ 'cl_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->cl_to] = $row->cl_sortkey_prefix;
                }
@@ -795,9 +853,9 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array
         */
        private function getExistingInterlangs() {
-               $res = $this->mDb->select( 'langlinks', array( 'll_lang', 'll_title' ),
-                       array( 'll_from' => $this->mId ), __METHOD__, $this->mOptions );
-               $arr = array();
+               $res = $this->mDb->select( 'langlinks', [ 'll_lang', 'll_title' ],
+                       [ 'll_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->ll_lang] = $row->ll_title;
                }
@@ -810,12 +868,12 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array (prefix => array(dbkey => 1))
         */
        protected function getExistingInterwikis() {
-               $res = $this->mDb->select( 'iwlinks', array( 'iwl_prefix', 'iwl_title' ),
-                       array( 'iwl_from' => $this->mId ), __METHOD__, $this->mOptions );
-               $arr = array();
+               $res = $this->mDb->select( 'iwlinks', [ 'iwl_prefix', 'iwl_title' ],
+                       [ 'iwl_from' => $this->mId ], __METHOD__, $this->mOptions );
+               $arr = [];
                foreach ( $res as $row ) {
                        if ( !isset( $arr[$row->iwl_prefix] ) ) {
-                               $arr[$row->iwl_prefix] = array();
+                               $arr[$row->iwl_prefix] = [];
                        }
                        $arr[$row->iwl_prefix][$row->iwl_title] = 1;
                }
@@ -829,9 +887,9 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         * @return array Array of property names and values
         */
        private function getExistingProperties() {
-               $res = $this->mDb->select( 'page_props', array( 'pp_propname', 'pp_value' ),
-                       array( 'pp_page' => $this->mId ), __METHOD__, $this->mOptions );
-               $arr = array();
+               $res = $this->mDb->select( 'page_props', [ 'pp_propname', 'pp_value' ],
+                       [ 'pp_page' => $this->mId ], __METHOD__, $this->mOptions );
+               $arr = [];
                foreach ( $res as $row ) {
                        $arr[$row->pp_propname] = $row->pp_value;
                }
@@ -904,7 +962,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                        if ( isset( $wgPagePropLinkInvalidations[$name] ) ) {
                                $inv = $wgPagePropLinkInvalidations[$name];
                                if ( !is_array( $inv ) ) {
-                                       $inv = array( $inv );
+                                       $inv = [ $inv ];
                                }
                                foreach ( $inv as $table ) {
                                        DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this->mTitle, $table ) );
@@ -922,7 +980,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                if ( $this->linkInsertions === null ) {
                        return null;
                }
-               $result = array();
+               $result = [];
                foreach ( $this->linkInsertions as $insertion ) {
                        $result[] = Title::makeTitle( $insertion['pl_namespace'], $insertion['pl_title'] );
                }
@@ -939,7 +997,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                if ( $this->linkDeletions === null ) {
                        return null;
                }
-               $result = array();
+               $result = [];
                foreach ( $this->linkDeletions as $ns => $titles ) {
                        foreach ( $titles as $title => $unused ) {
                                $result[] = Title::makeTitle( $ns, $title );
@@ -957,8 +1015,8 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                        // The link updates made here only reflect the freshness of the parser output
                        $timestamp = $this->mParserOutput->getCacheTime();
                        $this->mDb->update( 'page',
-                               array( 'page_links_updated' => $this->mDb->timestamp( $timestamp ) ),
-                               array( 'page_id' => $this->mId ),
+                               [ 'page_links_updated' => $this->mDb->timestamp( $timestamp ) ],
+                               [ 'page_id' => $this->mId ],
                                __METHOD__
                        );
                }
@@ -966,10 +1024,10 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
 
        public function getAsJobSpecification() {
                if ( $this->user ) {
-                       $userInfo = array(
+                       $userInfo = [
                                'userId' => $this->user->getId(),
                                'userName' => $this->user->getName(),
-                       );
+                       ];
                } else {
                        $userInfo = false;
                }
@@ -980,20 +1038,20 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                        $triggeringRevisionId = false;
                }
 
-               return array(
+               return [
                        'wiki' => $this->mDb->getWikiID(),
                        'job'  => new JobSpecification(
                                'refreshLinksPrioritized',
-                               array(
+                               [
                                        // Reuse the parser cache if it was saved
                                        'rootJobTimestamp' => $this->mParserOutput->getCacheTime(),
                                        'useRecursiveLinksUpdate' => $this->mRecursive,
                                        'triggeringUser' => $userInfo,
                                        'triggeringRevisionId' => $triggeringRevisionId,
-                               ),
-                               array( 'removeDuplicates' => true ),
+                               ],
+                               [ 'removeDuplicates' => true ],
                                $this->getTitle()
                        )
-               );
+               ];
        }
 }