MediaWiki  1.28.0
ServiceContainer.php
Go to the documentation of this file.
1 <?php
2 namespace MediaWiki\Services;
3 
7 
47 
51  private $services = [];
52 
56  private $serviceInstantiators = [];
57 
61  private $disabled = [];
62 
67 
71  private $destroyed = false;
72 
78  public function __construct( array $extraInstantiationParams = [] ) {
79  $this->extraInstantiationParams = $extraInstantiationParams;
80  }
81 
90  public function destroy() {
91  foreach ( $this->getServiceNames() as $name ) {
92  $service = $this->peekService( $name );
93  if ( $service !== null && $service instanceof DestructibleService ) {
94  $service->destroy();
95  }
96  }
97 
98  $this->destroyed = true;
99  }
100 
106  public function loadWiringFiles( array $wiringFiles ) {
107  foreach ( $wiringFiles as $file ) {
108  // the wiring file is required to return an array of instantiators.
109  $wiring = require $file;
110 
111  Assert::postcondition(
112  is_array( $wiring ),
113  "Wiring file $file is expected to return an array!"
114  );
115 
116  $this->applyWiring( $wiring );
117  }
118  }
119 
127  Assert::parameterElementType( 'callable', $serviceInstantiators, '$serviceInstantiators' );
128 
129  foreach ( $serviceInstantiators as $name => $instantiator ) {
130  $this->defineService( $name, $instantiator );
131  }
132  }
133 
144  public function importWiring( ServiceContainer $container, $skip = [] ) {
145  $newInstantiators = array_diff_key(
146  $container->serviceInstantiators,
147  array_flip( $skip )
148  );
149 
150  $this->serviceInstantiators = array_merge(
151  $this->serviceInstantiators,
152  $newInstantiators
153  );
154  }
155 
164  public function hasService( $name ) {
165  return isset( $this->serviceInstantiators[$name] );
166  }
167 
183  public function peekService( $name ) {
184  if ( !$this->hasService( $name ) ) {
185  throw new NoSuchServiceException( $name );
186  }
187 
188  return isset( $this->services[$name] ) ? $this->services[$name] : null;
189  }
190 
194  public function getServiceNames() {
195  return array_keys( $this->serviceInstantiators );
196  }
197 
212  public function defineService( $name, callable $instantiator ) {
213  Assert::parameterType( 'string', $name, '$name' );
214 
215  if ( $this->hasService( $name ) ) {
217  }
218 
219  $this->serviceInstantiators[$name] = $instantiator;
220  }
221 
238  public function redefineService( $name, callable $instantiator ) {
239  Assert::parameterType( 'string', $name, '$name' );
240 
241  if ( !$this->hasService( $name ) ) {
242  throw new NoSuchServiceException( $name );
243  }
244 
245  if ( isset( $this->services[$name] ) ) {
246  throw new CannotReplaceActiveServiceException( $name );
247  }
248 
249  $this->serviceInstantiators[$name] = $instantiator;
250  unset( $this->disabled[$name] );
251  }
252 
272  public function disableService( $name ) {
273  $this->resetService( $name );
274 
275  $this->disabled[$name] = true;
276  }
277 
301  final protected function resetService( $name, $destroy = true ) {
302  Assert::parameterType( 'string', $name, '$name' );
303 
304  $instance = $this->peekService( $name );
305 
306  if ( $destroy && $instance instanceof DestructibleService ) {
307  $instance->destroy();
308  }
309 
310  unset( $this->services[$name] );
311  unset( $this->disabled[$name] );
312  }
313 
334  public function getService( $name ) {
335  if ( $this->destroyed ) {
336  throw new ContainerDisabledException();
337  }
338 
339  if ( isset( $this->disabled[$name] ) ) {
340  throw new ServiceDisabledException( $name );
341  }
342 
343  if ( !isset( $this->services[$name] ) ) {
344  $this->services[$name] = $this->createService( $name );
345  }
346 
347  return $this->services[$name];
348  }
349 
356  private function createService( $name ) {
357  if ( isset( $this->serviceInstantiators[$name] ) ) {
358  $service = call_user_func_array(
359  $this->serviceInstantiators[$name],
360  array_merge( [ $this ], $this->extraInstantiationParams )
361  );
362  // NOTE: when adding more wiring logic here, make sure copyWiring() is kept in sync!
363  } else {
364  throw new NoSuchServiceException( $name );
365  }
366 
367  return $service;
368  }
369 
375  public function isServiceDisabled( $name ) {
376  return isset( $this->disabled[$name] );
377  }
378 }
peekService($name)
Returns the service instance for $name only if that service has already been instantiated.
importWiring(ServiceContainer $container, $skip=[])
Imports all wiring defined in $container.
the array() calling protocol came about after MediaWiki 1.4rc1.
__construct(array $extraInstantiationParams=[])
applyWiring(array $serviceInstantiators)
Registers multiple services (aka a "wiring").
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
Exception thrown when a service was already defined, but the caller expected it to not exist...
hasService($name)
Returns true if a service is defined for $name, that is, if a call to getService( $name ) would retur...
resetService($name, $destroy=true)
Resets a service by dropping the service instance.
ServiceContainer provides a generic service to manage named services using lazy instantiation based o...
Exception thrown when trying to access a disabled service.
Exception thrown when trying to replace an already active service.
defineService($name, callable $instantiator)
Define a new service.
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
redefineService($name, callable $instantiator)
Replace an already defined service.
boolean[] $disabled
disabled status, per service name
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
Exception thrown when trying to access a service on a disabled container or factory.
disableService($name)
Disables a service.
destroy()
Destroys all contained service instances that implement the DestructibleService interface.
getService($name)
Returns a service object of the kind associated with $name.
DestructibleService defines a standard interface for shutting down a service instance.
Exception thrown when the requested service is not known.
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:300