phoole / di
为PHP设计的一个精简、强大且完全兼容PSR-11的依赖注入库
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
为PHP设计的一个精简、强大且完全兼容PSR-11的依赖注入库。
它基于功能丰富的phoole/config库,并支持对象装饰、对象作用域等功能。它需要PHP 7.2+版本。它符合PSR-1、PSR-4、PSR-11和PSR-12规范。
安装
通过 composer
工具进行安装。
composer require "phoole/di"
或者在您的 composer.json
文件中添加以下行
{ "require": { "phoole/di": "1.*" } }
用法
-
使用文件或定义数组中的配置
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');
与容器相关的配置位于
di
节点下,服务定义位于di.service
节点下。
特性
-
可以使用类似 '${reference}' 的形式来引用配置或容器中预定义的参数。
引用名称中不允许使用
'$'
、'{'
、'}'
和'.'
字符。字符'#'
和'@'
有特殊含义,因此不应包含在 普通 服务名称中。-
$config = [ ... // use predefined 'sytem.tmpdir' in arguments etc. 'di.service.cacheDriver' => [ 'class' => FileAdaptor::class, 'args' => ['${system.tmpdir}'], ], ... ];
有关详细信息,请参阅phoole/config 引用。参数引用从配置文件或数组中读取。
-
可以使用类似
${#serviceId}
的形式来引用容器中的服务实例。$configData = [ ... 'di.service' => [ 'cache' => [ 'class' => Cache::class, 'args' => ['${#cacheDriver}'] // object reference ], 'cacheDriver' => ... ...
有两个保留的服务引用:
${#container}
和${#config}
。这两个引用分别指向容器实例本身及其所使用的配置实例。这两个可以像其他服务引用一样使用。 -
使用引用
引用可以在配置的任何位置使用。
$confData = [ // methods executed after ALL object initiation 'di.after' => [ [['${#logger}', 'notice'], ['object created using ${log.facility}']] ] ];
-
-
对象装饰 是在服务实例实例化前后应用装饰更改(执行方法等)。
-
仅针对 单个实例 的装饰方法
$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 }, ] ], ... ] ];
通过在
cache
服务定义中添加before
或after
节,形式为[callableOrMethodName, OptionalArgumentArray]
,这些方法将在cache
实例化前后执行。callableOrMethodName
可以是:-
初始化对象的
method name
... 'after' => [ // $obj->setLogger($logger), $logger will be injected automatically 'setLogger', // object implementing 'LoggerAwareInterface' ], ...
-
一个接受初始化对象作为参数的有效可调用对象
... 'after' => [ // callable takes initiated object as parameter function($obj) { }, ], ...
-
一个具有引用的伪可调用对象(在解析引用后,它是一个有效的可调用对象)。
... 'after' => [ // a pseudo callable with references [['${#logger}', 'info'], ['just a info']], // $logger->info(...) ], ...
OptionalArgumentArray
可以是:-
空
-
值或引用的数组
-
-
针对 所有实例 的通用装饰方法
$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 ], ];
可以在 'di.before' 或 'di.after' 节中配置通用方法,以在所有实例实例化前后应用。
-
-
-
共享对象和新对象
默认情况下,容器中的服务实例在容器内是共享的。如果用户希望每次使用不同的实例,他们只需在服务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
-
对象范围
您可以通过以下方式在自己的 作用域 中获取一个实例:
// 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
服务引用也可以按如下方式定义作用域:
$container->set('cache', [ 'class' => Cache::class, 'args' => ['${#driver@myScope}'] // use driver of myScope ]);
-
-
-
静态访问预定义服务
容器中的服务也可以通过静态方式访问。但
get
和has
是保留的。// 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');
-
通过依赖注入初始化对象
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();
-
-
-
参数自动装配(解析)
构造器/可调用参数将通过以下方式解析:
-
是否存在于类映射中(已创建的服务对象)?
-
脚本中已知的类名(已定义的类)?
-
-
自动注入
我们鼓励使用 '注解' 而不是使用
*AwareInterface
来实现您自己类的依赖注入。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();
Container
在di.after
部分预定义了所有常见的注入。$config = [ 'di.after' => [ 'setLogger', // logger aware 'setCache', // cache aware 'setDispatcher', // event aware 'setContainer', // container aware ... ], ]; ...
-
-
两者都可用:
ContainerAwareInterface
和ContainerAwareTrait
。
APIs
-
-
从 ContainerInterface 中获取
get(string $id): object
-
从 ContainerInterface 中获取
has(string $id): bool
$id
可能后跟有@
或@scope
。
-
测试
$ composer test
依赖关系
-
PHP >= 7.2.0
-
phoole/config >= 1.*