Avatar ·

Как правильно настроить полную поддержку UTF-8 от PHP до MySQL и браузера без кракозябр?

Всем привет! Я начинающий PHP-разработчик, и уже вторую неделю мучаюсь с проблемой кодировок. Сайт на русском языке, база данных MySQL, всё вроде бы настроил, но при выводе данных из БД на странице появляются вопросительные знаки или кракозябры типа "Р РµСально?". Вот как я подключаюсь к базе: ```php $db = new mysqli('localhost', 'user', 'pass', 'mydb'); $db->query("SET NAMES cp1251"); ``` Файлы сохраняю в UTF-8 без BOM, в head страницы ставлю ``. Но когда ввожу русский текст через форму и сохраняю его — в базе он отображается нормально, а на странице — нет. Если же ввожу данные напрямую через phpMyAdmin, то на странице они показываются корректно. Пробовал менять `cp1251` на `utf8` — тогда в базе всё ломается, а на странице нормально. Пробовал `SET NAMES utf8mb4` — вообще перестало работать. Что я делаю не так? Как сделать так, чтобы русский язык работал везде: и в базе, и на странице, и при отправке форм? Может, есть какой-то чек-лист или полная инструкция для чайников? Заранее спасибо! Уже голову сломал. <<>>

Полная настройка UTF-8 в связке PHP + MySQL

Ваша проблема типична для начинающих: кодировка должна быть единой на всех уровнях — от базы данных до HTML-страницы. Если хотя бы один компонент "выпадает" из UTF-8, появляются кракозябры. Вот пошаговый чек-лист, как исправить ситуацию раз и навсегда.

1. База данных и таблицы

Убедитесь, что сама база данных, все таблицы и текстовые поля используют кодировку utf8mb4 (а не utf8, так как utf8 в MySQL не поддерживает эмодзи и некоторые редкие символы).

-- Проверить кодировку БД
SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = 'mydb';

-- Создать БД правильно
CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- Изменить существующую
ALTER DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- Для каждой таблицы
ALTER TABLE mytable CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

2. Подключение к MySQL из PHP

После установки соединения обязательно задайте кодировку через set_charset() — это правильный способ, в отличие от SET NAMES через запрос. Используйте utf8mb4.

$db = new mysqli('localhost', 'user', 'pass', 'mydb');

// Устанавливаем кодировку соединения (ОБЯЗАТЕЛЬНО)
if (!$db->set_charset('utf8mb4')) {
    die('Ошибка установки кодировки: ' . $db->error);
}

Не используйте $db->query("SET NAMES utf8mb4") — это не гарантирует корректную работу с подготовленными запросами и может привести к SQL-инъекциям при неправильной обработке.

3. HTML-страница

Убедитесь, что ваш PHP-файл сохранён в кодировке UTF-8 без BOM (используйте редактор вроде VS Code, Sublime или PhpStorm). В шапке HTML обязательно укажите:

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Мой сайт</title>
</head>

4. HTTP-заголовки

Иногда браузер игнорирует meta-тег, если сервер присылает другой заголовок. Добавьте в начало PHP-скрипта:

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

5. Формы и отправка данных

Когда пользователь отправляет форму, данные приходят в той же кодировке, что и страница. Если страница в UTF-8, то и данные будут в UTF-8. Ничего дополнительно конвертировать не нужно. Просто используйте подготовленные запросы:

$stmt = $db->prepare("INSERT INTO users (name) VALUES (?)");
$stmt->bind_param('s', $_POST['name']);
$stmt->execute();

6. Проверка результата

Если после всех настроек всё равно кракозябры — проверьте, что данные в БД действительно в UTF-8. В phpMyAdmin выполните:

SELECT name, HEX(name) FROM users LIMIT 1;

Для русской буквы "А" (кириллица) HEX должен быть D090. Если видите C0 — это CP1251, значит данные были вставлены до исправления кодировки таблицы. В этом случае удалите старые данные и введите заново.

Итоговый чек-лист:

  • БД и таблицы: utf8mb4
  • Соединение PHP: set_charset('utf8mb4')
  • Файлы PHP: UTF-8 без BOM
  • HTML: <meta charset="UTF-8">
  • HTTP-заголовок: Content-Type: text/html; charset=utf-8
  • Формы: без ручной конвертации, только подготовленные запросы

Следуйте этому списку — и кракозябры исчезнут навсегда.

<<>>

Альтернативный подход: использование PDO и глобальной настройки

Если вы используете PDO вместо mysqli, настройка UTF-8 делается один раз при создании подключения через DSN и массив опций. Это удобнее, чем каждый раз вызывать set_charset().

$dsn = 'mysql:host=localhost;dbname=mydb;charset=utf8mb4';
$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES => false,
];

$pdo = new PDO($dsn, 'user', 'pass', $options);

Параметр charset=utf8mb4 в DSN автоматически устанавливает кодировку соединения. Отключение эмуляции подготовленных запросов (ATTR_EMULATE_PREPARES => false) заставляет PDO использовать реальные prepared statements MySQL, что повышает безопасность и совместимость с UTF-8.

Дополнительно рекомендую установить кодировку для всей сессии MySQL при первом запросе:

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

Этот метод особенно полезен, если вы работаете с фреймворками (Laravel, Symfony) — они обычно используют PDO и уже имеют встроенную поддержку UTF-8. Просто проверьте настройки в файле .env или конфигурации базы данных.

Важно: после смены кодировки соединения убедитесь, что все существующие данные в таблицах перекодированы. Используйте команду ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4, как описано в первом ответе.