moro / container7
Dependency Injection container for PHP-7
Requires
- psr/container: 1.0.0
Requires (Dev)
- codeception/assert-throws: ~1.0
- codeception/codeception: ~2.3
- codeception/specify: ~1.0
- codeception/verify: ~1.0
- infection/infection: ~0.5
Provides
- psr/container-implementation: 1.0.0
This package is auto-updated.
Last update: 2024-10-17 10:30:58 UTC
README
Container7 is a medium Dependency Injection Container for PHP-7.
This package is compliant with PSR-1, PSR-2, PSR-4 and PSR-11. If you notice compliance oversights, please send a patch via pull request.
Features: providers, singletons, factories, parameters, aliases, tags, configuration, serialization.
Install
Via composer
$ composer require moro/container7
Requirements
- PHP 7.1
Usage
Creating a container is a matter of creating a Container instance:
<?php use Moro\Container7\Container; $container = new Container();
As many other dependency injection containers, Container7 manages two different kind of data: services and parameters. Services receives from container by their class or alias. Parameters stores in their service "parameters".
<?php if ($container->has(Service::class)) { $service = $container->get(Service::class); } $value = $container->get('parameters')->get('key');
For service definition used Service Providers.
<?php $container->addProvider(SomeProvider::class);
Service Providers allow you to package code or configuration for packages that you reuse regularly.
Service provider
Any Service Provider is simple PHP class. Services are defined by methods that return an instance of an object. You must define results of this methods as class or interface. And then you can receive service by that class or interface.
Singleton
<?php class SomeProvider { function someService(): Service { return new Service(); } }
Factory
If you want define factory - add variadic argument.
<?php class SomeProvider { function someService(...$arguments): Service { return new Service($arguments[0]); } }
Dependencies
When your service require other, you can add this dependency to method arguments.
<?php class SomeProvider { function someService(ServiceBeta $beta): ServiceAlpha { return new ServiceAlpha($beta); } }
Remember, that parameters is service too:
<?php use Moro\Container7\Parameters; class SomeProvider { function someService(Parameters $parameters): Service { return new Service($parameters->get('key')); } }
Modifying Services after Definition
In some cases you may want to modify a service definition after it has been defined. You can use method that receive service object and can return null.
<?php class SomeProvider { function extendService(Service $service) { $service->value = 'value'; // example } }
If you need to replace the service instance use that definition:
<?php class SomeProvider { function extendService(Service $service): ?Service { return new Decorator($service); } }
You can add dependencies here as in the definition of factories.
Parameters
Add default parameters.
<?php use Moro\Container7\Parameters; class SomeProvider { function parameters(Parameters $parameters) { $parameters->set('key1', 'value1'); $parameters->set('key2', '%key1%'); $parameters->add('key0', ['new item']); } function someService(Parameters $parameters): Service { $service = new Service(); $service->value = $parameters->get('key2'); return $service; } }
Set current values of parameters, that replace default values.
<?php use Moro\Container7\Container; use Moro\Container7\Parameters; $parameters = new Parameters(['key1' => 'value2']); $container = new Container($parameters); $container->addProvider(SomeProvider::class);
And use it.
<?php $service = $container->get(Service::class); assert($service->value === 'value2');
Aliases
When you can not get service by class or interface, then you must define alias for class, interface or method.
<?php use Moro\Container7\Aliases; class SomeProvider { function someService(): Service { return new Service(); } function aliases(Aliases $aliases) { // Add alias for unique interface in provider $aliases->add('kernel', Service::class); // or you can use method name $aliases->add('kernel', 'someService'); } }
Now you can get service by alias.
<?php $service = $container->get('kernel');
Tags
You can group services by setting a common tag for them.
<?php use Moro\Container7\Tags; class SomeProvider { function tags(Tags $tags) { // Add tag for unique interface in provider $tags->add('someTag', ServiceAlpha::class); $tags->add('someTag', ServiceBeta::class); // or you can use method name $tags->add('someTag', 'getServiceAlpha'); $tags->add('someTag', 'getServiceBeta'); } }
And then get a collection of the services.
<?php $collection = $container->getCollection('someTag');
Collection implements Iterator interface and you can use it in foreach.
Manipulation with collection
<?php use Moro\Container7\Container; $container = new Container(); $collection = $container->getCollection('A'); // Collection contains services with tag "A". $collection = $collection->merge('B'); // Collection contains services with tags "A" or "B". $collection = $collection->exclude('A'); // Collection contains services with tag "B" and without tag "A". $collection = $collection->merge('B')->with('C'); // The collection contains services that are marked // with "B" and "C" tags simultaneously.
Configuration
Container7 support configuration files in JSON format. You can create provider from that files.
<?php use Moro\Container7\Container; use Moro\Container7\Parameters; $configuration = Parameters::fromFile('conf.json'); $container = new Container($configuration);
Configuration files can be nested.
{ "@extends": [ "module1.json", "../module2.json" ] }
Singleton
{ "container": { "singletons": [ { "interface": "ServiceInterface", "class": "Service" } ] } }
Factory
{ "container": { "factories": [ { "interface": "ServiceInterface", "class": "Service" } ] } }
Dependencies
{ "container": { "singletons": [ { "interface": "ServiceInterface", "class": "ServiceAlpha", "args": [ "@ServiceBeta" ], "properties": { "value": 1 }, "calls": [ { "method": "add", "args": [ "key", "value" ] } ] } ] } }
Modifying Services after Definition
{ "container": { "extends": [ { "target": "ServiceInterface", "calls": [ { "method": "add", "args": [ "key", "value" ] } ] } ] } }
If you need to replace the service instance use that definition:
{ "container": { "extends": [ { "target": "ServiceInterface", "class": "Decorator", "args": [ "$target" ] } ] } }
Parameters, Aliases and Tags
{ "container": { "parameters": { "key1": "value1", "key2": "%key1%" }, "singletons": [ { "aliases": ["kernel"], "tags": ["someTag"], "interface": "ServiceInterface", "class": "ServiceAlpha", "properties": { "value": "%key2%" } }, { "tags": ["someTag"], "class": "ServiceBeta" }, { "class": "Service", "args": [ "$collections[someTag]" ] }, { "class": "Service", "calls": [ { "foreach": "$collections[someTag]", "method": "add", "args": ["$item"] } ] } ] } }
Dynamic services
<?php use Moro\Container7\Container; use Moro\Container7\Provider; class DynamicProvider { function boot(Container $container) { $configuration = []; // ... dynamic create of configuration array ... $container->addProvider(Provider::fromConfiguration(__METHOD__, $configuration)); } }
License
The MIT License (MIT). Please see License File for more information.