GregsAccount's avatar

How does the middleware::terminate() method work?

I thought that if I create a middleware with a terminate method, its content will be executed after the response is already sent back to the client. The following experiment disproved it:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\Response;

class QueueEmailsAfterResponseIsSent
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        return $next($request);
    }
    
    /**
     * Handle tasks after the response has been sent to the browser.
     */
    public function terminate(Request $request, Response $response): void
    {
        sleep(15);
        Log::info('Works after response is sent');
    }
}

So I have to wait 15 seconds before the web browser is refreshed (and logs the message). But I thought that the appropriate behavior would be that the browser refreshes immediately, and then I would have to wait 15 seconds to see the newly created message in the log file.

I have read the Symfony HttpKerlen component's documentation, from which Laravel implements its terminate() functionality (so I am not really interested in that part).  ...or maybe I have not paid enough attention?

Inside the Laravel's doc, it sas:

If you define a terminate method on your middleware and your web server is using FastCGI...

If I use the built-in server using the php artisan serve command, then the phpinfo() shows these lines:

CGI / FastCGI : Rasmus Lerdorf, Stig Bakken, Shane Caraveo, Dmitry Stogov FastCGI Process Manager : Andrei Nigmatulin, dreamcat4, Antony Dovgal, Jerome Loyet

... it is enough evidence for me, that FastCGI is used by the default dev server, isn't it?

So do I misunderstand the terminate() method, or does it work but not with the sleep(15) function? As you can see, I would use it to unload the current request-response lifecycle for sending multiple emails. Will it do its job?

0 likes
11 replies
Snapey's avatar

use a queue for sending multiple emails

1 like
GregsAccount's avatar

@Snapey Is there a way to use Queue without the need for the CLI? This website will work on a server where the CLI and the exec() function are banned.

...or at least fire the queues in a standalone session asynchronously which won't be affect the main one?

Snapey's avatar

CGI / FastCGI : Rasmus Lerdorf, Stig Bakken, Shane Caraveo, Dmitry Stogov FastCGI Process Manager : Andrei Nigmatulin, dreamcat4, Antony Dovgal, Jerome Loyet

Is just a statement of contributors to php and not related to your question

1 like
GregsAccount's avatar

@Snapey I see. So could it means that FastCGI is not available for the primitive server provided by the artisan? Maybe the question is silly because this server is not provided by the artisan but an implementation of PHP's native server, right?

Snapey's avatar

@GregsAccount the question is silly because you would never run php artisan serve in production.

GregsAccount's avatar

@Snapey Right, but now I am working on my local machine.

So theoretically the terminate() method will work on the production server if the FastCGI is enabled. Also it is a good alternative for this kind of task (sending 2 mails after a customer submitted a contact form) if the CLI does not available for queueing, isn't it?

Snapey's avatar

@GregsAccount just sent the email inline in your code.

There will be a tiny delay, which will be improved when you can afford a better service

Snapey's avatar

Does your host permit cron jobs?

If not, and no cli access either then you really need to find a better host.

GregsAccount's avatar

@Snapey Cron Jobs are available... I have thought about running all jobs every x seconds (or minutes), but isn't it a better solution to use the terminate functionality if this project does not have more queuable?

Please or to participate in this conversation.