MediaWiki  1.33.0
ServiceContainer.php
Go to the documentation of this file.
1 <?php
2 namespace Wikimedia\Services;
3 
4 use InvalidArgumentException;
5 use RuntimeException;
6 use Wikimedia\Assert\Assert;
7 
47 
51  private $services = [];
52 
56  private $serviceInstantiators = [];
57 
61  private $serviceManipulators = [];
62 
66  private $disabled = [];
67 
72 
76  private $destroyed = false;
77 
83  public function __construct( array $extraInstantiationParams = [] ) {
84  $this->extraInstantiationParams = $extraInstantiationParams;
85  }
86 
95  public function destroy() {
96  foreach ( $this->getServiceNames() as $name ) {
97  $service = $this->peekService( $name );
98  if ( $service !== null && $service instanceof DestructibleService ) {
99  $service->destroy();
100  }
101  }
102 
103  // Break circular references due to the $this reference in closures, by
104  // erasing the instantiator array. This allows the ServiceContainer to
105  // be deleted when it goes out of scope.
106  $this->serviceInstantiators = [];
107  // Also remove the services themselves, to avoid confusion.
108  $this->services = [];
109  $this->destroyed = true;
110  }
111 
117  public function loadWiringFiles( array $wiringFiles ) {
118  foreach ( $wiringFiles as $file ) {
119  // the wiring file is required to return an array of instantiators.
120  $wiring = require $file;
121 
122  Assert::postcondition(
123  is_array( $wiring ),
124  "Wiring file $file is expected to return an array!"
125  );
126 
127  $this->applyWiring( $wiring );
128  }
129  }
130 
138  Assert::parameterElementType( 'callable', $serviceInstantiators, '$serviceInstantiators' );
139 
140  foreach ( $serviceInstantiators as $name => $instantiator ) {
141  $this->defineService( $name, $instantiator );
142  }
143  }
144 
155  public function importWiring( ServiceContainer $container, $skip = [] ) {
156  $newInstantiators = array_diff_key(
157  $container->serviceInstantiators,
158  array_flip( $skip )
159  );
160 
161  $this->serviceInstantiators = array_merge(
162  $this->serviceInstantiators,
163  $newInstantiators
164  );
165 
166  $newManipulators = array_diff(
167  array_keys( $container->serviceManipulators ),
168  $skip
169  );
170 
171  foreach ( $newManipulators as $name ) {
172  if ( isset( $this->serviceManipulators[$name] ) ) {
173  $this->serviceManipulators[$name] = array_merge(
174  $this->serviceManipulators[$name],
175  $container->serviceManipulators[$name]
176  );
177  } else {
178  $this->serviceManipulators[$name] = $container->serviceManipulators[$name];
179  }
180  }
181  }
182 
191  public function hasService( $name ) {
192  return isset( $this->serviceInstantiators[$name] );
193  }
194 
210  public function peekService( $name ) {
211  if ( !$this->hasService( $name ) ) {
212  throw new NoSuchServiceException( $name );
213  }
214 
215  return $this->services[$name] ?? null;
216  }
217 
221  public function getServiceNames() {
222  return array_keys( $this->serviceInstantiators );
223  }
224 
239  public function defineService( $name, callable $instantiator ) {
240  Assert::parameterType( 'string', $name, '$name' );
241 
242  if ( $this->hasService( $name ) ) {
244  }
245 
246  $this->serviceInstantiators[$name] = $instantiator;
247  }
248 
268  public function redefineService( $name, callable $instantiator ) {
269  Assert::parameterType( 'string', $name, '$name' );
270 
271  if ( !$this->hasService( $name ) ) {
272  throw new NoSuchServiceException( $name );
273  }
274 
275  if ( isset( $this->services[$name] ) ) {
277  }
278 
279  $this->serviceInstantiators[$name] = $instantiator;
280  unset( $this->disabled[$name] );
281  }
282 
309  public function addServiceManipulator( $name, callable $manipulator ) {
310  Assert::parameterType( 'string', $name, '$name' );
311 
312  if ( !$this->hasService( $name ) ) {
313  throw new NoSuchServiceException( $name );
314  }
315 
316  if ( isset( $this->services[$name] ) ) {
318  }
319 
320  $this->serviceManipulators[$name][] = $manipulator;
321  }
322 
342  public function disableService( $name ) {
343  $this->resetService( $name );
344 
345  $this->disabled[$name] = true;
346  }
347 
371  final protected function resetService( $name, $destroy = true ) {
372  Assert::parameterType( 'string', $name, '$name' );
373 
374  $instance = $this->peekService( $name );
375 
376  if ( $destroy && $instance instanceof DestructibleService ) {
377  $instance->destroy();
378  }
379 
380  unset( $this->services[$name] );
381  unset( $this->disabled[$name] );
382  }
383 
404  public function getService( $name ) {
405  if ( $this->destroyed ) {
406  throw new ContainerDisabledException();
407  }
408 
409  if ( isset( $this->disabled[$name] ) ) {
410  throw new ServiceDisabledException( $name );
411  }
412 
413  if ( !isset( $this->services[$name] ) ) {
414  $this->services[$name] = $this->createService( $name );
415  }
416 
417  return $this->services[$name];
418  }
419 
426  private function createService( $name ) {
427  if ( isset( $this->serviceInstantiators[$name] ) ) {
428  $service = ( $this->serviceInstantiators[$name] )(
429  $this,
430  ...$this->extraInstantiationParams
431  );
432 
433  if ( isset( $this->serviceManipulators[$name] ) ) {
434  foreach ( $this->serviceManipulators[$name] as $callback ) {
435  $ret = call_user_func_array(
436  $callback,
437  array_merge( [ $service, $this ], $this->extraInstantiationParams )
438  );
439 
440  // If the manipulator callback returns an object, that object replaces
441  // the original service instance. This allows the manipulator to wrap
442  // or fully replace the service.
443  if ( $ret !== null ) {
444  $service = $ret;
445  }
446  }
447  }
448 
449  // NOTE: when adding more wiring logic here, make sure importWiring() is kept in sync!
450  } else {
451  throw new NoSuchServiceException( $name );
452  }
453 
454  return $service;
455  }
456 
462  public function isServiceDisabled( $name ) {
463  return isset( $this->disabled[$name] );
464  }
465 }
466 
471 class_alias( ServiceContainer::class, 'MediaWiki\Services\ServiceContainer' );
Wikimedia\Services\ServiceContainer\$serviceManipulators
callable[][] $serviceManipulators
Definition: ServiceContainer.php:61
$file
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Definition: router.php:42
Wikimedia\Services\CannotReplaceActiveServiceException
Exception thrown when trying to replace an already active service.
Definition: CannotReplaceActiveServiceException.php:33
Wikimedia\Services\ServiceContainer\hasService
hasService( $name)
Returns true if a service is defined for $name, that is, if a call to getService( $name ) would retur...
Definition: ServiceContainer.php:191
Wikimedia\Services\ServiceContainer\$serviceInstantiators
callable[] $serviceInstantiators
Definition: ServiceContainer.php:56
Wikimedia\Services\ServiceContainer\isServiceDisabled
isServiceDisabled( $name)
Definition: ServiceContainer.php:462
Wikimedia\Services\ServiceContainer\__construct
__construct(array $extraInstantiationParams=[])
Definition: ServiceContainer.php:83
Wikimedia\Services\ServiceContainer\getServiceNames
getServiceNames()
Definition: ServiceContainer.php:221
Wikimedia\Services\ServiceContainer\destroy
destroy()
Destroys all contained service instances that implement the DestructibleService interface.
Definition: ServiceContainer.php:95
Wikimedia\Services\ServiceAlreadyDefinedException
Exception thrown when a service was already defined, but the caller expected it to not exist.
Definition: ServiceAlreadyDefinedException.php:35
Wikimedia\Services\ServiceContainer\redefineService
redefineService( $name, callable $instantiator)
Replace an already defined service.
Definition: ServiceContainer.php:268
Wikimedia\Services\ServiceContainer\loadWiringFiles
loadWiringFiles(array $wiringFiles)
Definition: ServiceContainer.php:117
Wikimedia\Services\NoSuchServiceException
Exception thrown when the requested service is not known.
Definition: NoSuchServiceException.php:33
php
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
Wikimedia\Services\ServiceContainer\$disabled
bool[] $disabled
disabled status, per service name
Definition: ServiceContainer.php:66
Wikimedia\Services\ServiceContainer
ServiceContainer provides a generic service to manage named services using lazy instantiation based o...
Definition: ServiceContainer.php:46
Wikimedia\Services\DestructibleService
DestructibleService defines a standard interface for shutting down a service instance.
Definition: DestructibleService.php:35
Wikimedia\Services\ServiceContainer\applyWiring
applyWiring(array $serviceInstantiators)
Registers multiple services (aka a "wiring").
Definition: ServiceContainer.php:137
Wikimedia\Services\ServiceContainer\peekService
peekService( $name)
Returns the service instance for $name only if that service has already been instantiated.
Definition: ServiceContainer.php:210
Wikimedia\Services\ServiceContainer\resetService
resetService( $name, $destroy=true)
Resets a service by dropping the service instance.
Definition: ServiceContainer.php:371
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
Wikimedia\Services\ServiceContainer\defineService
defineService( $name, callable $instantiator)
Define a new service.
Definition: ServiceContainer.php:239
Wikimedia\Services\ServiceContainer\createService
createService( $name)
Definition: ServiceContainer.php:426
array
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:271
Wikimedia\Services\ServiceDisabledException
Exception thrown when trying to access a disabled service.
Definition: ServiceDisabledException.php:33
Wikimedia\Services\ServiceContainer\$services
object[] $services
Definition: ServiceContainer.php:51
Wikimedia\Services\ServiceContainer\importWiring
importWiring(ServiceContainer $container, $skip=[])
Imports all wiring defined in $container.
Definition: ServiceContainer.php:155
Wikimedia\Services\ServiceContainer\disableService
disableService( $name)
Disables a service.
Definition: ServiceContainer.php:342
$ret
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition: hooks.txt:1985
Wikimedia\Services\ContainerDisabledException
Exception thrown when trying to access a service on a disabled container or factory.
Definition: ContainerDisabledException.php:33
Wikimedia\Services\ServiceContainer\$destroyed
bool $destroyed
Definition: ServiceContainer.php:76
Wikimedia\Services
Definition: CannotReplaceActiveServiceException.php:2
Wikimedia\Services\ServiceContainer\getService
getService( $name)
Returns a service object of the kind associated with $name.
Definition: ServiceContainer.php:404
as
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
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
Wikimedia\Services\ServiceContainer\$extraInstantiationParams
array $extraInstantiationParams
Definition: ServiceContainer.php:71
Wikimedia\Services\ServiceContainer\addServiceManipulator
addServiceManipulator( $name, callable $manipulator)
Add a service manipulator callback for the given service.
Definition: ServiceContainer.php:309