function () : ActorMigration { return new ActorMigration( SCHEMA_COMPAT_NEW ); }, 'AuthManager' => function ( MediaWikiServices $services ) : AuthManager { $authManager = new AuthManager( RequestContext::getMain()->getRequest(), $services->getMainConfig(), $services->getObjectFactory(), $services->getPermissionManager(), $services->getHookContainer() ); $authManager->setLogger( LoggerFactory::getInstance( 'authentication' ) ); return $authManager; }, 'BadFileLookup' => function ( MediaWikiServices $services ) : BadFileLookup { return new BadFileLookup( function () { return wfMessage( 'bad_image_list' )->inContentLanguage()->plain(); }, $services->getLocalServerObjectCache(), $services->getRepoGroup(), $services->getTitleParser(), $services->getHookContainer() ); }, 'BlobStore' => function ( MediaWikiServices $services ) : BlobStore { return $services->getService( '_SqlBlobStore' ); }, 'BlobStoreFactory' => function ( MediaWikiServices $services ) : BlobStoreFactory { return new BlobStoreFactory( $services->getDBLoadBalancerFactory(), $services->getExternalStoreAccess(), $services->getMainWANObjectCache(), new ServiceOptions( BlobStoreFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ) ); }, 'BlockErrorFormatter' => function () : BlockErrorFormatter { return new BlockErrorFormatter(); }, 'BlockManager' => function ( MediaWikiServices $services ) : BlockManager { return new BlockManager( new ServiceOptions( BlockManager::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ), $services->getPermissionManager(), LoggerFactory::getInstance( 'BlockManager' ), $services->getHookContainer() ); }, 'BlockPermissionCheckerFactory' => function ( MediaWikiServices $services ) : BlockPermissionCheckerFactory { return new BlockPermissionCheckerFactory( $services->getPermissionManager() ); }, 'BlockRestrictionStore' => function ( MediaWikiServices $services ) : BlockRestrictionStore { return new BlockRestrictionStore( $services->getDBLoadBalancer() ); }, 'ChangeTagDefStore' => function ( MediaWikiServices $services ) : NameTableStore { return $services->getNameTableStoreFactory()->getChangeTagDef(); }, 'CommentStore' => function ( MediaWikiServices $services ) : CommentStore { return new CommentStore( $services->getContentLanguage(), MIGRATION_NEW ); }, 'ConfigFactory' => function ( MediaWikiServices $services ) : ConfigFactory { // Use the bootstrap config to initialize the ConfigFactory. $registry = $services->getBootstrapConfig()->get( 'ConfigRegistry' ); $factory = new ConfigFactory(); foreach ( $registry as $name => $callback ) { $factory->register( $name, $callback ); } return $factory; }, 'ConfigRepository' => function ( MediaWikiServices $services ) : ConfigRepository { return new ConfigRepository( $services->getConfigFactory() ); }, 'ConfiguredReadOnlyMode' => function ( MediaWikiServices $services ) : ConfiguredReadOnlyMode { $config = $services->getMainConfig(); return new ConfiguredReadOnlyMode( $config->get( 'ReadOnly' ), $config->get( 'ReadOnlyFile' ) ); }, 'ContentHandlerFactory' => function ( MediaWikiServices $services ) : IContentHandlerFactory { $contentHandlerConfig = $services->getMainConfig()->get( 'ContentHandlers' ); return new ContentHandlerFactory( $contentHandlerConfig, $services->getObjectFactory(), $services->getHookContainer(), LoggerFactory::getInstance( 'ContentHandler' ) ); }, 'ContentLanguage' => function ( MediaWikiServices $services ) : Language { return $services->getLanguageFactory()->getLanguage( $services->getMainConfig()->get( 'LanguageCode' ) ); }, 'ContentModelChangeFactory' => function ( MediaWikiServices $services ) : ContentModelChangeFactory { return $services->getService( '_PageCommandFactory' ); }, 'ContentModelStore' => function ( MediaWikiServices $services ) : NameTableStore { return $services->getNameTableStoreFactory()->getContentModels(); }, 'ContributionsLookup' => function ( MediaWikiServices $services ) : ContributionsLookup { return new ContributionsLookup( $services->getRevisionStore() ); }, 'CryptHKDF' => function ( MediaWikiServices $services ) : CryptHKDF { $config = $services->getMainConfig(); $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 ); }, 'DateFormatterFactory' => function () : DateFormatterFactory { return new DateFormatterFactory; }, 'DBLoadBalancer' => function ( MediaWikiServices $services ) : Wikimedia\Rdbms\ILoadBalancer { // just return the default LB from the DBLoadBalancerFactory service return $services->getDBLoadBalancerFactory()->getMainLB(); }, 'DBLoadBalancerFactory' => function ( MediaWikiServices $services ) : Wikimedia\Rdbms\LBFactory { $mainConfig = $services->getMainConfig(); try { $stash = $services->getMainObjectStash(); } catch ( RecursiveServiceDependencyException $e ) { $stash = new EmptyBagOStuff(); // T141804: handle cases like CACHE_DB } if ( $stash instanceof EmptyBagOStuff ) { // Use process cache if the main stash is disabled or there was recursion $stash = new HashBagOStuff( [ 'maxKeys' => 100 ] ); } try { $wanCache = $services->getMainWANObjectCache(); } catch ( RecursiveServiceDependencyException $e ) { $wanCache = WANObjectCache::newEmpty(); // T141804: handle cases like CACHE_DB } $lbConf = MWLBFactory::applyDefaultConfig( $mainConfig->get( 'LBFactoryConf' ), new ServiceOptions( MWLBFactory::APPLY_DEFAULT_CONFIG_OPTIONS, $mainConfig ), $services->getConfiguredReadOnlyMode(), $services->getLocalServerObjectCache(), $stash, $wanCache ); $class = MWLBFactory::getLBFactoryClass( $lbConf ); $instance = new $class( $lbConf ); MWLBFactory::setDomainAliases( $instance ); return $instance; }, 'Emailer' => function ( MediaWikiServices $services ) : IEmailer { return new Emailer(); }, 'EventRelayerGroup' => function ( MediaWikiServices $services ) : EventRelayerGroup { return new EventRelayerGroup( $services->getMainConfig()->get( 'EventRelayerConfig' ) ); }, 'ExternalStoreAccess' => function ( MediaWikiServices $services ) : ExternalStoreAccess { return new ExternalStoreAccess( $services->getExternalStoreFactory(), LoggerFactory::getInstance( 'ExternalStore' ) ); }, 'ExternalStoreFactory' => function ( MediaWikiServices $services ) : ExternalStoreFactory { $config = $services->getMainConfig(); $writeStores = $config->get( 'DefaultExternalStore' ); return new ExternalStoreFactory( $config->get( 'ExternalStores' ), ( $writeStores !== false ) ? (array)$writeStores : [], $services->getDBLoadBalancer()->getLocalDomainID(), LoggerFactory::getInstance( 'ExternalStore' ) ); }, 'FileBackendGroup' => function ( MediaWikiServices $services ) : FileBackendGroup { $mainConfig = $services->getMainConfig(); $ld = WikiMap::getCurrentWikiDbDomain(); $fallbackWikiId = WikiMap::getWikiIdFromDbDomain( $ld ); // If the local wiki ID and local domain ID do not match, probably due to a non-default // schema, issue a warning. A non-default schema indicates that it might be used to // disambiguate different wikis. $legacyDomainId = strlen( $ld->getTablePrefix() ) ? "{$ld->getDatabase()}-{$ld->getTablePrefix()}" : $ld->getDatabase(); if ( $ld->getSchema() !== null && $legacyDomainId !== $fallbackWikiId ) { wfWarn( "Legacy default 'domainId' is '$legacyDomainId' but wiki ID is '$fallbackWikiId'." ); } $cache = $services->getLocalServerObjectCache(); if ( $cache instanceof EmptyBagOStuff ) { $cache = new HashBagOStuff; } return new FileBackendGroup( new ServiceOptions( FileBackendGroup::CONSTRUCTOR_OPTIONS, $mainConfig, [ 'fallbackWikiId' => $fallbackWikiId ] ), $services->getConfiguredReadOnlyMode(), $cache, $services->getMainWANObjectCache(), $services->getMimeAnalyzer(), $services->getLockManagerGroupFactory(), $services->getTempFSFileFactory(), $services->getObjectFactory() ); }, 'GenderCache' => function ( MediaWikiServices $services ) : GenderCache { $nsInfo = $services->getNamespaceInfo(); // Database layer may be disabled, so processing without database connection $dbLoadBalancer = $services->isServiceDisabled( 'DBLoadBalancer' ) ? null : $services->getDBLoadBalancer(); return new GenderCache( $nsInfo, $dbLoadBalancer, $services->get( '_DefaultOptionsLookup' ) ); }, 'GlobalIdGenerator' => function ( MediaWikiServices $services ) : GlobalIdGenerator { $mainConfig = $services->getMainConfig(); return new GlobalIdGenerator( $mainConfig->get( 'TmpDirectory' ), // Ignore APC-like caches in CLI mode since there is no meaningful persistence. // This avoids having counters restart with each script run. The ID generator // will fallback to using the disk in those cases. $mainConfig->get( 'CommandLineMode' ) ? new EmptyBagOStuff() : $services->getLocalServerObjectCache(), function ( $command ) { return wfShellExec( $command ); } ); }, 'HookContainer' => function ( MediaWikiServices $services ) : HookContainer { $extRegistry = ExtensionRegistry::getInstance(); $extDeprecatedHooks = $extRegistry->getAttribute( 'DeprecatedHooks' ); $deprecatedHooks = new DeprecatedHooks( $extDeprecatedHooks ); $hookRegistry = new GlobalHookRegistry( $extRegistry, $deprecatedHooks ); return new HookContainer( $hookRegistry, $services->getObjectFactory() ); }, 'HtmlCacheUpdater' => function ( MediaWikiServices $services ) : HtmlCacheUpdater { $config = $services->getMainConfig(); return new HtmlCacheUpdater( $services->getHookContainer(), $config->get( 'CdnReboundPurgeDelay' ), $config->get( 'UseFileCache' ), $config->get( 'CdnMaxAge' ) ); }, 'HttpRequestFactory' => function ( MediaWikiServices $services ) : HttpRequestFactory { return new HttpRequestFactory( new ServiceOptions( HttpRequestFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ), LoggerFactory::getInstance( 'http' ) ); }, 'InterwikiLookup' => function ( MediaWikiServices $services ) : InterwikiLookup { $config = $services->getMainConfig(); return new ClassicInterwikiLookup( $services->getContentLanguage(), $services->getMainWANObjectCache(), $services->getHookContainer(), $config->get( 'InterwikiExpiry' ), $config->get( 'InterwikiCache' ), $config->get( 'InterwikiScopes' ), $config->get( 'InterwikiFallbackSite' ) ); }, 'JobRunner' => function ( MediaWikiServices $services ) : JobRunner { return new JobRunner( new ServiceOptions( JobRunner::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ), $services->getDBLoadBalancerFactory(), JobQueueGroup::singleton(), $services->getReadOnlyMode(), $services->getLinkCache(), $services->getStatsdDataFactory(), LoggerFactory::getInstance( 'runJobs' ) ); }, 'LanguageConverterFactory' => function ( MediaWikiServices $services ) : LanguageConverterFactory { $usePigLatinVariant = $services->getMainConfig()->get( 'UsePigLatinVariant' ); return new LanguageConverterFactory( $usePigLatinVariant, function () use ( $services ) { return $services->getContentLanguage(); } ); }, 'LanguageFactory' => function ( MediaWikiServices $services ) : LanguageFactory { return new LanguageFactory( new ServiceOptions( LanguageFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ), $services->getLocalisationCache(), $services->getLanguageNameUtils(), $services->getLanguageFallback(), $services->getLanguageConverterFactory(), $services->getHookContainer() ); }, 'LanguageFallback' => function ( MediaWikiServices $services ) : LanguageFallback { return new LanguageFallback( $services->getMainConfig()->get( 'LanguageCode' ), $services->getLocalisationCache(), $services->getLanguageNameUtils() ); }, 'LanguageNameUtils' => function ( MediaWikiServices $services ) : LanguageNameUtils { return new LanguageNameUtils( new ServiceOptions( LanguageNameUtils::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ), $services->getHookContainer() ); }, 'LinkBatchFactory' => function ( MediaWikiServices $services ) : LinkBatchFactory { return new LinkBatchFactory( $services->getLinkCache(), $services->getTitleFormatter(), $services->getContentLanguage(), $services->getGenderCache(), $services->getDBLoadBalancer() ); }, 'LinkCache' => function ( MediaWikiServices $services ) : LinkCache { return new LinkCache( $services->getTitleFormatter(), $services->getMainWANObjectCache(), $services->getNamespaceInfo() ); }, 'LinkRenderer' => function ( MediaWikiServices $services ) : LinkRenderer { if ( defined( 'MW_NO_SESSION' ) ) { return $services->getLinkRendererFactory()->create(); } else { // Normally information from the current request would not be passed in here; // this is an exception. (See also the class documentation.) return $services->getLinkRendererFactory()->createForUser( RequestContext::getMain()->getUser() ); } }, 'LinkRendererFactory' => function ( MediaWikiServices $services ) : LinkRendererFactory { return new LinkRendererFactory( $services->getTitleFormatter(), $services->getLinkCache(), $services->getNamespaceInfo(), $services->getSpecialPageFactory(), $services->getHookContainer() ); }, 'LocalisationCache' => function ( MediaWikiServices $services ) : LocalisationCache { $conf = $services->getMainConfig()->get( 'LocalisationCacheConf' ); $logger = LoggerFactory::getInstance( 'localisation' ); $store = LocalisationCache::getStoreFromConf( $conf, $services->getMainConfig()->get( 'CacheDirectory' ) ); $logger->debug( 'LocalisationCache using store ' . get_class( $store ) ); return new $conf['class']( new ServiceOptions( LocalisationCache::CONSTRUCTOR_OPTIONS, // Two of the options are stored in $wgLocalisationCacheConf $conf, // In case someone set that config variable and didn't reset all keys, set defaults. [ 'forceRecache' => false, 'manualRecache' => false, ], // Some other options come from config itself $services->getMainConfig() ), $store, $logger, [ function () use ( $services ) { // NOTE: Make sure we use the same cache object that is assigned in the // constructor of the MessageBlobStore class used by ResourceLoader. // T231866: Avoid circular dependency via ResourceLoader. MessageBlobStore::clearGlobalCacheEntry( $services->getMainWANObjectCache() ); } ], $services->getLanguageNameUtils(), $services->getHookContainer() ); }, 'LocalServerObjectCache' => function ( MediaWikiServices $services ) : BagOStuff { return ObjectCache::makeLocalServerCache(); }, 'LockManagerGroupFactory' => function ( MediaWikiServices $services ) : LockManagerGroupFactory { return new LockManagerGroupFactory( WikiMap::getCurrentWikiDbDomain()->getId(), $services->getMainConfig()->get( 'LockManagers' ), $services->getDBLoadBalancerFactory() ); }, 'MagicWordFactory' => function ( MediaWikiServices $services ) : MagicWordFactory { return new MagicWordFactory( $services->getContentLanguage(), $services->getHookContainer() ); }, 'MainConfig' => function ( MediaWikiServices $services ) : Config { // Use the 'main' config from the ConfigFactory service. return $services->getConfigFactory()->makeConfig( 'main' ); }, 'MainObjectStash' => function ( MediaWikiServices $services ) : BagOStuff { $mainConfig = $services->getMainConfig(); $id = $mainConfig->get( 'MainStash' ); if ( !isset( $mainConfig->get( 'ObjectCaches' )[$id] ) ) { throw new UnexpectedValueException( "Cache type \"$id\" is not present in \$wgObjectCaches." ); } $params = $mainConfig->get( 'ObjectCaches' )[$id]; $store = ObjectCache::newFromParams( $params, $mainConfig ); $store->getLogger()->debug( 'MainObjectStash using store {class}', [ 'class' => get_class( $store ) ] ); return $store; }, 'MainWANObjectCache' => function ( MediaWikiServices $services ) : WANObjectCache { $mainConfig = $services->getMainConfig(); $wanId = $mainConfig->get( 'MainWANCache' ); $wanParams = $mainConfig->get( 'WANObjectCaches' )[$wanId] ?? null; if ( !$wanParams ) { throw new UnexpectedValueException( "wgWANObjectCaches must have \"$wanId\" set (via wgMainWANCache)" ); } $cacheId = $wanParams['cacheId']; $wanClass = $wanParams['class']; unset( $wanParams['cacheId'] ); unset( $wanParams['class'] ); $storeParams = $mainConfig->get( 'ObjectCaches' )[$cacheId] ?? null; if ( !$storeParams ) { throw new UnexpectedValueException( "wgObjectCaches must have \"$cacheId\" set (via wgWANObjectCaches)" ); } $store = ObjectCache::newFromParams( $storeParams, $mainConfig ); $logger = $store->getLogger(); $logger->debug( 'MainWANObjectCache using store {class}', [ 'class' => get_class( $store ) ] ); $wanParams['cache'] = $store; $wanParams['logger'] = $logger; $wanParams['secret'] = $wanParams['secret'] ?? $mainConfig->get( 'SecretKey' ); if ( !$mainConfig->get( 'CommandLineMode' ) ) { // Send the statsd data post-send on HTTP requests; avoid in CLI mode (T181385) $wanParams['stats'] = $services->getStatsdDataFactory(); // Let pre-emptive refreshes happen post-send on HTTP requests $wanParams['asyncHandler'] = [ DeferredUpdates::class, 'addCallableUpdate' ]; } $instance = new $wanClass( $wanParams ); '@phan-var WANObjectCache $instance'; return $instance; }, 'MediaHandlerFactory' => function ( MediaWikiServices $services ) : MediaHandlerFactory { return new MediaHandlerFactory( $services->getMainConfig()->get( 'MediaHandlers' ) ); }, 'MergeHistoryFactory' => function ( MediaWikiServices $services ) : MergeHistoryFactory { return $services->getService( '_PageCommandFactory' ); }, 'MessageCache' => function ( MediaWikiServices $services ) : MessageCache { $mainConfig = $services->getMainConfig(); $clusterCache = ObjectCache::getInstance( $mainConfig->get( 'MessageCacheType' ) ); $srvCache = $mainConfig->get( 'UseLocalMessageCache' ) ? $services->getLocalServerObjectCache() : new EmptyBagOStuff(); $logger = LoggerFactory::getInstance( 'MessageCache' ); $logger->debug( 'MessageCache using store {class}', [ 'class' => get_class( $clusterCache ) ] ); return new MessageCache( $services->getMainWANObjectCache(), $clusterCache, $srvCache, $services->getContentLanguage(), $services->getLanguageConverterFactory()->getLanguageConverter(), $logger, [ 'useDB' => $mainConfig->get( 'UseDatabaseMessages' ) ], $services->getLanguageFactory(), $services->getLocalisationCache(), $services->getLanguageNameUtils(), $services->getLanguageFallback(), $services->getHookContainer() ); }, 'MessageFormatterFactory' => function () : IMessageFormatterFactory { return new MessageFormatterFactory(); }, 'MimeAnalyzer' => function ( MediaWikiServices $services ) : MimeAnalyzer { $logger = LoggerFactory::getInstance( 'Mime' ); $mainConfig = $services->getMainConfig(); $hookRunner = new HookRunner( $services->getHookContainer() ); $params = [ 'typeFile' => $mainConfig->get( 'MimeTypeFile' ), 'infoFile' => $mainConfig->get( 'MimeInfoFile' ), 'xmlTypes' => $mainConfig->get( 'XMLMimeTypes' ), 'guessCallback' => function ( $mimeAnalyzer, &$head, &$tail, $file, &$mime ) use ( $logger, $hookRunner ) { // Also test DjVu $deja = new DjVuImage( $file ); if ( $deja->isValid() ) { $logger->info( "Detected $file as image/vnd.djvu\n" ); $mime = 'image/vnd.djvu'; return; } // Some strings by reference for performance - assuming well-behaved hooks $hookRunner->onMimeMagicGuessFromContent( $mimeAnalyzer, $head, $tail, $file, $mime ); }, 'extCallback' => function ( $mimeAnalyzer, $ext, &$mime ) use ( $hookRunner ) { // Media handling extensions can improve the MIME detected $hookRunner->onMimeMagicImproveFromExtension( $mimeAnalyzer, $ext, $mime ); }, 'initCallback' => function ( $mimeAnalyzer ) use ( $hookRunner ) { // Allow media handling extensions adding MIME-types and MIME-info $hookRunner->onMimeMagicInit( $mimeAnalyzer ); }, 'logger' => $logger ]; if ( $params['infoFile'] === 'includes/mime.info' ) { $params['infoFile'] = MimeAnalyzer::USE_INTERNAL; } if ( $params['typeFile'] === 'includes/mime.types' ) { $params['typeFile'] = MimeAnalyzer::USE_INTERNAL; } $detectorCmd = $mainConfig->get( 'MimeDetectorCommand' ); if ( $detectorCmd ) { $factory = $services->getShellCommandFactory(); $params['detectCallback'] = function ( $file ) use ( $detectorCmd, $factory ) { $result = $factory->create() // $wgMimeDetectorCommand can contain commands with parameters ->unsafeParams( $detectorCmd ) ->params( $file ) ->execute(); return $result->getStdout(); }; } return new MimeAnalyzer( $params ); }, 'MovePageFactory' => function ( MediaWikiServices $services ) : MovePageFactory { return $services->getService( '_PageCommandFactory' ); }, 'NamespaceInfo' => function ( MediaWikiServices $services ) : NamespaceInfo { return new NamespaceInfo( new ServiceOptions( NamespaceInfo::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ), $services->getHookContainer() ); }, 'NameTableStoreFactory' => function ( MediaWikiServices $services ) : NameTableStoreFactory { return new NameTableStoreFactory( $services->getDBLoadBalancerFactory(), $services->getMainWANObjectCache(), LoggerFactory::getInstance( 'NameTableSqlStore' ) ); }, 'ObjectFactory' => function ( MediaWikiServices $services ) : ObjectFactory { return new ObjectFactory( $services ); }, 'OldRevisionImporter' => function ( MediaWikiServices $services ) : OldRevisionImporter { return new ImportableOldRevisionImporter( true, LoggerFactory::getInstance( 'OldRevisionImporter' ), $services->getDBLoadBalancer(), $services->getRevisionStore(), $services->getSlotRoleRegistry() ); }, 'PageEditStash' => function ( MediaWikiServices $services ) : PageEditStash { $config = $services->getMainConfig(); return new PageEditStash( ObjectCache::getLocalClusterInstance(), $services->getDBLoadBalancer(), LoggerFactory::getInstance( 'StashEdit' ), $services->getStatsdDataFactory(), $services->getHookContainer(), defined( 'MEDIAWIKI_JOB_RUNNER' ) || $config->get( 'CommandLineMode' ) ? PageEditStash::INITIATOR_JOB_OR_CLI : PageEditStash::INITIATOR_USER ); }, 'Parser' => function ( MediaWikiServices $services ) : Parser { return $services->getParserFactory()->create(); }, 'ParserCache' => function ( MediaWikiServices $services ) : ParserCache { $config = $services->getMainConfig(); $cache = ObjectCache::getInstance( $config->get( 'ParserCacheType' ) ); wfDebugLog( 'caches', 'parser: ' . get_class( $cache ) ); return new ParserCache( $cache, $config->get( 'CacheEpoch' ), $services->getHookContainer(), $services->getStatsdDataFactory() ); }, 'ParserFactory' => function ( MediaWikiServices $services ) : ParserFactory { $options = new ServiceOptions( Parser::CONSTRUCTOR_OPTIONS, // 'class' // Note that this value is ignored by ParserFactory and is always // Parser::class for legacy reasons; we'll introduce a new // mechanism for selecting an alternate parser in the future // (T236809) $services->getMainConfig()->get( 'ParserConf' ), // Make sure to have defaults in case someone overrode ParserConf with something silly [ 'class' => Parser::class ], // Plus a buch of actual config options $services->getMainConfig() ); return new ParserFactory( $options, $services->getMagicWordFactory(), $services->getContentLanguage(), wfUrlProtocols(), $services->getSpecialPageFactory(), $services->getLinkRendererFactory(), $services->getNamespaceInfo(), LoggerFactory::getInstance( 'Parser' ), $services->getBadFileLookup(), $services->getLanguageConverterFactory(), $services->getHookContainer() ); }, 'PasswordFactory' => function ( MediaWikiServices $services ) : PasswordFactory { $config = $services->getMainConfig(); return new PasswordFactory( $config->get( 'PasswordConfig' ), $config->get( 'PasswordDefault' ) ); }, 'PasswordReset' => function ( MediaWikiServices $services ) : PasswordReset { $options = new ServiceOptions( PasswordReset::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ); return new PasswordReset( $options, $services->getAuthManager(), $services->getPermissionManager(), $services->getDBLoadBalancer(), LoggerFactory::getInstance( 'authentication' ), $services->getHookContainer() ); }, 'PerDbNameStatsdDataFactory' => function ( MediaWikiServices $services ) : StatsdDataFactoryInterface { $config = $services->getMainConfig(); $wiki = $config->get( 'DBname' ); return new PrefixingStatsdDataFactoryProxy( $services->getStatsdDataFactory(), $wiki ); }, 'PermissionManager' => function ( MediaWikiServices $services ) : PermissionManager { return new PermissionManager( new ServiceOptions( PermissionManager::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ), $services->getSpecialPageFactory(), $services->getRevisionLookup(), $services->getNamespaceInfo(), $services->getBlockErrorFormatter(), $services->getHookContainer() ); }, 'PreferencesFactory' => function ( MediaWikiServices $services ) : PreferencesFactory { $factory = new DefaultPreferencesFactory( new ServiceOptions( DefaultPreferencesFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ), $services->getContentLanguage(), $services->getAuthManager(), $services->getLinkRendererFactory()->create(), $services->getNamespaceInfo(), $services->getPermissionManager(), $services->getLanguageConverterFactory()->getLanguageConverter(), $services->getLanguageNameUtils(), $services->getHookContainer() ); $factory->setLogger( LoggerFactory::getInstance( 'preferences' ) ); return $factory; }, 'ProxyLookup' => function ( MediaWikiServices $services ) : ProxyLookup { $mainConfig = $services->getMainConfig(); return new ProxyLookup( $mainConfig->get( 'CdnServers' ), $mainConfig->get( 'CdnServersNoPurge' ) ); }, 'ReadOnlyMode' => function ( MediaWikiServices $services ) : ReadOnlyMode { return new ReadOnlyMode( $services->getConfiguredReadOnlyMode(), $services->getDBLoadBalancer() ); }, 'RepoGroup' => function ( MediaWikiServices $services ) : RepoGroup { $config = $services->getMainConfig(); return new RepoGroup( $config->get( 'LocalFileRepo' ), $config->get( 'ForeignFileRepos' ), $services->getMainWANObjectCache() ); }, 'ResourceLoader' => function ( MediaWikiServices $services ) : ResourceLoader { // @todo This should not take a Config object, but it's not so easy to remove because it // exposes it in a getter, which is actually used. global $IP; $config = $services->getMainConfig(); $rl = new ResourceLoader( $config, LoggerFactory::getInstance( 'resourceloader' ), $config->get( 'ResourceLoaderUseObjectCacheForDeps' ) ? new KeyValueDependencyStore( $services->getMainObjectStash() ) : new SqlModuleDependencyStore( $services->getDBLoadBalancer() ) ); $extRegistry = ExtensionRegistry::getInstance(); // Attribute has precedence over config $modules = $extRegistry->getAttribute( 'ResourceModules' ) + $config->get( 'ResourceModules' ); $moduleSkinStyles = $extRegistry->getAttribute( 'ResourceModuleSkinStyles' ) + $config->get( 'ResourceModuleSkinStyles' ); $rl->setModuleSkinStyles( $moduleSkinStyles ); $rl->addSource( $config->get( 'ResourceLoaderSources' ) ); // Core modules, then extension/skin modules $rl->register( include "$IP/resources/Resources.php" ); $rl->register( $modules ); $hookRunner = new \MediaWiki\ResourceLoader\HookRunner( $services->getHookContainer() ); $hookRunner->onResourceLoaderRegisterModules( $rl ); $msgPosterAttrib = $extRegistry->getAttribute( 'MessagePosterModule' ); $rl->register( 'mediawiki.messagePoster', [ 'localBasePath' => '', 'debugRaw' => false, 'scripts' => array_merge( [ "$IP/resources/src/mediawiki.messagePoster/factory.js", "$IP/resources/src/mediawiki.messagePoster/MessagePoster.js", "$IP/resources/src/mediawiki.messagePoster/WikitextMessagePoster.js", ], $msgPosterAttrib['scripts'] ?? [] ), 'dependencies' => array_merge( [ 'oojs', 'mediawiki.api', 'mediawiki.ForeignApi', ], $msgPosterAttrib['dependencies'] ?? [] ), 'targets' => [ 'desktop', 'mobile' ], ] ); if ( $config->get( 'EnableJavaScriptTest' ) === true ) { $rl->registerTestModules(); } return $rl; }, 'RevisionFactory' => function ( MediaWikiServices $services ) : RevisionFactory { return $services->getRevisionStore(); }, 'RevisionLookup' => function ( MediaWikiServices $services ) : RevisionLookup { return $services->getRevisionStore(); }, 'RevisionRenderer' => function ( MediaWikiServices $services ) : RevisionRenderer { $renderer = new RevisionRenderer( $services->getDBLoadBalancer(), $services->getSlotRoleRegistry() ); $renderer->setLogger( LoggerFactory::getInstance( 'SaveParse' ) ); return $renderer; }, 'RevisionStore' => function ( MediaWikiServices $services ) : RevisionStore { return $services->getRevisionStoreFactory()->getRevisionStore(); }, 'RevisionStoreFactory' => function ( MediaWikiServices $services ) : RevisionStoreFactory { $config = $services->getMainConfig(); if ( $config->has( 'MultiContentRevisionSchemaMigrationStage' ) ) { if ( $config->get( 'MultiContentRevisionSchemaMigrationStage' ) !== SCHEMA_COMPAT_NEW ) { throw new UnexpectedValueException( 'The MultiContentRevisionSchemaMigrationStage setting is no longer supported!' ); } } $store = new RevisionStoreFactory( $services->getDBLoadBalancerFactory(), $services->getBlobStoreFactory(), $services->getNameTableStoreFactory(), $services->getSlotRoleRegistry(), $services->getMainWANObjectCache(), $services->getCommentStore(), $services->getActorMigration(), LoggerFactory::getInstance( 'RevisionStore' ), $services->getContentHandlerFactory(), $services->getHookContainer() ); return $store; }, 'SearchEngineConfig' => function ( MediaWikiServices $services ) : SearchEngineConfig { // @todo This should not take a Config object, but it's not so easy to remove because it // exposes it in a getter, which is actually used. return new SearchEngineConfig( $services->getMainConfig(), $services->getContentLanguage(), $services->getHookContainer(), ExtensionRegistry::getInstance()->getAttribute( 'SearchMappings' ) ); }, 'SearchEngineFactory' => function ( MediaWikiServices $services ) : SearchEngineFactory { return new SearchEngineFactory( $services->getSearchEngineConfig(), $services->getHookContainer() ); }, 'ShellCommandFactory' => function ( MediaWikiServices $services ) : CommandFactory { $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; }, 'SiteLookup' => function ( MediaWikiServices $services ) : SiteLookup { // Use SiteStore as the SiteLookup as well. This was originally separated // to allow for a cacheable read-only interface, but this was never used. // SiteStore has caching (see below). return $services->getSiteStore(); }, 'SiteStore' => function ( MediaWikiServices $services ) : SiteStore { $rawSiteStore = new DBSiteStore( $services->getDBLoadBalancer() ); $cache = $services->getLocalServerObjectCache(); if ( $cache instanceof EmptyBagOStuff ) { $cache = ObjectCache::getLocalClusterInstance(); } return new CachingSiteStore( $rawSiteStore, $cache ); }, /** @suppress PhanTypeInvalidCallableArrayKey */ 'SkinFactory' => function ( MediaWikiServices $services ) : SkinFactory { $factory = new SkinFactory( $services->getObjectFactory() ); $names = $services->getMainConfig()->get( 'ValidSkinNames' ); foreach ( $names as $name => $skin ) { if ( is_array( $skin ) ) { $spec = $skin; $displayName = $skin['displayname'] ?? $name; } else { $displayName = $skin; $spec = [ 'class' => "Skin$skin" ]; } $factory->register( $name, $displayName, $spec ); } // Register a hidden "fallback" skin $factory->register( 'fallback', 'Fallback', [ 'class' => SkinFallback::class, 'args' => [ [ 'styles' => [ 'mediawiki.skinning.interface' ], 'templateDirectory' => __DIR__ . '/skins/templates/fallback', ] ] ] ); // Register a hidden skin for api output $factory->register( 'apioutput', 'ApiOutput', [ 'class' => SkinApi::class, 'args' => [ [ 'styles' => [ 'mediawiki.skinning.interface' ], 'templateDirectory' => __DIR__ . '/skins/templates/apioutput', ] ] ] ); return $factory; }, 'SlotRoleRegistry' => function ( MediaWikiServices $services ) : SlotRoleRegistry { $config = $services->getMainConfig(); $contentHandlerFactory = $services->getContentHandlerFactory(); $registry = new SlotRoleRegistry( $services->getSlotRoleStore() ); $registry->defineRole( 'main', function () use ( $config, $contentHandlerFactory ) { return new MainSlotRoleHandler( $config->get( 'NamespaceContentModels' ), $contentHandlerFactory ); } ); return $registry; }, 'SlotRoleStore' => function ( MediaWikiServices $services ) : NameTableStore { return $services->getNameTableStoreFactory()->getSlotRoles(); }, 'SpamChecker' => function ( MediaWikiServices $services ) : SpamChecker { return new SpamChecker( (array)$services->getMainConfig()->get( 'SpamRegex' ), (array)$services->getMainConfig()->get( 'SummarySpamRegex' ) ); }, 'SpecialPageFactory' => function ( MediaWikiServices $services ) : SpecialPageFactory { return new SpecialPageFactory( new ServiceOptions( SpecialPageFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ), $services->getContentLanguage(), $services->getObjectFactory(), $services->getHookContainer() ); }, 'StatsdDataFactory' => function ( MediaWikiServices $services ) : IBufferingStatsdDataFactory { return new BufferingStatsdDataFactory( rtrim( $services->getMainConfig()->get( 'StatsdMetricPrefix' ), '.' ) ); }, 'TalkPageNotificationManager' => function ( MediaWikiServices $services ) : TalkPageNotificationManager { return new TalkPageNotificationManager( new ServiceOptions( TalkPageNotificationManager::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ), $services->getDBLoadBalancer(), $services->getReadOnlyMode(), $services->getRevisionLookup() ); }, 'TempFSFileFactory' => function ( MediaWikiServices $services ) : TempFSFileFactory { return new TempFSFileFactory( $services->getMainConfig()->get( 'TmpDirectory' ) ); }, 'TitleFactory' => function () : TitleFactory { return new TitleFactory(); }, 'TitleFormatter' => function ( MediaWikiServices $services ) : TitleFormatter { return $services->getService( '_MediaWikiTitleCodec' ); }, 'TitleParser' => function ( MediaWikiServices $services ) : TitleParser { return $services->getService( '_MediaWikiTitleCodec' ); }, 'UploadRevisionImporter' => function ( MediaWikiServices $services ) : UploadRevisionImporter { return new ImportableUploadRevisionImporter( $services->getMainConfig()->get( 'EnableUploads' ), LoggerFactory::getInstance( 'UploadRevisionImporter' ) ); }, 'UserEditTracker' => function ( MediaWikiServices $services ) : UserEditTracker { return new UserEditTracker( $services->getActorMigration(), $services->getDBLoadBalancer() ); }, 'UserFactory' => function ( MediaWikiServices $services ) : UserFactory { return new UserFactory( $services->getUserNameUtils() ); }, 'UserGroupManager' => function ( MediaWikiServices $services ) : UserGroupManager { return $services->getUserGroupManagerFactory()->getUserGroupManager(); }, 'UserGroupManagerFactory' => function ( MediaWikiServices $services ) : UserGroupManagerFactory { return new UserGroupManagerFactory( new ServiceOptions( UserGroupManager::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ), $services->getConfiguredReadOnlyMode(), $services->getDBLoadBalancerFactory(), $services->getHookContainer(), $services->getUserEditTracker(), LoggerFactory::getInstance( 'UserGroupManager' ), [ function ( UserIdentity $user ) use ( $services ) { $services->getPermissionManager()->invalidateUsersRightsCache( $user ); User::newFromIdentity( $user )->invalidateCache(); } ] ); }, 'UserNameUtils' => function ( MediaWikiServices $services ) : UserNameUtils { $messageFormatterFactory = new MessageFormatterFactory( Message::FORMAT_PLAIN ); return new UserNameUtils( new ServiceOptions( UserNameUtils::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ), $services->getContentLanguage(), LoggerFactory::getInstance( 'UserNameUtils' ), $services->getTitleParser(), $messageFormatterFactory->getTextFormatter( $services->getContentLanguage()->getCode() ), $services->getHookContainer() ); }, 'UserOptionsLookup' => function ( MediaWikiServices $services ) : UserOptionsLookup { return $services->getUserOptionsManager(); }, 'UserOptionsManager' => function ( MediaWikiServices $services ) : UserOptionsManager { return new UserOptionsManager( new ServiceOptions( UserOptionsManager::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ), $services->get( '_DefaultOptionsLookup' ), $services->getLanguageConverterFactory(), $services->getDBLoadBalancer(), LoggerFactory::getInstance( 'UserOptionsManager' ), $services->getHookContainer() ); }, 'VirtualRESTServiceClient' => function ( MediaWikiServices $services ) : VirtualRESTServiceClient { $config = $services->getMainConfig()->get( 'VirtualRestConfig' ); $vrsClient = new VirtualRESTServiceClient( $services->getHttpRequestFactory()->createMultiClient() ); 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 ) : WatchedItemQueryService { return new WatchedItemQueryService( $services->getDBLoadBalancer(), $services->getCommentStore(), $services->getActorMigration(), $services->getWatchedItemStore(), $services->getPermissionManager(), $services->getHookContainer(), $services->getMainConfig()->get( 'WatchlistExpiry' ) ); }, 'WatchedItemStore' => function ( MediaWikiServices $services ) : WatchedItemStore { $store = new WatchedItemStore( new ServiceOptions( WatchedItemStore::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ), $services->getDBLoadBalancerFactory(), JobQueueGroup::singleton(), $services->getMainObjectStash(), new HashBagOStuff( [ 'maxKeys' => 100 ] ), $services->getReadOnlyMode(), $services->getNamespaceInfo(), $services->getRevisionLookup(), $services->getHookContainer() ); $store->setStatsdDataFactory( $services->getStatsdDataFactory() ); if ( $services->getMainConfig()->get( 'ReadOnlyWatchedItemStore' ) ) { $store = new NoWriteWatchedItemStore( $store ); } return $store; }, 'WatchlistNotificationManager' => function ( MediaWikiServices $services ) : WatchlistNotificationManager { return new WatchlistNotificationManager( new ServiceOptions( WatchlistNotificationManager::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ), $services->getHookContainer(), $services->getPermissionManager(), $services->getReadOnlyMode(), $services->getRevisionLookup(), $services->getTalkPageNotificationManager(), $services->getWatchedItemStore() ); }, 'WikiRevisionOldRevisionImporterNoUpdates' => function ( MediaWikiServices $services ) : ImportableOldRevisionImporter { return new ImportableOldRevisionImporter( false, LoggerFactory::getInstance( 'OldRevisionImporter' ), $services->getDBLoadBalancer(), $services->getRevisionStore(), $services->getSlotRoleRegistry() ); }, '_DefaultOptionsLookup' => function ( MediaWikiServices $services ) : DefaultOptionsLookup { return new DefaultOptionsLookup( new ServiceOptions( DefaultOptionsLookup::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ), $services->getContentLanguage(), $services->getHookContainer() ); }, '_MediaWikiTitleCodec' => function ( MediaWikiServices $services ) : MediaWikiTitleCodec { return new MediaWikiTitleCodec( $services->getContentLanguage(), $services->getGenderCache(), $services->getMainConfig()->get( 'LocalInterwikis' ), $services->getInterwikiLookup(), $services->getNamespaceInfo() ); }, '_PageCommandFactory' => function ( MediaWikiServices $services ) : PageCommandFactory { return new PageCommandFactory( new ServiceOptions( PageCommandFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ), $services->getDBLoadBalancer(), $services->getNamespaceInfo(), $services->getWatchedItemStore(), $services->getPermissionManager(), $services->getRepoGroup(), $services->getContentHandlerFactory(), $services->getRevisionStore(), $services->getSpamChecker(), $services->getHookContainer() ); }, '_SqlBlobStore' => function ( MediaWikiServices $services ) : SqlBlobStore { 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 // $this->getService( 'FooBarService' ). /////////////////////////////////////////////////////////////////////////// ];