Home

Awesome

Standard Service Providers

This project attempts to define a common standard for service providers, aka "bundles" or "modules" in various frameworks.

Service providers are classes that provide service definitions to a PSR-11 container.

The PSR depends on the PSR-11 Container Interface. Service providers operate on PSR-11 compatible containers.

⚠️ Work in progress

the project is currently experimental and is being tried in frameworks, containers and modules until considered viable. Until a 1.0.0 release, the code in this repository is not stable. Expect breaking changes between versions such as 0.1.x and 0.2.0.

👉 Refer to the current PSR draft for the PSR proposal itself.

🧐 Refer to the PSR meta document for the history and reasoning behind this proposal.

💬 Join us to review open issues or participate in ongoing discussions.

<!-- TODO resolve #67 ## Usage To declare a service provider, simply implement the `ServiceProviderInterface` interface. ```php use Psr\Container\ServiceProviderInterface; class MyServiceProvider implements ServiceProviderInterface { public function getFactories() { return [ 'my_service' => function(ContainerInterface $container) { $dependency = $container->get('my_other_service'); return new MyService($dependency); } ]; } public function getExtensions() { return [ 'my_extended_service' => function(ContainerInterface $container, $extendedService) { $extendedService->registerExtension($container->get('my_service')); return $extendedService; } ]; } } ``` ### Aliases To alias a container entry to another, you can get the aliased entry from the container and return it: ```php class MyServiceProvider implements ServiceProviderInterface { public function getFactories() { return [ 'my_service' => fn() => new MyService(), 'my_alias' => fn(ContainerInterface $container) => $container->get('my_service'), ]; } // ... } ``` ### Entry overriding Overriding an entry defined in another service provider is as easy as defining it again. Module A: ```php class A implements ServiceProviderInterface { public function getFactories() { return [ 'foo' => fn() => 'abc', ]; } // ... } ``` Module B: ```php class B implements ServiceProviderInterface { public function getFactories() { return [ 'foo' => fn() => 'def', ]; } // ... } ``` If you register the service providers in the correct order in your container (A first, then B), then the entry `foo` will be `'def'` because B's definition will override A's. ### Entry extension Extending an entry before it is returned by the container is done via the `getExtensions` method. Module A: ```php class A implements ServiceProviderInterface { public function getFactories() { return [ 'logger' => fn() => new MyLogger(), ]; } // ... } ``` Module B: ```php class B implements ServiceProviderInterface { public function getExtensions() { return [ 'logger' => function (ContainerInterface $container, MyLogger $logger) { $logger->addHandler(new MyHandler()); return $logger; }, ]; } // ... } ``` The second parameter of extensions SHOULD use type-hinting when applicable. ```php function (ContainerInterface $container, MyLogger $logger) ``` If a container passes a service that does not match the type hint, a `TypeError` will be thrown while bootstrapping the Container (in PHP 7+), or a catchable fatal error in PHP 5. The second parameter of extensions CAN be nullable. ```php function (ContainerInterface $container, ?MyLogger $logger) ``` This allows an extension to handle a service that has been explicitly registered as `null` - for example: ```php class B implements ServiceProviderInterface { public function getExtensions() { return [ 'logger' => function (ContainerInterface $container, ?MyLogger $logger) { if ($logger) { $logger->addHandler(new MyHandler()); } return $logger; // if `logger` is `null`, the extension will simply return `null` }, ]; } // ... } ``` -->

Compatible projects

Projects consuming v0.4 service providers

Packages providing v0.4 service providers

<!-- TODO resolve #65 ## Best practices ### Managing configuration The service created by a factory should only depend on the input parameters of the factory (`$container` and `$getPrevious`). If the factory needs to fetch parameters, those should be fetched from the container directly. ```php class MyServiceProvider implements ServiceProviderInterface { public function getFactories() { return [ 'logger' => [ MyServiceProvider::class, 'createLogger' ], ]; } public function getExtensions() { return []; } public static function createLogger(ContainerInterface $container) { // The path to the log file is fetched from the container, not from the service provider state. return new FileLogger($this->container->get('logFilePath')); } } ``` -->