UTF-8 até o fim
Estou configurando um novo servidor e desejo oferecer suporte total a UTF-8 em meu aplicativo da web. Eu tentei isso no passado em servidores existentes e sempre acabo tendo que voltar para ISO-8859-1.
Onde exatamente eu preciso definir a codificação / conjuntos de caracteres? Estou ciente de que preciso configurar Apache, MySQL e PHP para fazer isso - há alguma lista de verificação padrão que eu possa seguir ou talvez solucionar onde ocorrem as incompatibilidades?
Isso é para um novo servidor Linux, executando MySQL 5, PHP, 5 e Apache 2.
Respostas
Armazenamento de dados :
Especifique o
utf8mb4
conjunto de caracteres em todas as tabelas e colunas de texto em seu banco de dados. Isso faz com que o MySQL armazene e recupere fisicamente os valores codificados nativamente em UTF-8. Observe que o MySQL usará implicitamente autf8mb4
codificação se umutf8mb4_*
agrupamento for especificado (sem nenhum conjunto de caracteres explícito).Em versões anteriores do MySQL (<5.5.3), você infelizmente será forçado a usar o simples
utf8
, que suporta apenas um subconjunto de caracteres Unicode. Eu gostaria de estar brincando.
Acesso a dados :
Em seu código de aplicativo (por exemplo, PHP), em qualquer método de acesso de banco de dados que você usar, 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 ao transferir 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 o MySQL sobre a codificação a ser usada na conexão - esta geralmente é a abordagem preferida. Em 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 estiver usando mysqli , você pode chamar set_charset():
$mysqli->set_charset('utf8mb4'); // object oriented style mysqli_set_charset($link, 'utf8mb4'); // procedural style
Se você estiver preso com o mysql simples, mas por acaso estiver executando o PHP ≥ 5.2.3, você pode chamar mysql_set_charset.
Se o motorista não fornece seu próprio mecanismo para definir o conjunto de caracteres de conexão, você pode ter que emitir uma consulta para contar MySQL como o aplicativo espera que os dados sobre a conexão a ser codificado: SET NAMES 'utf8mb4'.
A mesma consideração em relação a
utf8mb4
/utf8
se aplica como acima.
Produto :
Se seu aplicativo transmitir texto para outros sistemas, eles também precisarão ser informados sobre a codificação de caracteres. Com os aplicativos da web, o navegador deve ser informado sobre a codificação em que os dados são enviados (por meio de cabeçalhos de resposta HTTP ou metadados HTML ).
No PHP, você pode usar a default_charsetopção php.ini ou emitir manualmente o
Content-Type
cabeçalho MIME você mesmo, o que é apenas mais trabalhoso, mas tem o mesmo efeito.Ao codificar a saída usando
json_encode()
, adicioneJSON_UNESCAPED_UNICODE
como um segundo parâmetro.
Entrada :
Infelizmente, você deve verificar cada string recebida como sendo UTF-8 válida antes de tentar armazená-la ou usá-la em qualquer lugar. PHP mb_check_encoding()faz o truque, mas você tem que usá-lo religiosamente. Não há realmente nenhuma maneira de contornar isso, já que clientes mal-intencionados podem enviar dados em qualquer codificação que quiserem, e eu não encontrei um truque para fazer o PHP fazer isso para você de forma confiável.
Pela minha leitura da especificação HTML atual , os sub-marcadores a seguir não são necessários ou mesmo válidos para HTML moderno. Meu entendimento é que os navegadores trabalharão e enviarão dados no conjunto de caracteres especificado para o documento. No entanto, se você está direcionando para versões mais antigas de HTML (XHTML, HTML4, etc.), estes pontos ainda podem ser úteis:
- Apenas para HTML antes de HTML5 : você deseja que todos os dados enviados a você pelos navegadores estejam em UTF-8. Infelizmente, se você vai pela única maneira de fazer de forma confiável é adicionar o
accept-charset
atributo para todas as suas<form>
tags:<form ... accept-charset="UTF-8">
. - Apenas para HTML antes de HTML5 : observe que a especificação W3C HTML diz que os clientes "devem" enviar formulários de volta ao servidor por padrão em qualquer conjunto de caracteres que o servidor serviu, mas isso aparentemente é apenas uma recomendação, daí a necessidade de ser explícito em cada um
<form>
marcação.
- Apenas para HTML antes de HTML5 : você deseja que todos os dados enviados a você pelos navegadores estejam em UTF-8. Infelizmente, se você vai pela única maneira de fazer de forma confiável é adicionar o
Outras considerações sobre o código :
Obviamente, todos os arquivos que você servirá (PHP, HTML, JavaScript, etc.) devem ser codificados em UTF-8 válido.
Você precisa se certificar de que sempre que processar uma string UTF-8, o faça com segurança. Essa é, infelizmente, a parte difícil. Você provavelmente vai querer fazer uso extensivo da mbstringextensão do PHP .
As operações de string embutidas do PHP não são, por padrão, seguras para UTF-8. Existem algumas coisas que você pode fazer com segurança com operações normais de string PHP (como concatenação), mas para a maioria das coisas você deve usar a
mbstring
função equivalente .Para saber o que você está fazendo (leia: não bagunçar), você realmente precisa saber o UTF-8 e como ele funciona no nível mais baixo possível. Verifique qualquer um dos links de utf8.com para alguns bons recursos para aprender tudo o que você precisa saber.
Eu gostaria de acrescentar uma coisa à excelente resposta de chazomaticus :
Não se esqueça da META tag (como esta, ou a versão HTML4 ou XHTML dela ):
<meta charset="utf-8">
Isso parece trivial, mas o IE7 já me deu problemas com isso antes.
Eu estava fazendo tudo certo; o banco de dados, a conexão do banco de dados e o cabeçalho Content-Type HTTP foram configurados para UTF-8 e funcionou 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 sem a tag META. Adicionar isso resolveu o problema.
Editar:
O W3C na verdade tem uma seção bastante grande dedicada ao I18N . Eles têm vários artigos relacionados a esse problema - descrevendo o lado HTTP, (X) HTML e CSS das coisas:
- FAQ: Alteração da codificação da página (X) HTML para UTF-8
- Declaração de codificações de caracteres em HTML
- Tutorial: conjuntos de caracteres e codificações em XHTML, HTML e CSS
- Definir o parâmetro de conjunto de caracteres HTTP
Eles recomendam o uso do cabeçalho HTTP e da meta tag HTML (ou declaração XML no caso de XHTML servido como XML).
Além de definir 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, contanto que você perceba que a maioria das funções de string não funciona com Unicode e algumas podem mutilar completamente as strings . O PHP considera "caracteres" como tendo 1 byte de comprimento. Às vezes, não há problema (por exemplo, explode()
apenas procura uma sequência de bytes e a usa como separador - portanto, não importa quais caracteres você está procurando). Mas outras vezes, quando a função é realmente projetada para funcionar em caracteres , o PHP não tem ideia de que seu texto possui caracteres multibyte 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 para o mercado de massa, então isso é importante para mim). Mas phputf8 pode usar mbstring nos bastidores, de qualquer maneira, para aumentar o desempenho.
Eu encontrei um problema com alguém usando PDO e a resposta foi usar isso para a string de conexão 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 de onde tirei isso está fora do ar, mas, felizmente, consegui acessá-lo usando o cache do Google.
No meu caso, eu estava usando mb_split
, que usa regex. Portanto, eu também tive que verificar manualmente se a codificação regex era utf-8 fazendomb_regex_encoding('UTF-8');
Como observação lateral, 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");
.
Em primeiro lugar, se você estiver em <5.3PHP, não. Você tem uma tonelada de problemas para resolver.
Estou surpreso que ninguém tenha mencionado a biblioteca intl , aquela que tem um bom suporte para Unicode , grafemas , operações de string , localização e muito mais, veja abaixo.
Citarei algumas informações sobre o suporte a Unicode em PHP pelos slides de Elizabeth Smith em PHPBenelux'14
INTL
Bom:
- Wrapper em torno da biblioteca ICU
- Localidades padronizadas, definir localidade por script
- Formatação de número
- Formatação de moeda
- Formatação de mensagem (substitui gettext)
- Calendários, datas, fuso horário e hora
- Transliterador
- Spoofchecker
- Pacotes de recursos
- Conversores
- Apoio IDN
- Grafemas
- Collation
- Iteradores
Mau:
- Não suporta zend_multibite
- Não suporta conversão de entrada e saída HTTP
- Não suporta sobrecarga de função
mb_string
- Ativa o suporte zend_multibyte
- Suporta codificação de entrada / saída HTTP transparente
- Fornece alguns invólucros para funcionalidade, como strtoupper
ICONV
- Principal para conversão de conjunto de caracteres
- Manipulador de buffer de saída
- funcionalidade de codificação mime
- conversão
- alguns auxiliares de string (len, substr, strpos, strrpos)
- Filtro de fluxo
stream_filter_append($fp, 'convert.iconv.ISO-2022-JP/EUC-JP')
BASES DE DADOS
- mysql: conjunto de caracteres e agrupamento em tabelas e na conexão (não o agrupamento). 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 pegadinhas
- Você não pode usar nomes de arquivo Unicode com PHP e Windows, a menos que use uma extensão de 3ª parte.
- Envie tudo em ASCII se 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 rapidamente com o filtro iconv
Atualizarei esta resposta caso alguma coisa altere os recursos adicionados e assim por diante.
A única coisa que gostaria de acrescentar a essas respostas incríveis é enfatizar em salvar seus arquivos na codificação utf8, percebi que os navegadores aceitam essa propriedade em vez de definir utf8 como sua codificação de código. Qualquer editor de texto decente mostrará isso, por exemplo, o Notepad ++ tem uma opção de menu para codificação de arquivo, mostra a codificação atual e permite alterá-la. Para todos os meus arquivos php eu uso utf8 sem BOM.
Algum tempo atrás, alguém me pediu para adicionar suporte a utf8 para um aplicativo php / mysql desenvolvido por outra pessoa, percebi que todos os arquivos estavam 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 do 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 alterar as funções de string para usar o php multibyte funções de string equivalentes.
Recentemente, descobri que o uso strtolower()
pode causar problemas em que 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 personagens, mas em geral é um pouco mais lento.
Acabei de passar pelo mesmo problema e encontrei uma boa solução nos manuais de PHP.
Mudei toda a codificação de meu arquivo para UTF8 e, em seguida, a codificação padrão em 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 fonte
No PHP, você precisará usar as funções multibyte ou ativar mbstring.func_overload . Dessa forma, coisas como strlen funcionarão se você tiver caracteres que ocupam mais de um byte.
Você também precisará identificar o conjunto de caracteres de suas respostas. Você pode usar AddDefaultCharset, como acima, ou escrever o código PHP que retorna o cabeçalho. (Ou você pode adicionar uma tag META aos seus documentos HTML.)
O suporte a Unicode em PHP ainda é uma grande bagunça. Embora seja capaz de converter uma string ISO8859 (que ele usa internamente) em utf8, ele não tem a capacidade de trabalhar com strings Unicode nativamente, o que significa que todas as funções de processamento de string irão danificar e corromper suas strings. Então você tem que usar uma biblioteca separada para suporte utf8 apropriado ou reescrever todas as funções de manipulação de strings você mesmo.
A parte fácil é apenas especificar o conjunto de caracteres nos cabeçalhos HTTP e no banco de dados e tal, mas nada disso importa se o seu código PHP não produzir UTF8 válido. Essa é a parte difícil, e o PHP não oferece praticamente nenhuma ajuda nisso. (Acho que o PHP6 deve consertar o pior disso, mas ainda falta um pouco)
Se você deseja que o servidor MySQL decida o conjunto de caracteres, e não o PHP como cliente (comportamento antigo; preferido, na minha opinião), tente adicionar skip-character-set-client-handshake
ao seu my.cnf
, em [mysqld]
e reiniciar mysql
.
Isso pode causar problemas caso você esteja usando algo diferente de UTF8.
A primeira resposta é excelente. Aqui está o que eu tive que fazer em uma configuração regular 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 !
se você quiser uma solução mysql, tive problemas semelhantes com 2 dos meus projetos, após uma migração de servidor. Depois de pesquisar e tentar várias soluções, encontrei este / nada antes deste funcionou):
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 tentando resolver uma inserção de consulta html
boa sorte!
Apenas uma nota:
Você está enfrentando o problema de seus caracteres não-latinos está mostrando como ?????????
, você fez uma pergunta, e ele ficou fechado com uma referência a esta questão canônica, você tentou de tudo e não importa o que você faz você ainda receber ??????????
a partir MySQL
.
Isso ocorre principalmente porque você está testando seus dados antigos que foram inseridos no banco de dados usando o conjunto de caracteres errado e foram convertidos e armazenados em caracteres de ponto de interrogação ?
. O que significa que você perdeu seu texto original para sempre e não importa o que tente, você receberá ???????
.
Aplicar novamente o que você aprendeu com as respostas desta pergunta em dados novos pode resolver o seu problema.
em connection.php: mysqli_set_charset ($ con, “utf8”); e em agrupamento sql utf = 8