xtompie/container

1.14.0 2024-09-23 19:47 UTC

This package is auto-updated.

Last update: 2024-09-24 19:45:13 UTC


README

Container 类为 PHP 应用程序中管理依赖关系和类实例提供了基本功能。它允许您

  • 检索一个唯一的共享容器实例(单例模式)。
  • 自动使用反射解析类实例,包括处理构造函数依赖项。
  • 将接口绑定到具体类,以便进行依赖注入。
  • 将一个类别名为另一个类,允许不同类实现可互换使用。
  • 管理共享(单例)实例,确保多次使用时返回相同的实例。
  • 使用服务提供程序来控制如何解析特定服务。
  • 配置瞬态服务,每次访问时返回不同的实例。

要求

  • PHP >= 8

安装

使用 composer

composer require xtompie/container

使用方法

单例

Container 类实现了单例模式,确保整个应用程序使用相同的容器实例。这对于管理全局服务和依赖项很有用。

use Xtompie\Container\Container;

$container1 = Container::container();
$container2 = Container::container();

var_dump($container1 === $container2); // true

获取

Container 会自动使用 PHP 的反射功能解析类实例。它检查类构造函数以确定所需的依赖项,并自动解析它们。

use Xtompie\Container\Container;

class Foo
{
    // Foo class logic
}

$container = new Container();
$foo = $container->get(Foo::class);

var_dump($foo instanceof Foo); // true

解析

Container 类提供了 resolve 方法,允许您在解析依赖项时直接传递特定值到类的构造函数。当您想覆盖或注入容器无法自动解析的某些参数时,这很有用。

与可以返回共享实例的 get 方法不同,resolve 方法始终返回类的新的实例,即使多次调用。这确保了瞬态行为并避免了实例之间的共享状态。

use Xtompie\Container\Container;

class Bar
{
    public function __construct(
        public Foo $foo,
        public ?string $qux = null
    ) {
    }
}

$container = new Container();
$values = ['qux' => 'quxx'];
$bar = $container->resolve(Bar::class, $values);

var_dump($bar instanceof Bar); // true
var_dump($bar->qux); // 'quxx'

在此示例中,resolve() 方法手动提供值 quxxqux 参数,而容器自动解析 Foo 依赖项。每次调用 resolve 都创建一个新的 Bar 实例。

依赖项

如果类在其构造函数中需要依赖项,则 Container 也会解析它们。它会检查构造函数并创建所需的类的实例。

use Xtompie\Container\Container;

class Bar
{
    public function __construct(
        public Foo $foo,
        public ?string $qux = null
    ) {
    }
}

$container = new Container();
$bar = $container->get(Bar::class);

var_dump($bar instanceof Bar); // true
var_dump($bar->foo instanceof Foo); // true
var_dump($bar->qux); // null

绑定

您可以将接口绑定到具体类,使容器在请求接口时解析正确的实现。

use Xtompie\Container\Container;

interface FooInterface {}

class Foo implements FooInterface {}

$container = new Container();
$container->bind(FooInterface::class, Foo::class);

$fooInterface = $container->get(FooInterface::class);
var_dump($fooInterface instanceof Foo); // true

别称

容器允许您将一个类别名为另一个类,使不同类实现可互换使用。

use Xtompie\Container\Container;

class Foo2 {}

$container = new Container();
$container->bind(Foo::class, Foo2::class);

$foo = $container->get(Foo::class);
var_dump($foo instanceof Foo2); // true

共享

容器管理共享服务,确保每次检索服务时返回相同的实例。这对于管理单例很有用。

use Xtompie\Container\Container;

$container = new Container();

$instance1 = $container->get(Foo::class);
$instance2 = $container->get(Foo::class);

var_dump($instance1 === $instance2); // true

瞬态

瞬态服务每次访问时都返回一个新的实例。通过将类标记为瞬态,Container 确保每次访问都返回不同的实例。

您可以通过使用 transient() 方法手动定义服务为瞬态,或者实现 Transient 接口以自动将类标记为瞬态。

use Xtompie\Container\Container;

$container = new Container();
$container->transient(Foo::class);

$instance1 = $container->get(Foo::class);
$instance2 = $container->get(Foo::class);

var_dump($instance1 === $instance2); // false

或者,如果服务类实现了 Transient 接口,则 Container 将自动将其视为瞬态,这意味着每次检索都会返回一个新的实例。

use Xtompie\Container\Container;
use Xtompie\Container\Transient;

class Foo implements Transient {}

$container = new Container();
$instance1 = $container->get(Foo::class);
$instance2 = $container->get(Foo::class);

var_dump($instance1 === $instance2); // false

在两种情况下,这都确保了瞬态对象总是新的实例,而不是共享服务,每次都返回相同的对象。

提供者

提供者可用于更复杂的服务解析。提供者是一个实现了 Provider 接口的类,这允许您控制容器内特定服务的创建或管理方式。提供者将额外的逻辑注入到解析过程中。

这是 Provider 接口

namespace Xtompie\Container;

interface Provider
{
    public static function provide(string $abstract, Container $container): mixed;
}

要使用提供者,首先创建一个实现了 Provider 接口的类。应返回服务实例的 provide() 方法,它将在需要时由容器调用。

提供者类示例

use Xtompie\Container\Container;
use Xtompie\Container\Provider;

class Baz {}

class BazProvider implements Provider
{
    public static function provide(string $abstract, Container $container): mixed
    {
        return new Baz();
    }
}

然后您可以向容器注册提供者并检索服务

use Xtompie\Container\Container;

$container = new Container();
$container->provider('Quux', BazProvider::class);

$quux = $container->get('Quux');
var_dump($quux instanceof Baz); // true

这种方法允许在容器内解析服务时进行更复杂或条件逻辑。

自提供服务

一个服务可以通过实现 Provider 接口来提供自己的提供者。这允许一个类自动为容器提供实例,而无需在容器中注册提供者。

如果一个类实现了 Provider 接口,容器会识别并自动将其用作提供者在创建实例时使用。

use Xtompie\Container\Container;
use Xtompie\Container\Provider;

class Qux implements Provider
{
    public static function provide(string $abstract, Container $container): object
    {
        return new Qux(val: 42);
    }

    public function __construct(
        public int $val,
    ) {
    }
}

$container = new Container();
$qux = $container->get(Qux::class);

var_dump($qux instanceof Qux); // true
var_dump($qux->val); // 42

在此示例中,Qux 类实现了 Provider 接口,这意味着容器将自动在类中找到提供者并使用它来创建实例。这样就无需手动在容器中注册提供者。

多绑定

如果您绑定多个类,容器将解析最具体的类。这对于管理多个抽象级别或类继承很有用。

use Xtompie\Container\Container;

$container = new Container();
$container->bind(FooInterface::class, Foo::class);
$container->bind(Foo::class, Foo2::class);

$fooInterface = $container->get(FooInterface::class);
var_dump($fooInterface instanceof Foo2); // true

调用

容器 类包含一个 call 方法,允许您使用自动依赖注入调用任何回调。此方法适用于闭包(匿名函数)、静态类方法和实例方法。容器自动解析并注入回调所需的依赖项。

使用闭包(匿名函数)

use Xtompie\Container\Container;

class Foo
{
    public function method(): string
    {
        return 'Foo';
    }
}

$container = new Container();

$result = $container->call(function(Foo $foo) {
    return $foo->method();  // 'Foo'
});

echo $result; // Foo

在此示例中,容器自动解析 Foo 依赖项并将其注入到闭包中。

使用静态类方法

use Xtompie\Container\Container;

class Foo
{
    public function method(): string
    {
        return 'Foo';
    }
}

class Call
{
    public static function f1(Foo $foo): string
    {
        return $foo->method();
    }
}

$container = new Container();

$result = $container->call([Call::class, 'f1']);

echo $result; // Foo

对于静态类方法,容器在调用静态方法之前解析依赖项并将它们注入其中。

使用实例方法

use Xtompie\Container\Container;

class Foo
{
    public function method(): string
    {
        return 'Foo';
    }
}

class Call
{
    public function f2(Foo $foo): string
    {
        return $foo->method();
    }
}

$container = new Container();
$call = new Call();

$result = $container->call([$call, 'f2']);

echo $result; // Foo

在这里,容器将 Foo 依赖项注入到实例方法中,然后在该提供对象上调用该方法。

带值调用

Container 类包含一个 call 方法,允许您使用自动依赖注入调用可调用的。此外,您还可以传递自定义值以覆盖容器的自动解析。

use Xtompie\Container\Container;

class Foo
{
    public function method(): string
    {
        return 'Foo';
    }
}

class Bar
{
    public function execute(Foo $foo, string $name): string
    {
        return $foo->method() . ' ' . $name;
    }
}

$container = new Container();
$customValues = [
    'name' => 'CustomName',
];

$bar = new Bar();
$result = $container->call([$bar, 'execute'], $customValues);

echo $result; // Foo CustomName

在此示例中

  • Container 解析一个 Foo 实例。
  • 我们传递一个自定义字符串值 'CustomName' 作为 $name 参数,这覆盖了容器解析它的需求。

callArgs

callArgs 方法允许您检索调用给定可调用(函数、方法等)所需的自定义值,并允许使用自动依赖解析。它会检查可调用参数并解析必要的依赖项,可选地允许使用自定义值或解析器。

use Xtompie\Container\Container;

class Foo
{
    public function method(): string
    {
        return 'Foo';
    }
}

$container = new Container();
$args = $container->callArgs(function(Foo $foo) {
    return $foo->method();
});

var_dump($args); // Array with resolved arguments, e.g., [Foo instance]

自定义值

您可以将自定义值传递给 callArgs 以覆盖容器的默认解析行为。

$customValues = ['foo' => new Foo2()];
$args = $container->callArgs(function(Foo $foo) {
    return $foo->method();
}, $customValues);

var_dump($args); // Custom values take precedence over container resolution

自定义参数解析器($arg

callArgs 方法还接受一个可选的 $arg 可调用,在回退到容器解析或自定义值之前解析参数。如果 $arg 可调用返回 null,则解析回退到容器或自定义值。

$customResolver = function (\ReflectionParameter $parameter) {
    if ($parameter->getType()?->getName() === Foo::class) {
        return new Foo2();
    }
    return null;
};

$args = $container->callArgs(function(Foo $foo) {
    return $foo->method();
}, [], $customResolver);

var_dump($args[0] instanceof Foo2); // true

在这种情况下,自定义解析器具有优先权。如果解析器未处理参数(返回 null),则使用容器或提供的自定义值作为回退。

要点

  • $arg 解析器具有最高优先级。
  • 接下来检查自定义值。
  • 如果没有找到匹配项,容器将自动解析依赖项。

扩展

您可以通过扩展 Container 类来添加自定义功能或特定行为以适应您的应用程序。

use Xtompie\Container\Container;

class CustomContainer extends Container
{
    // Custom methods or properties
}

这允许您根据项目特定需求定制容器,在依赖项解析过程中添加自定义逻辑或行为。