neunerlei/container-delegate

一个具有自动绑定、自动缓存和懒加载服务的PSR-11容器实现

0.0.1 2020-02-27 13:30 UTC

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