Acentuação em PHP com MySQL
Ao iniciar sua jornada na linguagem PHP e estabelecer conexões com os bancos de dados MySQL ou MariaDB, é comum deparar-se com desafios relacionados à exibição adequada de acentuação no navegador. Nesse cenário, é possível observar substituições de acentos e do caractere "ç" por símbolos estranhos ou mesmo interrogações dentro de losangos. Essa questão também pode surgir ao salvar informações no banco de dados. Essa problemática é frequentemente originada da discrepância entre as diversas tabelas de caracteres utilizadas pelos diferentes sistemas em comunicação.
Com a chegada e popularização do HTML5, isso fica mais evidente, já que, por padrão, páginas HTML5 usam o conjunto de caracteres UTF-8.
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<title>Minha página</title>
</head>
<body>
...
</body>
</html>
Assim, é importante verificar se todos os sistemas também usam UTF-8 que é uma charset universal que pode representar e converter caracteres de praticamente qualquer sistema, porque usa como base o padrão Unicode.
Criando sites em UTF-8
Se você usa PHP, a primeira linha a ser interpretada pelo seu script deve ser:
<?php header("Content-type: text/html; charset=utf-8"); ?>
No HTML5, como já vimos, é importante que a linha abaixo esteja dentro da tag <header>...</header>
:
<meta charset="UTF-8">
Outra coisa importante é setar o UTF-8 como conjunto de caracteres padrão do SGBD, quando este se relacionar com seus scripts PHP. Para isso, costumo criar um arquivo de configuração inicial das páginas, que é "incluído" em todos os scripts do site, antes de qualquer outro código PHP ou de "frontend". Observe que isso já faz a conexão com o banco de dados e seta as queries para UTF-8:
<?php
// Informa qual o conjunto de caracteres será usado
header("Content-Type: text/html; charset=utf-8");
// Conecta ao banco de dados
$conn = new mysqli("servidor", "usuario", "senha", "banco");
// Trap de erros de conexão
if ($conn->connect_error) die("Erro no servidor: " . $conn->connect_error);
// Aqui está o segredo da conexão em UTF-8
$conn->query("SET NAMES utf8");
$conn->query("SET character_set_connection=utf8");
$conn->query("SET character_set_client=utf8");
$conn->query("SET character_set_results=utf8");
// Meses e dias da semana em pt/BR
$conn->query("SET lc_time_names = 'pt_BR'");
// Outras configurações iniciais
•••
O código acima faz a conexão com o MySQL usando a biblioteca "MySQLi" e seta todas as trocas de dados entre banco de dados e o PHP para UTF-8. Ainda aproveitei para setar o MySQL para traduzir os nomes dos meses e dias da semana para português/Brasil no trexo:
$conn->query("SET lc_time_names = 'pt_BR'");
Bases de dados em UTF-8
No MySQL ou MariaDB, sempre crie bancos de dados com o conjunto de caracteres UTF-8, como no exemplo abaixo:
CREATE DATABASE bancoteste CHARACTER SET utf8 COLLATE utf8_general_ci;
No exemplo, definimos o conjunto de caracteres a ser usando com o banco de dados como UTF-8 e também o agrupamento usado nas consultas. No caso do agrupamento, existem várias alternativas onde, algumas são:
utf8_general_ci
Consultas em qualquer idioma devem usar o conjunto UTF-8 e são "case-insensitive", ou seja, ignoram a diferença entre maiúsculas e minúsculas. Assim, a query:
SELECT * FROM usuario WHERE nome = 'Joca Silva';
e a query:
SELECT * FROM usuario WHERE nome = 'joca silva';
tem o mesmo resultado.
utf8_swedish_ci
A "consulta sueca" também deve usar o conjunto UTF-8, são "case-insensitive", ou seja, ignoram a diferença entre maiúsculas e minúsculas. A diferença para utf8_general_ci é que esta ignora os acentos. Assim, todas as query abaixo retornam o mesmo resultado:
SELECT * FROM cadastro WHERE cidade = 'São Paulo';
SELECT * FROM cadastro WHERE cidade = 'Sao Paulo';
SELECT * FROM cadastro WHERE cidade = 'são paulo';
SELECT * FROM cadastro WHERE cidade = 'Sao Paulo';
...
Essa é a teoria (que está na documentação do MySQL) porque na prática, talvez por questões relacionadas ao sistema operacional e ao modo de compilação do SGBD, as consultas a tabelas com collate utf8_swedish_ci se comportam de forma diferente.
utf8_bin
A "consulta binária" é bem mais rígida já que, em vez de usar a tabela de caracteres, faz uma consulta binária. Isso significa que maiúsculas são diferentes de minúsculas. As queries abaixo retornam resultados diferentes:
SELECT * FROM sistema WHERE empresa = 'Microsoft';
SELECT * FROM sistema WHERE empresa = 'microsoft';
SELECT * FROM sistema WHERE empresa = 'MICROSOFT';
Você pode usar collates e conjuntos de caracteres diferentes para tabelas diferentes de um banco de dados. Basta criar a tabela conforme o exemplo:
CREATE TABLE documentos (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
data TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
titulo VARCHAR(200) NOT NULL,
corpo LONGTEXT,
status ENUM('ativo', 'inativo', 'revisando', 'apagado') DEFAULT 'ativo'
) CHARACTER SET utf8 COLLATE utf8_bin;
Assim, mesmo estando em um banco de dados, por exemplo, utf8_general_ci, as consultas nesta tabela serão "case-sensitive", ou seja, utf8_bin.
Conclusão
Vimos então que, para resolver problemas de acentuação entre o PHP e o MySQL, bastam algumas linhas de código adicionais e um cuidado especial ao criar bancos de dados e tabelas.
Se gostou do conteúdo, tem alguma dica, sugestão de melhoria ou achou algum "bug", não deixe de me contactar.
E, até a próxima!