Alphabetize service lists
authorAryeh Gregor <ayg@aryeh.name>
Fri, 3 Aug 2018 08:05:44 +0000 (11:05 +0300)
committerKunal Mehta <legoktm@member.fsf.org>
Fri, 10 Aug 2018 04:19:58 +0000 (22:19 -0600)
Adding everything at the end makes the list arbitrarily ordered, and
also invites lots of merge conflicts as new things are added.

Change-Id: I58bcca4fa79140f5d5f2f6ef447e67035cc37aae

includes/MediaWikiServices.php
includes/ServiceWiring.php
tests/phpunit/includes/MediaWikiServicesTest.php
tests/phpunit/includes/ServiceWiringTest.php [new file with mode: 0644]

index fd9b472..080a7f9 100644 (file)
@@ -403,128 +403,117 @@ class MediaWikiServices extends ServiceContainer {
        // CONVENIENCE GETTERS ////////////////////////////////////////////////////
 
        /**
-        * Returns the Config object containing the bootstrap configuration.
-        * Bootstrap configuration would typically include database credentials
-        * and other information that may be needed before the ConfigFactory
-        * service can be instantiated.
-        *
-        * @note This should only be used during bootstrapping, in particular
-        * when creating the MainConfig service. Application logic should
-        * use getMainConfig() to get a Config instances.
-        *
-        * @since 1.27
-        * @return Config
+        * @since 1.31
+        * @return ActorMigration
         */
-       public function getBootstrapConfig() {
-               return $this->getService( 'BootstrapConfig' );
+       public function getActorMigration() {
+               return $this->getService( 'ActorMigration' );
        }
 
        /**
-        * @since 1.27
-        * @return ConfigFactory
+        * @since 1.31
+        * @return BlobStore
         */
-       public function getConfigFactory() {
-               return $this->getService( 'ConfigFactory' );
+       public function getBlobStore() {
+               return $this->getService( '_SqlBlobStore' );
        }
 
        /**
-        * Returns the Config object that provides configuration for MediaWiki core.
-        * This may or may not be the same object that is returned by getBootstrapConfig().
-        *
-        * @since 1.27
-        * @return Config
+        * @since 1.31
+        * @return BlobStoreFactory
         */
-       public function getMainConfig() {
-               return $this->getService( 'MainConfig' );
+       public function getBlobStoreFactory() {
+               return $this->getService( 'BlobStoreFactory' );
        }
 
        /**
+        * Returns the Config object containing the bootstrap configuration.
+        * Bootstrap configuration would typically include database credentials
+        * and other information that may be needed before the ConfigFactory
+        * service can be instantiated.
+        *
+        * @note This should only be used during bootstrapping, in particular
+        * when creating the MainConfig service. Application logic should
+        * use getMainConfig() to get a Config instances.
+        *
         * @since 1.27
-        * @return SiteLookup
+        * @return Config
         */
-       public function getSiteLookup() {
-               return $this->getService( 'SiteLookup' );
+       public function getBootstrapConfig() {
+               return $this->getService( 'BootstrapConfig' );
        }
 
        /**
-        * @since 1.27
-        * @return SiteStore
+        * @since 1.32
+        * @return NameTableStore
         */
-       public function getSiteStore() {
-               return $this->getService( 'SiteStore' );
+       public function getChangeTagDefStore() {
+               return $this->getService( 'ChangeTagDefStore' );
        }
 
        /**
-        * @since 1.28
-        * @return InterwikiLookup
+        * @since 1.31
+        * @return CommentStore
         */
-       public function getInterwikiLookup() {
-               return $this->getService( 'InterwikiLookup' );
+       public function getCommentStore() {
+               return $this->getService( 'CommentStore' );
        }
 
        /**
         * @since 1.27
-        * @return IBufferingStatsdDataFactory
+        * @return ConfigFactory
         */
-       public function getStatsdDataFactory() {
-               return $this->getService( 'StatsdDataFactory' );
+       public function getConfigFactory() {
+               return $this->getService( 'ConfigFactory' );
        }
 
        /**
         * @since 1.32
-        * @return IBufferingStatsdDataFactory
-        */
-       public function getPerDbNameStatsdDataFactory() {
-               return $this->getService( 'PerDbNameStatsdDataFactory' );
-       }
-
-       /**
-        * @since 1.27
-        * @return EventRelayerGroup
+        * @return ConfigRepository
         */
-       public function getEventRelayerGroup() {
-               return $this->getService( 'EventRelayerGroup' );
+       public function getConfigRepository() {
+               return $this->getService( 'ConfigRepository' );
        }
 
        /**
-        * @since 1.27
-        * @return SearchEngine
+        * @since 1.29
+        * @return \ConfiguredReadOnlyMode
         */
-       public function newSearchEngine() {
-               // New engine object every time, since they keep state
-               return $this->getService( 'SearchEngineFactory' )->create();
+       public function getConfiguredReadOnlyMode() {
+               return $this->getService( 'ConfiguredReadOnlyMode' );
        }
 
        /**
-        * @since 1.27
-        * @return SearchEngineFactory
+        * @since 1.32
+        * @return \Language
         */
-       public function getSearchEngineFactory() {
-               return $this->getService( 'SearchEngineFactory' );
+       public function getContentLanguage() {
+               return $this->getService( 'ContentLanguage' );
        }
 
        /**
-        * @since 1.27
-        * @return SearchEngineConfig
+        * @since 1.31
+        * @return NameTableStore
         */
-       public function getSearchEngineConfig() {
-               return $this->getService( 'SearchEngineConfig' );
+       public function getContentModelStore() {
+               return $this->getService( 'ContentModelStore' );
        }
 
        /**
-        * @since 1.27
-        * @return SkinFactory
+        * @since 1.28
+        * @return CryptHKDF
         */
-       public function getSkinFactory() {
-               return $this->getService( 'SkinFactory' );
+       public function getCryptHKDF() {
+               return $this->getService( 'CryptHKDF' );
        }
 
        /**
         * @since 1.28
-        * @return LBFactory
+        * @deprecated since 1.32, use random_bytes()/random_int()
+        * @return CryptRand
         */
-       public function getDBLoadBalancerFactory() {
-               return $this->getService( 'DBLoadBalancerFactory' );
+       public function getCryptRand() {
+               return $this->getService( 'CryptRand' );
        }
 
        /**
@@ -537,91 +526,69 @@ class MediaWikiServices extends ServiceContainer {
 
        /**
         * @since 1.28
-        * @return WatchedItemStoreInterface
-        */
-       public function getWatchedItemStore() {
-               return $this->getService( 'WatchedItemStore' );
-       }
-
-       /**
-        * @since 1.28
-        * @return WatchedItemQueryService
+        * @return LBFactory
         */
-       public function getWatchedItemQueryService() {
-               return $this->getService( 'WatchedItemQueryService' );
+       public function getDBLoadBalancerFactory() {
+               return $this->getService( 'DBLoadBalancerFactory' );
        }
 
        /**
-        * @since 1.28
-        * @deprecated since 1.32, use random_bytes()/random_int()
-        * @return CryptRand
+        * @since 1.27
+        * @return EventRelayerGroup
         */
-       public function getCryptRand() {
-               return $this->getService( 'CryptRand' );
+       public function getEventRelayerGroup() {
+               return $this->getService( 'EventRelayerGroup' );
        }
 
        /**
-        * @since 1.28
-        * @return CryptHKDF
+        * @since 1.31
+        * @return \ExternalStoreFactory
         */
-       public function getCryptHKDF() {
-               return $this->getService( 'CryptHKDF' );
+       public function getExternalStoreFactory() {
+               return $this->getService( 'ExternalStoreFactory' );
        }
 
        /**
         * @since 1.28
-        * @return MediaHandlerFactory
+        * @return GenderCache
         */
-       public function getMediaHandlerFactory() {
-               return $this->getService( 'MediaHandlerFactory' );
+       public function getGenderCache() {
+               return $this->getService( 'GenderCache' );
        }
 
        /**
-        * @since 1.28
-        * @return MimeAnalyzer
+        * @since 1.31
+        * @return HttpRequestFactory
         */
-       public function getMimeAnalyzer() {
-               return $this->getService( 'MimeAnalyzer' );
+       public function getHttpRequestFactory() {
+               return $this->getService( 'HttpRequestFactory' );
        }
 
        /**
         * @since 1.28
-        * @return ProxyLookup
-        */
-       public function getProxyLookup() {
-               return $this->getService( 'ProxyLookup' );
-       }
-
-       /**
-        * @since 1.29
-        * @return Parser
-        */
-       public function getParser() {
-               return $this->getService( 'Parser' );
-       }
-
-       /**
-        * @since 1.30
-        * @return ParserCache
+        * @return InterwikiLookup
         */
-       public function getParserCache() {
-               return $this->getService( 'ParserCache' );
+       public function getInterwikiLookup() {
+               return $this->getService( 'InterwikiLookup' );
        }
 
        /**
         * @since 1.28
-        * @return GenderCache
+        * @return LinkCache
         */
-       public function getGenderCache() {
-               return $this->getService( 'GenderCache' );
+       public function getLinkCache() {
+               return $this->getService( 'LinkCache' );
        }
 
        /**
+        * LinkRenderer instance that can be used
+        * if no custom options are needed
+        *
         * @since 1.28
-        * @return LinkCache
+        * @return LinkRenderer
         */
-       public function getLinkCache() {
-               return $this->getService( 'LinkCache' );
+       public function getLinkRenderer() {
+               return $this->getService( 'LinkRenderer' );
        }
 
        /**
@@ -633,30 +600,30 @@ class MediaWikiServices extends ServiceContainer {
        }
 
        /**
-        * LinkRenderer instance that can be used
-        * if no custom options are needed
-        *
         * @since 1.28
-        * @return LinkRenderer
+        * @return \BagOStuff
         */
-       public function getLinkRenderer() {
-               return $this->getService( 'LinkRenderer' );
+       public function getLocalServerObjectCache() {
+               return $this->getService( 'LocalServerObjectCache' );
        }
 
        /**
-        * @since 1.28
-        * @return TitleFormatter
+        * @since 1.32
+        * @return MagicWordFactory
         */
-       public function getTitleFormatter() {
-               return $this->getService( 'TitleFormatter' );
+       public function getMagicWordFactory() {
+               return $this->getService( 'MagicWordFactory' );
        }
 
        /**
-        * @since 1.28
-        * @return TitleParser
+        * Returns the Config object that provides configuration for MediaWiki core.
+        * This may or may not be the same object that is returned by getBootstrapConfig().
+        *
+        * @since 1.27
+        * @return Config
         */
-       public function getTitleParser() {
-               return $this->getService( 'TitleParser' );
+       public function getMainConfig() {
+               return $this->getService( 'MainConfig' );
        }
 
        /**
@@ -677,90 +644,98 @@ class MediaWikiServices extends ServiceContainer {
 
        /**
         * @since 1.28
-        * @return \BagOStuff
+        * @return MediaHandlerFactory
         */
-       public function getLocalServerObjectCache() {
-               return $this->getService( 'LocalServerObjectCache' );
+       public function getMediaHandlerFactory() {
+               return $this->getService( 'MediaHandlerFactory' );
        }
 
        /**
         * @since 1.28
-        * @return VirtualRESTServiceClient
+        * @return MimeAnalyzer
         */
-       public function getVirtualRESTServiceClient() {
-               return $this->getService( 'VirtualRESTServiceClient' );
+       public function getMimeAnalyzer() {
+               return $this->getService( 'MimeAnalyzer' );
        }
 
        /**
-        * @since 1.29
-        * @return \ConfiguredReadOnlyMode
+        * @since 1.32
+        * @return OldRevisionImporter
         */
-       public function getConfiguredReadOnlyMode() {
-               return $this->getService( 'ConfiguredReadOnlyMode' );
+       public function getOldRevisionImporter() {
+               return $this->getService( 'OldRevisionImporter' );
        }
 
        /**
         * @since 1.29
-        * @return \ReadOnlyMode
+        * @return Parser
         */
-       public function getReadOnlyMode() {
-               return $this->getService( 'ReadOnlyMode' );
+       public function getParser() {
+               return $this->getService( 'Parser' );
        }
 
        /**
-        * @since 1.31
-        * @return \UploadRevisionImporter
+        * @since 1.30
+        * @return ParserCache
         */
-       public function getWikiRevisionUploadImporter() {
-               return $this->getService( 'UploadRevisionImporter' );
+       public function getParserCache() {
+               return $this->getService( 'ParserCache' );
        }
 
        /**
-        * @since 1.31
-        * @return \OldRevisionImporter
+        * @since 1.32
+        * @return PasswordFactory
         */
-       public function getWikiRevisionOldRevisionImporter() {
-               return $this->getService( 'OldRevisionImporter' );
+       public function getPasswordFactory() {
+               return $this->getService( 'PasswordFactory' );
+       }
+
+       /**
+        * @since 1.32
+        * @return IBufferingStatsdDataFactory
+        */
+       public function getPerDbNameStatsdDataFactory() {
+               return $this->getService( 'PerDbNameStatsdDataFactory' );
        }
 
        /**
         * @since 1.31
-        * @return \OldRevisionImporter
+        * @return PreferencesFactory
         */
-       public function getWikiRevisionOldRevisionImporterNoUpdates() {
-               return $this->getService( 'WikiRevisionOldRevisionImporterNoUpdates' );
+       public function getPreferencesFactory() {
+               return $this->getService( 'PreferencesFactory' );
        }
 
        /**
-        * @since 1.30
-        * @return CommandFactory
+        * @since 1.28
+        * @return ProxyLookup
         */
-       public function getShellCommandFactory() {
-               return $this->getService( 'ShellCommandFactory' );
+       public function getProxyLookup() {
+               return $this->getService( 'ProxyLookup' );
        }
 
        /**
-        * @since 1.31
-        * @return \ExternalStoreFactory
+        * @since 1.29
+        * @return \ReadOnlyMode
         */
-       public function getExternalStoreFactory() {
-               return $this->getService( 'ExternalStoreFactory' );
+       public function getReadOnlyMode() {
+               return $this->getService( 'ReadOnlyMode' );
        }
 
        /**
         * @since 1.31
-        * @return BlobStoreFactory
+        * @return RevisionFactory
         */
-       public function getBlobStoreFactory() {
-               return $this->getService( 'BlobStoreFactory' );
+       public function getRevisionFactory() {
+               return $this->getService( 'RevisionFactory' );
        }
 
        /**
         * @since 1.31
-        * @return BlobStore
+        * @return RevisionLookup
         */
-       public function getBlobStore() {
-               return $this->getService( '_SqlBlobStore' );
+       public function getRevisionLookup() {
+               return $this->getService( 'RevisionLookup' );
        }
 
        /**
@@ -780,75 +755,92 @@ class MediaWikiServices extends ServiceContainer {
        }
 
        /**
-        * @since 1.31
-        * @return RevisionLookup
+        * @since 1.27
+        * @return SearchEngine
         */
-       public function getRevisionLookup() {
-               return $this->getService( 'RevisionLookup' );
+       public function newSearchEngine() {
+               // New engine object every time, since they keep state
+               return $this->getService( 'SearchEngineFactory' )->create();
        }
 
        /**
-        * @since 1.31
-        * @return RevisionFactory
+        * @since 1.27
+        * @return SearchEngineConfig
         */
-       public function getRevisionFactory() {
-               return $this->getService( 'RevisionFactory' );
+       public function getSearchEngineConfig() {
+               return $this->getService( 'SearchEngineConfig' );
        }
 
        /**
-        * @since 1.31
-        * @return NameTableStore
+        * @since 1.27
+        * @return SearchEngineFactory
         */
-       public function getContentModelStore() {
-               return $this->getService( 'ContentModelStore' );
+       public function getSearchEngineFactory() {
+               return $this->getService( 'SearchEngineFactory' );
        }
 
        /**
-        * @since 1.31
-        * @return NameTableStore
+        * @since 1.30
+        * @return CommandFactory
         */
-       public function getSlotRoleStore() {
-               return $this->getService( 'SlotRoleStore' );
+       public function getShellCommandFactory() {
+               return $this->getService( 'ShellCommandFactory' );
        }
 
        /**
-        * @since 1.32
-        * @return NameTableStore
+        * @since 1.27
+        * @return SiteLookup
         */
-       public function getChangeTagDefStore() {
-               return $this->getService( 'ChangeTagDefStore' );
+       public function getSiteLookup() {
+               return $this->getService( 'SiteLookup' );
        }
 
        /**
-        * @since 1.31
-        * @return PreferencesFactory
+        * @since 1.27
+        * @return SiteStore
         */
-       public function getPreferencesFactory() {
-               return $this->getService( 'PreferencesFactory' );
+       public function getSiteStore() {
+               return $this->getService( 'SiteStore' );
        }
 
        /**
-        * @since 1.31
-        * @return HttpRequestFactory
+        * @since 1.27
+        * @return SkinFactory
         */
-       public function getHttpRequestFactory() {
-               return $this->getService( 'HttpRequestFactory' );
+       public function getSkinFactory() {
+               return $this->getService( 'SkinFactory' );
        }
 
        /**
         * @since 1.31
-        * @return CommentStore
+        * @return NameTableStore
         */
-       public function getCommentStore() {
-               return $this->getService( 'CommentStore' );
+       public function getSlotRoleStore() {
+               return $this->getService( 'SlotRoleStore' );
        }
 
        /**
-        * @since 1.31
-        * @return ActorMigration
+        * @since 1.27
+        * @return IBufferingStatsdDataFactory
         */
-       public function getActorMigration() {
-               return $this->getService( 'ActorMigration' );
+       public function getStatsdDataFactory() {
+               return $this->getService( 'StatsdDataFactory' );
+       }
+
+       /**
+        * @since 1.28
+        * @return TitleFormatter
+        */
+       public function getTitleFormatter() {
+               return $this->getService( 'TitleFormatter' );
+       }
+
+       /**
+        * @since 1.28
+        * @return TitleParser
+        */
+       public function getTitleParser() {
+               return $this->getService( 'TitleParser' );
        }
 
        /**
@@ -860,43 +852,51 @@ class MediaWikiServices extends ServiceContainer {
        }
 
        /**
-        * @since 1.32
-        * @return OldRevisionImporter
+        * @since 1.28
+        * @return VirtualRESTServiceClient
         */
-       public function getOldRevisionImporter() {
-               return $this->getService( 'OldRevisionImporter' );
+       public function getVirtualRESTServiceClient() {
+               return $this->getService( 'VirtualRESTServiceClient' );
        }
 
        /**
-        * @since 1.32
-        * @return ConfigRepository
+        * @since 1.28
+        * @return WatchedItemQueryService
         */
-       public function getConfigRepository() {
-               return $this->getService( 'ConfigRepository' );
+       public function getWatchedItemQueryService() {
+               return $this->getService( 'WatchedItemQueryService' );
        }
 
        /**
-        * @since 1.32
-        * @return MagicWordFactory
+        * @since 1.28
+        * @return WatchedItemStoreInterface
         */
-       public function getMagicWordFactory() {
-               return $this->getService( 'MagicWordFactory' );
+       public function getWatchedItemStore() {
+               return $this->getService( 'WatchedItemStore' );
        }
 
        /**
-        * @since 1.32
-        * @return \Language
+        * @since 1.31
+        * @return \OldRevisionImporter
         */
-       public function getContentLanguage() {
-               return $this->getService( 'ContentLanguage' );
+       public function getWikiRevisionOldRevisionImporter() {
+               return $this->getService( 'OldRevisionImporter' );
        }
 
        /**
-        * @since 1.32
-        * @return PasswordFactory
+        * @since 1.31
+        * @return \OldRevisionImporter
         */
-       public function getPasswordFactory() {
-               return $this->getService( 'PasswordFactory' );
+       public function getWikiRevisionOldRevisionImporterNoUpdates() {
+               return $this->getService( 'WikiRevisionOldRevisionImporterNoUpdates' );
+       }
+
+       /**
+        * @since 1.31
+        * @return \UploadRevisionImporter
+        */
+       public function getWikiRevisionUploadImporter() {
+               return $this->getService( 'UploadRevisionImporter' );
        }
 
        ///////////////////////////////////////////////////////////////////////////
index 671544e..6202f83 100644 (file)
@@ -51,46 +51,48 @@ use MediaWiki\Storage\RevisionStoreFactory;
 use Wikimedia\ObjectFactory;
 
 return [
-       'DBLoadBalancerFactory' => function ( MediaWikiServices $services ) {
-               $mainConfig = $services->getMainConfig();
-
-               $lbConf = MWLBFactory::applyDefaultConfig(
-                       $mainConfig->get( 'LBFactoryConf' ),
-                       $mainConfig,
-                       $services->getConfiguredReadOnlyMode()
+       'ActorMigration' => function ( MediaWikiServices $services ) {
+               return new ActorMigration(
+                       $services->getMainConfig()->get( 'ActorTableSchemaMigrationStage' )
                );
-               $class = MWLBFactory::getLBFactoryClass( $lbConf );
-
-               $instance = new $class( $lbConf );
-               MWLBFactory::setSchemaAliases( $instance, $mainConfig );
-
-               return $instance;
        },
 
-       'DBLoadBalancer' => function ( MediaWikiServices $services ) {
-               // just return the default LB from the DBLoadBalancerFactory service
-               return $services->getDBLoadBalancerFactory()->getMainLB();
+       'BlobStore' => function ( MediaWikiServices $services ) {
+               return $services->getService( '_SqlBlobStore' );
        },
 
-       'SiteStore' => function ( MediaWikiServices $services ) {
-               $rawSiteStore = new DBSiteStore( $services->getDBLoadBalancer() );
-
-               // TODO: replace wfGetCache with a CacheFactory service.
-               // TODO: replace wfIsHHVM with a capabilities service.
-               $cache = wfGetCache( wfIsHHVM() ? CACHE_ACCEL : CACHE_ANYTHING );
-
-               return new CachingSiteStore( $rawSiteStore, $cache );
+       'BlobStoreFactory' => function ( MediaWikiServices $services ) {
+               return new BlobStoreFactory(
+                       $services->getDBLoadBalancer(),
+                       $services->getMainWANObjectCache(),
+                       $services->getMainConfig(),
+                       $services->getContentLanguage()
+               );
        },
 
-       'SiteLookup' => function ( MediaWikiServices $services ) {
-               $cacheFile = $services->getMainConfig()->get( 'SitesCacheFile' );
+       'ChangeTagDefStore' => function ( MediaWikiServices $services ) {
+               return new NameTableStore(
+                       $services->getDBLoadBalancer(),
+                       $services->getMainWANObjectCache(),
+                       LoggerFactory::getInstance( 'NameTableSqlStore' ),
+                       'change_tag_def',
+                       'ctd_id',
+                       'ctd_name',
+                       null,
+                       false,
+                       function ( $insertFields ) {
+                               $insertFields['ctd_user_defined'] = 0;
+                               $insertFields['ctd_count'] = 0;
+                               return $insertFields;
+                       }
+               );
+       },
 
-               if ( $cacheFile !== false ) {
-                       return new FileBasedSiteLookup( $cacheFile );
-               } else {
-                       // Use the default SiteStore as the SiteLookup implementation for now
-                       return $services->getSiteStore();
-               }
+       'CommentStore' => function ( MediaWikiServices $services ) {
+               return new CommentStore(
+                       $services->getContentLanguage(),
+                       $services->getMainConfig()->get( 'CommentTableSchemaMigrationStage' )
+               );
        },
 
        'ConfigFactory' => function ( MediaWikiServices $services ) {
@@ -108,120 +110,177 @@ return [
                return new ConfigRepository( $services->getConfigFactory() );
        },
 
-       'MainConfig' => function ( MediaWikiServices $services ) {
-               // Use the 'main' config from the ConfigFactory service.
-               return $services->getConfigFactory()->makeConfig( 'main' );
+       'ConfiguredReadOnlyMode' => function ( MediaWikiServices $services ) {
+               return new ConfiguredReadOnlyMode( $services->getMainConfig() );
        },
 
-       'InterwikiLookup' => function ( MediaWikiServices $services ) {
-               $config = $services->getMainConfig();
-               return new ClassicInterwikiLookup(
-                       $services->getContentLanguage(),
-                       $services->getMainWANObjectCache(),
-                       $config->get( 'InterwikiExpiry' ),
-                       $config->get( 'InterwikiCache' ),
-                       $config->get( 'InterwikiScopes' ),
-                       $config->get( 'InterwikiFallbackSite' )
-               );
+       'ContentLanguage' => function ( MediaWikiServices $services ) {
+               return Language::factory( $services->getMainConfig()->get( 'LanguageCode' ) );
        },
 
-       'StatsdDataFactory' => function ( MediaWikiServices $services ) {
-               return new BufferingStatsdDataFactory(
-                       rtrim( $services->getMainConfig()->get( 'StatsdMetricPrefix' ), '.' )
+       'ContentModelStore' => function ( MediaWikiServices $services ) {
+               return new NameTableStore(
+                       $services->getDBLoadBalancer(),
+                       $services->getMainWANObjectCache(),
+                       LoggerFactory::getInstance( 'NameTableSqlStore' ),
+                       'content_models',
+                       'model_id',
+                       'model_name'
+                       /**
+                        * No strtolower normalization is added to the service as there are examples of
+                        * extensions that do not stick to this assumption.
+                        * - extensions/examples/DataPages define( 'CONTENT_MODEL_XML_DATA','XML_DATA' );
+                        * - extensions/Scribunto define( 'CONTENT_MODEL_SCRIBUNTO', 'Scribunto' );
+                        */
                );
        },
 
-       'PerDbNameStatsdDataFactory' => function ( MediaWikiServices $services ) {
+       'CryptHKDF' => function ( MediaWikiServices $services ) {
                $config = $services->getMainConfig();
-               $wiki = $config->get( 'DBname' );
-               return new BufferingStatsdDataFactory(
-                       rtrim( $services->getMainConfig()->get( 'StatsdMetricPrefix' ), '.' ) . '.' . $wiki
+
+               $secret = $config->get( 'HKDFSecret' ) ?: $config->get( 'SecretKey' );
+               if ( !$secret ) {
+                       throw new RuntimeException( "Cannot use MWCryptHKDF without a secret." );
+               }
+
+               // In HKDF, the context can be known to the attacker, but this will
+               // keep simultaneous runs from producing the same output.
+               $context = [ microtime(), getmypid(), gethostname() ];
+
+               // Setup salt cache. Use APC, or fallback to the main cache if it isn't setup
+               $cache = $services->getLocalServerObjectCache();
+               if ( $cache instanceof EmptyBagOStuff ) {
+                       $cache = ObjectCache::getLocalClusterInstance();
+               }
+
+               return new CryptHKDF( $secret, $config->get( 'HKDFAlgorithm' ), $cache, $context );
+       },
+
+       'CryptRand' => function () {
+               return new CryptRand();
+       },
+
+       'DBLoadBalancer' => function ( MediaWikiServices $services ) {
+               // just return the default LB from the DBLoadBalancerFactory service
+               return $services->getDBLoadBalancerFactory()->getMainLB();
+       },
+
+       'DBLoadBalancerFactory' => function ( MediaWikiServices $services ) {
+               $mainConfig = $services->getMainConfig();
+
+               $lbConf = MWLBFactory::applyDefaultConfig(
+                       $mainConfig->get( 'LBFactoryConf' ),
+                       $mainConfig,
+                       $services->getConfiguredReadOnlyMode()
                );
+               $class = MWLBFactory::getLBFactoryClass( $lbConf );
+
+               $instance = new $class( $lbConf );
+               MWLBFactory::setSchemaAliases( $instance, $mainConfig );
+
+               return $instance;
        },
 
        'EventRelayerGroup' => function ( MediaWikiServices $services ) {
                return new EventRelayerGroup( $services->getMainConfig()->get( 'EventRelayerConfig' ) );
        },
 
-       'SearchEngineFactory' => function ( MediaWikiServices $services ) {
-               return new SearchEngineFactory( $services->getSearchEngineConfig() );
-       },
+       'ExternalStoreFactory' => function ( MediaWikiServices $services ) {
+               $config = $services->getMainConfig();
 
-       'SearchEngineConfig' => function ( MediaWikiServices $services ) {
-               return new SearchEngineConfig( $services->getMainConfig(),
-                       $services->getContentLanguage() );
+               return new ExternalStoreFactory(
+                       $config->get( 'ExternalStores' )
+               );
        },
 
-       'SkinFactory' => function ( MediaWikiServices $services ) {
-               $factory = new SkinFactory();
-
-               $names = $services->getMainConfig()->get( 'ValidSkinNames' );
+       'GenderCache' => function ( MediaWikiServices $services ) {
+               return new GenderCache();
+       },
 
-               foreach ( $names as $name => $skin ) {
-                       $factory->register( $name, $skin, function () use ( $name, $skin ) {
-                               $class = "Skin$skin";
-                               return new $class( $name );
-                       } );
-               }
-               // Register a hidden "fallback" skin
-               $factory->register( 'fallback', 'Fallback', function () {
-                       return new SkinFallback;
-               } );
-               // Register a hidden skin for api output
-               $factory->register( 'apioutput', 'ApiOutput', function () {
-                       return new SkinApi;
-               } );
+       'HttpRequestFactory' => function ( MediaWikiServices $services ) {
+               return new \MediaWiki\Http\HttpRequestFactory();
+       },
 
-               return $factory;
+       'InterwikiLookup' => function ( MediaWikiServices $services ) {
+               $config = $services->getMainConfig();
+               return new ClassicInterwikiLookup(
+                       $services->getContentLanguage(),
+                       $services->getMainWANObjectCache(),
+                       $config->get( 'InterwikiExpiry' ),
+                       $config->get( 'InterwikiCache' ),
+                       $config->get( 'InterwikiScopes' ),
+                       $config->get( 'InterwikiFallbackSite' )
+               );
        },
 
-       'WatchedItemStore' => function ( MediaWikiServices $services ) {
-               $store = new WatchedItemStore(
-                       $services->getDBLoadBalancer(),
-                       new HashBagOStuff( [ 'maxKeys' => 100 ] ),
-                       $services->getReadOnlyMode(),
-                       $services->getMainConfig()->get( 'UpdateRowsPerQuery' )
+       'LinkCache' => function ( MediaWikiServices $services ) {
+               return new LinkCache(
+                       $services->getTitleFormatter(),
+                       $services->getMainWANObjectCache()
                );
-               $store->setStatsdDataFactory( $services->getStatsdDataFactory() );
+       },
 
-               if ( $services->getMainConfig()->get( 'ReadOnlyWatchedItemStore' ) ) {
-                       $store = new NoWriteWatchedItemStore( $store );
-               }
+       'LinkRenderer' => function ( MediaWikiServices $services ) {
+               global $wgUser;
 
-               return $store;
+               if ( defined( 'MW_NO_SESSION' ) ) {
+                       return $services->getLinkRendererFactory()->create();
+               } else {
+                       return $services->getLinkRendererFactory()->createForUser( $wgUser );
+               }
        },
 
-       'WatchedItemQueryService' => function ( MediaWikiServices $services ) {
-               return new WatchedItemQueryService(
-                       $services->getDBLoadBalancer(),
-                       $services->getCommentStore(),
-                       $services->getActorMigration()
+       'LinkRendererFactory' => function ( MediaWikiServices $services ) {
+               return new LinkRendererFactory(
+                       $services->getTitleFormatter(),
+                       $services->getLinkCache()
                );
        },
 
-       'CryptRand' => function () {
-               return new CryptRand();
+       'LocalServerObjectCache' => function ( MediaWikiServices $services ) {
+               $cacheId = \ObjectCache::detectLocalServerCache();
+               return \ObjectCache::newFromId( $cacheId );
        },
 
-       'CryptHKDF' => function ( MediaWikiServices $services ) {
-               $config = $services->getMainConfig();
+       'MagicWordFactory' => function ( MediaWikiServices $services ) {
+               return new MagicWordFactory( $services->getContentLanguage() );
+       },
 
-               $secret = $config->get( 'HKDFSecret' ) ?: $config->get( 'SecretKey' );
-               if ( !$secret ) {
-                       throw new RuntimeException( "Cannot use MWCryptHKDF without a secret." );
+       'MainConfig' => function ( MediaWikiServices $services ) {
+               // Use the 'main' config from the ConfigFactory service.
+               return $services->getConfigFactory()->makeConfig( 'main' );
+       },
+
+       'MainObjectStash' => function ( MediaWikiServices $services ) {
+               $mainConfig = $services->getMainConfig();
+
+               $id = $mainConfig->get( 'MainStash' );
+               if ( !isset( $mainConfig->get( 'ObjectCaches' )[$id] ) ) {
+                       throw new UnexpectedValueException(
+                               "Cache type \"$id\" is not present in \$wgObjectCaches." );
                }
 
-               // In HKDF, the context can be known to the attacker, but this will
-               // keep simultaneous runs from producing the same output.
-               $context = [ microtime(), getmypid(), gethostname() ];
+               return \ObjectCache::newFromParams( $mainConfig->get( 'ObjectCaches' )[$id] );
+       },
 
-               // Setup salt cache. Use APC, or fallback to the main cache if it isn't setup
-               $cache = $services->getLocalServerObjectCache();
-               if ( $cache instanceof EmptyBagOStuff ) {
-                       $cache = ObjectCache::getLocalClusterInstance();
+       'MainWANObjectCache' => function ( MediaWikiServices $services ) {
+               $mainConfig = $services->getMainConfig();
+
+               $id = $mainConfig->get( 'MainWANCache' );
+               if ( !isset( $mainConfig->get( 'WANObjectCaches' )[$id] ) ) {
+                       throw new UnexpectedValueException(
+                               "WAN cache type \"$id\" is not present in \$wgWANObjectCaches." );
                }
 
-               return new CryptHKDF( $secret, $config->get( 'HKDFAlgorithm' ), $cache, $context );
+               $params = $mainConfig->get( 'WANObjectCaches' )[$id];
+               $objectCacheId = $params['cacheId'];
+               if ( !isset( $mainConfig->get( 'ObjectCaches' )[$objectCacheId] ) ) {
+                       throw new UnexpectedValueException(
+                               "Cache type \"$objectCacheId\" is not present in \$wgObjectCaches." );
+               }
+               $params['store'] = $mainConfig->get( 'ObjectCaches' )[$objectCacheId];
+
+               return \ObjectCache::newWANCacheFromParams( $params );
        },
 
        'MediaHandlerFactory' => function ( MediaWikiServices $services ) {
@@ -289,11 +348,11 @@ return [
                return new MimeMagic( $params );
        },
 
-       'ProxyLookup' => function ( MediaWikiServices $services ) {
-               $mainConfig = $services->getMainConfig();
-               return new ProxyLookup(
-                       $mainConfig->get( 'SquidServers' ),
-                       $mainConfig->get( 'SquidServersNoPurge' )
+       'OldRevisionImporter' => function ( MediaWikiServices $services ) {
+               return new ImportableOldRevisionImporter(
+                       true,
+                       LoggerFactory::getInstance( 'OldRevisionImporter' ),
+                       $services->getDBLoadBalancer()
                );
        },
 
@@ -314,164 +373,56 @@ return [
                );
        },
 
-       'LinkCache' => function ( MediaWikiServices $services ) {
-               return new LinkCache(
-                       $services->getTitleFormatter(),
-                       $services->getMainWANObjectCache()
-               );
-       },
-
-       'LinkRendererFactory' => function ( MediaWikiServices $services ) {
-               return new LinkRendererFactory(
-                       $services->getTitleFormatter(),
-                       $services->getLinkCache()
-               );
-       },
-
-       'LinkRenderer' => function ( MediaWikiServices $services ) {
-               global $wgUser;
-
-               if ( defined( 'MW_NO_SESSION' ) ) {
-                       return $services->getLinkRendererFactory()->create();
-               } else {
-                       return $services->getLinkRendererFactory()->createForUser( $wgUser );
-               }
-       },
-
-       'GenderCache' => function ( MediaWikiServices $services ) {
-               return new GenderCache();
-       },
-
-       '_MediaWikiTitleCodec' => function ( MediaWikiServices $services ) {
-               return new MediaWikiTitleCodec(
-                       $services->getContentLanguage(),
-                       $services->getGenderCache(),
-                       $services->getMainConfig()->get( 'LocalInterwikis' )
-               );
-       },
-
-       'TitleFormatter' => function ( MediaWikiServices $services ) {
-               return $services->getService( '_MediaWikiTitleCodec' );
-       },
-
-       'TitleParser' => function ( MediaWikiServices $services ) {
-               return $services->getService( '_MediaWikiTitleCodec' );
-       },
-
-       'MainObjectStash' => function ( MediaWikiServices $services ) {
-               $mainConfig = $services->getMainConfig();
-
-               $id = $mainConfig->get( 'MainStash' );
-               if ( !isset( $mainConfig->get( 'ObjectCaches' )[$id] ) ) {
-                       throw new UnexpectedValueException(
-                               "Cache type \"$id\" is not present in \$wgObjectCaches." );
-               }
-
-               return \ObjectCache::newFromParams( $mainConfig->get( 'ObjectCaches' )[$id] );
-       },
-
-       'MainWANObjectCache' => function ( MediaWikiServices $services ) {
-               $mainConfig = $services->getMainConfig();
-
-               $id = $mainConfig->get( 'MainWANCache' );
-               if ( !isset( $mainConfig->get( 'WANObjectCaches' )[$id] ) ) {
-                       throw new UnexpectedValueException(
-                               "WAN cache type \"$id\" is not present in \$wgWANObjectCaches." );
-               }
-
-               $params = $mainConfig->get( 'WANObjectCaches' )[$id];
-               $objectCacheId = $params['cacheId'];
-               if ( !isset( $mainConfig->get( 'ObjectCaches' )[$objectCacheId] ) ) {
-                       throw new UnexpectedValueException(
-                               "Cache type \"$objectCacheId\" is not present in \$wgObjectCaches." );
-               }
-               $params['store'] = $mainConfig->get( 'ObjectCaches' )[$objectCacheId];
-
-               return \ObjectCache::newWANCacheFromParams( $params );
-       },
-
-       'LocalServerObjectCache' => function ( MediaWikiServices $services ) {
-               $cacheId = \ObjectCache::detectLocalServerCache();
-               return \ObjectCache::newFromId( $cacheId );
-       },
-
-       'VirtualRESTServiceClient' => function ( MediaWikiServices $services ) {
-               $config = $services->getMainConfig()->get( 'VirtualRestConfig' );
-
-               $vrsClient = new VirtualRESTServiceClient( new MultiHttpClient( [] ) );
-               foreach ( $config['paths'] as $prefix => $serviceConfig ) {
-                       $class = $serviceConfig['class'];
-                       // Merge in the global defaults
-                       $constructArg = $serviceConfig['options'] ?? [];
-                       $constructArg += $config['global'];
-                       // Make the VRS service available at the mount point
-                       $vrsClient->mount( $prefix, [ 'class' => $class, 'config' => $constructArg ] );
-               }
-
-               return $vrsClient;
-       },
-
-       'ConfiguredReadOnlyMode' => function ( MediaWikiServices $services ) {
-               return new ConfiguredReadOnlyMode( $services->getMainConfig() );
-       },
-
-       'ReadOnlyMode' => function ( MediaWikiServices $services ) {
-               return new ReadOnlyMode(
-                       $services->getConfiguredReadOnlyMode(),
-                       $services->getDBLoadBalancer()
-               );
-       },
-
-       'UploadRevisionImporter' => function ( MediaWikiServices $services ) {
-               return new ImportableUploadRevisionImporter(
-                       $services->getMainConfig()->get( 'EnableUploads' ),
-                       LoggerFactory::getInstance( 'UploadRevisionImporter' )
-               );
-       },
-
-       'OldRevisionImporter' => function ( MediaWikiServices $services ) {
-               return new ImportableOldRevisionImporter(
-                       true,
-                       LoggerFactory::getInstance( 'OldRevisionImporter' ),
-                       $services->getDBLoadBalancer()
+       'PasswordFactory' => function ( MediaWikiServices $services ) {
+               $config = $services->getMainConfig();
+               return new PasswordFactory(
+                       $config->get( 'PasswordConfig' ),
+                       $config->get( 'PasswordDefault' )
                );
        },
 
-       'WikiRevisionOldRevisionImporterNoUpdates' => function ( MediaWikiServices $services ) {
-               return new ImportableOldRevisionImporter(
-                       false,
-                       LoggerFactory::getInstance( 'OldRevisionImporter' ),
-                       $services->getDBLoadBalancer()
+       'PerDbNameStatsdDataFactory' => function ( MediaWikiServices $services ) {
+               $config = $services->getMainConfig();
+               $wiki = $config->get( 'DBname' );
+               return new BufferingStatsdDataFactory(
+                       rtrim( $services->getMainConfig()->get( 'StatsdMetricPrefix' ), '.' ) . '.' . $wiki
                );
        },
 
-       'ShellCommandFactory' => function ( MediaWikiServices $services ) {
+       'PreferencesFactory' => function ( MediaWikiServices $services ) {
+               $authManager = AuthManager::singleton();
+               $linkRenderer = $services->getLinkRendererFactory()->create();
                $config = $services->getMainConfig();
-
-               $limits = [
-                       'time' => $config->get( 'MaxShellTime' ),
-                       'walltime' => $config->get( 'MaxShellWallClockTime' ),
-                       'memory' => $config->get( 'MaxShellMemory' ),
-                       'filesize' => $config->get( 'MaxShellFileSize' ),
-               ];
-               $cgroup = $config->get( 'ShellCgroup' );
-               $restrictionMethod = $config->get( 'ShellRestrictionMethod' );
-
-               $factory = new CommandFactory( $limits, $cgroup, $restrictionMethod );
-               $factory->setLogger( LoggerFactory::getInstance( 'exec' ) );
-               $factory->logStderr();
+               $factory = new DefaultPreferencesFactory( $config, $services->getContentLanguage(),
+                       $authManager, $linkRenderer );
+               $factory->setLogger( LoggerFactory::getInstance( 'preferences' ) );
 
                return $factory;
        },
 
-       'ExternalStoreFactory' => function ( MediaWikiServices $services ) {
-               $config = $services->getMainConfig();
+       'ProxyLookup' => function ( MediaWikiServices $services ) {
+               $mainConfig = $services->getMainConfig();
+               return new ProxyLookup(
+                       $mainConfig->get( 'SquidServers' ),
+                       $mainConfig->get( 'SquidServersNoPurge' )
+               );
+       },
 
-               return new ExternalStoreFactory(
-                       $config->get( 'ExternalStores' )
+       'ReadOnlyMode' => function ( MediaWikiServices $services ) {
+               return new ReadOnlyMode(
+                       $services->getConfiguredReadOnlyMode(),
+                       $services->getDBLoadBalancer()
                );
        },
 
+       'RevisionFactory' => function ( MediaWikiServices $services ) {
+               return $services->getRevisionStore();
+       },
+
+       'RevisionLookup' => function ( MediaWikiServices $services ) {
+               return $services->getRevisionStore();
+       },
+
        'RevisionStore' => function ( MediaWikiServices $services ) {
                return $services->getRevisionStoreFactory()->getRevisionStore();
        },
@@ -492,46 +443,76 @@ return [
                return $store;
        },
 
-       'RevisionLookup' => function ( MediaWikiServices $services ) {
-               return $services->getRevisionStore();
+       'SearchEngineConfig' => function ( MediaWikiServices $services ) {
+               return new SearchEngineConfig( $services->getMainConfig(),
+                       $services->getContentLanguage() );
        },
 
-       'RevisionFactory' => function ( MediaWikiServices $services ) {
-               return $services->getRevisionStore();
+       'SearchEngineFactory' => function ( MediaWikiServices $services ) {
+               return new SearchEngineFactory( $services->getSearchEngineConfig() );
        },
 
-       'BlobStoreFactory' => function ( MediaWikiServices $services ) {
-               return new BlobStoreFactory(
-                       $services->getDBLoadBalancer(),
-                       $services->getMainWANObjectCache(),
-                       $services->getMainConfig(),
-                       $services->getContentLanguage()
-               );
+       'ShellCommandFactory' => function ( MediaWikiServices $services ) {
+               $config = $services->getMainConfig();
+
+               $limits = [
+                       'time' => $config->get( 'MaxShellTime' ),
+                       'walltime' => $config->get( 'MaxShellWallClockTime' ),
+                       'memory' => $config->get( 'MaxShellMemory' ),
+                       'filesize' => $config->get( 'MaxShellFileSize' ),
+               ];
+               $cgroup = $config->get( 'ShellCgroup' );
+               $restrictionMethod = $config->get( 'ShellRestrictionMethod' );
+
+               $factory = new CommandFactory( $limits, $cgroup, $restrictionMethod );
+               $factory->setLogger( LoggerFactory::getInstance( 'exec' ) );
+               $factory->logStderr();
+
+               return $factory;
        },
 
-       'BlobStore' => function ( MediaWikiServices $services ) {
-               return $services->getService( '_SqlBlobStore' );
+       'SiteLookup' => function ( MediaWikiServices $services ) {
+               $cacheFile = $services->getMainConfig()->get( 'SitesCacheFile' );
+
+               if ( $cacheFile !== false ) {
+                       return new FileBasedSiteLookup( $cacheFile );
+               } else {
+                       // Use the default SiteStore as the SiteLookup implementation for now
+                       return $services->getSiteStore();
+               }
        },
 
-       '_SqlBlobStore' => function ( MediaWikiServices $services ) {
-               return $services->getBlobStoreFactory()->newSqlBlobStore();
+       'SiteStore' => function ( MediaWikiServices $services ) {
+               $rawSiteStore = new DBSiteStore( $services->getDBLoadBalancer() );
+
+               // TODO: replace wfGetCache with a CacheFactory service.
+               // TODO: replace wfIsHHVM with a capabilities service.
+               $cache = wfGetCache( wfIsHHVM() ? CACHE_ACCEL : CACHE_ANYTHING );
+
+               return new CachingSiteStore( $rawSiteStore, $cache );
        },
 
-       'ContentModelStore' => function ( MediaWikiServices $services ) {
-               return new NameTableStore(
-                       $services->getDBLoadBalancer(),
-                       $services->getMainWANObjectCache(),
-                       LoggerFactory::getInstance( 'NameTableSqlStore' ),
-                       'content_models',
-                       'model_id',
-                       'model_name'
-                       /**
-                        * No strtolower normalization is added to the service as there are examples of
-                        * extensions that do not stick to this assumption.
-                        * - extensions/examples/DataPages define( 'CONTENT_MODEL_XML_DATA','XML_DATA' );
-                        * - extensions/Scribunto define( 'CONTENT_MODEL_SCRIBUNTO', 'Scribunto' );
-                        */
-               );
+       'SkinFactory' => function ( MediaWikiServices $services ) {
+               $factory = new SkinFactory();
+
+               $names = $services->getMainConfig()->get( 'ValidSkinNames' );
+
+               foreach ( $names as $name => $skin ) {
+                       $factory->register( $name, $skin, function () use ( $name, $skin ) {
+                               $class = "Skin$skin";
+                               return new $class( $name );
+                       } );
+               }
+               // Register a hidden "fallback" skin
+               $factory->register( 'fallback', 'Fallback', function () {
+                       return new SkinFallback;
+               } );
+               // Register a hidden skin for api output
+               $factory->register( 'apioutput', 'ApiOutput', function () {
+                       return new SkinApi;
+               } );
+
+               return $factory;
        },
 
        'SlotRoleStore' => function ( MediaWikiServices $services ) {
@@ -546,68 +527,87 @@ return [
                );
        },
 
-       'ChangeTagDefStore' => function ( MediaWikiServices $services ) {
-               return new NameTableStore(
-                       $services->getDBLoadBalancer(),
-                       $services->getMainWANObjectCache(),
-                       LoggerFactory::getInstance( 'NameTableSqlStore' ),
-                       'change_tag_def',
-                       'ctd_id',
-                       'ctd_name',
-                       null,
-                       false,
-                       function ( $insertFields ) {
-                               $insertFields['ctd_user_defined'] = 0;
-                               $insertFields['ctd_count'] = 0;
-                               return $insertFields;
-                       }
+       'StatsdDataFactory' => function ( MediaWikiServices $services ) {
+               return new BufferingStatsdDataFactory(
+                       rtrim( $services->getMainConfig()->get( 'StatsdMetricPrefix' ), '.' )
                );
        },
 
-       'PreferencesFactory' => function ( MediaWikiServices $services ) {
-               $authManager = AuthManager::singleton();
-               $linkRenderer = $services->getLinkRendererFactory()->create();
-               $config = $services->getMainConfig();
-               $factory = new DefaultPreferencesFactory( $config, $services->getContentLanguage(),
-                       $authManager, $linkRenderer );
-               $factory->setLogger( LoggerFactory::getInstance( 'preferences' ) );
-
-               return $factory;
+       'TitleFormatter' => function ( MediaWikiServices $services ) {
+               return $services->getService( '_MediaWikiTitleCodec' );
        },
 
-       'HttpRequestFactory' => function ( MediaWikiServices $services ) {
-               return new \MediaWiki\Http\HttpRequestFactory();
+       'TitleParser' => function ( MediaWikiServices $services ) {
+               return $services->getService( '_MediaWikiTitleCodec' );
        },
 
-       'CommentStore' => function ( MediaWikiServices $services ) {
-               return new CommentStore(
-                       $services->getContentLanguage(),
-                       $services->getMainConfig()->get( 'CommentTableSchemaMigrationStage' )
+       'UploadRevisionImporter' => function ( MediaWikiServices $services ) {
+               return new ImportableUploadRevisionImporter(
+                       $services->getMainConfig()->get( 'EnableUploads' ),
+                       LoggerFactory::getInstance( 'UploadRevisionImporter' )
                );
        },
 
-       'ActorMigration' => function ( MediaWikiServices $services ) {
-               return new ActorMigration(
-                       $services->getMainConfig()->get( 'ActorTableSchemaMigrationStage' )
+       'VirtualRESTServiceClient' => function ( MediaWikiServices $services ) {
+               $config = $services->getMainConfig()->get( 'VirtualRestConfig' );
+
+               $vrsClient = new VirtualRESTServiceClient( new MultiHttpClient( [] ) );
+               foreach ( $config['paths'] as $prefix => $serviceConfig ) {
+                       $class = $serviceConfig['class'];
+                       // Merge in the global defaults
+                       $constructArg = $serviceConfig['options'] ?? [];
+                       $constructArg += $config['global'];
+                       // Make the VRS service available at the mount point
+                       $vrsClient->mount( $prefix, [ 'class' => $class, 'config' => $constructArg ] );
+               }
+
+               return $vrsClient;
+       },
+
+       'WatchedItemQueryService' => function ( MediaWikiServices $services ) {
+               return new WatchedItemQueryService(
+                       $services->getDBLoadBalancer(),
+                       $services->getCommentStore(),
+                       $services->getActorMigration()
                );
        },
 
-       'MagicWordFactory' => function ( MediaWikiServices $services ) {
-               return new MagicWordFactory( $services->getContentLanguage() );
+       'WatchedItemStore' => function ( MediaWikiServices $services ) {
+               $store = new WatchedItemStore(
+                       $services->getDBLoadBalancer(),
+                       new HashBagOStuff( [ 'maxKeys' => 100 ] ),
+                       $services->getReadOnlyMode(),
+                       $services->getMainConfig()->get( 'UpdateRowsPerQuery' )
+               );
+               $store->setStatsdDataFactory( $services->getStatsdDataFactory() );
+
+               if ( $services->getMainConfig()->get( 'ReadOnlyWatchedItemStore' ) ) {
+                       $store = new NoWriteWatchedItemStore( $store );
+               }
+
+               return $store;
        },
 
-       'ContentLanguage' => function ( MediaWikiServices $services ) {
-               return Language::factory( $services->getMainConfig()->get( 'LanguageCode' ) );
+       'WikiRevisionOldRevisionImporterNoUpdates' => function ( MediaWikiServices $services ) {
+               return new ImportableOldRevisionImporter(
+                       false,
+                       LoggerFactory::getInstance( 'OldRevisionImporter' ),
+                       $services->getDBLoadBalancer()
+               );
        },
 
-       'PasswordFactory' => function ( MediaWikiServices $services ) {
-               $config = $services->getMainConfig();
-               return new PasswordFactory(
-                       $config->get( 'PasswordConfig' ),
-                       $config->get( 'PasswordDefault' )
+       '_MediaWikiTitleCodec' => function ( MediaWikiServices $services ) {
+               return new MediaWikiTitleCodec(
+                       $services->getContentLanguage(),
+                       $services->getGenderCache(),
+                       $services->getMainConfig()->get( 'LocalInterwikis' )
                );
        },
 
+       '_SqlBlobStore' => function ( MediaWikiServices $services ) {
+               return $services->getBlobStoreFactory()->newSqlBlobStore();
+       },
+
        ///////////////////////////////////////////////////////////////////////////
        // NOTE: When adding a service here, don't forget to add a getter function
        // in the MediaWikiServices class. The convenience getter should just call
index 413c1a2..eee4490 100644 (file)
@@ -309,67 +309,71 @@ class MediaWikiServicesTest extends MediaWikiTestCase {
        public function provideGetService() {
                // NOTE: This should list all service getters defined in ServiceWiring.php.
                return [
+                       '_MediaWikiTitleCodec' => [ '_MediaWikiTitleCodec', MediaWikiTitleCodec::class ],
+                       '_SqlBlobStore' => [ '_SqlBlobStore', SqlBlobStore::class ],
+                       'ActorMigration' => [ 'ActorMigration', ActorMigration::class ],
+                       'BlobStore' => [ 'BlobStore', BlobStore::class ],
+                       'BlobStoreFactory' => [ 'BlobStoreFactory', BlobStoreFactory::class ],
                        'BootstrapConfig' => [ 'BootstrapConfig', Config::class ],
+                       'ChangeTagDefStore' => [ 'ChangeTagDefStore', NameTableStore::class ],
+                       'CommentStore' => [ 'CommentStore', CommentStore::class ],
                        'ConfigFactory' => [ 'ConfigFactory', ConfigFactory::class ],
-                       'MainConfig' => [ 'MainConfig', Config::class ],
-                       'SiteStore' => [ 'SiteStore', SiteStore::class ],
-                       'SiteLookup' => [ 'SiteLookup', SiteLookup::class ],
-                       'StatsdDataFactory' => [ 'StatsdDataFactory', IBufferingStatsdDataFactory::class ],
-                       'PerDbNameStatsdDataFactory' =>
-                               [ 'PerDbNameStatsdDataFactory', IBufferingStatsdDataFactory::class ],
-                       'InterwikiLookup' => [ 'InterwikiLookup', InterwikiLookup::class ],
-                       'EventRelayerGroup' => [ 'EventRelayerGroup', EventRelayerGroup::class ],
-                       'SearchEngineFactory' => [ 'SearchEngineFactory', SearchEngineFactory::class ],
-                       'SearchEngineConfig' => [ 'SearchEngineConfig', SearchEngineConfig::class ],
-                       'SkinFactory' => [ 'SkinFactory', SkinFactory::class ],
-                       'DBLoadBalancerFactory' => [ 'DBLoadBalancerFactory', Wikimedia\Rdbms\LBFactory::class ],
-                       'DBLoadBalancer' => [ 'DBLoadBalancer', Wikimedia\Rdbms\LoadBalancer::class ],
-                       'WatchedItemStore' => [ 'WatchedItemStore', WatchedItemStore::class ],
-                       'WatchedItemQueryService' => [ 'WatchedItemQueryService', WatchedItemQueryService::class ],
-                       'CryptRand' => [ 'CryptRand', CryptRand::class ],
+                       'ConfigRepository' => [ 'ConfigRepository', \MediaWiki\Config\ConfigRepository::class ],
+                       'ConfiguredReadOnlyMode' => [ 'ConfiguredReadOnlyMode', ConfiguredReadOnlyMode::class ],
+                       'ContentLanguage' => [ 'ContentLanguage', Language::class ],
+                       'ContentModelStore' => [ 'ContentModelStore', NameTableStore::class ],
                        'CryptHKDF' => [ 'CryptHKDF', CryptHKDF::class ],
-                       'MediaHandlerFactory' => [ 'MediaHandlerFactory', MediaHandlerFactory::class ],
-                       'Parser' => [ 'Parser', Parser::class ],
-                       'ParserCache' => [ 'ParserCache', ParserCache::class ],
+                       'CryptRand' => [ 'CryptRand', CryptRand::class ],
+                       'DBLoadBalancer' => [ 'DBLoadBalancer', Wikimedia\Rdbms\LoadBalancer::class ],
+                       'DBLoadBalancerFactory' =>
+                               [ 'DBLoadBalancerFactory', Wikimedia\Rdbms\LBFactory::class ],
+                       'EventRelayerGroup' => [ 'EventRelayerGroup', EventRelayerGroup::class ],
+                       'ExternalStoreFactory' => [ 'ExternalStoreFactory', ExternalStoreFactory::class ],
                        'GenderCache' => [ 'GenderCache', GenderCache::class ],
+                       'HttpRequestFactory' => [ 'HttpRequestFactory', HttpRequestFactory::class ],
+                       'InterwikiLookup' => [ 'InterwikiLookup', InterwikiLookup::class ],
                        'LinkCache' => [ 'LinkCache', LinkCache::class ],
                        'LinkRenderer' => [ 'LinkRenderer', LinkRenderer::class ],
                        'LinkRendererFactory' => [ 'LinkRendererFactory', LinkRendererFactory::class ],
-                       '_MediaWikiTitleCodec' => [ '_MediaWikiTitleCodec', MediaWikiTitleCodec::class ],
-                       'MimeAnalyzer' => [ 'MimeAnalyzer', MimeAnalyzer::class ],
-                       'TitleFormatter' => [ 'TitleFormatter', TitleFormatter::class ],
-                       'TitleParser' => [ 'TitleParser', TitleParser::class ],
-                       'ProxyLookup' => [ 'ProxyLookup', ProxyLookup::class ],
+                       'LocalServerObjectCache' => [ 'LocalServerObjectCache', BagOStuff::class ],
+                       'MagicWordFactory' => [ 'MagicWordFactory', MagicWordFactory::class ],
+                       'MainConfig' => [ 'MainConfig', Config::class ],
                        'MainObjectStash' => [ 'MainObjectStash', BagOStuff::class ],
                        'MainWANObjectCache' => [ 'MainWANObjectCache', WANObjectCache::class ],
-                       'LocalServerObjectCache' => [ 'LocalServerObjectCache', BagOStuff::class ],
-                       'VirtualRESTServiceClient' => [ 'VirtualRESTServiceClient', VirtualRESTServiceClient::class ],
-                       'ShellCommandFactory' => [ 'ShellCommandFactory', CommandFactory::class ],
-                       'BlobStoreFactory' => [ 'BlobStoreFactory', BlobStoreFactory::class ],
-                       'BlobStore' => [ 'BlobStore', BlobStore::class ],
-                       '_SqlBlobStore' => [ '_SqlBlobStore', SqlBlobStore::class ],
+                       'MediaHandlerFactory' => [ 'MediaHandlerFactory', MediaHandlerFactory::class ],
+                       'MimeAnalyzer' => [ 'MimeAnalyzer', MimeAnalyzer::class ],
+                       'OldRevisionImporter' => [ 'OldRevisionImporter', OldRevisionImporter::class ],
+                       'Parser' => [ 'Parser', Parser::class ],
+                       'ParserCache' => [ 'ParserCache', ParserCache::class ],
+                       'PasswordFactory' => [ 'PasswordFactory', PasswordFactory::class ],
+                       'PerDbNameStatsdDataFactory' =>
+                               [ 'PerDbNameStatsdDataFactory', IBufferingStatsdDataFactory::class ],
+                       'PreferencesFactory' => [ 'PreferencesFactory', PreferencesFactory::class ],
+                       'ProxyLookup' => [ 'ProxyLookup', ProxyLookup::class ],
+                       'ReadOnlyMode' => [ 'ReadOnlyMode', ReadOnlyMode::class ],
+                       'RevisionFactory' => [ 'RevisionFactory', RevisionFactory::class ],
+                       'RevisionLookup' => [ 'RevisionLookup', RevisionLookup::class ],
                        'RevisionStore' => [ 'RevisionStore', RevisionStore::class ],
                        'RevisionStoreFactory' => [ 'RevisionStoreFactory', RevisionStoreFactory::class ],
-                       'RevisionLookup' => [ 'RevisionLookup', RevisionLookup::class ],
-                       'RevisionFactory' => [ 'RevisionFactory', RevisionFactory::class ],
-                       'ContentModelStore' => [ 'ContentModelStore', NameTableStore::class ],
+                       'SearchEngineConfig' => [ 'SearchEngineConfig', SearchEngineConfig::class ],
+                       'SearchEngineFactory' => [ 'SearchEngineFactory', SearchEngineFactory::class ],
+                       'ShellCommandFactory' => [ 'ShellCommandFactory', CommandFactory::class ],
+                       'SiteLookup' => [ 'SiteLookup', SiteLookup::class ],
+                       'SiteStore' => [ 'SiteStore', SiteStore::class ],
+                       'SkinFactory' => [ 'SkinFactory', SkinFactory::class ],
                        'SlotRoleStore' => [ 'SlotRoleStore', NameTableStore::class ],
-                       'HttpRequestFactory' => [ 'HttpRequestFactory', HttpRequestFactory::class ],
-                       'CommentStore' => [ 'CommentStore', CommentStore::class ],
-                       'ChangeTagDefStore' => [ 'ChangeTagDefStore', NameTableStore::class ],
-                       'ConfiguredReadOnlyMode' => [ 'ConfiguredReadOnlyMode', ConfiguredReadOnlyMode::class ],
-                       'ReadOnlyMode' => [ 'ReadOnlyMode', ReadOnlyMode::class ],
+                       'StatsdDataFactory' => [ 'StatsdDataFactory', IBufferingStatsdDataFactory::class ],
+                       'TitleFormatter' => [ 'TitleFormatter', TitleFormatter::class ],
+                       'TitleParser' => [ 'TitleParser', TitleParser::class ],
                        'UploadRevisionImporter' => [ 'UploadRevisionImporter', UploadRevisionImporter::class ],
-                       'OldRevisionImporter' => [ 'OldRevisionImporter', OldRevisionImporter::class ],
+                       'VirtualRESTServiceClient' =>
+                               [ 'VirtualRESTServiceClient', VirtualRESTServiceClient::class ],
+                       'WatchedItemQueryService' =>
+                               [ 'WatchedItemQueryService', WatchedItemQueryService::class ],
+                       'WatchedItemStore' => [ 'WatchedItemStore', WatchedItemStore::class ],
                        'WikiRevisionOldRevisionImporterNoUpdates' =>
-                               [ 'WikiRevisionOldRevisionImporterNoUpdates', ImportableOldRevisionImporter::class ],
-                       'ExternalStoreFactory' => [ 'ExternalStoreFactory', ExternalStoreFactory::class ],
-                       'PreferencesFactory' => [ 'PreferencesFactory', PreferencesFactory::class ],
-                       'ActorMigration' => [ 'ActorMigration', ActorMigration::class ],
-                       'ConfigRepository' => [ 'ConfigRepository', \MediaWiki\Config\ConfigRepository::class ],
-                       'MagicWordFactory' => [ 'MagicWordFactory', MagicWordFactory::class ],
-                       'ContentLanguage' => [ 'ContentLanguage', Language::class ],
-                       'PasswordFactory' => [ 'PasswordFactory', PasswordFactory::class ],
+                               [ 'WikiRevisionOldRevisionImporterNoUpdates',
+                               ImportableOldRevisionImporter::class ],
                ];
        }
 
@@ -409,4 +413,24 @@ class MediaWikiServicesTest extends MediaWikiTestCase {
                );
        }
 
+       public function testGettersAreSorted() {
+               $methods = ( new ReflectionClass( MediaWikiServices::class ) )
+                       ->getMethods( ReflectionMethod::IS_STATIC | ReflectionMethod::IS_PUBLIC );
+
+               $names = array_map( function ( $method ) {
+                       return $method->getName();
+               }, $methods );
+               $serviceNames = array_map( function ( $name ) {
+                       return "get$name";
+               }, array_keys( $this->provideGetService() ) );
+               $names = array_values( array_filter( $names, function ( $name ) use ( $serviceNames ) {
+                       return in_array( $name, $serviceNames );
+               } ) );
+
+               $sortedNames = $names;
+               sort( $sortedNames );
+
+               $this->assertSame( $sortedNames, $names,
+                       'Please keep service getters sorted alphabetically' );
+       }
 }
diff --git a/tests/phpunit/includes/ServiceWiringTest.php b/tests/phpunit/includes/ServiceWiringTest.php
new file mode 100644 (file)
index 0000000..74e8e1b
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+
+/**
+ * @coversNothing
+ */
+class ServiceWiringTest extends MediaWikiTestCase {
+       public function testServicesAreSorted() {
+               global $IP;
+               $services = array_keys( require "$IP/includes/ServiceWiring.php" );
+               $sortedServices = $services;
+               sort( $sortedServices );
+
+               $this->assertSame( $sortedServices, $services,
+                       'Please keep services sorted alphabetically' );
+       }
+}