MediaWiki REL1_31
ServiceContainer.php
Go to the documentation of this file.
1<?php
2namespace MediaWiki\Services;
3
4use InvalidArgumentException;
5use RuntimeException;
6use Wikimedia\Assert\Assert;
7
47
51 private $services = [];
52
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
126 public function applyWiring( array $serviceInstantiators ) {
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 ) ) {
216 throw new ServiceAlreadyDefinedException( $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}
Exception thrown when trying to replace an already active service.
Exception thrown when trying to access a service on a disabled container or factory.
Exception thrown when the requested service is not known.
Exception thrown when a service was already defined, but the caller expected it to not exist.
ServiceContainer provides a generic service to manage named services using lazy instantiation based o...
redefineService( $name, callable $instantiator)
Replace an already defined service.
bool[] $disabled
disabled status, per service name
disableService( $name)
Disables a service.
importWiring(ServiceContainer $container, $skip=[])
Imports all wiring defined in $container.
getService( $name)
Returns a service object of the kind associated with $name.
applyWiring(array $serviceInstantiators)
Registers multiple services (aka a "wiring").
destroy()
Destroys all contained service instances that implement the DestructibleService interface.
resetService( $name, $destroy=true)
Resets a service by dropping the service instance.
defineService( $name, callable $instantiator)
Define a new service.
hasService( $name)
Returns true if a service is defined for $name, that is, if a call to getService( $name ) would retur...
__construct(array $extraInstantiationParams=[])
peekService( $name)
Returns the service instance for $name only if that service has already been instantiated.
Exception thrown when trying to access a disabled service.
Allows to change the fields on the form that will be generated $name
Definition hooks.txt:302
DestructibleService defines a standard interface for shutting down a service instance.