neunerlei / container-delegate
一个具有自动绑定、自动缓存和懒加载服务的PSR-11容器实现
Requires
- php: ^7.3
- neunerlei/container-autowiring-declaration: ^1.0
- psr/container: ^1.0
Requires (Dev)
- league/container: ^3.3
- neunerlei/dbg: ^1.8
- phpunit/phpunit: ^9.0
This package is auto-updated.
Last update: 2024-09-28 00:37:41 UTC
README
本包包含一个兼容PSR-11的容器实现,但有一些变化。这个容器被设计为“代理”容器,这意味着你应该将其用作手动配置服务的后备。你可以在这里了解更多关于容器代理的信息。
容器没有任何配置选项,依赖于基于接口的自动绑定约定。如果你的服务需要参数或硬编码的实例,请使用复合容器,这不是此实现所期望的。
除了“零配置”方法之外,该实现还包含编译的工厂、自动绑定(当然)和使用懒加载代理的懒加载服务注入。
在我的项目中,我通常使用league container,它有一个非常棒的配置接口和大量选项。当然,它支持代理容器。
安装
使用composer安装此包
composer require neunerlei/container-delegate
创建容器
只需创建一个新的容器实例,你就可以开始了
use Neunerlei\ContainerDelegate\Container; $container = new Container();
获取实例
容器使用PHP的反射功能来自动绑定参数的依赖。在这个例子中,类B需要类A,当创建B的实例时,它将被自动注入。
use Neunerlei\ContainerDelegate\Container; class A {} class B { public $a; public function __construct(A $a) {$this->a = $a;} } $container = new Container(); $i = $container->get(B::class);
通过接口获取实例
许多容器实现都依赖于开发者将接口与实现映射起来。此容器实现将尝试猜测它应该查找哪个类。如果你的接口名称以“Interface”结尾,容器将自动尝试实例化一个同名但无“Interface”部分的类。
use Neunerlei\ContainerDelegate\Container; interface AInterface {} class A implements AInterface { } $container = new Container(); $i = $container->get(AInterface::class); var_dump($i instanceof A);
处理默认参数
如果你的实现允许使用不可直接注入的附加参数,容器将使用默认值(如果存在)。
use Neunerlei\ContainerDelegate\Container; class A { public function __construct(string $foo = "bar", ?Container $container = null) { var_dump($foo === "bar"); var_dump($container instanceof Container); } } $container = new Container(); $i = $container->get(A::class);
总是返回相同的实例(单例)
某些服务应该只实例化一次,但为多个服务分发(例如数据库连接)。虽然league container默认支持此功能,但其他实现则不支持。因此,容器自己实现了此功能,但会尝试使用提供单例功能的复合容器。
要在应用程序中定义单例服务实例,您可以将其添加到SingletonInterface中。之后,该服务将只实例化一次。
use Neunerlei\ContainerAutoWiringDeclaration\SingletonInterface; use Neunerlei\ContainerDelegate\Container; class A implements SingletonInterface { } $container = new Container(); $i = $container->get(A::class); var_dump($i instanceof A); var_dump($i === $container->get(A::class));
使用注入方法
如果您想在__construct()方法执行后注入额外的服务,您通常需要使用容器配置来绑定“set”方法。通过使用InjectableInterface,容器将自动扫描所有以“inject”开头的公共、非静态方法,并提供它们的参数。您可以使用单个注入方法注入多个实例。
use Neunerlei\ContainerAutoWiringDeclaration\InjectableInterface; use Neunerlei\ContainerDelegate\Container; class A {} class B implements InjectableInterface { public $a; public function injectA(A $a){ $this->a = $a; } } $container = new Container(); $i = $container->get(B::class); var_dump($i->a instanceof A);
延迟加载
有时,您的对象中可能需要的服务可能仅在特定情况下才需要。如果您有一个体积很小的服务,它不会产生开销,这在一般情况下是可以的。另一方面,如果您有一个体积较大的服务,可能需要一些时间来设置(例如数据库连接),您应该考虑使用延迟加载代理。
此功能将一个小型包装器而不是真实实现注入到您的请求实例中。只有在请求代理的某个功能时,才会创建真实实例,这意味着您可以节省大量的处理时间。
容器实现内置了对延迟参数注入的支持。任何类型为接口且名称以“lazy”开头属性都将注入为延迟加载对象。
use Neunerlei\ContainerDelegate\Container; interface AInterface { public function foo(); } class A implements AInterface { public function foo(){ return "foo!"; } } class B { public $a; public function __construct(AInterface $lazyA){ $this->a = $lazyA; } } $container = new Container(); $i = $container->get(B::class); var_dump($i->a instanceof AnInterface); var_dump($i->a instanceof A); // This is FALSE! var_dump($i->a->foo());
编译工厂
为了避免每次需要对象时都进行反射带来的开销,实现可以选择将工厂编译成PHP代码。
编译是在运行时进行的,并且仅针对至少被请求过一次的对象,这意味着您的容器不知道它应该能够实例化未来每个类或接口。这意味着:每当您忘记将服务类添加到您的配置.yml配置中时,“ServiceNotFoundException”就会再见。
为了提供此功能,只有工厂被编译成PHP代码并存储在一个文件中,每次容器“学习”它需要实例化的新类时,该文件都会被扩展。这为您提供了速度和动态查找,而不必烦恼配置。
如果您想使用编译功能(默认情况下是禁用的),您必须提供文件系统上的一个可写目录的绝对路径,然后容器将处理其余部分。要重置缓存的/编译的工厂,只需删除存储目录中的文件即可。
use Neunerlei\ContainerDelegate\Container; class A {} class B { public $a; public function __construct(A $a) {$this->a = $a;} } $container = new Container("/path/to/cache/directory"); $i = $container->get(B::class);
作为代理的使用(league container)
上述所有示例都使用了独立实现,但如上所述,这并不是预期的用例。它设计为与支持手动接口/类映射、参数或手动工厂的其他容器一起工作。
为了使用容器,我添加了一个简单的包装器,该包装器将递归对象解析和单例对象查找委托给league容器。
use Neunerlei\ContainerDelegate\Adapter\LeagueContainerAdapter; class A {} class B { public $a; public function __construct(A $a) {$this->a = $a;} } // Create the compound container $container = new \League\Container\Container(); // Create and register the delegated container $delegate = new LeagueContainerAdapter("/path/to/cache/directory"); $container->delegate($delegate); // Make sure the delegate container knows it's parent $delegate->setContainer($container); $delegate->setLeagueContainer($container); // Done, start requesting your stuff :) $i = $container->get(B::class);
Postcardware
您可以使用此包,但如果它进入了您的生产环境,我非常感谢您从您的家乡寄给我一张明信片,说明您正在使用我们的哪些包。
您可以在这里找到我的地址。
谢谢 :D