2012-01-23 9 views
6

È possibile chiudere il flusso di output di uno script PHP? Ho uno script che deve fare un po 'di post processing, ma durante e dopo l'elaborazione non invierà più alcun dato al client, quindi vorrei chiudere la connessione prima del post processing.PHP: close output stream

Modifica: Nella mia applicazione ho una cache che deve essere ricostruita di tanto in tanto. Tuttavia, non voglio rallentare un utente. Quello che voglio è determinare alla fine dello script se la cache deve essere ricostruita. Quindi voglio prima chiudere il flusso di output, quindi l'utente ottiene i suoi dati e quindi voglio ricostruire la cache. Non è davvero fondamentale farlo, ma penso che sia meglio chiudere prima la connessione, quindi l'utente non si accorgerà che la cache viene ricostruita se ciò richiede molto tempo.

+0

Perché avete bisogno di fare questo? Puoi pubblicare un po 'del tuo codice per aiutarci a capire un po' di più? –

+0

+1 a quello che ha detto Jonathan. Dicci qualcosa di più sul perché vuoi farlo. –

risposta

4

UPDATE

Il modo per gestire questo caso è una combinazione di buffer di uscita e le intestazioni HTTP appropriate.

Dal HTTP/1.1 Specification Section 14.10:

HTTP/1.1 definisce il "close" per collegare al mittente di segnale che la connessione verrà chiusa dopo il completamento della risposta .

Quindi, se si passa lungo un HTTP Content-Length intestazione oltre a Connection: close, il browser sa per chiudere la connessione dopo la lunghezza di risposta specificato ricevuto:

  1. Buffer TUTTO l'output degli script in modo da mantenere la capacità di inviare intestazioni
  2. Una volta che hai i dati di output completi, invia le intestazioni appropriate al client
  3. Continua tu r elaborazione ... ma non provare a inviare output o riceverai errori perché le intestazioni sono state inviate.

Inoltre, fare attenzione a come è possibile eseguire contro di script limiti di tempo di esecuzione in SAPI web server se si fa troppo l'elaborazione. Infine, dovresti dire a PHP di ignorare un "abortito utente" in questo particolare script usando ignore_user_abort() perché il browser chiuderà la connessione come risultato di ciò che stai facendo e vuoi che PHP continui l'elaborazione.

<?php 
ignore_user_abort(); 
ob_start(); 

// do stuff, generate output 

// get size of the content 
$length = ob_get_length(); 

// tell client to close the connection after $length bytes received 
header('Connection: close'); 
header("Content-Length: $length"); 

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

// close session if you have one ... 
// continue your processing tasks ... 
?> 

si potrebbe esaminare la sezione del manuale PHP su Connection handlingdocs.

In alternativa, perché non avviare il buffering dell'output? Quindi puoi catturare tutto l'output che verrebbe inviato quindi decidere in seguito se in realtà vuoi fare qualcosa con esso.

<?php 

echo 'before output buffering'; 
ob_start(); 
echo 'after output buffering'; 
$output = ob_get_contents(); 

// script's only output to this point will be 'before output buffering' 

// I changed my mind, send the output ... 
ob_end_flush(); 
?> 
+0

Il buffer di output mantiene ancora aperto il flusso di output al browser. Supponiamo quindi di dover eseguire alcune operazioni di post-elaborazione che richiedono alcuni secondi, il browser attenderà fino al termine dell'elaborazione. Soprattutto con AJAX questo rallenterà molto l'applicazione web. Quindi voglio essere in grado di chiudere la connessione al browser, in modo che il browser possa elaborare l'output e quindi continuare con la post-elaborazione. – Tiddo

+0

@Tiddo Ho aggiornato la mia risposta con (quello che credo sia) la soluzione corretta. – rdlowrey

+0

Questa è una soluzione carina! Lo proverò tra un minuto. Edit: Funziona alla grande! Molte grazie! – Tiddo

0

non ho abbastanza fama di commentare, ma voglio condividere che nella risposta @rdlowrey gzip potrebbe essere un problema.

Se hai gzip permesso al Transfer-Encoding intestazione è sempre impostato su Chunked, anche se si tenta di modificare con header("Transfer-encoding: none"); quindi non invierà il Content-Length intestazione.

Il modo in cui ho potuto risolvere questo stava usando il seguente prima:

<? 
@ini_set('zlib.output_compression', 'Off'); 
@ini_set('output_buffering', 'Off'); 
@ini_set('output_handler', ''); 
@apache_setenv('no-gzip', 1); 
?> 

E allora la soluzione:

<? 
ignore_user_abort(); 
ob_start(); 

// do stuff, generate output 

// get size of the content 
$length = ob_get_length(); 

// tell client to close the connection after $length bytes received 
header('Connection: close'); 
header("Content-Length: $length"); 

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

// close session if you have one ... 
// continue your processing tasks ... 
?>