Completed
Branch master (9ca75b)
by
unknown
26:06
created

MediaWikiServices::newSearchEngine()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
namespace MediaWiki;
3
4
use Config;
5
use ConfigFactory;
6
use CryptHKDF;
7
use CryptRand;
8
use EventRelayerGroup;
9
use GenderCache;
10
use GlobalVarConfig;
11
use Hooks;
12
use LBFactory;
13
use LinkCache;
14
use Liuggio\StatsdClient\Factory\StatsdDataFactory;
15
use LoadBalancer;
16
use MediaHandlerFactory;
17
use MediaWiki\Linker\LinkRenderer;
18
use MediaWiki\Linker\LinkRendererFactory;
19
use MediaWiki\Services\SalvageableService;
20
use MediaWiki\Services\ServiceContainer;
21
use MediaWiki\Services\NoSuchServiceException;
22
use MWException;
23
use MimeAnalyzer;
24
use ObjectCache;
25
use Parser;
26
use ProxyLookup;
27
use SearchEngine;
28
use SearchEngineConfig;
29
use SearchEngineFactory;
30
use SiteLookup;
31
use SiteStore;
32
use WatchedItemStore;
33
use WatchedItemQueryService;
34
use SkinFactory;
35
use TitleFormatter;
36
use TitleParser;
37
use VirtualRESTServiceClient;
38
use MediaWiki\Interwiki\InterwikiLookup;
39
40
/**
41
 * Service locator for MediaWiki core services.
42
 *
43
 * This program is free software; you can redistribute it and/or modify
44
 * it under the terms of the GNU General Public License as published by
45
 * the Free Software Foundation; either version 2 of the License, or
46
 * (at your option) any later version.
47
 *
48
 * This program is distributed in the hope that it will be useful,
49
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
50
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
51
 * GNU General Public License for more details.
52
 *
53
 * You should have received a copy of the GNU General Public License along
54
 * with this program; if not, write to the Free Software Foundation, Inc.,
55
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
56
 * http://www.gnu.org/copyleft/gpl.html
57
 *
58
 * @file
59
 *
60
 * @since 1.27
61
 */
62
63
/**
64
 * MediaWikiServices is the service locator for the application scope of MediaWiki.
65
 * Its implemented as a simple configurable DI container.
66
 * MediaWikiServices acts as a top level factory/registry for top level services, and builds
67
 * the network of service objects that defines MediaWiki's application logic.
68
 * It acts as an entry point to MediaWiki's dependency injection mechanism.
69
 *
70
 * Services are defined in the "wiring" array passed to the constructor,
71
 * or by calling defineService().
72
 *
73
 * @see docs/injection.txt for an overview of using dependency injection in the
74
 *      MediaWiki code base.
75
 */
76
class MediaWikiServices extends ServiceContainer {
77
78
	/**
79
	 * @var MediaWikiServices|null
80
	 */
81
	private static $instance = null;
82
83
	/**
84
	 * Returns the global default instance of the top level service locator.
85
	 *
86
	 * @since 1.27
87
	 *
88
	 * The default instance is initialized using the service instantiator functions
89
	 * defined in ServiceWiring.php.
90
	 *
91
	 * @note This should only be called by static functions! The instance returned here
92
	 * should not be passed around! Objects that need access to a service should have
93
	 * that service injected into the constructor, never a service locator!
94
	 *
95
	 * @return MediaWikiServices
96
	 */
97
	public static function getInstance() {
98
		if ( self::$instance === null ) {
99
			// NOTE: constructing GlobalVarConfig here is not particularly pretty,
100
			// but some information from the global scope has to be injected here,
101
			// even if it's just a file name or database credentials to load
102
			// configuration from.
103
			$bootstrapConfig = new GlobalVarConfig();
104
			self::$instance = self::newInstance( $bootstrapConfig, 'load' );
105
		}
106
107
		return self::$instance;
108
	}
109
110
	/**
111
	 * Replaces the global MediaWikiServices instance.
112
	 *
113
	 * @since 1.28
114
	 *
115
	 * @note This is for use in PHPUnit tests only!
116
	 *
117
	 * @throws MWException if called outside of PHPUnit tests.
118
	 *
119
	 * @param MediaWikiServices $services The new MediaWikiServices object.
120
	 *
121
	 * @return MediaWikiServices The old MediaWikiServices object, so it can be restored later.
122
	 */
123
	public static function forceGlobalInstance( MediaWikiServices $services ) {
124
		if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
125
			throw new MWException( __METHOD__ . ' must not be used outside unit tests.' );
126
		}
127
128
		$old = self::getInstance();
129
		self::$instance = $services;
130
131
		return $old;
132
	}
133
134
	/**
135
	 * Creates a new instance of MediaWikiServices and sets it as the global default
136
	 * instance. getInstance() will return a different MediaWikiServices object
137
	 * after every call to resetGlobalInstance().
138
	 *
139
	 * @since 1.28
140
	 *
141
	 * @warning This should not be used during normal operation. It is intended for use
142
	 * when the configuration has changed significantly since bootstrap time, e.g.
143
	 * during the installation process or during testing.
144
	 *
145
	 * @warning Calling resetGlobalInstance() may leave the application in an inconsistent
146
	 * state. Calling this is only safe under the ASSUMPTION that NO REFERENCE to
147
	 * any of the services managed by MediaWikiServices exist. If any service objects
148
	 * managed by the old MediaWikiServices instance remain in use, they may INTERFERE
149
	 * with the operation of the services managed by the new MediaWikiServices.
150
	 * Operating with a mix of services created by the old and the new
151
	 * MediaWikiServices instance may lead to INCONSISTENCIES and even DATA LOSS!
152
	 * Any class implementing LAZY LOADING is especially prone to this problem,
153
	 * since instances would typically retain a reference to a storage layer service.
154
	 *
155
	 * @see forceGlobalInstance()
156
	 * @see resetGlobalInstance()
157
	 * @see resetBetweenTest()
158
	 *
159
	 * @param Config|null $bootstrapConfig The Config object to be registered as the
160
	 *        'BootstrapConfig' service. This has to contain at least the information
161
	 *        needed to set up the 'ConfigFactory' service. If not given, the bootstrap
162
	 *        config of the old instance of MediaWikiServices will be re-used. If there
163
	 *        was no previous instance, a new GlobalVarConfig object will be used to
164
	 *        bootstrap the services.
165
	 *
166
	 * @param string $quick Set this to "quick" to allow expensive resources to be re-used.
167
	 * See SalvageableService for details.
168
	 *
169
	 * @throws MWException If called after MW_SERVICE_BOOTSTRAP_COMPLETE has been defined in
170
	 *         Setup.php (unless MW_PHPUNIT_TEST or MEDIAWIKI_INSTALL or RUN_MAINTENANCE_IF_MAIN
171
	 *          is defined).
172
	 */
173
	public static function resetGlobalInstance( Config $bootstrapConfig = null, $quick = '' ) {
174
		if ( self::$instance === null ) {
175
			// no global instance yet, nothing to reset
176
			return;
177
		}
178
179
		self::failIfResetNotAllowed( __METHOD__ );
180
181
		if ( $bootstrapConfig === null ) {
182
			$bootstrapConfig = self::$instance->getBootstrapConfig();
183
		}
184
185
		$oldInstance = self::$instance;
186
187
		self::$instance = self::newInstance( $bootstrapConfig, 'load' );
188
		self::$instance->importWiring( $oldInstance, [ 'BootstrapConfig' ] );
189
190
		if ( $quick === 'quick' ) {
191
			self::$instance->salvage( $oldInstance );
192
		} else {
193
			$oldInstance->destroy();
194
		}
195
196
	}
197
198
	/**
199
	 * Salvages the state of any salvageable service instances in $other.
200
	 *
201
	 * @note $other will have been destroyed when salvage() returns.
202
	 *
203
	 * @param MediaWikiServices $other
204
	 */
205
	private function salvage( self $other ) {
206
		foreach ( $this->getServiceNames() as $name ) {
207
			// The service could be new in the new instance and not registered in the
208
			// other instance (e.g. an extension that was loaded after the instantiation of
209
			// the other instance. Skip this service in this case. See T143974
210
			try {
211
				$oldService = $other->peekService( $name );
212
			} catch ( NoSuchServiceException $e ) {
213
				continue;
214
			}
215
216
			if ( $oldService instanceof SalvageableService ) {
217
				/** @var SalvageableService $newService */
218
				$newService = $this->getService( $name );
219
				$newService->salvage( $oldService );
220
			}
221
		}
222
223
		$other->destroy();
224
	}
225
226
	/**
227
	 * Creates a new MediaWikiServices instance and initializes it according to the
228
	 * given $bootstrapConfig. In particular, all wiring files defined in the
229
	 * ServiceWiringFiles setting are loaded, and the MediaWikiServices hook is called.
230
	 *
231
	 * @param Config|null $bootstrapConfig The Config object to be registered as the
232
	 *        'BootstrapConfig' service.
233
	 *
234
	 * @param string $loadWiring set this to 'load' to load the wiring files specified
235
	 *        in the 'ServiceWiringFiles' setting in $bootstrapConfig.
236
	 *
237
	 * @return MediaWikiServices
238
	 * @throws MWException
239
	 * @throws \FatalError
240
	 */
241
	private static function newInstance( Config $bootstrapConfig, $loadWiring = '' ) {
242
		$instance = new self( $bootstrapConfig );
243
244
		// Load the default wiring from the specified files.
245
		if ( $loadWiring === 'load' ) {
246
			$wiringFiles = $bootstrapConfig->get( 'ServiceWiringFiles' );
247
			$instance->loadWiringFiles( $wiringFiles );
248
		}
249
250
		// Provide a traditional hook point to allow extensions to configure services.
251
		Hooks::run( 'MediaWikiServices', [ $instance ] );
252
253
		return $instance;
254
	}
255
256
	/**
257
	 * Disables all storage layer services. After calling this, any attempt to access the
258
	 * storage layer will result in an error. Use resetGlobalInstance() to restore normal
259
	 * operation.
260
	 *
261
	 * @since 1.28
262
	 *
263
	 * @warning This is intended for extreme situations only and should never be used
264
	 * while serving normal web requests. Legitimate use cases for this method include
265
	 * the installation process. Test fixtures may also use this, if the fixture relies
266
	 * on globalState.
267
	 *
268
	 * @see resetGlobalInstance()
269
	 * @see resetChildProcessServices()
270
	 */
271
	public static function disableStorageBackend() {
272
		// TODO: also disable some Caches, JobQueues, etc
273
		$destroy = [ 'DBLoadBalancer', 'DBLoadBalancerFactory' ];
274
		$services = self::getInstance();
275
276
		foreach ( $destroy as $name ) {
277
			$services->disableService( $name );
278
		}
279
280
		ObjectCache::clear();
281
	}
282
283
	/**
284
	 * Resets any services that may have become stale after a child process
285
	 * returns from after pcntl_fork(). It's also safe, but generally unnecessary,
286
	 * to call this method from the parent process.
287
	 *
288
	 * @since 1.28
289
	 *
290
	 * @note This is intended for use in the context of process forking only!
291
	 *
292
	 * @see resetGlobalInstance()
293
	 * @see disableStorageBackend()
294
	 */
295
	public static function resetChildProcessServices() {
296
		// NOTE: for now, just reset everything. Since we don't know the interdependencies
297
		// between services, we can't do this more selectively at this time.
298
		self::resetGlobalInstance();
299
300
		// Child, reseed because there is no bug in PHP:
301
		// http://bugs.php.net/bug.php?id=42465
302
		mt_srand( getmypid() );
303
	}
304
305
	/**
306
	 * Resets the given service for testing purposes.
307
	 *
308
	 * @since 1.28
309
	 *
310
	 * @warning This is generally unsafe! Other services may still retain references
311
	 * to the stale service instance, leading to failures and inconsistencies. Subclasses
312
	 * may use this method to reset specific services under specific instances, but
313
	 * it should not be exposed to application logic.
314
	 *
315
	 * @note With proper dependency injection used throughout the codebase, this method
316
	 * should not be needed. It is provided to allow tests that pollute global service
317
	 * instances to clean up.
318
	 *
319
	 * @param string $name
320
	 * @param bool $destroy Whether the service instance should be destroyed if it exists.
321
	 *        When set to false, any existing service instance will effectively be detached
322
	 *        from the container.
323
	 *
324
	 * @throws MWException if called outside of PHPUnit tests.
325
	 */
326 View Code Duplication
	public function resetServiceForTesting( $name, $destroy = true ) {
327
		if ( !defined( 'MW_PHPUNIT_TEST' ) && !defined( 'MW_PARSER_TEST' ) ) {
328
			throw new MWException( 'resetServiceForTesting() must not be used outside unit tests.' );
329
		}
330
331
		$this->resetService( $name, $destroy );
332
	}
333
334
	/**
335
	 * Convenience method that throws an exception unless it is called during a phase in which
336
	 * resetting of global services is allowed. In general, services should not be reset
337
	 * individually, since that may introduce inconsistencies.
338
	 *
339
	 * @since 1.28
340
	 *
341
	 * This method will throw an exception if:
342
	 *
343
	 * - self::$resetInProgress is false (to allow all services to be reset together
344
	 *   via resetGlobalInstance)
345
	 * - and MEDIAWIKI_INSTALL is not defined (to allow services to be reset during installation)
346
	 * - and MW_PHPUNIT_TEST is not defined (to allow services to be reset during testing)
347
	 *
348
	 * This method is intended to be used to safeguard against accidentally resetting
349
	 * global service instances that are not yet managed by MediaWikiServices. It is
350
	 * defined here in the MediaWikiServices services class to have a central place
351
	 * for managing service bootstrapping and resetting.
352
	 *
353
	 * @param string $method the name of the caller method, as given by __METHOD__.
354
	 *
355
	 * @throws MWException if called outside bootstrap mode.
356
	 *
357
	 * @see resetGlobalInstance()
358
	 * @see forceGlobalInstance()
359
	 * @see disableStorageBackend()
360
	 */
361
	public static function failIfResetNotAllowed( $method ) {
362
		if ( !defined( 'MW_PHPUNIT_TEST' )
363
			&& !defined( 'MW_PARSER_TEST' )
364
			&& !defined( 'MEDIAWIKI_INSTALL' )
365
			&& !defined( 'RUN_MAINTENANCE_IF_MAIN' )
366
			&& defined( 'MW_SERVICE_BOOTSTRAP_COMPLETE' )
367
		) {
368
			throw new MWException( $method . ' may only be called during bootstrapping and unit tests!' );
369
		}
370
	}
371
372
	/**
373
	 * @param Config $config The Config object to be registered as the 'BootstrapConfig' service.
374
	 *        This has to contain at least the information needed to set up the 'ConfigFactory'
375
	 *        service.
376
	 */
377
	public function __construct( Config $config ) {
378
		parent::__construct();
379
380
		// Register the given Config object as the bootstrap config service.
381
		$this->defineService( 'BootstrapConfig', function() use ( $config ) {
382
			return $config;
383
		} );
384
	}
385
386
	// CONVENIENCE GETTERS ////////////////////////////////////////////////////
387
388
	/**
389
	 * Returns the Config object containing the bootstrap configuration.
390
	 * Bootstrap configuration would typically include database credentials
391
	 * and other information that may be needed before the ConfigFactory
392
	 * service can be instantiated.
393
	 *
394
	 * @note This should only be used during bootstrapping, in particular
395
	 * when creating the MainConfig service. Application logic should
396
	 * use getMainConfig() to get a Config instances.
397
	 *
398
	 * @since 1.27
399
	 * @return Config
400
	 */
401
	public function getBootstrapConfig() {
402
		return $this->getService( 'BootstrapConfig' );
403
	}
404
405
	/**
406
	 * @since 1.27
407
	 * @return ConfigFactory
408
	 */
409
	public function getConfigFactory() {
410
		return $this->getService( 'ConfigFactory' );
411
	}
412
413
	/**
414
	 * Returns the Config object that provides configuration for MediaWiki core.
415
	 * This may or may not be the same object that is returned by getBootstrapConfig().
416
	 *
417
	 * @since 1.27
418
	 * @return Config
419
	 */
420
	public function getMainConfig() {
421
		return $this->getService( 'MainConfig' );
422
	}
423
424
	/**
425
	 * @since 1.27
426
	 * @return SiteLookup
427
	 */
428
	public function getSiteLookup() {
429
		return $this->getService( 'SiteLookup' );
430
	}
431
432
	/**
433
	 * @since 1.27
434
	 * @return SiteStore
435
	 */
436
	public function getSiteStore() {
437
		return $this->getService( 'SiteStore' );
438
	}
439
440
	/**
441
	 * @since 1.28
442
	 * @return InterwikiLookup
443
	 */
444
	public function getInterwikiLookup() {
445
		return $this->getService( 'InterwikiLookup' );
446
	}
447
448
	/**
449
	 * @since 1.27
450
	 * @return StatsdDataFactory
451
	 */
452
	public function getStatsdDataFactory() {
453
		return $this->getService( 'StatsdDataFactory' );
454
	}
455
456
	/**
457
	 * @since 1.27
458
	 * @return EventRelayerGroup
459
	 */
460
	public function getEventRelayerGroup() {
461
		return $this->getService( 'EventRelayerGroup' );
462
	}
463
464
	/**
465
	 * @since 1.27
466
	 * @return SearchEngine
467
	 */
468
	public function newSearchEngine() {
469
		// New engine object every time, since they keep state
470
		return $this->getService( 'SearchEngineFactory' )->create();
471
	}
472
473
	/**
474
	 * @since 1.27
475
	 * @return SearchEngineFactory
476
	 */
477
	public function getSearchEngineFactory() {
478
		return $this->getService( 'SearchEngineFactory' );
479
	}
480
481
	/**
482
	 * @since 1.27
483
	 * @return SearchEngineConfig
484
	 */
485
	public function getSearchEngineConfig() {
486
		return $this->getService( 'SearchEngineConfig' );
487
	}
488
489
	/**
490
	 * @since 1.27
491
	 * @return SkinFactory
492
	 */
493
	public function getSkinFactory() {
494
		return $this->getService( 'SkinFactory' );
495
	}
496
497
	/**
498
	 * @since 1.28
499
	 * @return LBFactory
500
	 */
501
	public function getDBLoadBalancerFactory() {
502
		return $this->getService( 'DBLoadBalancerFactory' );
503
	}
504
505
	/**
506
	 * @since 1.28
507
	 * @return LoadBalancer The main DB load balancer for the local wiki.
508
	 */
509
	public function getDBLoadBalancer() {
510
		return $this->getService( 'DBLoadBalancer' );
511
	}
512
513
	/**
514
	 * @since 1.28
515
	 * @return WatchedItemStore
516
	 */
517
	public function getWatchedItemStore() {
518
		return $this->getService( 'WatchedItemStore' );
519
	}
520
521
	/**
522
	 * @since 1.28
523
	 * @return WatchedItemQueryService
524
	 */
525
	public function getWatchedItemQueryService() {
526
		return $this->getService( 'WatchedItemQueryService' );
527
	}
528
529
	/**
530
	 * @since 1.28
531
	 * @return CryptRand
532
	 */
533
	public function getCryptRand() {
534
		return $this->getService( 'CryptRand' );
535
	}
536
537
	/**
538
	 * @since 1.28
539
	 * @return CryptHKDF
540
	 */
541
	public function getCryptHKDF() {
542
		return $this->getService( 'CryptHKDF' );
543
	}
544
545
	/**
546
	 * @since 1.28
547
	 * @return MediaHandlerFactory
548
	 */
549
	public function getMediaHandlerFactory() {
550
		return $this->getService( 'MediaHandlerFactory' );
551
	}
552
553
	/**
554
	 * @since 1.28
555
	 * @return MimeAnalyzer
556
	 */
557
	public function getMimeAnalyzer() {
558
		return $this->getService( 'MimeAnalyzer' );
559
	}
560
561
	/**
562
	 * @since 1.28
563
	 * @return ProxyLookup
564
	 */
565
	public function getProxyLookup() {
566
		return $this->getService( 'ProxyLookup' );
567
	}
568
569
	/**
570
	 * @since 1.28
571
	 * @return Parser
572
	 */
573
	public function getParser() {
574
		return $this->getService( 'Parser' );
575
	}
576
577
	/**
578
	 * @since 1.28
579
	 * @return GenderCache
580
	 */
581
	public function getGenderCache() {
582
		return $this->getService( 'GenderCache' );
583
	}
584
585
	/**
586
	 * @since 1.28
587
	 * @return LinkCache
588
	 */
589
	public function getLinkCache() {
590
		return $this->getService( 'LinkCache' );
591
	}
592
593
	/**
594
	 * @since 1.28
595
	 * @return LinkRendererFactory
596
	 */
597
	public function getLinkRendererFactory() {
598
		return $this->getService( 'LinkRendererFactory' );
599
	}
600
601
	/**
602
	 * LinkRenderer instance that can be used
603
	 * if no custom options are needed
604
	 *
605
	 * @since 1.28
606
	 * @return LinkRenderer
607
	 */
608
	public function getLinkRenderer() {
609
		return $this->getService( 'LinkRenderer' );
610
	}
611
612
	/**
613
	 * @since 1.28
614
	 * @return TitleFormatter
615
	 */
616
	public function getTitleFormatter() {
617
		return $this->getService( 'TitleFormatter' );
618
	}
619
620
	/**
621
	 * @since 1.28
622
	 * @return TitleParser
623
	 */
624
	public function getTitleParser() {
625
		return $this->getService( 'TitleParser' );
626
	}
627
628
	/**
629
	 * @since 1.28
630
	 * @return \BagOStuff
631
	 */
632
	public function getMainObjectStash() {
633
		return $this->getService( 'MainObjectStash' );
634
	}
635
636
	/**
637
	 * @since 1.28
638
	 * @return \WANObjectCache
639
	 */
640
	public function getMainWANObjectCache() {
641
		return $this->getService( 'MainWANObjectCache' );
642
	}
643
644
	/**
645
	 * @since 1.28
646
	 * @return \BagOStuff
647
	 */
648
	public function getLocalServerObjectCache() {
649
		return $this->getService( 'LocalServerObjectCache' );
650
	}
651
652
	/**
653
	 * @since 1.28
654
	 * @return VirtualRESTServiceClient
655
	 */
656
	public function getVirtualRESTServiceClient() {
657
		return $this->getService( 'VirtualRESTServiceClient' );
658
	}
659
660
	///////////////////////////////////////////////////////////////////////////
661
	// NOTE: When adding a service getter here, don't forget to add a test
662
	// case for it in MediaWikiServicesTest::provideGetters() and in
663
	// MediaWikiServicesTest::provideGetService()!
664
	///////////////////////////////////////////////////////////////////////////
665
666
}
667