phossa2 / di
一个强大的PHP依赖注入库的Container-Interop/PSR-11实现。
Requires
- php: >=5.4.0
- container-interop/container-interop: ~1.0
- phossa2/config: ^2.0.12
- phossa2/shared: ^2.0.19
Requires (Dev)
- phpunit/phpunit: 4.*
Provides
Replaces
This package is not auto-updated.
Last update: 2020-01-24 16:09:47 UTC
README
请使用phoole/di库代替
phossa2/di是一个快速且强大的Container-Interop或PSR-11实现,用于PHP依赖注入库。它建立在功能丰富的phossa2/config库之上,支持自动注入、容器委托、对象装饰、对象作用域等功能。
它需要PHP 5.4,支持PHP 7.0+和HHVM。它符合PSR-1、PSR-2、PSR-4,以及提议的PSR-5、PSR-11。
安装
通过composer
工具安装。
composer require "phossa2/di=2.*"
或者将以下行添加到您的composer.json
{ "require": { "phossa2/di": "2.*" } }
用法
-
使用类的自动注入。
以下是一些简单的类
// file cache.php class MyCache { private $driver; public function __construct(MyCacheDriver $driver) { $this->driver = $driver; } // ... } class MyCacheDriver { // ... }
而不是手动创建
MyCacheDriver
和MyCache
实例,您可以使用DI容器自动获取这两个实例use Phossa2\Di\Container; // should be aware of these classes require_once __DIR__ . '/cache.php'; // create the container $container = new Container(); // 'MyCache' classname as the service id if ($container->has('MyCache')) { $cache = $container->get('MyCache'); }
默认情况下,启用自动注入,当容器中没有定义名为'MyCache'的服务时,容器将查找
MyCache
类,并在创建MyCache
实例时自动解析其依赖注入(这里,是MyCacheDriver
实例)。 -
使用
set()
手动添加服务可以使用
set()
方法手动将服务添加到容器中。use Phossa2\Di\Container; // should be aware of these classes require_once __DIR__ . '/cache.php'; // create the container $container = new Container(); // turn off autowiring $container->auto(false); // define service with an array $container->set('cache', [ 'class' => 'MyCache', // classname 'args' => ['${#driver}'] // constructor arguments ]); // add service 'driver' with a callback $container->set('driver', function() { return new \MyCacheDriver(); }); // get the service var_dump($container->get('cache') instanceof \MyCache); // true
这里构造函数参数中使用的服务引用
'${#driver}'
表示它是容器中的driver
服务实例。 -
从文件或数组获取配置
容器使用
Phossa2\Config\Interfaces\ConfigInterface
实例作为参数和服务定义的定义解析器。配置实例可以从以下文件中读取配置,或者从数组中获取配置use Phossa2\Di\Container; use Phossa2\Config\Config; $configData = [ // container service definitions 'di.service' => [ // cache service 'cache' => ['class' => 'MyCache', 'args' => ['${#driver}']], // cache driver, classname directly 'driver' => 'MyCacheDriver', ], // common methods to run after each instantiation 'di.common' => [ [ function($obj) { return $obj instanceof \MyCacheDriver; }, // tester function($obj, $container) { echo "ok"; } // runner ], ], // init methods after container created 'di.init' => [ // default section 'default' => [ ['setLogger', ['${#logger}']], // ... ], // my own section 'mystuff' => [ ], ], ]; // create $config instance with provided data $config = new Config(null, null, $configData); // instantiate container with $config instance and definition base node 'di' $container = new Container($config, 'di'); // get service by id 'cache' (di.service.cache) $cache = $container->get('cache'); // true var_dump($cache instanceof \MyCache);
默认情况下,容器相关配置位于节点
di
下,服务定义位于di.service
节点下。
特性
-
可以使用形如 '${reference}' 的引用来引用配置或容器中的预定义参数。
引用名称中不允许包含
'$', '{', '}', '.'
字符。字符'#', '@'
有特殊含义,因此不应包含在 普通 服务名称中。-
请参阅 phossa2/config 引用 获取详细信息。参数引用可以从配置文件中读取,也可以通过容器的
param()
方法定义,如下所示:// define a new parameter for the container $container->param('cache.dir', '${system.tmpdir}/cache'); // use the cache.dir parameter defined above $container->set('cache', [ 'class' => '${cache.class}', // predefined in file 'args' => ['${cache.dir}'] // just defined above ]);
-
可以使用形如 '${#service_id}' 的服务引用来引用容器中的服务实例(或在 委托者 中)。
$container->set('cache', [ 'class' => '${cache.class}', 'args' => ['${#cache_driver}'] // service reference ]);
两个保留的服务引用是 '${#container}' 和 '${#config}'。这两个引用分别指向容器实例本身以及它使用的配置实例。这两个引用可以像其他服务引用一样使用。
-
使用引用
引用可以在配置文件中的任何位置使用,也可以作为所有容器方法(除方法的参数
$id
外)的参数。// run(callable, arguments) with references $container->run(['${#logger}', 'warning'], ['warning from ${log.facility}']); // resolve references $data = ['${system.dir}', '${#logger}']; $container->resolve($data); // all references in $data are now resolved
-
-
自动装配 是容器自动实例化对象并解决其依赖项的能力。自动装配的基础是 PHP 函数参数 类型提示。
通过反射类、构造函数和方法,phossa2/di 能够找到实例的正确类(用户需要使用类名作为服务 ID)以及其依赖项的正确类(使用类名类型提示)。
如果使用接口名称进行依赖类型提示,用户可以设置接口到正确类名的映射,如下所示:
// map an interface to a classname $container->set( 'Phossa2\\Cache\\CachePoolInterface', // MUST NO leading backslash 'Phossa2\\Cache\\CachePool' ); // map an interface to a service id reference $container->set('Phossa2\\Cache\\CachePoolInterface', '${#cache}'); // map an interface to a parameter reference $container->set('Phossa2\\Cache\\CachePoolInterface', '${cache.class}'); // map an interface to a callback $container->set('Phossa2\\Cache\\CachePoolInterface', function() { return new \Phossa2\Cache\CachePool(); });
或者,在配置节点
di.service
中定义映射,如下所示:$configData = [ // ... 'di.service' => [ 'Phossa2\\Cache\\CachePoolInterface' => '${cache.class}', // ... ], // ... ];
可以开启或关闭自动装配。关闭自动装配将允许用户在自动加载之前检查任何定义错误。
// turn off auto wiring $container->auto(false); // turn on auto wiring $container->auto(true);
-
对象装饰 是在服务实例化后立即应用装饰更改(执行方法等)。
-
仅对 单个实例 的装饰方法
$container->set('cache', [ 'class' => 'Phossa2\\Cache\\Cache', 'args' => ['${#cachedriver}'], // constructor arguments 'methods' => [ ['clearCache'], // method of $cache ['setLogger', ['${#logger}']], // method with arguments [[$logger, 'setLabel'], ['cache_label']], // callable with arguments [['${#driver}, 'init']], // pseduo callable // ... ], ]);
通过将
methods
部分添加到cache
服务定义中,形式为[ callableOrMethodName, OptionalArgumentArray ]
,这些方法将在cache
实例化后立即执行。这里的
callableOrMethodName
可以是:-
当前实例的方法名
-
一个有效的可调用函数
-
一个带有引用的伪可调用函数(在解析引用后,它是一个有效的可调用函数)。
这里的
OptionalArgumentArray
可以是:-
空的
-
值或引用的数组
-
-
对所有 实例 的通用装饰方法
$configData = [ // common methods for all instances 'di.common' => [ // [ tester(): bool, method ] [ function($object, $container) { return $object instanceof 'Psr\\Log\\LoggerAwareInterface' }, ['setLogger', ['${#logger}']] ], // ... ], ];
通用方法可以在 'di.common' 节点中配置,以在实例化后立即应用于所有实例。定义由两部分组成,第一部分是一个测试可调用函数,它接受当前实例和容器作为参数,并返回一个布尔值。第二部分与服务定义中的 'methods' 部分具有相同的方法格式。
要跳过某个服务的通用方法执行,可以将其定义为如下所示:
$container->set('logger', [ 'class' => 'Phossa2\Logger\Logger', 'skip' => true ]);
-
-
根据 Interop Container Delegate Lookup,容器可以注册一个委托容器(委托者),并且
-
对
get()
方法的调用应该只返回容器的一部分。如果条目不是容器的一部分,应抛出异常(如ContainerInterface
所请求的)。 -
has()
方法的调用应该只返回 true,如果条目是容器的一部分。如果条目不是容器的一部分,则应该返回 false。 -
如果获取的条目有依赖项,而不是在容器中执行依赖项查找,而是在委托容器(委托者)中执行查找。
-
重要 默认情况下,查找应该在委托容器上执行,而不是在容器本身上。
phossa2/di 完全支持委托功能。
use Phossa2\Di\Delegator; // create delegator $delegator = new Delegator(); // create container $container = new Container(); // insert container into delegator $delegator->addContainer($container); // get from delegator now $cache = $delegator->get('cache');
-
-
-
共享或单例作用域
默认情况下,容器中的服务实例在容器内是共享的。实际上,它们具有
Container::SCOPE_SHARED
的作用域。如果用户希望每次都使用不同的实例,他们可以使用one()
方法或以Container::SCOPE_SINGLE
作用域定义服务。// cache service by default is in shared scope $cache1 = $container->get('cache'); // get again $cache2 = $container->get('cache'); // same var_dump($cache1 === $cache2); // true // a new cache instance with 'one()' $cache3 = $container->one('cache'); // different instances var_dump($cache1 !== $cache3); // true // but both share the same cacheDriver dependent service var_dump($cache1->getDriver() === $cache3->getDriver()); // true
或将服务定义为
Container::SCOPE_SINGLE
$container->set('cache', [ 'class' => '\\Phossa2\\Cache\\CachePool'), 'scope' => Container::SCOPE_SINGLE ]); // each get() will return a new cache $cache1 = $container->get('cache'); $cache2 = $container->get('cache'); // different instances var_dump($cache1 !== $cache2); // true // dependent service are shared var_dump($cache1->getDriver() === $cache->getDriver()); // true
将容器的默认作用域设置为
Container::SCOPE_SINGLE
将导致每次get()
都返回一个新实例(除非明确为该服务定义了 'scope')。// set default scope to SCOPE_SINGLE $container->share(false); // a new copy of cache service $cache1 = $container->get('cache'); // another new cache service $cache2 = $container->get('cache'); // different instances var_dump($cache1 !== $cache2); // true // dependencies are different var_dump($cache1->getDriver() !== $cache->getDriver()); // true
-
使用自己的作用域
无论默认作用域或该实例定义的作用域如何,您可以如下获取自己的作用域实例:
// instance in scope 'myScope' $cacheOfMyScope = $container->get('cache@myScope'); // new instance in single scope, even though you specified one $cacheOfSingle = $container->one('cache@myScope'); // instance in shared scope $cache = $container->get('cache');
服务引用也可以按以下方式定义作用域:
$container->set('cache', [ 'class' => 'Phossa2\\Cache\\Cache', 'args' => ['${#driver@myScope}'] // use driver of myScope ]);
-
仅在特定对象中共享实例
有时,用户可能只想在特定对象内部共享一个实例。
class A { private $b, $c; public function __construct(B $b, C $c) { $this->b = $b; $this->c = $c; } public function getB() { return $this->b; } public function getC() { return $this->c; } } class B { private $c; public function __construct(C $c) { $this->c = $c; } } class C { } // an instance of A $a1 = $container->one('A'); // another instance of A $a2 = $container->one('A'); // $a1 and $a2 is different var_dump($a1 !== $a2); // true // C is the same under A var_dump($a1->getC() === $a1->getB()->getC()); // true // C is also shared among different A var_dump($a1->getC() === $a2->getC()); // true
在前面的代码中,
C
不仅在A
下共享,还在A
的不同实例之间共享。如果用户只想在A
下共享C
而不是在A
之间共享,怎么办?如下设置
C
的作用域为 '#A',// this scope only takes effect when under service A $container->set('C', [ 'class' => 'C', 'scope' => '#A']); // an instance of A $a1 = $container->one('A'); // another instance of A $a2 = $container->one('A'); // C is different among different A var_dump($a1->getC() !== $a2->getC()); // true // C is same under one A var_dump($a1->getC() === $a1->getB()->getC()); // true
-
-
Phossa2\Di\Container
和Phossa2\Di\Delegator
都实现了\ArrayAccess
接口。$container = new Container(); $delegator = new Delegator(); $delegator->addContainer($container); // equals to $delegator->has('A') if (isset($delegator['A'])) { var_dump($delegator['A'] === $container['A']); // true }
默认情况下,
Phossa2\Di\Container
是可写的,这意味着用户可以通过使用set()
手动将新服务定义添加到容器中。注意 如果容器设置为只读,则自动连接将自动关闭。
要获取只读容器,
$container = new Container(); $container->setWritable(false); var_dump($container->isWritable() === false); // true // delegator also $delegator = new Delegator(); $delegator->setWritable(false); var_dump($delegator->isWritable() === false); // true
APIs
-
-
get(string $id): object
来自 ContainerInterface如果未找到,将抛出
Phossa2\Di\Exception\NotFoundException
。可以附加 '@scope' 到$id
。可以将数组用作对象构造函数参数的第二个参数。 -
has(string $id): bool
来自 ContainerInterface$id
可以附加 '@scope'。 -
one(string $id, array $args = []): object
来自 ExtendedContainerInterface为这个
$id
获取一个新实例。可以为对象构造函数提供新参数。 -
run(mixed $callable, array $args = []): mixed
来自 ExtendedContainerInterface使用提供的参数执行可调用或伪可调用(带有引用)(参数可能包含引用)
-
param(string $name, mixed $value): bool
来自 ExtendedContainerInterface在容器的配置树中设置一个参数,以便稍后用作引用。返回一个布尔值以指示状态。
-
alias(string $id, object|string $object): bool
来自 ExtendedContainerInterface在容器中将对象别名为
$id
。与set()
方法的区别在于获取此对象将跳过公共方法的执行。成功时返回
true
,失败时返回false
。 -
auto(bool $flag): $this
来自 AutoWiringInterface设置容器自动装配为
ON
或OFF
。 -
isAuto(): bool
来自 AutoWiringInterface测试容器是否自动装配为
ON
或OFF
。 -
resolve(mixed &$data): $this
来自 ReferenceResolveInterface递归解析
$data
中的引用。$data
可能是string
或array
。其他数据类型将保持不变。 -
share(bool $flag = true): $this
来自 ScopeInterface将容器默认作用域设置为
Container::SCOPE_SHARED
或Container::SCOPE_SINGLE
。 -
set(string $id, mixed $value): bool
来自 WritableInterface将服务
$id
设置到容器中。$value
可以是以下形式的数组:$value = [ 'class' => mixed, // classname/object/callback etc. 'args' => array, // arguments for the constructor or callback 'scope' => string, // default scope for this service 'skip' => bool, // skip common methods for the instance ];
或者
$value
可以是类名、对象、回调函数等。成功时返回true
,失败时返回false
。容器必须是可写的,否则将抛出LogicException
。 -
isWritable(): bool
来自 WritableInterface这个容器是可写的吗?
-
setWritable(bool $writable): bool
来自 WritableInterface设置这个容器为可写或只读。
-
-
-
get(string $id): object
来自 ContainerInterface从委托中获取服务。如果未找到,将抛出
Phossa2\Di\Exception\NotFoundException
。如果底层容器支持此功能,则$id
可以附加 '@scope'。 -
has(string $id): bool
来自 ContainerInterface$id
可以附加 '@scope',前提是底层容器支持此功能。 -
set(string $id, mixed $value): bool
来自 WritableInterface如果委托是可写的,则将服务
$id
设置到委托中。 -
isWritable(): bool
来自 WritableInterface这个委托是可写的吗?
-
setWritable(bool $writable): bool
来自 WritableInterface设置这个委托为可写或只读。
-
addContainer(ContainerInterface $container): $this
来自 DelegatorInterface将容器添加到委托中。
-
变更日志
请参阅 CHANGELOG 以获取更多信息。
测试
$ composer test
贡献
有关更多信息,请参阅 CONTRIBUTE。
依赖
-
PHP >= 5.4.0
-
phossa2/config >= 2.0.12
-
phossa2/shared >= 2.0.21