php - 如何使用特定实例覆盖 DI 容器依赖项?

标签 php dependency-injection php-di

假设我们有一个 Request 对象和一个 Controller 对象。 Controller 对象是用 Request 对象构建的,如下所示:

abstract class Controller {

    public $request;

    public function __construct(Request $request)
    {
        $this->request = $request;
    }

}

如您所见,这是一个抽象类,因此实际上将构造Controller 的子类。让我们想象一下代码是这样的:

// Instantiate the request.
$request = new Request($params); 

// Instantiate the registration controller.
$controller = new RegistrationController($request);

现在假设我们向 RegistrationController 添加依赖项,如下所示:

class RegistrationController extends Controller {

    private $user_repo;

    public function __construct(Request $request, UserRepo $user_repo)
    {
        parent::__construct($request);

        $this->user_repo = $user_repo;
    }

}

此时,我想做的是引入一个依赖注入(inject)容器,通过构造函数自动注入(inject)依赖。为此,我一直在使用 PHP-DI .通常,这会是这样的:

// Instantiate the registration controller.
$controller = $container->get('RegistrationController');

然后,这将使用 Request 实例和 UserRepo 实例实例化 RegistrationController。由于反射,它知道用这些对象构造,但如果我愿意,我也可以通过配置文件覆盖它。

问题在于 Request 对象采用动态参数。我需要传递给 RegistrationControllerRequest 对象是一个特定的实例,一个我刚刚创建的实例。

我基本上希望能够说:“给我一个这个类的实例,其中注入(inject)了它的所有依赖项,但是对于特定参数,传入这个特定实例”。

我查看了是否 PHP-DI (以及一大堆其他 DI 容器)支持这种针对特定参数的“覆盖”,但到目前为止我找不到任何东西。

我想知道的是:

  • 是否有可以执行此操作的 DI 容器?
  • 是否有另一种方法可以使类保持干净(我不想使用注释或任何其他会添加我用作依赖项的容器的方法)?

最佳答案

这里是 PHP-DI 作者。

所以有两件事,首先我会回答你的问题:

PHP-DI 的容器提供了一个make 方法,你可以这样使用:

$request = new Request($myParameters);

$controller = $container->make('RegistrationController', array(
    'request' => $request
));

如您所见,此 make 方法与 get 相同,只是它总是会创建一个新实例(这是您在这里想要的,因为您可能不知道' 想要重用 Controller 的现有实例)并且它将采用您提供的额外参数。这是 工厂 的行为,容器的好处是可以找到您未提供的其余参数。

这就是我要在这里使用的内容。你也可以这样做:

$request = new Request($myParameters);
$container->set('Request', $request);
$controller = $container->get('RegistrationController');

但这不太干净,因为它会在容器中设置请求,这是不好的(下面解释)。


第二件事是请求对象并不是真正的服务,它是一个“值对象”。容器通常应该只包含服务对象,即无状态的对象。

这样做的原因是假设您在同一个进程中有多个请求(例如,您执行“子请求”,或者您有一个处理多个请求的工作进程,等等):您的服务将一团糟因为他们会注入(inject)请求并且请求对象可能会更改。

Symfony 就是这样做的,并意识到这是一个错误。自 Symfony 2.4 以来,他们已弃用容器中的请求:http://symfony.com/blog/new-in-symfony-2-4-the-request-stack

无论如何,我建议您不要在容器中放置 Request 对象,而是使用我向您展示的 make 方法。

或者,更好的是,我会这样做:

class RegistrationController extends Controller {

    private $user_repo;

    public function __construct(UserRepo $user_repo)
    {
        $this->user_repo = $user_repo;
    }

    public function userListAction(Request $request)
    {
        // ...
    }

}


// in the front controller
$controller = $container->make('RegistrationController');

// This is what the router should do:
$action = ... // e.g. 'userListAction'
$controller->$action(new Request($myParameters));

(这是 Symfony 和其他框架顺便做的事情)

关于php - 如何使用特定实例覆盖 DI 容器依赖项?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23409203/

相关文章:

php - 无法修改 header 信息 - header 已发送

php - fatal error : Cannot use isset, 使用空表达式

web-services - Grails 打包和命名约定

php - 为什么在 PHP 框架中使用依赖注入(inject)组件

PHP传递mysql查询结果显示在另一个页面

javascript - 我想提交一个表单,但在提交之前我想从模态表单中获取一个值

具有动态参数的 Angular Factory Provider

java - 提供者可以在不同的实例之间进行选择吗

PHP DI 和类型转换接口(interface)

PHP-DI:注入(inject)构造函数的接口(interface)无法正确解析