128 votes

continuer à traiter php après avoir envoyé la réponse http

Mon script est appelé par le serveur. Du serveur, je recevrai ID_OF_MESSAGE y TEXT_OF_MESSAGE .

Dans mon script, je vais gérer le texte entrant et générer la réponse avec les paramètres : ANSWER_TO_ID y RESPONSE_MESSAGE .

Le problème est que j'envoie une réponse à une demande incommensurable. "ID_OF_MESSAGE" Mais le serveur qui m'a envoyé un message à gérer va considérer que son message m'a été remis (ce qui signifie que je peux lui envoyer une réponse à cet ID), après avoir reçu la réponse http 200.

L'une des solutions consiste à enregistrer le message dans la base de données et à créer un cron qui s'exécutera toutes les minutes, mais j'ai besoin de générer un message de réponse immédiatement.

Existe-t-il une solution pour envoyer au serveur une réponse http 200 et continuer à exécuter le script php script ?

Merci beaucoup.

225voto

vcampitelli Points 3085

Oui. Vous pouvez le faire :

ignore_user_abort(true);//not required
set_time_limit(0);

ob_start();
// do initial processing here
echo $response; // send the response
header('Connection: close');
header('Content-Length: '.ob_get_length());
ob_end_flush();
@ob_flush();
flush();
fastcgi_finish_request();//required for PHP-FPM (PHP > 5.3.3)

// now the request is sent to the browser, but the script is still running
// so, you can continue...

die(); //a must especially if set_time_limit=0 is used and the task ends

68voto

Kosta Kontos Points 630

J'ai vu beaucoup de réponses ici qui suggèrent d'utiliser ignore_user_abort(true); mais ce code n'est pas nécessaire. Tout ce que cela fait est de s'assurer que votre script continue à s'exécuter avant qu'une réponse soit envoyée dans le cas où l'utilisateur abandonne (en fermant son navigateur ou en appuyant sur escape pour arrêter la requête). Mais ce n'est pas ce que vous demandez. Vous demandez de poursuivre l'exécution APRÈS l'envoi d'une réponse. Tout ce dont vous avez besoin est le suivant :

// Buffer all upcoming output...
ob_start();

// Send your response.
echo "Here be response";

// Get the size of the output.
$size = ob_get_length();

// Disable compression (in case content length is compressed).
header("Content-Encoding: none");

// Set the content length of the response.
header("Content-Length: {$size}");

// Close the connection.
header("Connection: close");

// Flush all output.
ob_end_flush();
@ob_flush();
flush();

// Close current session (if it exists).
if(session_id()) session_write_close();

// Start your background work here.
...

Si vous craignez que votre travail en arrière-plan prenne plus de temps que la limite de temps d'exécution du script par défaut de PHP, alors tenez-en compte. set_time_limit(0); en haut.

45voto

DarkNeuron Points 59

Si vous utilisez le traitement FastCGI ou PHP-FPM, vous pouvez le faire :

session_write_close(); //close the session
ignore_user_abort(true); //Prevent echo, print, and flush from killing the script
fastcgi_finish_request(); //this returns 200 to the user, and processing continues

// do desired processing ...
$expensiveCalulation = 1+1;
error_log($expensiveCalculation);

Fuente: https://www.php.net/manual/en/function.fastcgi-finish-request.php

Problème PHP #68722 : https://bugs.php.net/bug.php?id=68772

26voto

Ehsan Points 155

J'ai passé quelques heures sur ce problème et je suis arrivé à cette fonction qui fonctionne sur Apache et Nginx :

/**
 * respondOK.
 */
protected function respondOK()
{
    // check if fastcgi_finish_request is callable
    if (is_callable('fastcgi_finish_request')) {
        /*
         * This works in Nginx but the next approach not
         */
        session_write_close();
        fastcgi_finish_request();

        return;
    }

    ignore_user_abort(true);

    ob_start();
    $serverProtocole = filter_input(INPUT_SERVER, 'SERVER_PROTOCOL', FILTER_SANITIZE_STRING);
    header($serverProtocole.' 200 OK');
    header('Content-Encoding: none');
    header('Content-Length: '.ob_get_length());
    header('Connection: close');

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

Vous pouvez appeler cette fonction avant votre traitement long.

5voto

Justin Points 4591

Modifié un peu la réponse de @vcampitelli. Je ne pense pas que vous ayez besoin du close en-tête. Je voyais des en-têtes de fermeture en double dans Chrome.

<?php

ignore_user_abort(true);

ob_start();
echo '{}';
header($_SERVER["SERVER_PROTOCOL"] . " 202 Accepted");
header("Status: 202 Accepted");
header("Content-Type: application/json");
header('Content-Length: '.ob_get_length());
ob_end_flush();
ob_flush();
flush();

sleep(10);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X