¡No hay salida antes de enviar encabezados!
Las funciones que envían / modifican encabezados HTTP deben invocarse antes de realizar cualquier salida .
summary ⇊
De lo contrario, la llamada falla:
Advertencia: no se puede modificar la información del encabezado: los encabezados ya se enviaron (la salida comenzó en el script: línea )
Algunas funciones que modifican el encabezado HTTP son:
La salida puede ser:
Intencional:
print
, echo
Y otras funciones de salida de la producción de
<html>
Secciones en bruto <?php
código anterior .
¿Por que sucede?
Para comprender por qué los encabezados deben enviarse antes de la salida, es necesario observar una
respuesta HTTP típica . Los scripts PHP generan principalmente contenido HTML, pero también pasan un conjunto de encabezados HTTP / CGI al servidor web:
HTTP/1.1 200 OK
Powered-By: PHP/5.3.7
Vary: Accept-Encoding
Content-Type: text/html; charset=utf-8
<html><head><title>PHP page output page</title></head>
<body><h1>Content</h1> <p>Some more output follows...</p>
and <a href="/"> <img src=internal-icon-delayed> </a>
La página / salida siempre sigue a los encabezados. PHP tiene que pasar los encabezados al servidor web primero. Solo puede hacer eso una vez. Después del doble salto de línea, nunca más podrá enmendarlos.
Cuando PHP recibe la primera salida ( print
, echo
, <html>
) se
lave todos los encabezados recogidos. Luego puede enviar toda la salida que desee. Pero enviar más encabezados HTTP es imposible entonces.
¿Cómo puede averiguar dónde se produjo la salida prematura?
La header()
advertencia contiene toda la información relevante para localizar la causa del problema:
Advertencia: No se puede modificar la información del encabezado: los encabezados ya enviados por
(salida iniciada en / www / usr2345 / htdocs / auth.php: 52 ) en /www/usr2345/htdocs/index.php en la línea 100
Aquí "línea 100" se refiere al script donde falló la header()
invocación .
La nota " salida iniciada en " entre paréntesis es más significativa. Se denomina la fuente de salida anterior. En este ejemplo es auth.php
y línea52
. Ahí es donde tenía que buscar resultados prematuros.
Causas típicas:
Impresión, eco
La salida intencional de print
y echo
declaraciones terminará la oportunidad de enviar encabezados HTTP. El flujo de la aplicación debe ser reestructurado para evitar eso. Usar funciones
y esquemas de plantillas. Asegúrese de que header()
ocurran llamadas antes de que se escriban los mensajes.
Las funciones que producen resultados incluyen
print
, echo
, printf
,vprintf
trigger_error
` ob_flush
` ob_end_flush
` var_dump
`print_r
readfile
` passthru
` flush
` imagepng
`imagejpeg
entre otros y funciones definidas por el usuario.
Áreas HTML sin procesar
Las secciones HTML no analizadas en un .php
archivo también son salida directa. Las condiciones del script que activarán una header()
llamada deben anotarse antes de cualquier<html>
bloque sin procesar .
<!DOCTYPE html>
<?php
// Too late for headers already.
Use un esquema de plantillas para separar el procesamiento de la lógica de salida.
- Coloque el código de procesamiento de formularios encima de los scripts.
- Use variables de cadena temporales para diferir mensajes.
- La lógica de salida real y la salida HTML entremezclada deben seguir al final.
Espacio en blanco antes <?php
para las advertencias "script.php line 1 "
Si la advertencia se refiere a la salida en línea 1
, entonces se trata principalmente de espacios en blanco , texto o HTML antes del <?php
token de apertura .
<?php
# There's a SINGLE space/newline before <? - Which already seals it.
Del mismo modo, puede ocurrir para scripts o secciones de scripts anexados:
?>
<?php
PHP en realidad come un solo salto de línea después de cerrar las etiquetas. Pero no compensará múltiples líneas nuevas, pestañas o espacios desplazados a tales huecos.
UTF-8 BOM
Los saltos de línea y los espacios solos pueden ser un problema. Pero también hay secuencias de caracteres "invisibles" que pueden causar esto. La más famosa es la
UTF-8 BOM (Byte-Order-Mark)
que no se muestra en la mayoría de los editores de texto. Es la secuencia de bytes EF BB BF
, que es opcional y redundante para documentos codificados UTF-8. Sin embargo, PHP tiene que tratarlo como salida sin procesar. Puede aparecer como caracteres 
en la salida (si el cliente interpreta el documento como Latin-1) o "basura" similar.
En particular, los editores gráficos y los IDE basados en Java son ajenos a su presencia. No lo visualizan (obligado por el estándar Unicode). Sin embargo, la mayoría de los editores de consola y programadores:
Ahí es fácil reconocer el problema desde el principio. Otros editores pueden identificar su presencia en un menú de archivo / configuración (Notepad ++ en Windows puede identificar y
solucionar el problema ). Otra opción para inspeccionar la presencia de BOM es recurrir a un editor hexadecimal . En los sistemas * nix hexdump
generalmente está disponible, si no una variante gráfica que simplifica la auditoría de estos y otros problemas:
Una solución fácil es configurar el editor de texto para guardar archivos como "UTF-8 (sin BOM)" o una nomenclatura similar. A menudo, los recién llegados recurren a la creación de nuevos archivos y simplemente copian y pegan el código anterior nuevamente.
Utilidades de corrección
También hay herramientas automatizadas para examinar y reescribir archivos de texto ( sed
/awk
o recode
). Para PHP específicamente, está la phptags
etiqueta tidier . Reescribe las etiquetas cercanas y abiertas en formas largas y cortas, pero también corrige fácilmente los problemas de espacios en blanco iniciales y finales, Unicode y UTF-x BOM:
phptags --whitespace *.php
Es sensato usarlo en un directorio completo de inclusión o proyecto.
Espacio en blanco después ?>
Si la fuente del error se menciona detrás del
cierre,?>
entonces es donde se escribieron algunos espacios en blanco o texto sin formato. El marcador final de PHP no termina la ejecución del script en este punto. Los caracteres de texto / espacio después de esto se escribirán como contenido de la página todavía.
Se recomienda comúnmente, en particular a los recién llegados, que ?>
se omitan las etiquetas de cierre de PHP al final . Esto evita una pequeña porción de estos casos. (Muy comúnmente los include()d
guiones son los culpables).
Fuente de error mencionada como "Desconocido en la línea 0"
Por lo general, es una configuración de extensión de PHP o php.ini si no se concreta ninguna fuente de error.
- Es ocasionalmente la
gzip
configuración de codificación de flujo
o elob_gzhandler
.
- Pero también podría ser cualquier
extension=
módulo doblemente cargado que genere un mensaje implícito de inicio / advertencia de PHP.
Mensajes de error anteriores
Si otra declaración o expresión de PHP hace que se imprima un mensaje o aviso de advertencia, eso también cuenta como salida prematura.
En este caso, debe evitar el error, retrasar la ejecución de la declaración o suprimir el mensaje con, por ejemplo,
isset()
o @()
cuando no obstruya la depuración más adelante.
Sin mensaje de error
Si tiene error_reporting
o display_errors
deshabilitado por php.ini
, entonces no aparecerá ninguna advertencia. Pero ignorar los errores no hará que el problema desaparezca. Los encabezados aún no se pueden enviar después de una salida prematura.
Entonces, cuando las header("Location: ...")
redirecciones fallan silenciosamente, es muy recomendable buscar advertencias. Vuelva a habilitarlos con dos comandos simples sobre el script de invocación:
error_reporting(E_ALL);
ini_set("display_errors", 1);
O set_error_handler("var_dump");
si todo lo demás falla.
Hablando de encabezados de redireccionamiento, a menudo debes usar un modismo como este para las rutas de código finales:
exit(header("Location: /finished.html"));
Preferiblemente, incluso una función de utilidad, que imprime un mensaje de usuario en caso de header()
fallas.
Búfer de salida como solución alternativa
El almacenamiento en búfer de salida de PHP
es una solución alternativa para aliviar este problema. A menudo funciona de manera confiable, pero no debe sustituir la estructuración adecuada de la aplicación y la separación de la salida de la lógica de control. Su propósito real es minimizar las transferencias fragmentadas al servidor web.
Sin output_buffering=
embargo, la configuración puede ayudar. Configúrelo en php.ini
o mediante .htaccess
o incluso .user.ini en configuraciones modernas de FPM / FastCGI.
Habilitarlo permitirá que PHP almacene la memoria intermedia en lugar de pasarla al servidor web al instante. PHP, por lo tanto, puede agregar encabezados HTTP.
También se puede activar con una llamada para ob_start();
encima de la secuencia de comandos de invocación. Lo que, sin embargo, es menos confiable por múltiples razones:
Incluso si <?php ob_start(); ?>
comienza el primer script, es posible que antes se barajen espacios en blanco o una lista de materiales, lo que lo hace ineficaz .
Puede ocultar espacios en blanco para la salida HTML. Pero tan pronto como la lógica de la aplicación intente enviar contenido binario (una imagen generada, por ejemplo), la salida externa almacenada en búfer se convierte en un problema. (Necesaria ob_clean()
como una solución alternativa).
El búfer tiene un tamaño limitado y puede desbordarse fácilmente cuando se deja el valor predeterminado. Y tampoco es una ocurrencia rara, difícil de rastrear
cuando sucede.
Por lo tanto, ambos enfoques pueden volverse poco confiables, en particular cuando se cambia entre configuraciones de desarrollo y / o servidores de producción. Es por eso que el búfer de salida se considera ampliamente solo una muleta / estrictamente una solución.
Vea también el ejemplo de uso básico
en el manual, y para más ventajas y desventajas:
¿Pero funcionó en el otro servidor?
Si no recibió la advertencia de encabezados antes, entonces la configuración de php.ini de búfer de salida
ha cambiado. Es probable que no esté configurado en el servidor actual / nuevo.
Comprobando con headers_sent()
Siempre puede usar headers_sent()
para sondear si aún es posible ... enviar encabezados. Lo cual es útil para imprimir condicionalmente una información o aplicar otra lógica de respaldo.
if (headers_sent()) {
die("Redirect failed. Please click on this link: <a href=...>");
}
else{
exit(header("Location: /user.php"));
}
Las soluciones alternativas útiles son:
<meta>
Etiqueta HTML
Si su aplicación es estructuralmente difícil de arreglar, entonces una forma fácil (pero poco profesional) de permitir redirecciones es inyectando una <meta>
etiqueta HTML
. Se puede lograr una redirección con:
<meta http-equiv="Location" content="http://example.com/">
O con un breve retraso:
<meta http-equiv="Refresh" content="2; url=../target.html">
Esto conduce a HTML no válido cuando se utiliza más allá de la <head>
sección. La mayoría de los navegadores aún lo aceptan.
Redireccionamiento de JavaScript
Como alternativa,
se puede utilizar una redirección de JavaScript para las redirecciones de página:
<script> location.replace("target.html"); </script>
Si bien esto a menudo es más compatible con HTML que la <meta>
solución alternativa, incurre en una dependencia de clientes con capacidad JavaScript.
Sin embargo, ambos enfoques hacen retrocesos aceptables cuando fallan las llamadas genuinas de encabezado HTTP (). Idealmente, siempre combinaría esto con un mensaje fácil de usar y un enlace en el que se pueda hacer clic como último recurso. (Que, por ejemplo, es lo que hace la extensión http_redirect ()
PECL).
Por qué setcookie()
y session_start()
también se ven afectados
Ambos setcookie()
y session_start()
necesitan enviar un Set-Cookie:
encabezado HTTP. Por lo tanto, se aplican las mismas condiciones, y se generarán mensajes de error similares para situaciones de salida prematuras.
(Por supuesto, también se ven afectados por las cookies deshabilitadas en el navegador, o incluso por problemas de proxy. La funcionalidad de la sesión obviamente también depende del espacio libre en el disco y otras configuraciones de php.ini, etc.)
Enlaces adicionales