Wikibase
MediaWiki Wikibase extension
Loading...
Searching...
No Matches
Services

Since the Wikibase Service Migration of spring 2021, Wikibase uses the MediaWiki service container for practically all of its services. The services are defined in the wiring files, repo/WikibaseRepo.ServiceWiring.php and client/WikibaseClient.ServiceWiring.php, using either the WikibaseRepo. or WikibaseClient. prefix. The WikibaseRepo and WikibaseClient classes act as accessors for those services, getting them from a given service container (falling back to the default MediaWiki one) and adding static return types.

Code that requires a service should always get that service injected, rather than ad-hoc calling WikibaseRepo::getSomeService(). (At the time of writing, the WikibaseClient Lua-related code is the largest “area” violating this principle. One hopes it’ll be fixed eventually.) API modules, special pages, and hook handlers can directly specify the services they require in the extension JSON file, using the "services" key; MediaWiki will then directly call the constructor or factory function with all the declared services. Other classes should usually also take their required services as constructor arguments, and it’s then up to the code calling the constructor to obtain an instance of the service class. Often, that code is in one of the wiring files, in which case it can get the service from the $services container.

Adding new services

The following steps are necessary to add a new service to Wikibase:

Start by a callback to create a service instance to the service wiring file, like this:

'WikibaseRepo.ServiceName' => function ( MediaWikiServices $services ): ServiceClass {
return new ServiceClass(
$services->getSomeService(),
WikibaseRepo::getOtherService( $services ),
// ...
);
},
return[ 'item'=>[Def::PREFETCHING_TERM_LOOKUP_CALLBACK=> function(DatabaseEntitySource $entitySource) { $termIdsResolver=WikibaseClient::getTermInLangIdsResolverFactory() ->getResolverForEntitySource( $entitySource);return new PrefetchingItemTermLookup( $termIdsResolver);},], 'property'=>[Def::PREFETCHING_TERM_LOOKUP_CALLBACK=> function(DatabaseEntitySource $entitySource) { $mwServices=MediaWikiServices::getInstance();$cacheSecret=hash( 'sha256', $mwServices->getMainConfig() ->get( 'SecretKey'));$bagOStuff=$mwServices->getLocalServerObjectCache();$termIdsResolver=WikibaseClient::getTermInLangIdsResolverFactory() ->getResolverForEntitySource( $entitySource);$prefetchingPropertyTermLookup=new PrefetchingPropertyTermLookup( $termIdsResolver);if( $bagOStuff instanceof EmptyBagOStuff) { return $prefetchingPropertyTermLookup;} $cache=new SimpleCacheWithBagOStuff($bagOStuff, 'wikibase.prefetchingPropertyTermLookup.', $cacheSecret);$cache=new StatsdRecordingSimpleCache($cache, $mwServices->getStatsdDataFactory(), ['miss'=> 'wikibase.prefetchingPropertyTermLookupCache.miss', 'hit'=> 'wikibase.prefetchingPropertyTermLookupCache.hit']);return new CachingPrefetchingTermLookup($cache, $prefetchingPropertyTermLookup, WikibaseClient::getRedirectResolvingLatestRevisionLookup( $mwServices), WikibaseClient::getTermsLanguages( $mwServices));},]]
Definition WikibaseClient.entitytypes.php:77

The wiring functions should be sorted in each file, but PHPCBF can take care of that for you.

Next, add an accessor method to the WikibaseRepo or WikibaseClient class, like this:

public static function getServiceName( ContainerInterface $services = null ): ServiceClass {
return ( $services ?: MediaWikiServices::getInstance() )
->get( 'WikibaseRepo.ServiceName' );
}

Then, add a test for the service wiring in either repo/tests/phpunit/unit/ServiceWiring/ or client/tests/phpunit/unit/includes/ServiceWiring/, in a new class named after the service name. You can follow the existing tests for guidance; in many cases, there will only be a single test function, typically named testConstruction(), with three “sections”: mocking other services, getting an instance of the service being tested, and performing assertions on it.

Mocking all the services used in the service wiring is necessary to ensure that the test is a proper unit test and doesn’t use any services from the default container. Wikibase services are mocked like this:

$this->mockService( 'WikibaseRepo.ServiceName',
$this->createMock( ServiceClass::class ) );

(Sometimes, a real implementation is easy enough to use that createMock() is not necessary.) MediaWiki services are mocked like this:

$this->serviceContainer->expects( $this->once() )
->method( 'getSomeService' )
->willReturn( ... );

The willReturn() can be omitted if you don’t have any special requirements for the return value: all the MediaWikiServices methods have static return types, so PHPUnit automatically generates a mock object of the appropriate type by default.

To get the service instance, use the getService() method:

$serviceName = $this->getService( 'WikibaseRepo.ServiceName' );

Assertions on the value should at least include a type check:

$this->assertInstanceOf( ServiceClass::class, $serviceName );

Sometimes, additional assertions are possible. Use your own judgment, or discuss in code review, at which point those additions are really tests for the service class rather than the wiring (which means they should live elsewhere). If assertInstanceOf() is the only assertion, the $serviceName variable is often inlined.

Finally, use the service wherever you need it.

Legacy service containers

Certain classes still act as service containers or factories in their own right, and have not yet been fully migrated to the MediaWiki service container. These include:

Please do not add any new services to these classes.