Symfony - 更改控制器的实例化和执行方式

时间:2016-01-19 11:15:14

标签: php symfony

  

注意:从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 +中的服务选项。

3 个答案:

答案 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/

在以下几点满足您的需求:

  • autowire injector
  • 回退到默认(FrameworkBundle)的控制器解析器,如果找不到
  • 它还应该保持所有流程正常工作,因为在控制器解析过程中没有黑客攻击