MySQL ha un set di caratteri chiamato utf8 che non è UTF-8. Conserva al massimo tre byte per carattere, mentre ogni emoji e qualche carattere più raro ne richiedono quattro. Un testo che ne contiene uno viene respinto oppure troncato in silenzio proprio lì — ecco perché un commento può sparire da un paragrafo in poi.

Usa utf8mb4, ovunque

ALTER DATABASE shop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE comments CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

E nella connessione: è questa la metà che sfugge

// PDO
new PDO('mysql:host=localhost;dbname=shop;charset=utf8mb4', $u, $p);
; my.cnf
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
Una tabella utf8mb4 letta su una connessione latin1 restituisce testo storpiato e testo storpiato riscrive. La tabella è giusta, la connessione no, e i dati si guastano per strada. Imposta entrambe.

Quale collazione

  • utf8mb4_unicode_ci — ordina e confronta secondo le regole Unicode fra le lingue. È la scelta sicura per impostazione predefinita.
  • utf8mb4_0900_ai_ci — predefinita in MySQL 8, Unicode più recente, un filo più veloce.
  • utf8mb4_bin — i byte esatti. Usala per token e hash, mai per i nomi.

È la collazione a decidere il confronto. Con una collazione che non distingue le maiuscole, WHERE name = 'ali' trova anche Ali: di solito è ciò che vuoi in una ricerca, e non è ciò che vuoi per una chiave API.

Trova quel che è ancora sbagliato

SELECT table_name, table_collation
  FROM information_schema.tables
 WHERE table_schema = 'shop' AND table_collation NOT LIKE 'utf8mb4%';

SELECT table_name, column_name, character_set_name
  FROM information_schema.columns
 WHERE table_schema = 'shop' AND character_set_name IS NOT NULL
   AND character_set_name <> 'utf8mb4';

Le colonne si convertono separatamente dalle tabelle. Una tabella dichiarata utf8mb4 può contenere ancora colonne latin1 di anni fa, e sono proprio quelle a rompersi.

Fai un backup prima di convertire. Riscrive ogni riga, su una tabella grande può volerci un po', e sulle versioni più vecchie i limiti di dimensione degli indici ogni tanto mordono.