marcosh/fundic

该包已被废弃,不再维护。未建议替代包。
该包的最新版本(0.1.0)没有可用的许可证信息。

0.1.0 2017-08-30 17:18 UTC

This package is auto-updated.

Last update: 2021-11-16 15:30:36 UTC


README

Latest Stable Version Build Status Code Climate Coverage Status Codacy Badge Scrutinizer Code Quality

PHP的纯函数式依赖注入容器

安装

使用Composerfundic作为依赖项添加到您的项目中

composer require marcosh/fundic

测试

使用以下命令运行测试

php vendor\bin\phpunit

理论

本质上,依赖注入容器就是一个能够根据键获取相应工作对象的组件。

实现这一目标的一种常见方法是配置如何构造相应的对象,甚至可以根据类名依赖自动装配。

另一种方法是把依赖注入容器看作是一个映射,将键关联到一个工厂,该工厂根据键构建对象,可能还会递归地使用容器本身。

fundic将这个想法贯彻到底,实际上,如果你看本质,它只是一个将键关联到工厂的映射,形式如下

interface ValueFactory
{
    /**
     * @return mixed
     */
    public function __invoke(ContainerInterface $container, string $name);
}

基本用法

实例化

实际上fundic提供了两个容器

  • Psr11Container实现了PSR-11规范

    ;
  • TypedContainer,一个更安全的版本

您可以通过调用它们的静态方法create来创建它们的新实例

$container = TypedContainer::create();

这将创建一个空的容器实例,您可以按需填充。

配置

一个空的容器并不真正有用。您可以使用以下方法向容器添加新条目

$container = $container->add($key, $factory);

其中$key是一个字符串,$factoryValueFactory的实例。

请注意,$container是不可变的,并且add返回一个新的实例。因此,您必须记得将结果赋给变量。

检索

两者Psr11ContainerTypedContainer都实现了Psr\Container\ContainerInterface(即使TypedContainer只是遵守方法的签名,而不符合注解),因此您可以使用以下方式查询它们

// create a new empty container
$container = Psr11Container::create();

// instructs the container on how to build the object
// associated with the provided key
$container = $container->add('foo', $factory);

$container->has('foo'); // returns true

$object = $container->get('foo'); // retrieves the object associated to the key

容器返回值和异常

Psr11Container作为一个标准容器工作,完全符合PSR-11规范。因此,对get方法的任何调用都将返回预期的对象。

Psr11Container::get 如果找不到键或构建返回值时发生错误,也会抛出异常。

另一方面,TypedContainer 本身并不完全符合 PSR-11 的规范,特别是在 get 的返回值和异常处理方面。

为了使代码纯函数式并避免不必要的副作用,get 的结果不是直接的对象,而是一个可能具有以下值的 Result 数据结构:

  • Just,它是一个包装所需值的包装器,可以使用 Just::get 获取;
  • NotFound,表示这样的条目不在容器中;
  • Exception,表示在调用工厂时发生了错误;

以上只是值,你可以对它们做任何想做的事情(立即响应它们,传递它们等 ...)

工厂

一些工厂被提供以简化 Fundic\Factory\ValueFactory 实例的创建。

不言而喻,你可以提供自己的特定实现 Fundic\Factory\ValueFactory

常量工厂

如果你需要将常量值存储在容器中,无论是原生数据类型、数组还是对象,你可以使用如下所示的 Fundic\Factory\ConstantFactory

$value = // your constant that needs to be stored in the container

$container->add('foo', new ConstantFactory($value));

$container->get('foo');

ConstanceFactory 类将值包装在 Fundic\Factory\ValueFactory 中,它总是返回提供的值。

类名工厂

如果你需要从容器中检索一个没有(或只有可选)依赖的对象,你可以使用 Fundic\Factory\ClassNameFactory,只需传递类名,如下所示

class Foo { ... } // no non optional dependencies in the constructor

$container->add(Foo::class, new ClassNameFactory(Foo::class));

$container->get(Foo::class);

ClassNameFactory 只会调用提供的类名并返回该类的新实例。

可调用工厂

我们提供的最通用的 Fundic\Factory\ValueFactory 实现是 Fundic\Factory\CallableFactory,它只是将任何与 ValueFactory 具有相同签名的可调用包装起来(即它需要输入参数为 Psr\Container\ContainerInterface 和一个 string,即类名)。例如

$callable = function (ContainerInterface $container, string $name) { ... };

$container->add(Foo::class, new CallableFactory($callable));

$container->get(Foo::class);

工厂装饰器

有时你想要修改从容器中构建和检索特定键的方式,而不修改提供的工厂。

允许这种可能性的简单机制是使用装饰器模式。这意味着我们将我们的工厂包装在另一个工厂中,该工厂接收第一个工厂作为构造函数参数。在函数式术语中,假设我们有一个为特定 foo 键的工厂 f(即 f : (ContainerInterface, string) -> foo);我们做的是将整个 f 传递给 g,其中 g(f) : (ContainerInterface, string) -> foo

这允许我们在返回内部工厂的结果之前修改它,甚至避免调用内部工厂并返回新构建的值。

你可以提供自己的工厂装饰器以创建复杂的对象创建工作流。装饰器具有高度的组合性,因此你可以使用多个装饰器来构建单个对象。

库提供了常用的一些装饰器。

记忆化

如果您希望每次向容器请求特定键时都检索到该对象的同一实例,您需要将第一次获取的结果存储在某个地方,并在每次都返回该结果,而不是创建新的实例。

这正是记忆化装饰器所做的事情。第一次它会调用内部工厂来构建对象,然后总是返回那个特定的实例。

class Foo { ... }

$container->add(Foo::class, new Memoize(new ClassNameFactory(Foo::class)));

$container->get(Foo::class); // a new instance of Foo is built and returned
$container->get(Foo::class); // the same instance of Foo is returned

代理

如果一个对象的构建过程非常耗时,您可能希望将其推迟到最后一刻,即您确定需要该特定对象的实例时。

为此,您可以代理您的对象,并最初返回一个包装器,该包装器只有在调用它的方法时才会构建实际的对象。

您可以使用以下方法使用代理装饰器来实现这一点:

class Foo { ... } // class which is heavy to build

$container->add(Foo::class, new Proxy(new ClassNameFactory(Foo::class)));

$foo = $container->get(Foo::class); // returns a proxy

$foo->bar(); // here we really instantiates Foo and call the bar method on it