2012-01-23 17 views
6

¿Es posible cerrar la secuencia de salida de un script PHP? Tengo un script que necesita hacer un procesamiento posterior, pero durante y después del procesamiento posterior ya no enviará más datos al cliente, por lo que me gustaría cerrar la conexión antes del procesamiento posterior.PHP: cerrar flujo de salida

Editar: En mi aplicación tengo un caché que necesita ser reconstruido de vez en cuando. Sin embargo, no quiero retrasar a un usuario. Lo que quiero es determinar al final del script si es necesario reconstruir el caché. Así que quiero cerrar la secuencia de salida primero, para que el usuario obtenga sus datos y luego quiero reconstruir la memoria caché. No es realmente crítico hacer esto, pero creo que es mejor cerrar primero la conexión, para que el usuario no se dé cuenta de que la memoria caché se está reconstruyendo si eso lleva mucho tiempo.

+0

¿Por qué necesita para hacer esto? ¿Puedes publicar un poco de tu código para ayudarnos a entender un poco más? –

+0

+1 a lo que dijo Jonathan. Cuéntanos un poco más sobre por qué quieres hacer esto. –

Respuesta

4

ACTUALIZACIÓN

La manera de manejar este caso es una combinación de búferes de salida y las cabeceras HTTP apropiados.

Desde el HTTP/1.1 Specification Section 14.10:

HTTP/1.1 define el "cierre" opción de conexión para que el remitente señal de que la conexión se cerrará después de la finalización de la respuesta .

lo tanto, si se pasa a lo largo de un HTTP Content-Length cabecera, además de Connection: close, el navegador sabe para cerrar la conexión después de que se reciba la longitud de respuesta especificada:

  1. Buffer TODO el script de salida para que conserve la capacidad de enviar encabezados
  2. Una vez que tenga los datos completos de salida, envíe los encabezados correspondientes al cliente
  3. Continúe r procesamiento ... pero no intente enviar resultados o recibirá errores porque se han enviado encabezados.

Además, tenga cuidado ya que puede ejecutar contra los límites de tiempo de ejecución del script en el servidor web SAPI si realiza demasiado procesamiento. Finalmente, debe decirle a PHP que ignore un "abortar usuario" en este script en particular usando ignore_user_abort() porque el navegador cerrará la conexión como resultado de lo que está haciendo y desea que PHP continúe procesando.

<?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 ... 
?> 

que te pueden examinar la sección del manual de PHP en Connection handlingdocs.

Alternativamente, ¿por qué no iniciar el almacenamiento en búfer de salida? Luego puede capturar toda la salida que se enviará y luego decidir si realmente desea hacer algo con ella.

<?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

El almacenamiento en búfer de salida mantiene la secuencia de salida abierta en el navegador. Entonces, supongamos que necesito hacer un procesamiento posterior que tarda unos segundos más o menos, el navegador esperará hasta que se complete el procesamiento posterior. Especialmente con AJAX esto ralentizará mucho la aplicación de weba. Así que quiero poder cerrar la conexión al navegador, para que el navegador pueda procesar la salida y luego continuar con el procesamiento posterior. – Tiddo

+0

@Tiddo He actualizado mi respuesta con (lo que creo que es) la solución correcta. – rdlowrey

+0

¡Esa es una solución muy buena! Voy a intentar eso en un minuto. Editar: ¡Funciona genial! ¡Muchas gracias! – Tiddo

0

No tengo la reputación suficiente para comentar, pero quiero compartir esto en la respuesta @rdlowrey gzip podría ser un problema.

Si tiene gzip permitido la transferencia de codificación cabecera siempre se establece en fragmentada, incluso si se intenta cambiar con header("Transfer-encoding: none"); por lo tanto, no enviará el Content-Length cabecera.

La manera de que pudiera resolver este estaba usando el siguiente antes:

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

y después la solución:

<? 
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 ... 
?>