X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fservices%2FServiceContainer.php;h=30f82955c53ba7eb94ba0c259e76dda1bd331009;hb=d9327f9a57213488ed35b6a5dd532feb4f70125f;hp=7814194fea03660755339d633d43efc4a80a3194;hpb=72871eb3fb60cf61df8965347ebc77f93685dd98;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/services/ServiceContainer.php b/includes/services/ServiceContainer.php index 7814194fea..30f82955c5 100644 --- a/includes/services/ServiceContainer.php +++ b/includes/services/ServiceContainer.php @@ -55,6 +55,11 @@ class ServiceContainer implements DestructibleService { */ private $serviceInstantiators = []; + /** + * @var callable[][] + */ + private $serviceManipulators = []; + /** * @var bool[] disabled status, per service name */ @@ -151,6 +156,22 @@ class ServiceContainer implements DestructibleService { $this->serviceInstantiators, $newInstantiators ); + + $newManipulators = array_diff( + array_keys( $container->serviceManipulators ), + $skip + ); + + foreach ( $newManipulators as $name ) { + if ( isset( $this->serviceManipulators[$name] ) ) { + $this->serviceManipulators[$name] = array_merge( + $this->serviceManipulators[$name], + $container->serviceManipulators[$name] + ); + } else { + $this->serviceManipulators[$name] = $container->serviceManipulators[$name]; + } + } } /** @@ -199,7 +220,7 @@ class ServiceContainer implements DestructibleService { * Define a new service. The service must not be known already. * * @see getService(). - * @see replaceService(). + * @see redefineService(). * * @param string $name The name of the service to register, for use with getService(). * @param callable $instantiator Callback that returns a service instance. @@ -224,7 +245,9 @@ class ServiceContainer implements DestructibleService { * * @see defineService(). * - * @note This causes any previously instantiated instance of the service to be discarded. + * @note This will fail if the service was already instantiated. If the service was previously + * disabled, it will be re-enabled by this call. Any manipulators registered for the service + * will remain in place. * * @param string $name The name of the service to register. * @param callable $instantiator Callback function that returns a service instance. @@ -233,7 +256,8 @@ class ServiceContainer implements DestructibleService { * Any extra instantiation parameters provided to the constructor will be * passed as subsequent parameters when invoking the instantiator. * - * @throws RuntimeException if $name is not a known service. + * @throws NoSuchServiceException if $name is not a known service. + * @throws CannotReplaceActiveServiceException if the service was already instantiated. */ public function redefineService( $name, callable $instantiator ) { Assert::parameterType( 'string', $name, '$name' ); @@ -250,6 +274,46 @@ class ServiceContainer implements DestructibleService { unset( $this->disabled[$name] ); } + /** + * Add a service manipulator callback for the given service. + * This method may be used by extensions that need to wrap, replace, or re-configure a + * service. It would typically be called from a MediaWikiServices hook handler. + * + * The manipulator callback is called just after the service is instantiated. + * It can call methods on the service to change configuration, or wrap or otherwise + * replace it. + * + * @see defineService(). + * @see redefineService(). + * + * @note This will fail if the service was already instantiated. + * + * @since 1.32 + * + * @param string $name The name of the service to manipulate. + * @param callable $manipulator Callback function that manipulates, wraps or replaces a + * service instance. The callback receives the new service instance and this the + * ServiceContainer as parameters, as well as any extra instantiation parameters specified + * when constructing this ServiceContainer. If the callback returns a value, that + * value replaces the original service instance. + * + * @throws NoSuchServiceException if $name is not a known service. + * @throws CannotReplaceActiveServiceException if the service was already instantiated. + */ + public function addServiceManipulator( $name, callable $manipulator ) { + Assert::parameterType( 'string', $name, '$name' ); + + if ( !$this->hasService( $name ) ) { + throw new NoSuchServiceException( $name ); + } + + if ( isset( $this->services[$name] ) ) { + throw new CannotReplaceActiveServiceException( $name ); + } + + $this->serviceManipulators[$name][] = $manipulator; + } + /** * Disables a service. * @@ -359,7 +423,24 @@ class ServiceContainer implements DestructibleService { $this, ...$this->extraInstantiationParams ); - // NOTE: when adding more wiring logic here, make sure copyWiring() is kept in sync! + + if ( isset( $this->serviceManipulators[$name] ) ) { + foreach ( $this->serviceManipulators[$name] as $callback ) { + $ret = call_user_func_array( + $callback, + array_merge( [ $service, $this ], $this->extraInstantiationParams ) + ); + + // If the manipulator callback returns an object, that object replaces + // the original service instance. This allows the manipulator to wrap + // or fully replace the service. + if ( $ret !== null ) { + $service = $ret; + } + } + } + + // NOTE: when adding more wiring logic here, make sure importWiring() is kept in sync! } else { throw new NoSuchServiceException( $name ); }