marcosh / fundic
Requires
- php: ^7.1
- psr/container: ^1.0
Requires (Dev)
- ocramius/proxy-manager: ^2.1
- phpspec/prophecy: ^1.7
- phpunit/phpunit: ^6.2
Suggests
- ocramius/proxy-manager: Allows you to use the Proxy factory decorator
Provides
This package is auto-updated.
Last update: 2021-11-16 15:30:36 UTC
README
PHP的纯函数式依赖注入容器
安装
使用Composer将fundic
作为依赖项添加到您的项目中
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
是一个字符串,$factory
是ValueFactory
的实例。
请注意,$container
是不可变的,并且add
返回一个新的实例。因此,您必须记得将结果赋给变量。
检索
两者Psr11Container
和TypedContainer
都实现了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