Tenho um projeto PHP 7.3 que atualmente usa MySQL 5.5, com utf8
tabelas. Algumas das tabelas contêm dados de emoji, que aparecem bem no projeto atual. Estou tentando atualizar o projeto para MySQL 8.x, mas quando o faço, os dados de emoji aparecem incorretamente.
Primeiro, atualizei todas as tabelas 5.5 para usar uf8mb4
. Nesse estado, os dados apareceram. Então atualizei para 5.7, e as coisas continuaram a funcionar. Eu despejei esses dados, atualizei para 8.0 e recarreguei (eu usei o --default-character-set=utf8mb4
sinalizador em dump e load), e então os dados pararam de aparecer corretamente, por exemplo, uma lâmpada aparecendo como 💡
.
Estou executando cada um desses serviços no docker. Consegui atualizar de 5.5 para 5.7 usando o mesmo volume de dados sem problemas, mas ao tentar atualizar de 5.7 para 8.0, obtive erros que não consegui resolver e acabei fazendo um dump/restauração de dados.
Uma tabela de exemplo com um campo com um emoji:
DROP TABLE IF EXISTS `forums`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `forums` (
`forumID` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(200) COLLATE utf8_unicode_ci NOT NULL,
`description` text COLLATE utf8_unicode_ci,
`forumType` varchar(1) COLLATE utf8_unicode_ci DEFAULT 'f',
`parentID` int(11) DEFAULT NULL,
`heritage` varchar(25) COLLATE utf8_unicode_ci NOT NULL,
`order` int(5) NOT NULL,
`gameID` int(11) DEFAULT NULL,
`threadCount` int(11) NOT NULL,
PRIMARY KEY (`forumID`),
UNIQUE KEY `heritage` (`heritage`),
KEY `parentID` (`parentID`)
) ENGINE=MyISAM AUTO_INCREMENT=11551 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `forums`
--
LOCK TABLES `forums` WRITE;
/*!40000 ALTER TABLE `forums` DISABLE KEYS */;
INSERT INTO `forums` VALUES (8003,'💡 Gamers\' Plane development',NULL,'f',2,'0002-8003',3180,3181,4);
/*!40000 ALTER TABLE `forums` ENABLE KEYS */;
UNLOCK TABLES;
Para atualizar a tabela para utf8mb4 eu fiz
ALTER TABLE forums CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Como estou testando o retorno desses dados:
<?php
$mysql = new PDO("mysql:host=mysql;dbname=gamersplane", 'gamersplane', 'mypass');
$mysql->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$mysql->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$forum = $mysql->query('select * from forums where forumID = 8003')->fetch();
?>
<html>
<header>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</header>
<body>
<?php print_r($forum['title']); ?>
</body>
</html>
Eu li [UTF-8 até o fim][1] e
[1]: UTF-8 em todo o caminho e
- estou usando
utf8mb4
no banco de dados - tenho
charset=utf8mb4
na minha string PDO - configurei
default_charset
explicitamente no meu php.ini e também tentei configurá-lo em tempo de execução - tentei
Content-Type: text/html; charset=utf-8
como um cabeçalho PHP, bem como uma metatag HTML
Codificar em DataBases é sempre muito divertido! Infelizmente, quando você altera o conjunto de caracteres, ele não atualiza os dados, apenas como o banco de dados interpreta os dados, assim como o MySQL não realiza alterações de codificação em tempo real e sempre grava os bytes conforme eles vêm do cliente. No exemplo, você pode ver que essa
💡
é alatin1
representação do💡
, e quando você despeja os dados, ele os despeja já na codificação incorreta.Para verificar o problema, você pode tentar converter os dados com a consulta:
no seu ambiente MySQL8 mais recente, ele deve exibir emojis corretamente. Se sim, você deve tentar despejar os dados novamente, e dessa vez usar o charset que foi codificado originalmente, provavelmente
latin1
usando--default-character-set=latin1
. O arquivo de despejo deve conter emojis em vez de💡
texto -like.Esteja ciente de que, se houver novo conteúdo na tabela, ele será codificado duas vezes ou o dump fill falhará. Se o novo texto não for compatível com a
latin1
codificação, seria melhor fazê-lo com o conjunto original, se você ainda tiver acesso a ele.