phoole / di
Slim, powerful and full compatible PSR-11 dependency injection library for PHP
Installs: 18
Dependents: 0
Suggesters: 0
Security: 0
Stars: 4
Watchers: 1
Forks: 1
Open Issues: 0
pkg:composer/phoole/di
Requires
- php: >=7.2.0
- phoole/config: ^1.0.13
- psr/container: ^1.0
Requires (Dev)
- phpunit/phpunit: ^8
Provides
- psr/container-implementation: 1.0.0
README
Slim, powerful and full compatible PSR-11 dependency injection library for PHP.
It builds upon the versatile phoole/config library and supports object decorating, object scope and more. It requires PHP 7.2+. It is compliant with PSR-1, PSR-4, PSR-11 and PSR-12.
Installation
Install via the composer utility.
composer require "phoole/di"
or add the following lines to your composer.json
{
    "require": {
       "phoole/di": "1.*"
    }
}
Usage
- 
With configuration from files or definition array use Phoole\Di\Container; use Phoole\Config\Config; use Phoole\Cache\Cache; use Phoole\Cache\Adaptor\FileAdaptor; $configData = [ // service definitions 'di.service' => [ // classname & constructor arguments 'cache' => [ 'class' => Cache::class, 'args' => ['${#cacheDriver}'] // optional ], // use classname directly 'cacheDriver' => FileAdaptor::class ], // methods to run after each object initiation 'di.after' => [ // a callable, takes THE object as parameter function($obj) { echo "ok"; }, // will be converted to $obj->setLogger($logger) 'setLogger', ] ]; // inject configurations into container $container = new Container(new Config($configData)); // get service by id 'cache' (di.service.cache) $cache = $container->get('cache'); Container related configurations are under the node diand service definitions are under thedi.servicenode.
Features
- 
References in the form of '${reference}' can be used to refer to predefined parameters from the config or services in the container. Characters of '$', '{', '}', '.'are not allowed in reference name. Characters of'#', '@'have special meanings, such that should not be part of normal service names.- 
Parameter references like ${system.tempdir}$config = [ ... // use predefined 'sytem.tmpdir' in arguments etc. 'di.service.cacheDriver' => [ 'class' => FileAdaptor::class, 'args' => ['${system.tmpdir}'], ], ... ]; See phoole/config reference for detail. Parameter references are read from configuration files or array. 
- 
Service references like ${#cache}Service object reference in the form of ${#serviceId}can be used to referring a service instance in the container.$configData = [ ... 'di.service' => [ 'cache' => [ 'class' => Cache::class, 'args' => ['${#cacheDriver}'] // object reference ], 'cacheDriver' => ... ... Two reserved service references are ${#container}and${#config}. These two are referring the container instance itself and the config instance it is using. These two can be used just like other service references.
- 
Using references References can be used anywhere in the configuration. $confData = [ // methods executed after ALL object initiation 'di.after' => [ [['${#logger}', 'notice'], ['object created using ${log.facility}']] ] ]; 
 
- 
- 
Object decorating is to apply decorating changes (executing methods etc.) right before or after the instantiation of a service instance. - 
Decorating methods for individual instance only $config = [ 'di.service' => [ ... 'cache', [ 'class' => '${cache.class}', 'args' => ['${#cachedriver}'], // constructor arguments 'before' => [ [['${#logger}', 'info'], ['before initiating cache']], // $logger->info(...) ], 'after' => [ 'clearCache', // $cache->clearCache() method ['setLogger', ['${#logger}']], // $cache->setLogger($logger), argument is optional [['${#logger}', 'info'], ['just a info']], // $logger->info(...) function($cache) { // a callable takes object in parameter }, ] ], ... ] ]; By adding beforeoraftersection into thecacheservice definition in the form of[callableOrMethodName, OptionalArgumentArray], these methods will be executed right before/aftercacheinstantiation.callableOrMethodNamehere can be,- 
method name of initiated object ... 'after' => [ // $obj->setLogger($logger), $logger will be injected automatically 'setLogger', // object implementing 'LoggerAwareInterface' ], ... 
- 
a valid callable which takes initiated object as parameter ... 'after' => [ // callable takes initiated object as parameter function($obj) { }, ], ... 
- 
a pseudo callable with references (after resolving the references, it is a valid callable). ... 'after' => [ // a pseudo callable with references [['${#logger}', 'info'], ['just a info']], // $logger->info(...) ], ... 
 OptionalArgumentArrayhere can be,- 
empty 
- 
array of values or references 
 
- 
- 
Common decorating methods for all instances $configData = [ // before all instances initiated 'di.before' => [ [['${#logger}', 'info'], ['before create']], ], // after methods for all instances 'di.after' => [ ['setLogger', ['${#logger}']], // arguments are optional 'setDispatcher', // simple enough, set event dispatcher ], ]; Common methods can be configured in the 'di.before' or 'di.after' node to apply to all the instances right before or after their instantiation. 
 
- 
- 
- 
Shared objects and new objects By default, service instances in the container are shared inside the container. If users want different instance each time, they may just append '@' to the service id. // cache service by default is in shared scope $cache1 = $container->get('cache'); // get again $cache2 = $container->get('cache'); // same var_dump($cache1 === $cache2); // true // get a NEW cache instance $cache3 = $container->get('cache@'); // different instances var_dump($cache1 !== $cache3); // true // but both share the same cacheDriver dependent service var_dump($cache1->getAdaptor() === $cache3->getAdaptor()); // true 
- 
Object scope You may get an instance in your own scope as follows // no scope $cache1 = $container->get('cache'); // in `myScope` $cache2 = $container->get('cache@myScope'); // different instances var_dump($cache1 !== $cache2); // true // shared in myScope $cache3 = $container->get('cache@myScope'); var_dump($cache2 === $cache2); // true Service references can also have scope defined as follows, $container->set('cache', [ 'class' => Cache::class, 'args' => ['${#driver@myScope}'] // use driver of myScope ]); 
 
- 
- 
- 
Access predefined services statically Services in the container can also be access through a static way. But getandhasare reserved.// after container initiated $container = new Container(new Config(...)); // equals to $cache = $container->get('cache') $cache = Container::cache(); // if myservice defined and invokable $obj = Container::myservice('test'); 
- 
Initiating object by taking advantage of dependency injection use Phoole\Cache\Cache; use Psr\Log\LoggerAwareTrait; use Psr\Log\LoggerAwareInterface; class MyClass implements LoggerAwareInterface { use LoggerAwareTrait; public function __construct(Cache $cache) { } } // $cache will be injected automatically $obj = Container::create(MyClass::class); // also 'setLogger' will be executed if defined in 'di.after' section $logger = $obj->getLogger(); 
 
- 
- 
- 
Parameter autowiring (resolving) Parameters of a constructor/callable will be resolved by looking - 
exists in the classmap (service objects created already) ? 
- 
classname known to the script (class defined already) ? 
 
- 
- 
Auto injection Instead of using 'annotation', we encourage of using *AwareInterfacefor your own classes' dependency injection.use Psr\Log\LoggerAwareTrait; use Psr\Log\LoggerAwareInterface; class MyOwnClass implements LoggerAwareInterface { use LoggerAwareTrait; ... } // create your object with arguments $obj = Container::create(MyOnwClass::class, [...]); // $logger injected by the container automatically $logger = $obj->getLogger(); Containerhas all the common injection predefined in thedi.aftersection$config = [ 'di.after' => [ 'setLogger', // logger aware 'setCache', // cache aware 'setDispatcher', // event aware 'setContainer', // container aware ... ], ]; ... 
 
- 
- 
Both ContainerAWareInterfaceandContainerAWareTraitavailable.
APIs
- 
- 
get(string $id): objectfrom ContainerInterface
- 
has(string $id): boolfrom ContainerInterface$idmay have@or@scopeappended.
 
- 
Testing
$ composer test
Dependencies
- 
PHP >= 7.2.0 
- 
phoole/config >= 1.*