Problemas com a codificação UTF-8 em PHP

Quando comecei a programar em PHP, não sabia muita coisa sobre codificações de caracteres e tudo mais. Aliás, não sei muita coisa até agora. Como todo bom usuário de Windows, tinha tudo pronto ali na minha frente e tudo rodava direitinho, até enviar os arquivos para um servidor Linux…

O grande problema começou quando comecei a usar por padrão a codificação Unicode, e a trabalhar direto com UTF-8. Logo na na primeira vez que chamei session_start(), apareceu a seguinte mensagem de erro:

Warning: session_start() [function.session-start]: Cannot send session cookie – headers already sent by (output started at /var/www/teste.php:1) in /var/www/teste.php on line 2

O curioso de tudo é que o session_start() só retorna erro se alguma coisa tiver sido enviada para o cliente antes de chamar essa função, mas vamos dar uma olhada no código:
<?php
session_start();
?>

Observe bem, não existe nada antes do código. Mas será que não mesmo? Sabemos que o PHP trabalha nativamente com a codificação ISO-8859-1 e que até o momento não tem um suporte muito bom para Unicode (UTF-8). Então através do Quanta, vamos “emular” o arquivo como se ele estivesse sendo visto pelo PHP, transformando a codificação dos caracteres para ISO-8859-1 através do comando Tools - Encoding - Western European:
<?php
session_start();
?>

Bingo! Já descobrimos o que está sendo enviado para o navegador. Aqueles caracteres estranhos  são os responsáveis pelo problema. Mas… o que são eles?

Através de algumas pesquisas no Google, descobri que esses caracteres são uma espécie de assinatura do Unicode, chamada BOM, que é onde quero chegar e a solução de todos os nossos problemas envolvendo UTF-8 e PHP.

De acordo com esta página, a BOM consiste numa sequencia de caracteres no início dos dados recebidos pelo navegador/agente, a qual define a ordem dos bytes e a forma de codificação. (minha tradução mal-feita).

Segundo a mesma página, não é necessário utilizar a BOM quando se estiver codificando em UTF-8, afinal ele é mais indicado para UTF-16 e UTF-32. O UTF-8 pode ou não usar BOM. No nosso caso, para resolver esse problema com o PHP, devemos programar em arquivos UTF-8 sem BOM. Como fazer isso?

Bom, o Quanta, pelo que eu sei, já faz esse tipo de conversão automaticamente. Desde que migrei para o Ubuntu, só tenho utilizado ele e não tenho tido problemas com codificação de caracteres. Se você utiliza o Windows, recomendo um ótimo editor que eu utilizava quando desenvolvia no Windows: o Notepad++. Existem outros, é claro, e cada desenvolvedor escolhe o que é melhor para si. Não tenho certeza, mas parece que o Dreamweaver tem a opção de salvar sem BOM, mas quero me ater a indicar software livre por aqui.

Para finalizar, quero indicar uma leitura interessante sobre codificação UTF-8, que encontrei quando procurava a solução desse problema. Um artigo que se chama O Mínimo Absoluto Que Todo Desenvolvedor de Software Absolutamente, Positivamente Precisa Saber Sobre Unicode e Conjuntos de Caracteres (Sem Desculpas!). Boa leitura!

27 respostas para “Problemas com a codificação UTF-8 em PHP”

  1. Opa Alessandro,
    Muito bom esse artigo. Então, qual charset que você recomenda utilizarmos? UTF-8 sem BOM?

    Abraços,
    Newton Calegari

  2. Alessandro Santos disse:

    Opa!

    O charset ideal é UTF-8 mesmo, que está se tornando padrão pro ser “universal”. Orkut, Twitter, Gmail, etc… todos utilizam UTF-8 como conjunto de caracteres padrão.

    Recomendo utilizar UTF-8 sem BOM quando for trabalhar com PHP, ou quando estiver aparecendo aqueles caracteres no início do arquivo.

  3. Márcio disse:

    Ainda não entendi qual a diferença então entre UTF-8 com BOM e sem BOM? Até agora, todos os testes que fiz apresentaram o mesmo resultado, com a codificação correta.

    E no caso eu não preciso então converter caracteres como:
    á = á
    â = â
    ã = ã

    Só converter o arquivo no Notepad++ para UTF-8 (sem BOM)?

  4. Alessandro Santos disse:

    Observação no comentário acima: os caracteres comparados pelo Márcio foram convertidos automaticamente pelo Wordpress para letras acentuadas. "á = &aacute;" é a forma que ele mandou, e assim por diante.

    Márcio, na verdade a utilização do UTF-8 sem BOM se refere à codificação do arquivo. Claro que isso afeta diretamente a forma como os caracteres são exibidos. Você pode, sim, utilizar o formato de entidades HTML (&aacute;) normalmente tanto em UTF-8 quanto em ISO-8859-1 ou em outras codificações. Vai funcionar como o esperado. Mas acredito que seja bem mais cômodo usar o próprio caractere acentuado. Por isso o incentivo de se utilizar UTF-8 em tudo.

    Pelo Notepad++, você pode converter o arquivo pelo menu "Format -> Convert to UTF-8 without BOM". Faça o teste criando um arquivo ANSI com caracteres acentuados e convertendo.

    Mas, você não precisa converter as entidades HTML para caracteres acentuados e vice-versa.

  5. Márcio disse:

    Ok Alessandro, obrigado pela sua resposta. Fiz os testes e funcionou mesmo de forma correta, e a conversão ficou bem simples de se fazer, bem melhor do que dar replace em cada caractere.

    Só mais uma pergunta:
    “UTF-8 sem BOM se refere à codificação do arquivo.”
    e simplesmente quando eu converto para UTF-8 (com BOM), daí não se trata também da codificação do arquivo? Pois também fiz esse teste de conversão no Notepad++ e o resultado foi idêntico.

  6. Alessandro Santos disse:

    É isso mesmo, Márcio, é quase a mesma coisa. A diferença é que o BOM são alguns caracteres no início do arquivo usados para instruir o navegador/editor/o-que-quer-que-seja qual a ordem dos bytes dentro do encoding. É necessário para UTF-16, mas não para UTF-8.

    O que acaba acontecendo é que quando você usa o BOM, e vai trabalhar com sessões ou cookies ou qualquer outra funcionalidade do PHP que necessita enviar cabeçalhos (headers) para o navegador, vai dar um erro. Isso ocorre porque os cabeçalhos são enviados antes de qualquer informação: antes de aparecer o primeiro "<" abrindo a tag HTML ou PHP. Como o BOM são alguns caracteres, e os mesmos são enviados no início do arquivo, isso obriga o servidor a enviar os headers para o navegador. Por isso quando você for tentar abrir um session_start() vai dar erro.

    Se você não for trabalhar com sessões, cookies, headers ou outras funções que precisam ser enviadas antes de qualquer texto, pode usar tanto UTF-8 com BOM ou sem BOM.

    Agora, se for precisar manipular esses dados, aí não tem jeito.

    Faz o seguinte:

    <?php
    session_start(); // Inicia a sessão
    ?>

    E testa o mesmo com e sem BOM. Um vai dar erro e o outro não.

    Pra ficar mais claro ainda:

    <p>Oi!</p>
    <?php
    session_start(); // Inicia a sessão
    ?>

    Esse último não importa como você salve, sempre vai dar erro.

    Enquanto você não enviar nada para o navegador, pode trabalhar com funções que usam headers à vontade. Se ecoar um único ponto e tentar dar um session_start() depois, vai dar erro porque os cabeçalhos só podem ser enviados uma vez, e antes de qualquer caractere.

  7. Márcio disse:

    Perfeita explicação Alessandro!
    Agora sim compreendi esse detalhe. Neste caso realmente continuarei convertendo para UTF-8 sem BOM, pois é muito comum utilizar sessions, cookies etc.

  8. Renato disse:

    Ola Rogério, ate q enfim achei algo na net sobre o assunto. Mas nao consegui ainda firar estes caracteres do q to desenvolvendo com php include nos menus como sermpre faço.
    Q codigo eu poderia colocar no meu HTML q faria ele sumir. É uma pagina simples com charset=iso-8859-1

  9. Vinicius disse:

    Cara.. muito boa a explicacao..

    estava quebrando a cabeça há dias em cima deste problema!

    foi soh converter pra UTF-8 sem BOM que deu certo!

    eita informação difícil de achar!

    ainda bem q cruzei com este blog!

    Obrigado!

  10. [...] muitos desenvolvedores no início da carreira (e até mesmo alguns mais experientes). Redigi um post que esclareceu muitas dúvidas quanto ao desenvolvimento PHP com arquivos codificados em [...]

  11. Táizel Girão disse:

    Muito bom seu artigo, finalmente consegui resolver meu problema, já tinha procurado bastante mas foi essa a real solução. Usava iso-8859-1 mesmo sabendo que não era o melhor, mas era o único que mostrava os acentos corretamente em todos servidores sem problemas, ou então usava o formatdo de entidades HTML mas finalmente encontrei a solução ideal.

    Muito Obrigado

  12. Miguel Praça disse:

    Olá pessoal!

    Encontrei um mesmo problema quando quiz inserir um menu feito em css em uma página php. Segui os procedimentos desta lista e resolvi o problema. Mas no entanto tive que retirar do arquivo o seguinte trecho:

    (content=”text/html; charset=iso-8859-1) da tag :

    Então tudo funcionou normalmente!
    Muito grato pela ajuda deste Forum

  13. Haz disse:

    Muito bom o artigo, estava procurando sobre a diferença de utf-8 com e sem BOM, e o artigo e os comentários me ajudaram bastante.

  14. Marcio disse:

    Olá Alessandro, tudo bem?

    Estou tendo um problema por causa desse ‘BOM’ que de BOM não tem nada..rs

    Estou precisando carregar um XML de um servidor externo. O arquivo está salvo com UTF-8 Com BOM. Como eu poderia converte-lo para ‘Sem BOM’ pelo php? Afinal não tenho acesso ao arquivo para converte-lo para ‘Sem BOM’.

    Consegui fazer removendo com substr(), mas acho que fica um serviço meio ‘porco’ fazer dessa maneira.

    Tem alguma alternativa?

    Abraços!

  15. Alessandro Santos disse:

    @Marcio: às vezes precisamos fazer um tipo de serviço “porco” mesmo. Como você não tem acesso ao fonte do XML externo, acho que a solução mais adequada mesmo seria usar o substr(). nem tudo é perfeito né rsrsrs. pode ser que numa nova atualização do PHP ele venha com melhor suporte a UTF-8 e esse prob seja resolvido. enfim, esperemos.

  16. [...] as páginas HTML, passando pelo PHP e por fim no MySQL. Antes de tudo, aconselho que aprenda a criar os seus arquivos utilizando o UTF-8 sem BOM, que evitará dores de cabeça futuras quando estiver trabalhando com cookies, sessões e headers. [...]

  17. [...] as páginas HTML, passando pelo PHP e por fim no MySQL. Antes de tudo, aconselho que aprenda a criar os seus arquivos utilizando o UTF-8 sem BOM, que evitará dores de cabeça futuras quando estiver trabalhando com cookies, sessões e headers. [...]

  18. Cara, vc é um santo.

    Deus abençoe.

    Vc me ajudou muito..

    uma dica, tabem

    usar no php a funcao ob_star();

  19. Diego Sampaio disse:

    Muito bom seu post Alessandro parabéns, trabalho como programador php numa empresa, e esta empresa trocou de servidor trazendo a tona a essa questão que até então estava passando despercebida, fuçando no google, me deparei com seu blog, e esse seu post que salvou minha pele, fiz as devidas alterações de UTF-8 e tudo voltou a normalidade, parabéns mesmo pelo blog, e sucesso pra vc!!!

  20. luiz disse:

    cara eu sei q vc postou isso a muito tempo!! mais resolveu minha duvida hj !! VALEUUU

  21. Colaço disse:

    Bacana, estou cheio de problemas em relação a acentos. Principalmente em formulários de contato. Os emails chegam com acentuação tudo doida.
    Muito bom este seu artigo.
    Agora estou com a teoria na mente, vou colocar em prática e espero resolver meus problemas.
    Parabens pelo belo conteúdo.
    Depois vou voltar ao seu site.
    Se as outras postagens forem desse nível, seu site será a página inicial do meu browser. Obrigado pela ajuda.

  22. HENRIQUE C. MAGRI disse:

    bom dia
    estou com problema que cerca de 3 dias esta me tirando o sono…
    é assim… tenho um script em php que envia email para meus clientes até ai normal… porem quando no banco tem o mes de março e chamo isso atraves de uma variavel que faz a conexão com o banco e tal… ele mostra no email assim MARÇO ao inves de mostra MARÇO no meu banco de dados MYSQL esta MARÇO normal… se escrevo a palavra MARÇO no meu corpo de email como eu já tenho coisas escrita ele aparece normal para mim MARÇO porem quando mando ele pegar do banco ele traz esse erro MARÇO. resumindo.. minha tabela esta UTF8 – UTF8_GENERAL_CI no meu script esta lá no final de todo meu corpo do email esta assim $mens .= ‘Content-Type: text/html; charset=”ISO-8859-1″‘
    Se eu mudar no meu script para $mens .= ‘Content-Type: text/html; charset=”UTF8″‘ ele mostra o mes MARÇO certo porem todo o resto do corpo do email desconfigurado… por favor alguem pode me ajudar? grato Henrique C. Magri – henrimagri@hotmail.com

  23. Claudio Myst disse:

    Só quero dizer que vc me ajudou imensamente com este artigo, estava trabalhando com o Dreamwaver configurado de forma errada e quando comecei a ter problemas com includes simples, removi códigos e mais códigos para tentar achar o problema e nada.

    Após ler seu artigo fui tentar configurar o Dreamwaver melhor i finalmente consegui achar a configuração certa e uma forma simples de mudar a codificação dos arquivos já criados com o bendito BOM.

    Valeu mesmo pela ajuda e pra quem quer corrigir o problema no Dreamwaver tecle CTRL+U e vá até “novo Documento” e ai tem as configurações para não aplicar o BOM.
    E coloque “Nenhum(a)” em “Formato de normalização Unicode”

    E para converter arquivos já criados com o bendito BOM abra o arquivo e vá até o menu “modificar” e depois clique em “propriedades da Página”

    Coloque “Nenhum(a)” em “Formato de normalização Unicode” mande aplicar e pronto.

    Muito obrigado pela ajuda com este artigo vc me quebrou uma floresta Amazônica

  24. Petronio disse:

    brigadãooo, ajudou de mais aki.

    abç

  25. Marcus disse:

    Obrigado! Instalei o Notepad++ hoje, estou amando, mas não fazia idéia da diferença entre UTF-8 e UTF-8 sem BOM.

    Valeu mesmo!

  26. Renato Aguiar disse:

    Dica para quem tem MAC, um ótimo editor para colocar e/ou tirar o BOM é o TextWrangler (gratuito).
    O Dreamweaver, todos eles até o CS4 tem um serio problema com isto, apensar de ter como tirar o colocar o BOM ele da erro ao savar-como com ou sem BOM.
    A melhor coisa a fazer é salvar-como “SEM BOM” com o notepad (windows), TextWrangler (Mac OS X) e no caso do Linux fazer como esta indicado aqui.

Deixe um comentário