spiffy / spiffy-inject
Spiffy\Inject 是一个轻量级、HHVM 兼容、依赖轻量级的依赖注入(DI)容器。
Requires
- php: >=5.4
- doctrine/annotations: ~1.1
Requires (Dev)
- phpunit/phpunit: ~4.0
- squizlabs/php_codesniffer: ~1.0
This package is not auto-updated.
Last update: 2022-02-01 12:33:17 UTC
README
安装
Spiffy\Inject 可以使用 composer 安装,这将为您设置任何自动加载。
composer require spiffy/spiffy-inject
此外,您可以下载或克隆存储库,并设置自己的自动加载。
简介
Spiffy\Inject 是一个轻量级、HHVM 兼容且依赖轻量级的依赖注入(DI)容器。您可以在 维基百科 或 Martin Fowler 的网站 上了解更多关于 DI 的信息。Spiffy\Inject 旨在帮助您管理参数和服务。
参数
use Spiffy\Inject\Injector; $i = new Injector(); // assign a parameter is as easy as using ArrayAccess $i['foo'] = 'bar'; // output is 'bar' echo $i['foo'];
服务
Spiffy\Inject 的主要目的是管理您的服务。您可以通过以下三种方式之一创建服务
- 使用字符串类名设置
- 直接设置服务
- 通过工厂闭包创建服务
- 使用数组配置
- 使用实现 ServiceFactory 的对象
- 使用注释结合生成器
无论您选择哪种风格,所有服务都通过 nject
方法设置。每种风格都有其优点和缺点。您需要决定哪种方法最适合您的应用程序。
设置服务
$i = new Injector(); // setting using the string class name $i->nject('foo', 'StdClass'); // setting the service directly $i->nject('foo', new \StdClass()); // setting the service using a closure factory $i->nject('foo', function() { return new \StdClass(); }); // setting the service using array configuration $i->nject('foo', ['StdClass']); // setting the service using an object that implements ServiceFactory class StdClassFactory implements ServiceFactory { public function createService(Injector $i) { return new \StdClass(); } } $i->nject('foo', new StdClassFactory()); // each method listed above is identical
检查服务是否存在
// false $i->has('foo'); $i->nject('foo', new \StdClass()); // true $i->has('foo');
获取服务
// assuming the configuration from 'Setting Services' above // the following retrieves the 'foo' service $foo = $i->nvoke('foo');
数组配置
数组配置提供了一些额外的选项,使其非常灵活。
构造函数注入
$i = new Injector(); // 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->nject('foo', ['Foo', ['I am a string', 1]]);
设置器注入
$i = new Injector(); // 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->nject('foo', ['Foo',[1],['setString' => 'string']]);
引用其他服务和参数
Spiffy\Inject 的数组配置包括在字符串前面加上特殊字符来引用其他服务的能力。默认情况下,您使用 @
符号引用服务,使用 $
符号引用参数。您可以使用 setServiceIdentifier
和 setParamIdentifier
方法分别修改这些。
$i = new Injector(); 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->nject('bar', new \Bar()); // create the foo service using array configuration and parameter/service references $i->nject('foo', ['Foo',['@bar'],['setBaz' => '$baz']]); // the resulting Foo service would have '$this->bar' set to '\Bar' and '$this->baz' set to 'boogly'
服务工厂
如果您从数组配置中返回 Spiffy\Inject\ServiceFactory
的实例,它将自动为您创建实例并返回该实例。这使得您可以轻松地将参数注入服务工厂,并在重用工厂的同时返回所需的实例。
class ArrayObjectFactory implements ServiceFactory { private $defaults; public function __construct(array $defaults) { $this->defaults = $defaults; } public function createService(Injector $i) { return new \ArrayObject($this->defaults); } } $i = new Injector(); // Result is an ArrayObject and *not* an ArrayObjectFactory $i->nject('ArrayObject', ['ArrayObjectFactory', [['foo' => 'bar']]);
与生成器结合的注解
SpiffyInject 提供了您可以用来辅助创建服务配置的注解。
namespace Spiffy\Inject\TestAsset; use Spiffy\Inject\Annotation as Injector; /** * @Injector\Component("inject.test-asset.annotated-component") */ class AnnotatedComponent { /** @var \StdClass */ private $foo; /** @var array */ private $params; /** @var array */ private $setter; /** * @Injector\Method({@Injector\Inject("foo"), @Injector\Param("params")}) */ public function __construct(\StdClass $foo, array $params) { $this->foo = $foo; $this->params = $params; } /** * @Injector\Method({@Injector\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 Spiffy\Inject\Generator; use Spiffy\Inject\Metadata; $mdf = new Metadata\MetadataFactory(); $md = $mdf->getMetadataForClass('Spiffy\Inject\TestAsset\AnnotatedComponent'); $generator = new Generator\ArrayGenerator(); $i = new Injector(); $i->nject($md->getName(), $generator->generate($md)); $i->nvoke($md->getName()); // instanceof Spiffy\Inject\TestAsset\AnnotatedComponent
读取文件、创建元数据和生成配置是一个 繁重 的过程,并不适用于生产。您应该缓存生成结果,并在生产中使用缓存。
装饰您的服务
有时您可能想要覆盖 DI 容器中设置的服务,而不修改原始配置。Spiffy\Inject 通过提供两种类型的装饰器来处理这个问题。
装饰
decorate
方法允许您在返回服务之前对其应用任何修改。装饰器闭包接收注入器和服务作为参数。
$i = new Injector(); $i->nject('foo', new \StdClass()); $i->decorate('foo', function(Injector $i, \StdClass $foo) { $foo->bar = 'bar'; $foo->baz = 'baz'; }); $foo = $i->nvoke('foo'); // output is 'barbaz'; echo $foo->bar; echo $foo->baz;
或者,您可以提供一个实现 Spiffy\Inject\ServiceDecorator
接口的类的实例。
namespace My; use Spiffy\Inject\Injector; use Spiffy\Inject\ServiceDecorator; class FooDecorator implements ServiceDecorator { public function decorateService(Injector $i, $instance) { $instance->bar = 'bar'; $instance->baz = 'baz'; } }
use Spiffy\Inject\Injector; $i = new Injector(); $i->nject('foo', new \StdClass()); $i->decorate('foo', new \My\FooDecorator()); $foo = $i->nvoke('foo'); // output is 'barbaz'; echo $foo->bar; echo $foo->baz;
包装
wrap
方法比 decorate
更强大。包装让您可以完全改变创建的对象,或者完全绕过原始配置。包装闭包接收三个参数:注入器、服务名称和创建服务的可调用对象。
$i = new Injector(); $i->nject('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(Injector $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(Injector $i, $name, $callable) { return new \ArrayObject(); }); // output is 'ArrayObject' echo get_class($i->nvoke('foo'));
或者,您可以提供一个实现 Spiffy\Inject\ServiceWrapper
接口的类的实例。
namespace My; use Spiffy\Inject\Injector; use Spiffy\Inject\ServiceWrapper; class FooWrapper implements ServiceWrapper { public function wrapService(Injector $i, $name, $callable) { $foo = $callable(); $foo->bar = 'bar'; $foo->name = $name; return $foo; } }
use Spiffy\Inject\Injector; $i = new Injector(); $i->nject('foo', new \StdClass()); $i->wrap('foo', new \My\FooWrapper()); $foo = $i->nvoke('foo'); echo $foo->bar; // outputs 'bar' echo $foo->name; // outputs 'foo'
为什么使用 nvoke 和 nject?
因为这看起来非常可爱,这就是原因!不过,如果您愿意的话,您也可以使用 set()
代替 nject()
,使用 get()
代替 nvoke()
。