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.
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.
Any idea how can I do that?
<?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;
}
}
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.
I used these answers to write a Response class that has this functionality: https://stackoverflow.com/a/28738208/1153227
This implementation will work on Apache and not just PHP FPM. However, to make this work we must prevent Apache from using gzip (by using an invalid Content-Encoding) so it makes sense to have a custom Response class to specify exactly when having an early response is more important than compression.
You also need to add a method to your AppKernel.php to make this work (don't forget to add a use statement for your EarlyResponse class)
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.
Also you can store custom data in the attributes bag of the Request. See this link: Symfony and HTTP Fundamentals
Your code could look something like this:
Edit:
The kernel.terminate event is designed to run after the response is sent. But the symfony documentation is saying the following (taken from here):
Edit 2:
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:
I did not try it, but it should actually work.
To solve this issue for some of my use cases I simply create symfony commands to do the heavy tasks, and call them via exec() to make them run in a separate process.