How to continue executing a script even after sending the data to the browser?

Asked

Viewed 1,514 times

10

When I asked the question What is the solution for asynchronous PHP processes?, basically what I had in mind was this question:

"There is some way to complete a request from a client, but leave a process running in the same script that serves this client, such as a record in the database followed by an email submission?

I ended up realizing that thinking about the word "asynchronous" might not be the most correct approach, since I don’t want parallel process.

When a request is sent to a PHP script, all processing is done there to, at the end of the operations, the response is sent to the client. And the client (browser), in turn, is waiting for the end of the execution of that process.

The question I asked earlier about asynchronous processes already has several answers on how to "dribble" this small PHP problem.

But what I’d like to know is: Is there any way to, when making a request to a PHP script, send a response to the client, but that same script keeps running on the server side, until a long operation is completed?

For example:

gravar_dados_no_banco();

// Envia a resposta pro navegador
// Porém ele não precisa mais esperar o término desse script
echo json_encode(['status' => true]);

// Depois da resposta acima
// Faço algumas operações demoradas
// Que não serão "esperadas" pelo navegador

 sleep(3);

 mandar_email_pro_admin();

 atualizar_todos_webservices();
  • I think what you want to do is not possible with PHP, using ajax does not help you??

  • @Fernandovr is possible yes, I asked the question more in the informative sense. If no one answer, I will answer, so the staff learn this trick :)

  • Ahh ok, because the easiest way I imagined to solve what you need would be in ajax requests. You send a command to record the data and return the json response, and then send another one to send the Email. I have long thought about this possibility of q vc wants to do in PHP, but never found a solution for this, since php blocks the return until the end of the execution of all script.

  • 1

    @Fernandovr I’ll give you a hint: https://stackoverflow.com/questions/15273570/continue-processing-php-after-sending-http-response

  • 1

    I remember answering that already. I’ll see if I can find.

  • 2

    https://answall.com/a/217559/5878

  • Very cool this @Wallacemaxters method, and Anderson’s response, tbm is very good. Using ignore_user_abort(true); and set_time_limit(0); I even closed the browser after the return of the screen, and even then it continued running the script until the end. When I tried it in the past, I wanted it to continue displaying results on the screen without stopping the script. But this tbm method is very good.

  • @Fernandovr. Here where I work, there is a script that I need to register that a request has been canceled, after it is canceled (bank registration), I reply to the customer that he has successfully canceled, but after that reply, I need to at the same time send an e-mail to the owner of the request and make a request on the webservice. It saved my life!

  • Ahh understood @Wallacemaxters, I’ve done something similar, but I always expected the script to finish to work everything on screen. But really this way is great and ends up gaining enough speed in response. I really liked, and now q also learned how to do, I will use a lot. Hug friend, have a great day.

  • @Fernandovr I’m testing here and mine is waiting for the end. But that’s not the intention. I’ll keep testing here, he has to finish the answer, but continue the script.

Show 5 more comments

2 answers

9

How to do this in PHP with common settings?

I took a test in the Laravel framework and got the expected result.

I did it this way:

Route::get('/', function () {

    $response = Response::json([
        'process' => true
    ]);

    return Response::withShutdownTask($response, function () {
        minha_tarefa_demorada();
    });

});


Response::macro('withShutdownTask', function ($response, callable $callback) {

    $response->header('Connection', 'Close');
    $response->header('Content-Encoding', 'none');
    $response->header('Content-Length', mb_strlen($response->getContent()));

    register_shutdown_function(static function () use ($callback) {
        ignore_user_abort(true);
        flush();
        @ob_end_flush();
        @ob_flush();
        sleep(3);
        $callback();
    });

    ob_start();
    return $response;
    
});

The value {"process" : true} is sent immediately to the browser, but you will notice that the browser will no longer load.

We have some important points that need to be understood here:

  • You need to run the ob_start, it starts capturing the output buffer;

  • You need the flush and ob_end_flush to download the buffer to the output. I did the test without the function flush, leaving only ob_end_flush, and worked perfectly.

  • You will need the ob_get_length. In my case I used mb_strlen($response->getContent()) because it is the specific Framework class I used, but in the case of pure PHP, you can use ob_get_length to get the current size. This seems to be an important point, as the browser seems to read the size of the content received and the size sent in Content-Length to decide whether to close the connection to the server.

  • I put the ignore_user_abort(true) in case of caution, to tell PHP not to stop running the script after closing the connection. In my tests with Laravel did not make a difference.

I put the sleep(3) also, just to ensure that the test would work properly, since this function would delay the request by 3 seconds. With the above code, the delay occurs in the server execution, but it will not affect the browser, since the browser has already closed the connection;

FAST CGI

In Fastcgi, it seems that to solve this problem, just call your "task" after the function call fastcgi_finish_request:

 fastcgi_finish_request();
 minha_tarefa_demorada();
  • All this code is in the route file (web.php)?

5


Based on Anderson’s answer, it didn’t work out that way Wallace?

<?php
ignore_user_abort(true);
set_time_limit(0);

gravar_dados_no_banco();

ob_start();

// Envia a resposta pro navegador
// Porém ele não precisa mais esperar o término desse script
echo json_encode(['status' => true]);

header('Connection: close');
header('Content-Length: '.ob_get_length());

ob_end_flush();
ob_flush();
flush();

// Depois da resposta acima
// Faço algumas operações demoradas
// Que não serão "esperadas" pelo navegador

sleep(3);

mandar_email_pro_admin();

atualizar_todos_webservices();
?>

For me here after the flush(); it displayed the result on the screen, and even q I close the browser it continued running the rest of the script.

It has to see then if it is the case, if your other functions further down, there is something q is preventing the return. By q I know, php first reads the entire code structure, includes, functions and classes that are being used to then start the execution.

  • 2

    Yes, it worked perfectly!

  • Ahhh ok q good q worked ^_^

  • Fernando, get in the chat there, beast, kkkkk

  • @Wallacemaxters I just saw the msg kkk was bad, I in the biggest rush here with a rsrs tramp, will not be able to follow the chat for now rsrs, but always q can I promise to enter there rsrs Hugs friend

Browser other questions tagged

You are not signed in. Login or sign up in order to post.