1

What I'm trying to do is this: User makes an API call, I give the response, and then I start doing actions that take long such as sending emails. (Emails take long because I'm using SMTP, so swiftmailer does that in a synchronous way.)

According to the documentation finish() should be called after the response has been sent. When I test this however, this seems to be not the case. My client side stays waiting until the finish() is done.

Server side code:

$app = new Silex\Application();

$app->get('/hello', function (Request $r) use($app) {

    return $app->json("I should load within a few milliseconds.");
});


$app->finish(function() use () {
    //Pretend to do some slow action afterwards
    sleep(5);

});

$app->run();

Now if I load the page http://localhost/hello it will take 5 seconds for it to load. While my expectation is that it would load instantaneous. Am I misunderstanding the documentation?

Update:

Technically The accepted answer did what I asked for it. Practically I found a different solution:

I was looking for a way to get my emails sent in a way that the end-user wouldn't have to wait for it. For my purposes I switched over to using Swift_MailTransport instead of Swift_SmtpTransport, and configured exim4 locally as a smarthost. This way the email gets handed off to Exim4 and the script continues immediately.

If you are looking for other long processes to execute than just emails, have a look at message queues such as Beanstalk, I read great things about them.

The accepted answer below does work (also follow the links in the comments to the documentation page), but I found the solution not clean enough for my particular situation.

Coo
  • 1,842
  • 3
  • 19
  • 37

1 Answers1

2

Am I misunderstanding the documentation?

No, you don't. The content of the web response is send before that the finish filters are run, but the output is not flush. Try to add

 ob_flush();
 flush();

before sleep(5), and it will work as you might expect.

You may want to spool emails like the SwiftmailerBundle. See swiftmailer.use_spool.

By default, the Swiftmailer provider sends the emails using the KernelEvents::TERMINATE event, which is fired after the response has been sent.

Federkun
  • 36,084
  • 8
  • 78
  • 90
  • Flushing gives me half of what I want. I do indeed see the text immediately, but the little spinner still goes on for 5 seconds. As if the server says "hold on, there might be more". Could that be handled as well? – Coo Oct 31 '15 at 14:01
  • That's because your browser use `keep-alive`. The `Connection: close` header lets the browser know that it should not maintain a persistent connection: [see here](http://stackoverflow.com/q/138374/711206). – Federkun Oct 31 '15 at 14:13
  • What I don't understand is that Symfony's `Reponse` use `fastcgi_finish_request`, so all this should not be a problem. Are you using php 5.3+? – Federkun Oct 31 '15 at 14:20
  • I'm using PHP 5.5.12 on this dev machine. (WAMP) Could I add the `Connection: Close` header myself in `->after()`? – Coo Nov 01 '15 at 01:03