Como resolver o erro Headers Already Sent no PHP

Veja as causas e soluções para o erro Cannot modify header information Headers Already Sent no PHP

Como resolver o erro Headers Already Sent no PHP

O artigo a seguir é uma edição melhorada da tradução de uma das melhores respostas do StackOverflow para um dos se não o erro mais pesquisado da internet.

Esse erro deve ser o mais perguntado da história da Internet. Esta resposta é uma tradução de outra mais completa e explicativa originalmente postada no StackOverflow em Inglês feita por um colega de comunidade.

Adicionalmente eu revisei muito da tradução (incluindo gramática) e, obviamente, refiz toda a formatação para o markdown usado no/pelo Stack Overflow. Mas ainda assim, se houver(em) erro(s), sintam-se livres para corrigir ou complementar.

Não emita output antes de enviar os headers!

Funções que enviam/modificam headers HTTP devem ser chamadas antes de que qualquer output seja feito. Caso contrário, a chamada falhará.

Warning: Cannot modify header information - headers already sent (output started at file:line)

Algumas funções que modificam os headers HTTP são:

Output pode ser:

  • Acidental
  • Intencional:
    • print, echo e outras funções que emitem output, como var_dump()
    • Código <html> antes do código dentro de <?php.

Por que isso acontece?

Para entender porque os headers devem ser enviados antes do output, é necessário olhar uma típica HTTP ResponseScripts PHP geram conteúdo HTML principalmente, mas também passam alguns headers HTTP/CGI ao servidor.

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=about:note> ...

A página/output sempre segue os headers. O PHP é obrigado a passar os headers ao servidor primeiramente. Ele pode apenas fazer isto uma vez. E depois da quebra de linha dupla (envio de output para simplificar), ele não pode adicionar mais headers.

Quando o PHP recebe o primeiro output (print, echo, ), ele irá enviar os headers coletados. Mais tarde, ele pode enviar todos o conteúdo do output que quiser. Mas, não é possível enviar novos headers a partir deste momento.

Como você pode descobrir onde o output ocorreu?

warning da função header() contém todas as informações relevantes para localizar a raiz do problema:

Warning: Cannot modify header information - headers already sent by (output started at /www/usr2345/htdocs/auth.php:52) in /www/usr2345/htdocs/index.php on line 100

Aqui, a line 100 refere-se à linha do script onde a chamada da função header() falhou. A mensagem dentro dos parênteses é mais importante. Ela menciona que a linha 52 do arquivo auth.php é a raiz do output. Uma das causas mais populares deste erro são:

Print, echo

Output intencional de execuções print e echo vão acabar com a oportunidade de enviar headers HTTP. O fluxo da aplicação deve ser reestruturado para evitar isso. Use funções e templates. Verifique se as chamadas para a função header() ocorrem antes de que as mensagens são enviadas.

Funções que podem enviar output incluem mas não se limitam a: print, echo, printf()trigger_error()vprintf()ob_flush()var_dump()readfile()passthru(), entre outras. E também funções definidas por você.

Código HTML

Códigos HTML que não são interpretados em um arquivo .php são outputs também. Códigos que chamarão a função header() devem ser feitas antes de qualquer código .

<!DOCTYPE html> <?php // Já não é possível enviar *headers*.

Espaços em branco antes de <?php quando "arquivo.php line 1" é mencionado. Se a mensagem diz que o erro está na linha 1, então é normalmente espaços em branco, texto ou HTML antes da abertura da tag <?php.

 <?php # Há um espaço/linha em branco antes de <?php

Podem igualmente ocorrer com scripts "juntos".

?> <?php

O PHP, na verdade, coloca uma quebra de linha depois de fechar as tags.

UTF-8 BOM

Quebras de linhas e espaços podem ser um problema. Mas também há sequências de caracteres "invisíveis" que podem causar isto. o mais popular é o UTF-8 BOM (Byte-Order-Mark) que não é exibido pela maioria dos editores de texto. É uma sequência de bytes, que é opcional e redundante para arquivos em UTF-8.

Mas o interpretador do PHP o trata como output. Ele também pode mostrar como caracteres ï»¿ no output (se o cliente interpreta o documento em Latin-1) ou algum "lixo" semelhante.

Particularmente, alguns editores gráficos e IDEs Java-based não notam sua presença. Elas não vêem isso graças ao padrão Unicode. Entretanto, alguns editores e consoles mostram:

 

Não é fácil reconhecer o problema logo no início. Sem um editor desses disponível ou Notepad++ no Windows (que pode resolver o problema), uma outra solução seria um hexeditor. Programadores deveriam ter um, pois eles simplificam a identificação desses problemas:

A solução fácil é definir o editor de texto usado para salvar arquivos como UTF-8 (no BOM) ou alguma outra nomenclatura semelhante ou traduzida. Muitos novos programadores criam novos arquivos por copiar/colar os antigos, apenas alterando seu conteúdo depois.

Utilitários de correção

Há ferramentas automatizadas para reescrever arquivos de texto. Para o PHP, especificamente, há o phptags tag tidier. Ele reescreve tags de abertura e fechamento em tags longas e curtas, mas também resolve facilmente problemas de whitespace e BOM:

phptags --whitespace *.php

É sensato usar em um diretório completo ou no diretório do projeto.

Espaços em branco após ?>

Se a raiz do erro é mencionada depois do fechamento da ?>, então aí é onde há algum espaço em branco ou texto escrito.

tag de fechamento do PHP não termina a execução do script neste ponto. Quaisquer caracteres depois dela serão impressos como output.

É comumente dito aos novatos que a tag de fechamento deveria ser omitida. Isso evita uma parte significante destes casos. Includes normalmente são os culpados.

Novamente, phptags --whitespace *.php resolve isso rapidamente.

Igualmente, o comando phptags --unclosed ./includes pode remover tags ?> redundantes dos scripts.

Raíz do erro mencionada como "Unknown on line 0"

Acontece tipicamente quando extensões PHP ou o php.ini definem se a raiz do erro não é especificada. Isso acontece principalmente com a gzip stream ou o ob_gzhandler().

Porém, isso também poderia ser qualquer módulo carregado duas vezes, que deixam uma mensagem de warning implícita.

Mensagens de erro anteriores

Se algum outro comando PHP causa um warning ou notice sendo exibida, isso também conta como output. Neste casso, você precisa corrigir o erro, atrasar a execução do comando ou suprimir o erro com isset() ou @ - quando este não obstrui o debugging mais tarde.

Nenhuma mensagem de erro

Se você tem error_reporting ou display_errors desabilitados pelo php.ini, então nenhum warning será exibido. Mas ignorar erros não resolverão o problema (\o/). Mesmo assim, os headers não poderão ser enviados após o output.

Então, quando header("Location: ...") o redirecionamento falha silenciosamente, é bom examinar os warnings. Habilite-os novamente com dois comandos simples (no início do script):

error_reporting(E_ALL); ini_set("display_errors", 1);

Ou se todo o resto falhar:

set_error_handler("var_dump");

Falando de redirecionamentos, você deveria usar algo parecido com isso para caminhos de código no final.

exit(header("Location: /finished.html"));

Ou mesmo uma função, que imprima uma mensagem quando uma chamada header() falhar.

Controle de saída como gambiarra 

controle de saída do PHP é adequado para aliviar este problema. Isso não é tão confiável, mas deveria ser considerado uma gambiarra válida. Seu propósito real é minimizar transferências fragmentadas ao servidor. Reestruturar a aplicação para evitar output é preferível.

Todavia configurar output_buffering ajuda. Configure isso no php.ini ou via .htaccess ou até mesmo .user.ini. Com isso habilitado, o conteúdo fica armazenado em um buffer e não é instantaneamente passado para o servidor. Então headers HTTP podem ser agregados.

Isso pode também ser feito com uma chamada ao ob_start() no topo do script. Isto, entretanto, é menos confiável por algumas razões:

  • Mesmo se <?php ob_start(); ?> começar o primeiro script, espaços em branco ou um problema de BOM podem ser ocasionados antes, tornando está técnica ineficiente.
  • Isso pode ocultar espaços em branco para output HTML; mas na medida em que a lógica da aplicação tenta enviar conteúdo binário (uma imagem gerada, por exemplo), os espaços irrelevantes armazenados no buffer tornam-se um problema. Mesmo que ob_clean() seja uma outra gambiarra válida.
  • buffer tem um limite de tamanho. Embora geralmente um problema hipotético, pode no entanto acontecer - o que não seria fácil de descobrir/examinar.

Veja também um exemplo básico de uso no manual.

Mas isso funcionou em um outro servidor!?

Se você não teve warnings antes, então a configuração do php.ini mudou. O controle de saída então estava habilitado no outro servidor, mas não no atual. Veja a seção anterior.

Verificando com headers_sent()

Você pode sempre usar headers_sent() para examinar se ainda é possível enviar headers. Isso é útil para condicionalmente mostrar uma informação ou aplicar alguma outra lógica.

if (headers_sent()) {

    die("O redirecionamento falhou. Por favor, clique neste link: <a href=...>");

}

else{

    exit(header("Location: /user.php"));

}

Gambiarra com a tag HTML 

Se a estrutura da sua aplicação é difícil de corrigir, então uma forma fácil (mas amadora) de criar um redirecionamento é injetando HTML. Um redirecionamento pode ser feito assim:

<meta http-equiv="Location" content="http://example.com/">

Ou com um simples atraso (o sobrinho aprova!):

<meta http-equiv="Refresh" content="2; url=../target.html">

Isso tornará seu site inválido (mesmo com falso XHTML) quando inserido fora do . A maioria dos browsers ainda aceitam isso. Como alternativa, um redirect em Javascript poderia ser feito:

<script> location.replace("target.html"); </script>

É uma abordagem aceitável se este é usado como um fallback pelas funções de redirecionamento especializadas, o qual deveria primeiro tentar enviar um header() propriamente dito, mas usar a meta tag e uma mensagem amigável e um link como último recurso.

Porque setcookie() e session_start() são também afetados

Tanto o setcookie() quanto o session_start() precisam enviar um header Set-Cookie. As mesmas condições portanto se aplicam e mensagens de erros semelhantes serão exibidas.

Problemas de output de headers não são a única causa para a não-funcionalidade com eles, é claro. Cookies desativados no navegador ou até mesmo problemas de proxy deveriam ser sempre verificados. A funcionalidade da session também depende do espaço livre em disco e de outras configurações do php.ini.

Links adicionais

 

Comentários

Nome:

Email (não será publicado):

Comentário: