Nenhuma saída antes de enviar cabeçalhos!
As funções que enviam / modificam cabeçalhos HTTP devem ser chamadas antes que qualquer saída seja feita .
summary ⇊
Caso contrário, a chamada falhará:
Aviso: Não é possível modificar as informações do cabeçalho - cabeçalhos já enviados (saída iniciada no script: linha )
Algumas funções que modificam o cabeçalho HTTP são:
A saída pode ser:
Intencional:
print
, echo
E outras funções de produção de saída
- Matérias-
<html>
seções antes <?php
de código.
Por que isso acontece?
Para entender por que os cabeçalhos devem ser enviados antes da saída, é necessário analisar uma
resposta HTTP típica . Os scripts PHP geram principalmente conteúdo HTML, mas também transmitem um conjunto de cabeçalhos HTTP / CGI para o servidor da 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>
A página / saída sempre segue os cabeçalhos. O PHP precisa passar os cabeçalhos para o servidor da web primeiro. Só pode fazer isso uma vez. Após a quebra de linha dupla, ele nunca mais poderá alterá-los.
Quando o PHP recebe a primeira saída ( print
, echo
, <html>
) vai
expulsar todos os cabeçalhos recolhidos. Depois, ele pode enviar toda a saída que desejar. Mas enviar cabeçalhos HTTP adicionais é impossível então.
Como você pode descobrir onde ocorreu a saída prematura?
O header()
aviso contém todas as informações relevantes para localizar a causa do problema:
Aviso: Não é possível modificar as informações do cabeçalho - os cabeçalhos já enviados por
(saída iniciada em / www / usr2345 / htdocs / auth.php: 52 ) em /www/usr2345/htdocs/index.php na linha 100
Aqui "linha 100" refere-se ao script em que a header()
chamada falhou.
A nota " saída iniciada em " entre parênteses é mais significativa. Denomina a fonte da saída anterior. Neste exemplo, é auth.php
e linha52
. É aí que você precisa procurar resultados prematuros.
Causas típicas:
Imprimir, eco
A saída intencional de print
e echo
instruções encerrará a oportunidade de enviar cabeçalhos HTTP. O fluxo do aplicativo deve ser reestruturado para evitar isso. Use funções
e esquemas de modelos. Verifique se as header()
chamadas ocorrem antes que as mensagens sejam gravadas.
As funções que produzem saída incluem
print
, echo
, printf
,vprintf
trigger_error
, ob_flush
, ob_end_flush
, var_dump
,print_r
readfile
, passthru
, flush
, imagepng
,imagejpeg
entre outros e funções definidas pelo usuário.
Áreas HTML brutas
Seções HTML não analisadas em um .php
arquivo também são saída direta. As condições de script que acionarão uma header()
chamada devem ser observadas antes de qualquer<html>
bloco bruto .
<!DOCTYPE html>
<?php
// Too late for headers already.
Use um esquema de modelo para separar o processamento da lógica de saída.
- Coloque o código de processamento do formulário no topo dos scripts.
- Use variáveis de string temporárias para adiar mensagens.
- A lógica de saída real e a saída HTML misturada devem seguir por último.
<?php
Espaço em branco antes para avisos "script.php line 1 "
Se o aviso se referir à saída em linha 1
, é principalmente espaço em branco , texto ou HTML antes do <?php
token de abertura .
<?php
# There's a SINGLE space/newline before <? - Which already seals it.
Da mesma forma, isso pode ocorrer para scripts anexados ou seções de script:
?>
<?php
Na verdade, o PHP consome uma única quebra de linha após fechar tags. Mas não compensará várias novas linhas, guias ou espaços alterados para essas lacunas.
BOM UTF-8
Quebras de linha e espaços por si só podem ser um problema. Mas também existem seqüências de caracteres "invisíveis" que podem causar isso. O mais famoso é o
UTF-8 BOM (Byte-Order-Mark),
que não é exibido pela maioria dos editores de texto. É a sequência de bytes EF BB BF
, que é opcional e redundante para documentos codificados em UTF-8. O PHP, no entanto, deve tratá-lo como saída bruta. Pode aparecer como os caracteres 
na saída (se o cliente interpretar o documento como latino-1) ou "lixo" semelhante.
Em particular, editores gráficos e IDEs baseados em Java são alheios à sua presença. Eles não o visualizam (obrigados pelo padrão Unicode). A maioria dos editores de programadores e consoles, no entanto, faz:
É fácil reconhecer o problema desde o início. Outros editores podem identificar sua presença em um menu de arquivo / configurações (o Notepad ++ no Windows pode identificar e
solucionar o problema ). Outra opção para inspecionar a presença das listas técnicas está recorrendo a um hexeditor . Em sistemas * nix hexdump
geralmente está disponível, se não uma variante gráfica que simplifica a auditoria desses e de outros problemas:
Uma solução fácil é configurar o editor de texto para salvar arquivos como "UTF-8 (sem BOM)" ou uma nomenclatura semelhante. Geralmente, os recém-chegados recorrem à criação de novos arquivos e apenas copiam e colam o código anterior novamente.
Utilitários de correção
Também existem ferramentas automatizadas para examinar e reescrever arquivos de texto ( sed
/awk
ou recode
). Para PHP especificamente, há a phptags
tag mais arrumada . Ele reescreve as tags fechadas e abertas em formas longas e curtas, mas também corrige facilmente problemas de espaço em branco à esquerda e à direita, Unicode e UTF-x BOM:
phptags --whitespace *.php
É sensato usar em um diretório inteiro de inclusão ou projeto.
Espaço em branco depois ?>
Se a fonte do erro for mencionada como atrás do
fechamento?>
, é aqui que alguns espaços em branco ou texto bruto serão gravados. O marcador final do PHP não termina a execução do script neste momento. Quaisquer caracteres de texto / espaço depois dele serão gravados como conteúdo da página.
É geralmente recomendado, em particular para os novatos, que as ?>
tags de fechamento do PHP à direita sejam omitidas. Isso evita uma pequena parte desses casos. (Geralmente, os include()d
scripts são os culpados.)
Origem do erro mencionada como "Desconhecido na linha 0"
Geralmente, é uma extensão do PHP ou configuração do php.ini se nenhuma fonte de erro for concretizada.
- Ocasionalmente, é a
gzip
configuração de codificação de fluxo
ou oob_gzhandler
.
- Mas também pode ser qualquer
extension=
módulo duplamente carregado que gere uma mensagem implícita de inicialização / aviso do PHP.
Mensagens de erro anteriores
Se outra instrução ou expressão PHP causar a impressão de uma mensagem ou aviso de aviso, isso também conta como saída prematura.
Nesse caso, você precisa evitar o erro, atrasar a execução da instrução ou suprimir a mensagem com, por exemplo,
isset()
ou @()
- quando um deles não obstrui a depuração posteriormente.
Nenhuma mensagem de erro
Se você tiver error_reporting
ou display_errors
desativado por php.ini
, nenhum aviso será exibido. Mas ignorar erros não fará com que o problema desapareça. Cabeçalhos ainda não podem ser enviados após saída prematura.
Portanto, quando os header("Location: ...")
redirecionamentos falham silenciosamente, é aconselhável verificar se há avisos. Reative-os com dois comandos simples no topo do script de chamada:
error_reporting(E_ALL);
ini_set("display_errors", 1);
Ou set_error_handler("var_dump");
se tudo mais falhar.
Falando em cabeçalhos de redirecionamento, você deve usar um idioma como este para os caminhos finais do código:
exit(header("Location: /finished.html"));
De preferência, mesmo uma função de utilitário, que imprime uma mensagem do usuário em caso de header()
falhas.
Buffer de saída como solução alternativa
O buffer de saída do PHP
é uma solução alternativa para aliviar esse problema. Geralmente funciona de maneira confiável, mas não deve substituir a estruturação adequada do aplicativo e a separação da saída da lógica de controle. Seu objetivo real é minimizar as transferências fragmentadas para o servidor da web.
A output_buffering=
configuração, no entanto, pode ajudar. Configure-o no php.ini
ou via .htaccess
ou mesmo .user.ini nas configurações modernas do FPM / FastCGI.
Ativá-lo permitirá que o PHP armazene em buffer a saída em vez de passá-la ao servidor da web instantaneamente. Assim, o PHP pode agregar cabeçalhos HTTP.
Da mesma forma, ele pode ser envolvido com uma chamada para o ob_start();
topo do script de chamada. No entanto, o que é menos confiável por vários motivos:
Mesmo que <?php ob_start(); ?>
o primeiro script seja iniciado, o espaço em branco ou uma BOM podem ser embaralhados antes, tornando-o ineficaz .
Pode ocultar espaços em branco para a saída HTML. Mas assim que a lógica do aplicativo tenta enviar conteúdo binário (uma imagem gerada, por exemplo), a saída estranha em buffer se torna um problema. ( ob_clean()
Necessário como solução alternativa mais avançada.)
O tamanho do buffer é limitado e pode exceder facilmente quando deixado como padrão. E isso também não é uma ocorrência rara, difícil de rastrear
quando acontece.
Portanto, ambas as abordagens podem se tornar não confiáveis - principalmente ao alternar entre configurações de desenvolvimento e / ou servidores de produção. É por isso que o buffer de saída é amplamente considerado apenas uma muleta / estritamente uma solução alternativa.
Veja também o exemplo de uso básico
no manual e para mais prós e contras:
Mas funcionou no outro servidor !?
Se você não recebeu um aviso dos cabeçalhos antes, a configuração do buffer de saída do php.ini
foi alterada. Provavelmente não está configurado no servidor atual / novo.
Verificando com headers_sent()
Você sempre pode usar headers_sent()
para investigar se ainda é possível ... enviar cabeçalhos. O que é útil para imprimir condicionalmente uma informação ou aplicar outra lógica de fallback.
if (headers_sent()) {
die("Redirect failed. Please click on this link: <a href=...>");
}
else{
exit(header("Location: /user.php"));
}
Soluções alternativas úteis são:
<meta>
Tag HTML
Se seu aplicativo for estruturalmente difícil de corrigir, uma maneira fácil (mas um pouco não profissional) de permitir redirecionamentos é injetar uma <meta>
tag HTML
. É possível obter um redirecionamento com:
<meta http-equiv="Location" content="http://example.com/">
Ou com um pequeno atraso:
<meta http-equiv="Refresh" content="2; url=../target.html">
Isso leva ao HTML inválido quando utilizado após a <head>
seção. A maioria dos navegadores ainda o aceita.
Redirecionamento de JavaScript
Como alternativa, um redirecionamento JavaScript
pode ser usado para redirecionamentos de página:
<script> location.replace("target.html"); </script>
Embora geralmente seja mais compatível com HTML do que a <meta>
solução alternativa, incorre em dependência de clientes compatíveis com JavaScript.
Ambas as abordagens, no entanto, fazem fallbacks aceitáveis quando as chamadas genuínas de cabeçalho HTTP () falham. Idealmente, você sempre combinaria isso com uma mensagem amigável e um link clicável como último recurso. (Que, por exemplo, é o que a
extensão PECL http_redirect () faz.)
Por que setcookie()
e session_start()
também são afetados
Ambos setcookie()
e session_start()
precisam enviar um Set-Cookie:
cabeçalho HTTP. Portanto, as mesmas condições se aplicam e mensagens de erro semelhantes serão geradas para situações de saída prematura.
(É claro que eles também são afetados por cookies desabilitados no navegador ou até mesmo por problemas de proxy. A funcionalidade da sessão obviamente também depende do espaço livre em disco e de outras configurações do php.ini, etc.)
Links adicionais