发送响应后的Symfony运行代码 [英] Symfony run code after response was sent

查看:103
本文介绍了发送响应后的Symfony运行代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看了另一个问题.我正在寻找一种方法来解决该问题的操作要求,那就是

I took a look at this other question. I am looking for a way to do what the OP of that question wants as well, and that is to continue processing php after sending http response, but in Symfony2.

我实现了一个事件,该事件在每次内核终止后触发.到目前为止,一切都很好,但是我想要的是在特定的控制器操作中(例如,在发送表单之后)在CERTAIN终止后触发,而不是在每次请求时都执行一次.那是因为我想在某些时候做一些繁重的任务,而不希望最终用户等待页面加载.

I implemented an event that fires after every kernel termination. So far so good, but what I want is for it to fire after CERTAIN terminations, in specific controller actions, for instance after a form was sent, not every single time at every request. That is because I want to do some heavy tasks at certain times and don't want the end user to wait for the page to load.

任何想法我该怎么做?

<?php


namespace MedAppBundle\Event;

use JMS\DiExtraBundle\Annotation\InjectParams;
use JMS\DiExtraBundle\Annotation\Service;
use JMS\DiExtraBundle\Annotation\Tag;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use JMS\DiExtraBundle\Annotation\Inject;
/**
 * Class MedicListener
 * @package MedAppBundle\EventListener
 * @Service("medapp_test.listener")
 * @Tag(name="kernel.event_subscriber")
 */
class TestListener implements EventSubscriberInterface
{
    private $container;

    private $logger;

    /**
     * Constructor.
     *
     * @param ContainerInterface $container A ContainerInterface instance
     * @param LoggerInterface $logger A LoggerInterface instance
     * @InjectParams({
     *     "container" = @Inject("service_container"),
     *     "logger" = @Inject("logger")
     * })
     */
    public function __construct(ContainerInterface $container, LoggerInterface $logger = null)
    {
        $this->container = $container;
        $this->logger = $logger;
    }

    public function onTerminate()
    {
      $this->logger->notice('fired');
    }

    public static function getSubscribedEvents()
    {
        $listeners = array(KernelEvents::TERMINATE => 'onTerminate');

        if (class_exists('Symfony\Component\Console\ConsoleEvents')) {
            $listeners[ConsoleEvents::TERMINATE] = 'onTerminate';
        }

        return $listeners;
    }
}

到目前为止,我已经将事件订阅了kernel.terminate一个事件,但是显然这会在每次请求时触发它.我使它类似于Swiftmailer的 EmailSenderListener

So far I've subscribed the event to the kernel.terminate one, but obviously this fires it at each request. I made it similar to Swiftmailer's EmailSenderListener

内核每次都必须侦听此事件,即使它没有被触发,这种感觉还是很奇怪的.我宁愿只在需要时将其触发,但不确定如何执行.

It feels kind of strange that the kernel must listen each time for this event even when it's not triggered. I'd rather have it fired only when needed, but not sure how to do that.

推荐答案

在onTerminate回调中,您将获得

In the onTerminate callback you get an instance of PostResponseEvent as first parameter. You can get the Request as well as the Response from that object. Then you should be able to decide if you want to run the actual termination code.

您还可以将自定义数据存储在请求"的属性包中.请参阅此链接: Symfony和HTTP基础

Also you can store custom data in the attributes bag of the Request. See this link: Symfony and HTTP Fundamentals

Request类还具有一个公共属性属性,该属性包含与应用程序内部工作方式有关的特殊数据.对于Symfony框架,属性保存匹配的路由返回的值,例如_controller,id(如果有{id}通配符),甚至是匹配路由的名称(_route).完全可以使用attributes属性,在这里您可以准备和存储有关请求的特定于上下文的信息.

The Request class also has a public attributes property, which holds special data related to how the application works internally. For the Symfony Framework, the attributes holds the values returned by the matched route, like _controller, id (if you have an {id} wildcard), and even the name of the matched route (_route). The attributes property exists entirely to be a place where you can prepare and store context-specific information about the request.

您的代码可能看起来像这样:

Your code could look something like this:

// ...

class TestListener implements EventSubscriberInterface
{
    // ...

    public function onTerminate(PostResponseEvent $event)
    {
        $request = $event->getRequest();
        if ($request->attributes->get('_route') == 'some_route_name') {
            // do stuff
        }
    }

    // ...
}

kernel.terminate事件被设计为在发送响应后运行.但是symfony文档说了以下内容(摘自这里):

The kernel.terminate event is designed to run after the response is sent. But the symfony documentation is saying the following (taken from here):

内部,HttpKernel使用fastcgi_finish_request PHP函数.这意味着目前,只有PHP FPM服务器API才能将响应发送到客户端,而服务器的PHP进程仍然执行某些任务.使用所有其他服务器API,仍会执行kernel.terminate的侦听器,但是直到它们全部完成后,响应才会发送到客户端.

Internally, the HttpKernel makes use of the fastcgi_finish_request PHP function. This means that at the moment, only the PHP FPM server API is able to send a response to the client while the server's PHP process still performs some tasks. With all other server APIs, listeners to kernel.terminate are still executed, but the response is not sent to the client until they are all completed.

要在此处中使用该解决方案,您可以直接编辑web/app.php文件以将其添加到此处(但这是某种"hacking core" imo,尽管比以下示例更易于使用).或者您可以这样做:

To use the solution from here, you could either directly edit the web/app.php file to add it there (but this is some kind of "hacking core" imo, even though it would be easier to use than the following). Or you could do it like this:

  1. 将具有较高优先级的侦听器添加到kernel.request事件并开始输出缓冲(ob_start).
  2. 在kernel.response中添加一个侦听器,并将标头值添加到响应中.
  3. 将另一个具有最高优先级的侦听器添加到kernel.terminate并进行刷新(ob_flush,flush).
  4. 在单独的侦听器中运行代码,优先级低于kernel.terminate

我没有尝试过,但是它应该可以正常工作.

I did not try it, but it should actually work.

这篇关于发送响应后的Symfony运行代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆