garotosopa

Headers sent e a canonização do ob_start

Enviado em PHP by garotosopa em Junho 20th, 2007

Eu considero o ano de 2004 como sendo o ápice da quantidade de dúvidas sobre a mensagem Headers already sent, quando era comum muitos usuários perguntarem a solução diariamente no IRC. Um saco. Ainda hoje o Google retorna quase 2 milhões de resultados para esse termo, e a maioria deles como sendo erros legítimos que o crawler encontrou enquanto navegava.

Para entender o problema é preciso compreender um pouco como funciona a comunicação HTTP. Quando é feita uma requisição ao endereço http://meudominio/caminho/script.php, o navegador conecta na porta 80 do servidor meudominio e envia um cabeçalho (Request headers) semelhante a este seguido de uma linha em branco:

GET /caminho/script.php HTTP/1.1
Host: meudominio

O servidor recebe essa requisição, nesse caso chama o PHP para interpretar o script, e responde, começando pelo cabeçalho (Response headers), seguido de uma linha em branco e o conteúdo da resposta, se houver:

HTTP/1.1 200 OK
Date: Mon, 18 Jun 2007 05:49:07 GMT
Server: Apache
Transfer-Encoding: chunked
Content-Type: text/html; charset=ISO-8859-1
 
<html>
<body>
Oi mundo.
</body>
</html>

Cada uma das primeiras linhas representa um cabeçalho (header) e começa mostrando o status da resposta (tal como sucesso, não encontrado, erro interno, etc) além de informações como tempo de expiração, encoding, cookies, entre outras.

A mensagem de erro Headers already sent ocorre porque o script tenta enviar um header após o trecho destinado a eles já ter sido enviado ao cliente por razão do conteúdo da resposta ter começado, seja ele HTML, texto, mensagens de erro e até mesmo linhas em branco ou comentários fora das tags do PHP.

O mais comum é ter problemas com as funções setcookie, porque o cookie é enviado ao cliente no cabeçalho da resposta, session_start, por precisar enviar um cookie para iniciar a sessão, e, obviamente, com a função header em si, que é muito utilizada para redirecionar o usuário para outro endereço.

A resposta definitiva para o problema é mudar a disposição do script de forma que os headers necessários sejam realmente enviados antes da apresentação HTML, que é a forma natural do modelo MVC. Quando isso não é possível, ou é difícil de ser mudado, é um sinal claro de que o script está mal estruturado, uma vez que toda a lógica deve ocorrer antes, e não misturada com o que o usuário está vendo.

O modelo inapropriado da aplicação fica ainda mais evidente quando o erro ocorre com o header Location, usado para redirecionamento. Chega a ser desperdício de recursos e banda, além de não fazer nenhum sentido, enviar um trecho HTML, ou qualquer tipo de conteúdo, se o browser deve seguir pra outra página sem mostrar nada na tela.

Em função disso surgiu também o mito de colocar funções que enviam um header na primeira linha do script, em especial a session_start, o que não é exatamente verdade. A necessidade essencial é de utilizá-las antes de qualquer saída (output) ao usuário, não importando exatamente onde no script, desde que nada tenha sido enviado ainda.

De qualquer forma, o PHP possui o recurso de Output Buffering que armazena a saída em um buffer e envia o conteúdo ao cliente de uma só vez (ou em pedaços determinados, dependendo do tamanho do buffer), e é aí que a ilusão de muitos começa.

Quando utilizado de forma adequada para o propósito adequado, o Output Buffering é extremamente benéfico em termos de desempenho, pois diminui o número de vezes que ocorre a comunição PHP - Servidor Web - Sistema Operacional - Rede - Usuário. Outros usos comuns são na compressão da saída em Gzip, para diminuir o consumo de banda, e ao encapsular a saída em uma string, como por exemplo em alguma técnica de template.

Todavia esse recurso costuma ser utilizado indevidamente por desenvolvedores esotéricos, que têm a função ob_start como varinha mágica para os mais diversos tipos de bruxaria. Uma vez que o conteúdo passa a ficar em um buffer, aquele início da resposta HTTP não terminou, ainda existindo então a possibilidade de enviar headers, seja ele um cookie, início de sessão ou até mesmo redirecionamento.

Mas que fique claro que contar com Output Buffering como mágica para enviar headers no meio da apresentação é apenas concordar com a péssima estrutura montada.

Headers Sent e a canonização do ob_start

Referências

Etiquetado como:

4 Responses to 'Headers sent e a canonização do ob_start'

Subscribe to comments with RSS or TrackBack to 'Headers sent e a canonização do ob_start'.

  1. Felipe Nascimento said, on Junho 21st, 2007 at 10:02 pm

    Hahaha, esse é o guia definitivo! Dahora! ;)

  2. Petto said, on Junho 22nd, 2007 at 12:29 pm

    Poiséeh..
    Gostei do post.
    Mas ainda acho que o uso de ob_start() pode ocasionar em um gasto significativo de memoria do servidor caso existam muitos e muitos acessos.

  3. Caio Ariede said, on Julho 10th, 2007 at 9:01 am

    Parabéns pelo post! Acho que todos já tivemos o prazer de presenciar este erro, e sempre há questões sobre o mesmo. :)

  4. Marcos Antunes said, on Novembro 29th, 2008 at 2:48 pm

    Estou com um problema e não achei a solução na internet, gostaria de saber se alguém já passou por isso.
    Eu instalei o wordpress, configurei, mas quando eu mudo a configuração de Links Permanentes de Padrão para Estrutura Personalizada, o wordpress funcionama perfeitamente mais no rodapé do blog todo aparece esses códigos HTTP/1.1 200 OK Date: Fri, 28 Nov 2008 21:07:04 GMT Server: Apache/2.2.8 (Unix) mod_ssl/2.2.8
    OpenSSL/0.9.8b mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635 mod_perl/2.0.3 Perl/v5.8.8 Content-Length: 0 Keep-Alive: timeout=5, max=96 Connection: Keep-Alive Content-Type: application/x-httpd-php5

    Quando eu volto para a configuração de Links Permanentes de Padrão

    ele não aparece mais.

    Alguém já passou por isso??

Leave a Reply