Logotipo do CataBits

CataBits

A máquina movida à bits

Acentuação em PHP com MySQL

Por André Luferat em 03/11/2019.

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.

Acentuação em PHP com MySQL

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!

Comentários