tonis / di
一个轻量级、HHVM 兼容且依赖性轻的依赖注入(DI)容器。
该软件包的官方仓库似乎已消失,因此该软件包已被冻结。
Requires
- php: >=5.5
- container-interop/container-interop: ^1.1
Requires (Dev)
- doctrine/annotations: ^1.2
- phpunit/phpunit: ^4.0
- squizlabs/php_codesniffer: ^1.0
This package is not auto-updated.
Last update: 2023-03-04 09:17:09 UTC
README
安装
Tonis\Di 可以使用 composer 安装,这将为您设置自动加载。
composer require tonis-io/di
此外,您还可以下载或克隆仓库并设置自己的自动加载。
简介
Tonis\Di 是一个轻量级、HHVM 兼容且依赖性轻的依赖注入(DI)容器。您可以在 维基百科 或 Martin Fowler 的网站 上了解更多关于 DI 的信息。Tonis\Di 的目标是帮助您管理您的参数和服务。
参数
use Tonis\Di\Container; $i = new Container(); // assign a parameter is as easy as using ArrayAccess $i['foo'] = 'bar'; // output is 'bar' echo $i['foo'];
服务
Tonis\Di 的主要目的是管理您的服务。您可以通过以下三种方式之一创建服务:
- 使用字符串类名设置
- 直接设置服务
- 通过工厂闭包创建服务
- 使用数组配置
- 使用实现 ServiceFactory 的对象
- 使用注解结合生成器
无论您选择哪种风格,所有服务都通过 nject 方法设置。每种风格都有其优点和缺点。您需要决定哪种方法最适合您的应用程序。
设置服务
$i = new Container(); // setting using the string class name $i->set('foo', 'StdClass'); // setting the service directly $i->set('foo', new \StdClass()); // setting the service using a closure factory $i->set('foo', function() { return new \StdClass(); }); // setting the service using array configuration $i->set('foo', ['StdClass']); // setting the service using an object that implements ServiceFactory class StdClassFactory implements ServiceFactory { public function createService(Container $i) { return new \StdClass(); } } $i->set('foo', new StdClassFactory()); // each method listed above is identical
检查服务是否存在
// false $i->has('foo'); $i->set('foo', new \StdClass()); // true $i->has('foo');
获取服务
// assuming the configuration from 'Setting Services' above // the following retrieves the 'foo' service $foo = $i->get('foo');
数组配置
数组配置有一些额外的选项,使其非常灵活。
构造函数注入
$i = new Container(); // you can pass constructor parameters to the service class Foo { public function __construct($string, $int) { $this->string = $string; $this->int = $int; } } // the resulting object will have 'string set to 'I am a string' // and 'int' set to '1' $i->set('foo', ['Foo', ['I am a string', 1]]);
设置器注入
$i = new Container(); // you can pass constructor parameters to the service class Foo { public function __construct($int) { $this->int = $int; } public function setString($string) { $this->string = $string; } } // the resulting object will have 'string set to 'I am a string' // and 'int' set to '1' $i->set('foo', ['Foo',[1],['setString' => 'string']]);
引用其他服务和参数
Tonis\Di 的数组配置包括在字符串前缀使用特殊字符引用其他服务的功能。默认情况下,您使用 @ 符号引用服务,使用 $ 符号引用参数。您可以使用 setServiceIdentifier 和 setParamIdentifier 方法分别修改这些。
$i = new Container(); class Bar { } class Foo { public function __construct(Bar $bar) { $this->bar = $bar; } public function setBaz($baz) { $this->baz = $baz; } } // set the 'baz' parameter to 'boogly' $i['baz'] = 'boogly'; // set the 'bar' service to an instance of \Bar $i->set('bar', new \Bar()); // create the foo service using array configuration and parameter/service references $i->set('foo', ['Foo',['@bar'],['setBaz' => '$baz']]); // the resulting Foo service would have '$this->bar' set to '\Bar' and '$this->baz' set to 'boogly'
服务工厂
如果您从数组配置返回 Tonis\Di\ServiceFactory 的实例,它将自动为您创建实例并返回该实例。这使得您可以将参数注入到服务工厂中,并重用工厂,同时仍然返回您想要的实例。
class ArrayObjectFactory implements ServiceFactory { private $defaults; public function __construct(array $defaults) { $this->defaults = $defaults; } public function createService(Container $i) { return new \ArrayObject($this->defaults); } } $i = new Container(); // Result is an ArrayObject and *not* an ArrayObjectFactory $i->set('ArrayObject', ['ArrayObjectFactory', [['foo' => 'bar']]);
注解与生成器结合使用
Tonis\Di 提供了注释,您可以使用它们来协助创建服务的配置。
namespace Tonis\Di\TestAsset; use Tonis\Di\Annotation as Di; /** * @Di\Component("inject.test-asset.annotated-component") */ class AnnotatedComponent { /** @var \StdClass */ private $foo; /** @var array */ private $params; /** @var array */ private $setter; /** * @Di\Method({@Di\Inject("foo"), @Di\Param("params")}) */ public function __construct(\StdClass $foo, array $params) { $this->foo = $foo; $this->params = $params; } /** * @Di\Method({@Di\Param("setter")}) * * @param array $setter */ public function setSetter($setter) { $this->setter = $setter; } /** * @return array */ public function getSetter() { return $this->setter; } /** * @return \StdClass */ public function getFoo() { return $this->foo; } /** * @return array */ public function getParams() { return $this->params; } }
现在您有一个带有注释的类,请使用元数据工厂为该类创建元数据,并使用生成器生成容器配置。
use Tonis\Di\Generator; use Tonis\Di\Metadata; $mdf = new Metadata\MetadataFactory(); $md = $mdf->getMetadataForClass('Tonis\Di\TestAsset\AnnotatedComponent'); $generator = new Generator\ArrayGenerator(); $i = new Container(); $i->set($md->getName(), $generator->generate($md)); $i->set($md->getName()); // instanceof Tonis\Di\TestAsset\AnnotatedComponent
读取文件、创建元数据和生成配置是一个 复杂 的过程,并不适用于生产环境。您应该缓存生成结果并在生产环境中使用缓存。
装饰您的服务
有时您想覆盖 DI 容器中设置的服务,而不修改原始配置。Tonis\Di 通过提供两种类型的装饰器来处理这个问题。
装饰
decorate 方法允许您在返回之前对创建的服务进行任何修改。装饰器闭包接收容器和服务作为参数。
$i = new Container(); $i->set('foo', new \StdClass()); $i->decorate('foo', function(Container $i, \StdClass $foo) { $foo->bar = 'bar'; $foo->baz = 'baz'; }); $foo = $i->set('foo'); // output is 'barbaz'; echo $foo->bar; echo $foo->baz;
或者,您可以为实现 Tonis\Di\ServiceDecorator 接口的一个类的实例提供。
namespace My; use Tonis\Di\Container; use Tonis\Di\ServiceDecorator; class FooDecorator implements ServiceDecorator { public function decorateService(Container $i, $instance) { $instance->bar = 'bar'; $instance->baz = 'baz'; } }
use Tonis\Di\Container; $i = new Container(); $i->set('foo', new \StdClass()); $i->decorate('foo', new \My\FooDecorator()); $foo = $i->set('foo'); // output is 'barbaz'; echo $foo->bar; echo $foo->baz;
包装
wrap 方法比 decorate 更强大。包装允许您完全改变创建的对象,或者完全绕过原始配置。包装闭包接收三个参数:容器、服务的名称以及创建服务的可调用对象。
$i = new Container(); $i->set('foo', new \StdClass()); // if we use the $callable available to the closure we receive an instance of the original service // the \StdClass object would have two properties: 'bar' and 'name' // the values would be 'bar' and 'foo' respectively $i->wrap('foo', function(Container $i, $name, $callable) { $foo = $callable(); $foo->bar = 'bar'; $foo->name = $name; return $foo; }); // we can completely override the original service configuration by skipping the callable $i->wrap('foo', function(Container $i, $name, $callable) { return new \ArrayObject(); }); // output is 'ArrayObject' echo get_class($i->set('foo'));
或者,您可以提供一个实现 Tonis\Di\ServiceWrapper 接口的一个类的实例。
namespace My; use Tonis\Di\Container; use Tonis\Di\ServiceWrapper; class FooWrapper implements ServiceWrapper { public function wrapService(Container $i, $name, $callable) { $foo = $callable(); $foo->bar = 'bar'; $foo->name = $name; return $foo; } }
use Tonis\Di\Container; $i = new Container(); $i->set('foo', new \StdClass()); $i->wrap('foo', new \My\FooWrapper()); $foo = $i->set('foo'); echo $foo->bar; // outputs 'bar' echo $foo->name; // outputs 'foo'
为什么是 nvoke 和 nject?
因为它非常可爱,这就是原因!不过,如果您愿意的话,也可以使用 set() 代替 nject(),使用 get() 代替 nvoke()。