[php] fermer une connexion tôt



Answers

Il est nécessaire d'envoyer ces 2 en-têtes:

Connection: close
Content-Length: n (n = size of output in bytes )

Puisque vous devez connaître la taille de votre sortie, vous devez tamponner votre sortie, puis la vider dans le navigateur:

// buffer all upcoming output
ob_start();
echo "We'll email you as soon as this is done.";

// get the size of the output
$size = ob_get_length();

// send headers to tell the browser to close the connection
header("Content-Length: $size");
header('Connection: close');

// flush all output
ob_end_flush();
ob_flush();
flush();

// if you're using sessions, this prevents subsequent requests
// from hanging while the background process executes
if (session_id()) session_write_close();

/******** background process starts here ********/

En outre, si votre serveur Web utilise la compression gzip automatique sur la sortie (c'est-à-dire Apache avec mod_deflate), cela ne fonctionnera pas car la taille réelle de la sortie est modifiée et la longueur du contenu n'est plus exacte. Désactiver la compression gzip du script particulier.

Pour plus de détails, visitez http://www.zulius.com/how-to/close-browser-connection-continue-execution

Question

J'essaie de faire un appel AJAX (via JQuery) qui va lancer un processus assez long. Je voudrais que le script envoie simplement une réponse indiquant que le processus a commencé, mais JQuery ne retournera pas la réponse tant que le script PHP ne sera pas exécuté.

J'ai essayé ceci avec un en-tête «étroit» (ci-dessous), et également avec la mise en mémoire tampon de production; aucun ne semble fonctionner. Des suppositions? ou est-ce quelque chose que je dois faire dans JQuery?

<?php

echo( "We'll email you as soon as this is done." );

header( "Connection: Close" );

// do some stuff that will take a while

mail( 'dude@thatplace.com', "okay I'm done", 'Yup, all done.' );

?>



Une solution alternative consiste à ajouter le travail à une file d'attente et à créer un script cron qui vérifie les nouveaux travaux et les exécute.

J'ai dû le faire récemment pour contourner les limites imposées par un hôte partagé - exec () et al étaient désactivés pour PHP par le serveur web mais pouvaient fonctionner dans un script shell.




Dernière solution de travail

    // client can see outputs if any
    ignore_user_abort(true);
    ob_start();
    echo "success";
    $buffer_size = ob_get_length();
    session_write_close();
    header("Content-Encoding: none");
    header("Content-Length: $buffer_size");
    header("Connection: close");
    ob_end_flush();
    ob_flush();
    flush();

    sleep(2);
    ob_start();
    // client cannot see the result of code below



Vous pouvez utiliser Fast-CGI avec PHP-FPM pour utiliser la fonction fastcgi_end_request() . De cette façon, vous pouvez continuer à effectuer un traitement pendant que la réponse a déjà été envoyée au client.

  • exemple d'utilisation de fastcgi_finish_request () (Nov 2010)

Vous le trouverez dans le manuel de PHP ici: FastCGI Process Manager (FPM) ; Mais cette fonction n'est spécifiquement pas documentée dans le manuel. Voici l'extrait du PHP-FPM: PHP FastCGI Process Manager Wiki :

fastcgi_finish_request ()

Champ d'application: fonction php Catégorie: Optimisation

Cette fonctionnalité vous permet d'accélérer l'implémentation de certaines requêtes PHP. L'accélération est possible lorsqu'il y a des actions dans le processus d'exécution de script qui n'affectent pas la réponse du serveur. Par exemple, l'enregistrement de la session dans memcached peut se produire après que la page a été formée et transmise à un serveur Web. fastcgi_finish_request() est une fonctionnalité php qui arrête la sortie de la réponse. Le serveur Web commence immédiatement à transférer la réponse "lentement et tristement" au client, et php en même temps peut faire beaucoup de choses utiles dans le contexte d'une requête, comme enregistrer la session, convertir la vidéo téléchargée, manipuler toutes sortes de statistiques, etc.

fastcgi_finish_request() peut invoquer l'exécution de la fonction shutdown.




Voici une modification du code de Timbo qui fonctionne avec la compression gzip.

// buffer all upcoming output
if(!ob_start("ob_gzhandler")){
    define('NO_GZ_BUFFER', true);
    ob_start();
}
echo "We'll email you as soon as this is done.";

//Flush here before getting content length if ob_gzhandler was used.
if(!defined('NO_GZ_BUFFER')){
    ob_end_flush();
}

// get the size of the output
$size = ob_get_length();

// send headers to tell the browser to close the connection
header("Content-Length: $size");
header('Connection: close');

// flush all output
ob_end_flush();
ob_flush();
flush();

// if you're using sessions, this prevents subsequent requests
// from hanging while the background process executes
if (session_id()) session_write_close();

/******** background process starts here ********/



Si la fonction flush() ne fonctionne pas. Vous devez définir les prochaines options dans php.ini comme:

output_buffering = Off  
zlib.output_compression = Off  



Vous pourriez essayer de faire du multithreading.

vous pouvez créer un script qui appelle un système (en utilisant shell_exec ) qui appelle le binaire php avec le script pour faire votre travail en tant que paramètre. Mais je ne pense pas que ce soit le moyen le plus sûr. Peut-être que vous pouvez égayer les choses en chrootant le processus php et d'autres choses

Alternativement, il y a une classe à phpclasses qui fait cela http://www.phpclasses.org/browse/package/3953.html . Mais je ne connais pas les détails de la mise en œuvre




Cela a fonctionné pour moi

//avoid apache to kill the php running
ignore_user_abort(true);
//start buffer output
ob_start();

echo "show something to user1";
//close session file on server side to avoid blocking other requests
session_write_close();

//send length header
header("Content-Length: ".ob_get_length());
header("Connection: close");
//really send content, can't change the order:
//1.ob buffer to normal buffer,
//2.normal buffer to output
ob_end_flush();
flush();
//continue do something on server side
ob_start();
//replace it with the background task
sleep(20);



La réponse de Joeri Sebrechts est proche, mais elle détruit tout contenu existant qui peut être tamponné avant que vous souhaitiez vous déconnecter. Il n'appelle pas ignore_user_abort correctement, ce qui permet au script de se terminer prématurément. La réponse de diyism est bonne mais n'est pas génériquement applicable. Par exemple, une personne peut avoir plus ou moins de tampons de sortie que cette réponse ne gère pas, donc cela ne fonctionnera tout simplement pas dans votre situation et vous ne saurez pas pourquoi.

Cette fonction vous permet de vous déconnecter à tout moment (tant que les en-têtes n'ont pas encore été envoyés) et conserve le contenu que vous avez généré jusqu'à présent. Le temps de traitement supplémentaire est illimité par défaut.

function disconnect_continue_processing($time_limit = null) {
    ignore_user_abort(true);
    session_write_close();
    set_time_limit((int) $time_limit);//defaults to no limit
    while (ob_get_level() > 1) {//only keep the last buffer if nested
        ob_end_flush();
    }
    $last_buffer = ob_get_level();
    $length = $last_buffer ? ob_get_length() : 0;
    header("Content-Length: $length");
    header('Connection: close');
    if ($last_buffer) {
        ob_end_flush();
    }
    flush();
}

Si vous avez besoin de plus de mémoire, allouez-la avant d'appeler cette fonction.






Related



Tags

php php   jquery jquery