ServiceWiring.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. <?php
  2. /**
  3. * Default wiring for MediaWiki services.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @file
  21. *
  22. * This file is loaded by MediaWiki\MediaWikiServices::getInstance() during the
  23. * bootstrapping of the dependency injection framework.
  24. *
  25. * This file returns an array that associates service name with instantiator functions
  26. * that create the default instances for the services used by MediaWiki core.
  27. * For every service that MediaWiki core requires, an instantiator must be defined in
  28. * this file.
  29. *
  30. * @note As of version 1.27, MediaWiki is only beginning to use dependency injection.
  31. * The services defined here do not yet fully represent all services used by core,
  32. * much of the code still relies on global state for this accessing services.
  33. *
  34. * @since 1.27
  35. *
  36. * @see docs/injection.txt for an overview of using dependency injection in the
  37. * MediaWiki code base.
  38. */
  39. use MediaWiki\Interwiki\ClassicInterwikiLookup;
  40. use MediaWiki\Linker\LinkRendererFactory;
  41. use MediaWiki\Logger\LoggerFactory;
  42. use MediaWiki\MediaWikiServices;
  43. use MediaWiki\Shell\CommandFactory;
  44. return [
  45. 'DBLoadBalancerFactory' => function ( MediaWikiServices $services ) {
  46. $mainConfig = $services->getMainConfig();
  47. $lbConf = MWLBFactory::applyDefaultConfig(
  48. $mainConfig->get( 'LBFactoryConf' ),
  49. $mainConfig,
  50. $services->getConfiguredReadOnlyMode()
  51. );
  52. $class = MWLBFactory::getLBFactoryClass( $lbConf );
  53. return new $class( $lbConf );
  54. },
  55. 'DBLoadBalancer' => function ( MediaWikiServices $services ) {
  56. // just return the default LB from the DBLoadBalancerFactory service
  57. return $services->getDBLoadBalancerFactory()->getMainLB();
  58. },
  59. 'SiteStore' => function ( MediaWikiServices $services ) {
  60. $rawSiteStore = new DBSiteStore( $services->getDBLoadBalancer() );
  61. // TODO: replace wfGetCache with a CacheFactory service.
  62. // TODO: replace wfIsHHVM with a capabilities service.
  63. $cache = wfGetCache( wfIsHHVM() ? CACHE_ACCEL : CACHE_ANYTHING );
  64. return new CachingSiteStore( $rawSiteStore, $cache );
  65. },
  66. 'SiteLookup' => function ( MediaWikiServices $services ) {
  67. $cacheFile = $services->getMainConfig()->get( 'SitesCacheFile' );
  68. if ( $cacheFile !== false ) {
  69. return new FileBasedSiteLookup( $cacheFile );
  70. } else {
  71. // Use the default SiteStore as the SiteLookup implementation for now
  72. return $services->getSiteStore();
  73. }
  74. },
  75. 'ConfigFactory' => function ( MediaWikiServices $services ) {
  76. // Use the bootstrap config to initialize the ConfigFactory.
  77. $registry = $services->getBootstrapConfig()->get( 'ConfigRegistry' );
  78. $factory = new ConfigFactory();
  79. foreach ( $registry as $name => $callback ) {
  80. $factory->register( $name, $callback );
  81. }
  82. return $factory;
  83. },
  84. 'MainConfig' => function ( MediaWikiServices $services ) {
  85. // Use the 'main' config from the ConfigFactory service.
  86. return $services->getConfigFactory()->makeConfig( 'main' );
  87. },
  88. 'InterwikiLookup' => function ( MediaWikiServices $services ) {
  89. global $wgContLang; // TODO: manage $wgContLang as a service
  90. $config = $services->getMainConfig();
  91. return new ClassicInterwikiLookup(
  92. $wgContLang,
  93. $services->getMainWANObjectCache(),
  94. $config->get( 'InterwikiExpiry' ),
  95. $config->get( 'InterwikiCache' ),
  96. $config->get( 'InterwikiScopes' ),
  97. $config->get( 'InterwikiFallbackSite' )
  98. );
  99. },
  100. 'StatsdDataFactory' => function ( MediaWikiServices $services ) {
  101. return new BufferingStatsdDataFactory(
  102. rtrim( $services->getMainConfig()->get( 'StatsdMetricPrefix' ), '.' )
  103. );
  104. },
  105. 'EventRelayerGroup' => function ( MediaWikiServices $services ) {
  106. return new EventRelayerGroup( $services->getMainConfig()->get( 'EventRelayerConfig' ) );
  107. },
  108. 'SearchEngineFactory' => function ( MediaWikiServices $services ) {
  109. return new SearchEngineFactory( $services->getSearchEngineConfig() );
  110. },
  111. 'SearchEngineConfig' => function ( MediaWikiServices $services ) {
  112. global $wgContLang;
  113. return new SearchEngineConfig( $services->getMainConfig(), $wgContLang );
  114. },
  115. 'SkinFactory' => function ( MediaWikiServices $services ) {
  116. $factory = new SkinFactory();
  117. $names = $services->getMainConfig()->get( 'ValidSkinNames' );
  118. foreach ( $names as $name => $skin ) {
  119. $factory->register( $name, $skin, function () use ( $name, $skin ) {
  120. $class = "Skin$skin";
  121. return new $class( $name );
  122. } );
  123. }
  124. // Register a hidden "fallback" skin
  125. $factory->register( 'fallback', 'Fallback', function () {
  126. return new SkinFallback;
  127. } );
  128. // Register a hidden skin for api output
  129. $factory->register( 'apioutput', 'ApiOutput', function () {
  130. return new SkinApi;
  131. } );
  132. return $factory;
  133. },
  134. 'WatchedItemStore' => function ( MediaWikiServices $services ) {
  135. $store = new WatchedItemStore(
  136. $services->getDBLoadBalancer(),
  137. new HashBagOStuff( [ 'maxKeys' => 100 ] ),
  138. $services->getReadOnlyMode()
  139. );
  140. $store->setStatsdDataFactory( $services->getStatsdDataFactory() );
  141. return $store;
  142. },
  143. 'WatchedItemQueryService' => function ( MediaWikiServices $services ) {
  144. return new WatchedItemQueryService( $services->getDBLoadBalancer() );
  145. },
  146. 'CryptRand' => function ( MediaWikiServices $services ) {
  147. $secretKey = $services->getMainConfig()->get( 'SecretKey' );
  148. return new CryptRand(
  149. [
  150. // To try vary the system information of the state a bit more
  151. // by including the system's hostname into the state
  152. 'wfHostname',
  153. // It's mostly worthless but throw the wiki's id into the data
  154. // for a little more variance
  155. 'wfWikiID',
  156. // If we have a secret key set then throw it into the state as well
  157. function () use ( $secretKey ) {
  158. return $secretKey ?: '';
  159. }
  160. ],
  161. // The config file is likely the most often edited file we know should
  162. // be around so include its stat info into the state.
  163. // The constant with its location will almost always be defined, as
  164. // WebStart.php defines MW_CONFIG_FILE to $IP/LocalSettings.php unless
  165. // being configured with MW_CONFIG_CALLBACK (e.g. the installer).
  166. defined( 'MW_CONFIG_FILE' ) ? [ MW_CONFIG_FILE ] : [],
  167. LoggerFactory::getInstance( 'CryptRand' )
  168. );
  169. },
  170. 'CryptHKDF' => function ( MediaWikiServices $services ) {
  171. $config = $services->getMainConfig();
  172. $secret = $config->get( 'HKDFSecret' ) ?: $config->get( 'SecretKey' );
  173. if ( !$secret ) {
  174. throw new RuntimeException( "Cannot use MWCryptHKDF without a secret." );
  175. }
  176. // In HKDF, the context can be known to the attacker, but this will
  177. // keep simultaneous runs from producing the same output.
  178. $context = [ microtime(), getmypid(), gethostname() ];
  179. // Setup salt cache. Use APC, or fallback to the main cache if it isn't setup
  180. $cache = $services->getLocalServerObjectCache();
  181. if ( $cache instanceof EmptyBagOStuff ) {
  182. $cache = ObjectCache::getLocalClusterInstance();
  183. }
  184. return new CryptHKDF( $secret, $config->get( 'HKDFAlgorithm' ),
  185. $cache, $context, $services->getCryptRand()
  186. );
  187. },
  188. 'MediaHandlerFactory' => function ( MediaWikiServices $services ) {
  189. return new MediaHandlerFactory(
  190. $services->getMainConfig()->get( 'MediaHandlers' )
  191. );
  192. },
  193. 'MimeAnalyzer' => function ( MediaWikiServices $services ) {
  194. $logger = LoggerFactory::getInstance( 'Mime' );
  195. $mainConfig = $services->getMainConfig();
  196. $params = [
  197. 'typeFile' => $mainConfig->get( 'MimeTypeFile' ),
  198. 'infoFile' => $mainConfig->get( 'MimeInfoFile' ),
  199. 'xmlTypes' => $mainConfig->get( 'XMLMimeTypes' ),
  200. 'guessCallback' =>
  201. function ( $mimeAnalyzer, &$head, &$tail, $file, &$mime ) use ( $logger ) {
  202. // Also test DjVu
  203. $deja = new DjVuImage( $file );
  204. if ( $deja->isValid() ) {
  205. $logger->info( __METHOD__ . ": detected $file as image/vnd.djvu\n" );
  206. $mime = 'image/vnd.djvu';
  207. return;
  208. }
  209. // Some strings by reference for performance - assuming well-behaved hooks
  210. Hooks::run(
  211. 'MimeMagicGuessFromContent',
  212. [ $mimeAnalyzer, &$head, &$tail, $file, &$mime ]
  213. );
  214. },
  215. 'extCallback' => function ( $mimeAnalyzer, $ext, &$mime ) {
  216. // Media handling extensions can improve the MIME detected
  217. Hooks::run( 'MimeMagicImproveFromExtension', [ $mimeAnalyzer, $ext, &$mime ] );
  218. },
  219. 'initCallback' => function ( $mimeAnalyzer ) {
  220. // Allow media handling extensions adding MIME-types and MIME-info
  221. Hooks::run( 'MimeMagicInit', [ $mimeAnalyzer ] );
  222. },
  223. 'logger' => $logger
  224. ];
  225. if ( $params['infoFile'] === 'includes/mime.info' ) {
  226. $params['infoFile'] = __DIR__ . "/libs/mime/mime.info";
  227. }
  228. if ( $params['typeFile'] === 'includes/mime.types' ) {
  229. $params['typeFile'] = __DIR__ . "/libs/mime/mime.types";
  230. }
  231. $detectorCmd = $mainConfig->get( 'MimeDetectorCommand' );
  232. if ( $detectorCmd ) {
  233. $params['detectCallback'] = function ( $file ) use ( $detectorCmd ) {
  234. return wfShellExec( "$detectorCmd " . wfEscapeShellArg( $file ) );
  235. };
  236. }
  237. // XXX: MimeMagic::singleton currently requires this service to return an instance of MimeMagic
  238. return new MimeMagic( $params );
  239. },
  240. 'ProxyLookup' => function ( MediaWikiServices $services ) {
  241. $mainConfig = $services->getMainConfig();
  242. return new ProxyLookup(
  243. $mainConfig->get( 'SquidServers' ),
  244. $mainConfig->get( 'SquidServersNoPurge' )
  245. );
  246. },
  247. 'Parser' => function ( MediaWikiServices $services ) {
  248. $conf = $services->getMainConfig()->get( 'ParserConf' );
  249. return ObjectFactory::constructClassInstance( $conf['class'], [ $conf ] );
  250. },
  251. 'ParserCache' => function ( MediaWikiServices $services ) {
  252. $config = $services->getMainConfig();
  253. $cache = ObjectCache::getInstance( $config->get( 'ParserCacheType' ) );
  254. wfDebugLog( 'caches', 'parser: ' . get_class( $cache ) );
  255. return new ParserCache(
  256. $cache,
  257. $config->get( 'CacheEpoch' )
  258. );
  259. },
  260. 'LinkCache' => function ( MediaWikiServices $services ) {
  261. return new LinkCache(
  262. $services->getTitleFormatter(),
  263. $services->getMainWANObjectCache()
  264. );
  265. },
  266. 'LinkRendererFactory' => function ( MediaWikiServices $services ) {
  267. return new LinkRendererFactory(
  268. $services->getTitleFormatter(),
  269. $services->getLinkCache()
  270. );
  271. },
  272. 'LinkRenderer' => function ( MediaWikiServices $services ) {
  273. global $wgUser;
  274. if ( defined( 'MW_NO_SESSION' ) ) {
  275. return $services->getLinkRendererFactory()->create();
  276. } else {
  277. return $services->getLinkRendererFactory()->createForUser( $wgUser );
  278. }
  279. },
  280. 'GenderCache' => function ( MediaWikiServices $services ) {
  281. return new GenderCache();
  282. },
  283. '_MediaWikiTitleCodec' => function ( MediaWikiServices $services ) {
  284. global $wgContLang;
  285. return new MediaWikiTitleCodec(
  286. $wgContLang,
  287. $services->getGenderCache(),
  288. $services->getMainConfig()->get( 'LocalInterwikis' )
  289. );
  290. },
  291. 'TitleFormatter' => function ( MediaWikiServices $services ) {
  292. return $services->getService( '_MediaWikiTitleCodec' );
  293. },
  294. 'TitleParser' => function ( MediaWikiServices $services ) {
  295. return $services->getService( '_MediaWikiTitleCodec' );
  296. },
  297. 'MainObjectStash' => function ( MediaWikiServices $services ) {
  298. $mainConfig = $services->getMainConfig();
  299. $id = $mainConfig->get( 'MainStash' );
  300. if ( !isset( $mainConfig->get( 'ObjectCaches' )[$id] ) ) {
  301. throw new UnexpectedValueException(
  302. "Cache type \"$id\" is not present in \$wgObjectCaches." );
  303. }
  304. return \ObjectCache::newFromParams( $mainConfig->get( 'ObjectCaches' )[$id] );
  305. },
  306. 'MainWANObjectCache' => function ( MediaWikiServices $services ) {
  307. $mainConfig = $services->getMainConfig();
  308. $id = $mainConfig->get( 'MainWANCache' );
  309. if ( !isset( $mainConfig->get( 'WANObjectCaches' )[$id] ) ) {
  310. throw new UnexpectedValueException(
  311. "WAN cache type \"$id\" is not present in \$wgWANObjectCaches." );
  312. }
  313. $params = $mainConfig->get( 'WANObjectCaches' )[$id];
  314. $objectCacheId = $params['cacheId'];
  315. if ( !isset( $mainConfig->get( 'ObjectCaches' )[$objectCacheId] ) ) {
  316. throw new UnexpectedValueException(
  317. "Cache type \"$objectCacheId\" is not present in \$wgObjectCaches." );
  318. }
  319. $params['store'] = $mainConfig->get( 'ObjectCaches' )[$objectCacheId];
  320. return \ObjectCache::newWANCacheFromParams( $params );
  321. },
  322. 'LocalServerObjectCache' => function ( MediaWikiServices $services ) {
  323. $mainConfig = $services->getMainConfig();
  324. if ( function_exists( 'apc_fetch' ) ) {
  325. $id = 'apc';
  326. } elseif ( function_exists( 'apcu_fetch' ) ) {
  327. $id = 'apcu';
  328. } elseif ( function_exists( 'xcache_get' ) && wfIniGetBool( 'xcache.var_size' ) ) {
  329. $id = 'xcache';
  330. } elseif ( function_exists( 'wincache_ucache_get' ) ) {
  331. $id = 'wincache';
  332. } else {
  333. $id = CACHE_NONE;
  334. }
  335. if ( !isset( $mainConfig->get( 'ObjectCaches' )[$id] ) ) {
  336. throw new UnexpectedValueException(
  337. "Cache type \"$id\" is not present in \$wgObjectCaches." );
  338. }
  339. return \ObjectCache::newFromParams( $mainConfig->get( 'ObjectCaches' )[$id] );
  340. },
  341. 'VirtualRESTServiceClient' => function ( MediaWikiServices $services ) {
  342. $config = $services->getMainConfig()->get( 'VirtualRestConfig' );
  343. $vrsClient = new VirtualRESTServiceClient( new MultiHttpClient( [] ) );
  344. foreach ( $config['paths'] as $prefix => $serviceConfig ) {
  345. $class = $serviceConfig['class'];
  346. // Merge in the global defaults
  347. $constructArg = isset( $serviceConfig['options'] )
  348. ? $serviceConfig['options']
  349. : [];
  350. $constructArg += $config['global'];
  351. // Make the VRS service available at the mount point
  352. $vrsClient->mount( $prefix, [ 'class' => $class, 'config' => $constructArg ] );
  353. }
  354. return $vrsClient;
  355. },
  356. 'ConfiguredReadOnlyMode' => function ( MediaWikiServices $services ) {
  357. return new ConfiguredReadOnlyMode( $services->getMainConfig() );
  358. },
  359. 'ReadOnlyMode' => function ( MediaWikiServices $services ) {
  360. return new ReadOnlyMode(
  361. $services->getConfiguredReadOnlyMode(),
  362. $services->getDBLoadBalancer()
  363. );
  364. },
  365. 'ShellCommandFactory' => function ( MediaWikiServices $services ) {
  366. $config = $services->getMainConfig();
  367. $limits = [
  368. 'time' => $config->get( 'MaxShellTime' ),
  369. 'walltime' => $config->get( 'MaxShellWallClockTime' ),
  370. 'memory' => $config->get( 'MaxShellMemory' ),
  371. 'filesize' => $config->get( 'MaxShellFileSize' ),
  372. ];
  373. $cgroup = $config->get( 'ShellCgroup' );
  374. $factory = new CommandFactory( $limits, $cgroup );
  375. $factory->setLogger( LoggerFactory::getInstance( 'exec' ) );
  376. return $factory;
  377. },
  378. ///////////////////////////////////////////////////////////////////////////
  379. // NOTE: When adding a service here, don't forget to add a getter function
  380. // in the MediaWikiServices class. The convenience getter should just call
  381. // $this->getService( 'FooBarService' ).
  382. ///////////////////////////////////////////////////////////////////////////
  383. ];