假设我们有一个 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
对象采用动态参数。我需要传递给 RegistrationController
的 Request
对象是一个特定的实例,一个我刚刚创建的实例。
我基本上希望能够说:“给我一个这个类的实例,其中注入(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/