it-swarm.dev

UTF-8 todo o caminho

Estou configurando um novo servidor e quero oferecer suporte a UTF-8 totalmente no meu aplicativo da web. Eu tentei isso no passado em servidores existentes e sempre parece ter que cair de volta para ISO-8859-1.

Onde exatamente eu preciso definir a codificação/charset? Estou ciente de que preciso configurar o Apache, o MySQL e o PHP para fazer isso - existe alguma lista de verificação padrão que eu possa seguir ou, talvez, solucionar problemas onde as inconsistências ocorrem?

Isto é para um novo servidor Linux, executando o MySQL 5, PHP, 5 e Apache 2.

1107
mercutio

Armazenamento de dados :

  • Especifique o conjunto de caracteres utf8mb4 em todas as tabelas e colunas de texto em seu banco de dados. Isso faz com que o MySQL armazene fisicamente e recupere valores codificados nativamente em UTF-8. Observe que o MySQL implicitamente usará a codificação utf8mb4 se um agrupamento utf8mb4_* for especificado (sem nenhum conjunto de caracteres explícito).

  • Nas versões mais antigas do MySQL (<5.5.3), você infelizmente será forçado a usar simplesmente utf8, que suporta apenas um subconjunto de caracteres Unicode. Eu queria estar brincando.

Acesso a dados :

  • No código do aplicativo (por exemplo, PHP), em qualquer método de acesso ao banco de dados usado, você precisará definir o conjunto de caracteres de conexão como utf8mb4. Dessa forma, o MySQL não faz nenhuma conversão de seu UTF-8 nativo quando transfere dados para seu aplicativo e vice-versa.

  • Alguns drivers fornecem seu próprio mecanismo para configurar o conjunto de caracteres de conexão, que atualiza seu próprio estado interno e informa ao MySQL a codificação a ser usada na conexão - geralmente essa é a abordagem preferida. No PHP:

    • Se você estiver usando a camada de abstração PDO com PHP ≥ 5.3.6, você pode especificar charset no DSN :

      $dbh = new PDO('mysql:charset=utf8mb4');
      
    • Se você estiver usando mysqli , você pode chamar set_charset() :

      $mysqli->set_charset('utf8mb4');       // object oriented style
      mysqli_set_charset($link, 'utf8mb4');  // procedural style
      
    • Se você está preso com plain mysql mas por acaso está rodando PHP ≥ 5.2.3, você pode chamar mysql_set_charset .

  • Se o driver não fornecer seu próprio mecanismo para configurar o conjunto de caracteres de conexão, talvez seja necessário emitir uma consulta para informar ao MySQL como seu aplicativo espera que os dados na conexão sejam codificados: SET NAMES 'utf8mb4' .

  • A mesma consideração em relação a utf8mb4/utf8 se aplica como acima.

Saída :

  • Se o seu aplicativo transmitir texto para outros sistemas, eles também precisarão ser informados sobre a codificação de caracteres. Com aplicativos da web, o navegador deve ser informado sobre a codificação na qual os dados são enviados (por meio de cabeçalhos de resposta HTTP ou metadados HTML ).

  • No PHP, você pode usar a opção default_charset php.ini, ou manualmente emitir o cabeçalho MIME Content-Type, que é apenas mais trabalho, mas tem o mesmo efeito.

  • Ao codificar a saída usando json_encode(), adicione JSON_UNESCAPED_UNICODE como um segundo parâmetro.

Entrada :

  • Infelizmente, você deve verificar se todas as sequências recebidas são válidas em UTF-8 antes de tentar armazená-las ou usá-las em qualquer lugar. PHP's mb_check_encoding() faz o truque, mas você tem que usá-lo religiosamente. Não há como evitar isso, já que clientes mal-intencionados podem enviar dados em qualquer codificação que eles queiram, e eu não encontrei um truque para fazer PHP fazer isso para você de forma confiável.

  • Da minha leitura do atual especificação HTML , os seguintes sub-marcadores não são mais necessários nem mesmo válidos para HTML moderno. Meu entendimento é que os navegadores trabalharão e enviarão os dados no conjunto de caracteres especificado para o documento. No entanto, se você estiver segmentando versões antigas de HTML (XHTML, HTML4 etc.), esses pontos ainda poderão ser úteis:

    • Para HTML somente antes de HTML5 : você deseja que todos os dados enviados a você pelos navegadores estejam em UTF-8. Infelizmente, se você usar a única maneira de fazer isso de forma confiável, adicione o atributo accept-charset a todas as tags <form>: <form ... accept-charset="UTF-8">.
    • Somente para HTML antes de HTML5 : observe que a especificação HTML do W3C diz que os clientes "devem" usar como padrão o envio de formulários para o servidor em qualquer conjunto de caracteres servido pelo servidor, mas aparentemente uma recomendação, daí a necessidade de ser explícito em cada tag <form>.

Outras considerações de código :

  • Obviamente, todos os arquivos que você estará servindo (PHP, HTML, JavaScript, etc.) devem ser codificados em UTF-8 válido.

  • Você precisa ter certeza de que, toda vez que processar uma string UTF-8, faça isso com segurança. Esta é, infelizmente, a parte difícil. Você provavelmente vai querer fazer uso extensivo da extensão mbstring do PHP.

  • As operações de strings internas do PHP são não por padrão UTF-8 safe Existem algumas coisas que você pode fazer com segurança com normal PHP operações de cadeia (como concatenação), mas para a maioria das coisas você deve usar a função equivalente mbstring.

  • Para saber o que você está fazendo (leia: não estrague tudo), você realmente precisa conhecer o UTF-8 e como ele funciona no nível mais baixo possível. Confira alguns dos links de tf8.com para alguns bons recursos para aprender tudo o que você precisa saber.

953
chazomaticus

Eu gostaria de acrescentar uma coisa a excelente resposta de chazomaticus :

Não se esqueça da tag META (assim, ou a versão HTML4 ou XHTML ):

<meta charset="utf-8">

Isso parece trivial, mas o IE7 me deu problemas com isso antes.

Eu estava fazendo tudo certo; o banco de dados, a conexão de banco de dados e o cabeçalho HTTP Content-Type estavam todos configurados para UTF-8 e funcionavam bem em todos os outros navegadores, mas o Internet Explorer ainda insistia em usar a codificação "Western European".

Acontece que a página estava faltando a tag META. Adicionando isso resolveu o problema.

Edit:

O W3C na verdade tem um pouco grande seção dedicada ao I18N . Eles têm vários artigos relacionados a esse problema - descrevem o lado HTTP, (X) HTML e CSS das coisas:

Eles recomendam usar tanto o cabeçalho HTTP quanto a meta tag HTML (ou declaração XML no caso de XHTML ser servido como XML).

141
mercator

Além de configurar default_charset no php.ini, você pode enviar o conjunto de caracteres correto usando header() de dentro do seu código, antes de qualquer saída:

header('Content-Type: text/html; charset=utf-8');

Trabalhar com Unicode em PHP é fácil, desde que você perceba que a maioria das funções de string não funciona com Unicode, e algumas podem manipular as strings completamente . PHP considera "caracteres" como 1 byte de comprimento. Às vezes, tudo bem (por exemplo, explode() apenas procura por uma seqüência de bytes e usa-a como um separador - portanto, não importa quais caracteres reais você procura). Mas outras vezes, quando a função é realmente projetada para trabalhar com caracteres , PHP não tem idéia de que seu texto possui caracteres de múltiplos bytes que são encontrados com Unicode .

Uma boa biblioteca para verificar é phputf8 . Isso reescreve todas as funções "ruins" para que você possa trabalhar com segurança em strings UTF8. Existem extensões como a extensão mbstring que tentam fazer isso para você também, mas eu prefiro usar a biblioteca porque é mais portátil (mas eu escrevo produtos de mercado de massa, então isso é importante para mim). Mas o phputf8 pode usar o mbstring nos bastidores para aumentar o desempenho.

59
chroder

Tópico antigo, eu sei. Encontrou um problema com alguém usando o PDO e a resposta foi usar isso para a string de conexão do PDO:

$pdo = new PDO(
    'mysql:Host=mysql.example.com;dbname=example_db',
    "username",
    "password",
    array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));

O site que eu tirei é de baixo, foi capaz de obtê-lo usando o google cache por sorte.

26
Brad F Jacobs

No meu caso, eu estava usando mb_split, que usa regex. Portanto, também tive que garantir manualmente que a codificação de regex fosse utf-8 fazendo mb_regex_encoding('UTF-8');

Como uma nota lateral, eu também descobri executando mb_internal_encoding() que a codificação interna não era utf-8, e mudei isso executando mb_internal_encoding("UTF-8");.

20
JDelage

Primeiro de tudo, se você estiver em <5,3PHP, então não. Você tem muitos problemas para resolver.

Surpreende-me que nenhum tenha mencionado a biblioteca intl , aquela que tem um bom suporte para unicode , graphemes , operações de string , localização e muito mais, veja abaixo.

Vou citar algumas informações sobre o suporte unicode em PHP por Elizabeth Smith's slides at PHPBenelux'14

INTL

Boa:

  • Invólucro ao redor da biblioteca ICU
  • Localidades padronizadas, definir localidade por script
  • Formatação de números
  • Formatação de moeda
  • Formatação de mensagem (substitui gettext)
  • Calendários, datas, fuso horário e hora
  • Transliterator
  • Spoofchecker
  • Pacotes de recursos
  • Conversores
  • Suporte a IDNs
  • Grafemas
  • Agrupamento
  • Iteradores

Mau:

  • Não suporta zend_multibite
  • Não suporta conversão de saída de entrada HTTP
  • Não suporta sobrecarga de funções

mb_string

  • Habilita o suporte zend_multibyte
  • Suporta codificação transparente de entrada/saída HTTP
  • Fornece alguns wrappers para funtionallity como strtoupper

ICONV

  • Primária para conversão de charset
  • Manipulador de buffer de saída
  • funcionalidade de codificação mímica
  • conversão
  • alguns auxiliares de string (len, substr, strpos, strrpos)
  • Filtro de Fluxo stream_filter_append($fp, 'convert.iconv.ISO-2022-JP/EUC-JP')

BANCOS DE DADOS

  • mysql: Charset e collation nas tabelas e na conexão (não no collation). Também não use mysql - msqli ou PDO
  • postgresql: pg_set_client_encoding
  • sqlite (3): Certifique-se de que foi compilado com suporte a unicode e intl

Algumas outras Gotchas

  • Você não pode usar nomes de arquivos unicode com PHP e janelas, a menos que você use uma extensão de 3ª parte.
  • Enviar tudo em ASCII se você estiver usando exec, proc_open e outras chamadas de linha de comando
  • Texto simples não é texto simples, os arquivos têm codificações
  • Você pode converter arquivos em tempo real com o filtro iconv

Vou atualizar essa resposta caso as coisas mudem os recursos adicionados e assim por diante.

19
Jimmy Kane

Eu descobri recentemente que usando strtolower() pode causar problemas onde os dados são truncados após um caractere especial.

A solução foi usar

mb_strtolower($string, 'UTF-8');

mb_ usa MultiByte. Ele suporta mais caracteres, mas em geral é um pouco mais lento.

13
Notflip

A única coisa que gostaria de acrescentar a essas respostas incríveis é enfatizar que, ao salvar seus arquivos na codificação utf8, observei que os navegadores aceitam essa propriedade ao definir a utf8 como sua codificação de código. Qualquer editor de texto decente lhe mostrará isso, por exemplo, o Notepad ++ tem uma opção de menu para ativação de arquivos, mostra a codificação atual e permite que você a altere. Para todos os meus arquivos php eu uso utf8 sem BOM.

Há algum tempo atrás alguém me pediu para adicionar suporte utf8 para uma aplicação php/mysql projetada por outra pessoa, notei que todos os arquivos foram codificados em ANSI, então tive que usar o ICONV para converter todos os arquivos, alterar as tabelas do banco de dados para usar o utf8 charset e utf8_general_ci collate, adicione 'SET NAMES utf8' à camada de abstração de banco de dados após a conexão (se estiver usando 5.3.6 ou anterior, caso contrário você terá que usar charset = utf8 na string de conexão) e altere funções de string para usar o php multibyte funções de string equivalentes.

12
Puerto AGP

Acabei de passar pelo mesmo problema e encontrei uma boa solução nos manuais PHP.

Eu mudei toda a minha codificação de arquivos para UTF8, em seguida, a codificação padrão na minha conexão. Isso resolveu todos os problemas.

if (!$mysqli->set_charset("utf8")) {
    printf("Error loading character set utf8: %s\n", $mysqli->error);
} else {
   printf("Current character set: %s\n", $mysqli->character_set_name());
}

Ver código-fonte

8
Abdul Sadik Yalcin

No PHP, você precisará usar o funções multibyte , ou ativar mbstring.func_overload . Dessa forma, coisas como strlen funcionarão se você tiver caracteres que levam mais de um byte.

Você também precisará identificar o conjunto de caracteres de suas respostas. Você pode usar AddDefaultCharset, como acima, ou escrever PHP código que retorna o cabeçalho. (Ou você pode adicionar uma tag META aos seus documentos HTML.)

8
JW.

Suporte a Unicode em PHP ainda é uma grande bagunça. Embora seja capaz de converter uma cadeia ISO8859 (que usa internamente) em utf8, ela não tem a capacidade de trabalhar com cadeias unicode nativamente, o que significa que todas as funções de processamento de cadeias de caracteres manipularão e corromperão suas cadeias de caracteres. Então você tem que usar uma biblioteca separada para o suporte utf8 adequado, ou reescrever todas as funções de manipulação de strings você mesmo.

A parte fácil é apenas especificar o charset nos cabeçalhos HTTP e no banco de dados e tal, mas nada disso importa se o seu código PHPnão produzir um UTF8 válido. Essa é a parte difícil, e PHP praticamente não lhe ajuda. (Eu acho que o PHP6 deve consertar o pior disso, mas ainda está longe)

6
jalf

A resposta principal é excelente. Aqui está o que eu tive que fazer em uma configuração normal do debian/php/mysql:

// storage
// debian. apparently already utf-8

// retrieval
// the mysql database was stored in utf-8, 
// but apparently php was requesting iso. this worked: 
// ***notice "utf8", without dash, this is a mysql encoding***
mysql_set_charset('utf8');

// delivery
// php.ini did not have a default charset, 
// (it was commented out, shared Host) and
// no http encoding was specified in the Apache headers.
// this made Apache send out a utf-8 header
// (and perhaps made php actually send out utf-8)
// ***notice "utf-8", with dash, this is a php encoding***
ini_set('default_charset','utf-8');

// submission
// this worked in all major browsers once Apache
// was sending out the utf-8 header. i didnt add
// the accept-charset attribute.

// processing
// changed a few commands in php, like substr,
// to mb_substr

isso foi tudo !

5
commonpike

Se você quiser que o servidor MySQL decida o conjunto de caracteres, e não PHP como cliente (comportamento antigo; preferencial, na minha opinião), tente adicionar skip-character-set-client-handshake ao seu my.cnf, sob [mysqld], e reinicie mysql.

Isso pode causar problemas no caso de você estar usando algo diferente de UTF8.

5
Nikola Tulimirovic

se você quiser uma solução mysql, eu tive problemas semelhantes com dois dos meus projetos, após a migração do servidor. Depois de pesquisar e tentar muitas soluções, me deparei com este um/nada antes que este funcionasse):

mysqli_set_charset($con,"utf8");

Depois de adicionar esta linha ao meu arquivo de configuração, tudo funciona bem!

Eu encontrei esta solução https://www.w3schools.com/PHP/func_mysqli_set_charset.asp quando eu estava olhando para resolver uma inserção de consulta html

boa sorte!

0
castro_pereira