UTF-8 all the way through: полный гайд для PHP-разработчика

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему ваша кириллица превращается в "Привет" и как это исправить раз и навсегда

Вы когда-нибудь открывали свой сайт на PHP и вместо привычного "Привет, мир!" видели нечто вроде Привет, РјРёСЂ!? Если да — поздравляю, вы столкнулись с классической проблемой UTF-8 all the way through. Это та самая ситуация, когда кодировка ломается где-то на пути от базы данных до браузера, и виноваты в этом все, но никто не хочет брать на себя ответственность.

На Stack Overflow этот вопрос собрал тысячи просмотров и сотни ответов. И неспроста. Даже опытные разработчики иногда путаются в настройках кодировки. В этом туториале мы разберем пошаговый рецепт, как подружить PHP, MySQL и браузер, чтобы UTF-8 работал везде и всегда. Никакой магии — только правильные настройки.

Шаг 1. База данных: фундамент вашей кодировки

Всё начинается с базы. Если у вас в MySQL хранятся данные в latin1 или cp1251, а вы пытаетесь вывести их в UTF-8 — получите крякозябры. Но даже если база в UTF-8, есть нюанс: старая кодировка utf8 в MySQL не поддерживает символы эмодзи и некоторые редкие иероглифы. Решение — utf8mb4.

1.1. Создание таблиц с правильной кодировкой

При создании новой таблицы всегда указывайте:

CREATE TABLE articles (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Альтернатива — задать кодировку по умолчанию для всей базы данных:

ALTER DATABASE your_database_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Важно: utf8mb4_unicode_ci — это collation (правило сравнения). Оно корректно обрабатывает все символы Юникода. Если вам не нужна сортировка по разным языкам, подойдёт и utf8mb4_general_ci.

1.2. Конвертация существующих таблиц

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

ALTER TABLE articles CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Но будьте осторожны: если в таблице уже были данные в latin1, они могут превратиться в двойную кодировку (например, Привет). В таком случае нужно сначала перевести столбцы в binary, а потом в utf8mb4. Это отдельная история, но для простоты считаем, что вы начинаете с чистого листа.

Шаг 2. PHP-соединение с базой: настройка PDO

В современном PHP вы используете PDO (или mysqli). И здесь есть критический момент: после подключения к базе нужно явно указать кодировку. Иначе PHP будет общаться с MySQL в кодировке по умолчанию (часто latin1).

2.1. Правильная строка подключения PDO

$host = 'localhost';
$dbname = 'your_database';
$username = 'root';
$password = '';

$dsn = "mysql:host=$host;dbname=$dbname;charset=utf8mb4";

$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES => false,
];

try {
    $pdo = new PDO($dsn, $username, $password, $options);
} catch (PDOException $e) {
    echo "Connection failed: " . $e->getMessage();
}

Обратите внимание на charset=utf8mb4 в DSN. Это заставляет PHP и MySQL договариваться о кодировке на уровне соединения.

2.2. Альтернативный способ (если не работает DSN)

Иногда старые версии PHP или серверов не считывают charset из DSN. Тогда добавьте после подключения:

$pdo->exec("SET NAMES 'utf8mb4'");

Но первый способ предпочтительнее — он чище и современнее.

Шаг 3. HTTP-заголовки и HTML: говорим браузеру правду

Даже если база и PHP работают в UTF-8, браузер может решить, что вы отдаёте ему windows-1251. И тогда он попытается интерпретировать UTF-8 байты как однобайтовую кодировку — получите кракозябры.

3.1. HTTP-заголовок Content-Type

Самый надёжный способ — отправить заголовок через PHP ДО любого вывода:

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

Это должно быть в самом начале скрипта, до <html> или любого echo.

3.2. Мета-тег в HTML

На случай, если заголовок не сработал (например, при статическом HTML), продублируйте в <head>:

<meta charset="UTF-8">

Или старый вариант (но лучше используйте первый):

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

Важно: Если вы используете фреймворки (Laravel, Symfony), они обычно сами устанавливают заголовки. Но проверьте — иногда нужно явно указать 'charset' => 'utf-8' в конфиге.

Шаг 4. Работа с многобайтовыми строками в PHP

Стандартные PHP-функции вроде strlen(), substr() или strpos() работают с байтами, а не с символами. Для кириллицы (3 байта на символ в UTF-8) это приводит к ошибкам. Например, substr("Привет", 0, 3) вернёт "Пр" (первые 3 байта), а не "При". Решение — использовать mb_* функции.

4.1. Включаем mb_string

Убедитесь, что расширение mb_string включено в php.ini. Затем используйте:

// Вместо strlen
echo mb_strlen("Привет", 'UTF-8'); // 6

// Вместо substr
echo mb_substr("Привет мир", 0, 6, 'UTF-8'); // Привет

// Вместо strpos
echo mb_strpos("Привет мир", "мир", 0, 'UTF-8'); //                

Блоги

Рекомендаций по книгам