注意:从2.8版开始,Symfony提供了
autowire: true
用于服务配置,从版本3.3开始,Symfony提供了alias
(而不是autowire_types
)将一个具体对象别名化为一个接口,以便自动依赖注入控制器作为服务'。还有一个捆绑包允许自动装配控制器'动作'方法,虽然我已经远离这一点,并且更多地关注ADR模式的变化(基本上,这是一个单一的动作类,具有接口方法而不是推动负载单个类中的动作方法,最终导致建筑噩梦)。实际上,这就是我多年来一直在寻找的东西,现在不再需要“挂钩”了。一个体面的递归依赖注入器(auryn),因为框架现在处理它应该有四年前的东西。我将在这里留下这个答案,以防有人想跟踪我所做的步骤,看看内核的工作原理以及这个级别的一些选项。
注意:虽然这个问题主要针对Symfony 3,但它也应该与Symfony 2的用户相关,因为内核逻辑似乎没有太大变化。
我想改变在Symfony中实例化控制器的方式。其实例化的逻辑目前位于HttpKernel::handle,更具体地说,HttpKernel::handleRaw。我希望用我自己的注入器代替call_user_func_array($controller, $arguments)
执行该特定行。
到目前为止我尝试过的选项:
HttpKernel::handle
,然后通过symfony http_kernel:
class: AppBundle\HttpKernel
arguments: ['@event_dispatcher', '@controller_resolver', '@request_stack']
这样做的缺点是,因为handleRaw
是私有的,所以我无法在没有黑客反思的情况下对其进行扩展,因此我必须复制并粘贴一大堆代码。
controller_resolver:
class: AppBundle\ControllerResolver
arguments: []
这是我的一个根本性的误解,所以我想我会在这里记录下来。解析器的工作是解析 where 以找到控制器作为可调用对象。它还没有被实际调用过。我对Symfony如何从routes.yml
获取路由非常满意,并找出了调用控制器作为可调用的类和方法。
kernel.request
kernel.request:
class: MyCustomRequestListener
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 33 /** Important, we'll get to why in a minute **/ }
看一下Http Kernel Component Documentation,我们可以看到它具有以下典型目的:
要向请求添加更多信息,初始化系统的各个部分,或者尽可能返回响应(例如,拒绝访问的安全层)。
我认为通过创建一个新的侦听器,使用我的自定义注入器来创建我的控制器,然后在该侦听器中返回响应,将绕过实例化控制器的其余代码。 这就是我想要的!但是这有一个重大缺陷:
Symfony Profiler没有显示或任何这些东西,它只是我的回应而且就是这样。死了。我发现我可以将优先级从31切换到33并让它在我的代码和Symfonys之间切换,我相信这是因为路由器监听器priority。我觉得我在这里走错了路。
不,这允许我更改call_user_func_array()
将调用的可调用,而不是实际实例化控制器的方式,这是我的目标。
我已经记录了我的想法,但我已经出去了。我怎样才能实现以下目标?
call_user_func_array()
这是一种血腥的私有方法(感谢Symfony)为什么我想要这样做?
控制器可以针对不同的情况使用许多不同的方法,并且每个方法都应该能够为其单独需要的内容输入提示,而不是让构造函数接受所有的操作,根据正在执行的控制器方法,其中一些甚至可能无法使用。控制人员 但他们就是这样。
我想用我自己的递归自动装配注入器替换控制器的创建方式,以及它们的执行方式,再次通过我的注入器进行递归内省,因为默认的Symfony包似乎没有这个功能。即使使用最新的" autowire" Symfony 2.8 +中的服务选项。
答案 0 :(得分:1)
控制器解析器实际上做了两件事。首先是获得控制器。第二个是获取给定操作的参数列表。
$arguments = $this->resolver->getArguments($request, $controller);
$response = call_user_func_array($controller, $arguments);
您可以覆盖getArguments方法以实现特殊的“操作方法注入”功能。您只需要确定action方法需要哪些参数并返回它们的数组。
基于不同的问题,我也认为您可能误解了自动装配功能。 Autowire实际上只适用于构造函数注入。它不会对动作方法注入有所帮助。
如果getArguments没有解决您的要求,那么覆盖handle方法实际上是您唯一的选择。是的,有很多代码可以从handleRaw复制/粘贴,但这是因为在那里有很多工作要做。即使handleRaw受到保护,您仍然需要复制/粘贴代码才能获得要替换的一行。
答案 1 :(得分:0)
为什么不从自定义ControllerResolverInterface
返回自己的callable,以自己想要的方式实例化Controller
并调用它?
它基本上是一个装饰者。
您可以使用自己的Symfony\Component\HttpKernel\Controller\ControllerResolver
方法实现扩展instantiateController()
,也可以从头开始实施ControllerResolverInterface
。
UPD:
当Symfony在call_user_func_array($controller, $arguments);
中进行handleRaw()
来电时,$controller
变量就是您从自定义ControllerResolver
返回的内容。这意味着您可以从解析器中返回任何可调用对象(可以是[$this, "callController"]
f.e.),在此可调用对象中,您将使用Auryn创建一个新的Controller
并调用它。
UPD2:
如果你还在努力解决这个问题,我会添加一个例子,因为你可能会错过我在这里的意思。
use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver;
class AutowiringControllerResolver extends ControllerResolver
{
// ... constructor stuff; assume $injector is a part of this class
protected function createController($controller)
{
$controller = parent::createController($controller);
return function (...$arguments) use ($controller) {
// you can do with resolved $arguments whatever you want
// or you can override getArguments() method and return
// empty array to discard getArguments() functionality completely
return $this->injector->execute($controller);
};
}
protected function instantiateController($classname)
{
return $this->injector->make($classname);
}
}
答案 2 :(得分:0)
监听器为时已晚,无法解决您的需求,因为它排除了依赖注入容器,这对于创建有效对象(〜=服务)至关重要。
您可能正在寻找控制器自动装配功能。
如果是这样,您可能会在此捆绑包中找到解决方案或至少是灵感:http://www.tomasvotruba.cz/blog/2016/03/10/autowired-controllers-as-services-for-lazy-people/
在以下几点满足您的需求: