Avatar ·

Какая кодировка и сортировка (collation) лучше всего подходят для MySQL в связке с PHP?

Всем привет! Я начинающий PHP-разработчик, и у меня возникла проблема с выбором правильной кодировки и collation для MySQL. Перерыл кучу форумов, но везде пишут по-разному. Кто-то советует `utf8_general_ci`, кто-то — `utf8_unicode_ci`, а недавно узнал про `utf8mb4`. У меня простой сайт на PHP с формой обратной связи, где пользователи могут оставлять комментарии. Я заметил, что иногда вместо русских букв отображаются кракозябры (например, "Привет!"). После долгих мучений я выяснил, что проблема в кодировке таблицы. Вот как я создаю таблицу: ```php exec(" CREATE TABLE comments ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50), message TEXT ) DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci "); ?> ``` И вставляю данные так: ```php prepare("INSERT INTO comments (username, message) VALUES (?, ?)"); $stmt->execute([$_POST['username'], $_POST['message']]); ?> ``` Но если пользователь введет эмодзи (например, 😊) или какой-нибудь специальный символ вроде «ё», то либо запись не сохраняется, либо вместо него появляется знак вопроса. Подскажите, какая collation считается "золотым стандартом" для современных проектов на PHP/MySQL? И нужно ли как-то специально настраивать соединение с БД, чтобы всё работало корректно? Заранее спасибо! <<>>

Правильный выбор: utf8mb4_unicode_ci

Коллега, вы столкнулись с классической проблемой, которая мучает новичков (и не только) уже много лет. Краткий ответ: используйте utf8mb4 как кодировку и utf8mb4_unicode_ci как collation. Это современный стандарт де-факто.

Давайте разберемся, почему старый utf8_general_ci не подходит, и как всё правильно настроить.

1. Почему utf8 — это ловушка?

В MySQL utf8 — это псевдоним для utf8mb3, который поддерживает символы только из базовой многоязычной плоскости (BMP). Это значит, что символы, занимающие 4 байта (например, эмодзи 😊, математические символы, редкие иероглифы), в него не помещаются. При попытке вставить такой символ MySQL либо обрежет его, либо заменит на ?.

Решение — utf8mb4 (UTF-8 Multibyte 4). Он поддерживает полный набор Unicode, включая эмодзи и редкие символы.

2. Сравнение collation: general vs unicode

  • utf8mb4_general_ci — старая, упрощенная сортировка. Она быстрее, но менее точная. Например, она не различает некоторые символы (вроде 'ß' и 'ss', 'æ' и 'ae'). Для русского языка разница минимальна, но для интернациональных проектов — критична.
  • utf8mb4_unicode_ci — основана на стандарте Unicode Collation Algorithm (UCA). Она корректно обрабатывает все языки, правильно сортирует буквы с диакритикой и учитывает лигатуры. Это "золотой стандарт".
  • utf8mb4_unicode_520_ci — более новая версия (на основе Unicode 5.20). Ещё точнее, но для 99% проектов разница незаметна.

Рекомендация: ставьте utf8mb4_unicode_ci. Если используете MySQL 8.0+, можно взять utf8mb4_0900_ai_ci (самый современный и точный вариант, но он несовместим со старыми версиями).

3. Полный пример настройки (исправляем ваш код)

Вам нужно изменить три вещи: саму таблицу, соединение с БД и, желательно, заголовки HTML.

<?php
// 1. Правильное соединение с PDO (указываем кодировку)
$pdo = new PDO(
    'mysql:host=localhost;dbname=test;charset=utf8mb4', // <-- charset в DSN
    'root',
    '',
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES => false, // отключаем эмуляцию для корректной работы с кодировками
    ]
);

// 2. Создаем таблицу с правильной кодировкой
$pdo->exec("
    CREATE TABLE comments (
        id INT AUTO_INCREMENT PRIMARY KEY,
        username VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
        message TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
    ) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
");

// 3. Вставка данных (остается без изменений, но работает корректно)
$stmt = $pdo->prepare("INSERT INTO comments (username, message) VALUES (?, ?)");
$stmt->execute([$_POST['username'], $_POST['message']]);
?>

Также не забудьте указать кодировку в HTML-шапке:

<meta charset="UTF-8">

И отправлять заголовок в PHP, если выводите данные:

header('Content-Type: text/html; charset=utf-8');

4. Проверка существующей БД

Если у вас уже есть таблицы в старой кодировке, их можно конвертировать:

ALTER TABLE comments CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Но будьте осторожны: если в данных уже есть битые символы, они не восстановятся — сначала нужно починить данные.

Итог

Для любого нового проекта на PHP + MySQL используйте utf8mb4_unicode_ci. Это решит проблему с кракозябрами, эмодзи и любыми редкими символами. Старый utf8_general_ci оставьте в прошлом — он годится только для легаси-проектов.

<<>>

Альтернативный подход: utf8mb4_0900_ai_ci для MySQL 8.0+

Если вы используете MySQL версии 8.0 или выше, есть смысл обратить внимание на более современную collation — utf8mb4_0900_ai_ci.

Она основана на Unicode 9.0 (отсюда "0900") и использует алгоритм сортировки "accent insensitive" (ai — нечувствительность к диакритическим знакам) и "case insensitive" (ci — нечувствительность к регистру).

Преимущества перед utf8mb4_unicode_ci:

  • Более точная и быстрая сортировка для современных языков.
  • Лучшая поддержка редких символов и эмодзи.
  • Оптимизирована под новые версии MySQL.

Недостаток: она несовместима с MySQL 5.7 и более старыми версиями. Если ваш хостинг использует старую версию (а таких ещё много), вы не сможете создать таблицу с такой collation — получите ошибку.

Пример использования:

CREATE TABLE comments (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,
    message TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

Резюме: если вы точно знаете, что у вас MySQL 8.0+, смело берите utf8mb4_0900_ai_ci. Если проект может переезжать на разные серверы — остановитесь на проверенном utf8mb4_unicode_ci, чтобы избежать проблем с совместимостью.