spiffy/spiffy-inject

此包已被 弃用,不再维护。未建议替代包。

Spiffy\Inject 是一个轻量级、HHVM 兼容、依赖轻量级的依赖注入(DI)容器。

1.0.0-alpha 2014-07-17 01:58 UTC

This package is not auto-updated.

Last update: 2022-02-01 12:33:17 UTC


README

Build Status Code Coverage Scrutinizer Code Quality

安装

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 的数组配置包括在字符串前面加上特殊字符来引用其他服务的能力。默认情况下,您使用 @ 符号引用服务,使用 $ 符号引用参数。您可以使用 setServiceIdentifiersetParamIdentifier 方法分别修改这些。

$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()