#Симптомы, область применения и причины
PHP json_encode отказывается преобразовать строку с неверной последовательностью UTF-8, часто после чтения старой базы CP1251 или обработки двоичных данных. Диагностика источника байтов, исправление кодировки и безопасные параметры JSON. Отдельно отличайте невалидные последовательности UTF-8 от обычного экранирования кириллицы в JSON: запись вида \u041f сама по себе не свидетельствует об ошибке и корректно декодируется.
| Возможная причина | Как узнать | Что делать |
|---|---|---|
| Данные пришли из другой кодировкиСтрока CP1251, ISO-8859-1 или бинарные байты передаются как UTF-8. | json_last_error_msg() сообщает Malformed UTF-8; сбой на конкретной записи. | Определить кодировку источника и преобразовать однократно. |
| База/подключение использует неодинаковые настройкиСервер хранит данные в одном представлении, а соединение отдаёт их в другом. | Ошибки возникают на полях отдельных старых записей; новые строки работают. | Проверить кодировку колонок и клиентского соединения, исправить импорт в тестовой копии. |
| Массив содержит не текст, а бинарный файлИзображение или произвольные байты попали в структуру json_encode как строка. | Сбой зависит от файла; база текстовых полей в порядке. | Передавать файл отдельно либо в явном двоичном представлении. |
#Безопасная диагностика и пошаговое исправление
- Подтвердите именно ошибку кодировкиВ тестовом месте сразу после неудачного
json_encode($data)проверьте, действительно ли возвращеноfalse; затем вызовитеjson_last_error()иjson_last_error_msg(). Не публикуйте сами данные, поскольку они могут содержать персональные сведения. Если там ошибка глубины или рекурсивной структуры, действия по смене кодировки не подходят. - Переменная $data — данные вашей программы, а не готовое содержимое для копированияPHPПеременная $data — данные вашей программы, а не готовое содержимое для копирования. Код только для закрытой диагностики.
<?php $result = json_encode($data); if ($result === false) { error_log('JSON code='.json_last_error().'; message='.json_last_error_msg()); }
- Найдите конкретное поле с неверными байтамиНа тестовой копии проверьте входящие строки через
mb_check_encoding($value, "UTF-8"), если установлено расширение mbstring. Для массива проверяйте вложенные текстовые значения отдельно в контролируемом обходе, без записи исходных полей в общий лог. При ошибке фиксируйте техническое имя поля и идентификатор тестовой записи, а не её текст. Не применяйте функции к файлам изображения как к строкам UTF-8. - Требуется расширение mbstringPHPТребуется расширение mbstring. Переменная $value — конкретное проверяемое поле; не логируйте его содержимое.
<?php if (is_string($value) && !mb_check_encoding($value, 'UTF-8')) { error_log('Invalid UTF-8 in validated text field'); }
- Уточните происхождение данных и первоначальную кодировкуПосмотрите, откуда пришла строка: CSV-файл, форма, API, таблица MySQL, старый архив. Известная кодировка источника важнее автоматического угадывания. Если файл записан в Windows-1251, преобразуйте его при импорте с явным указанием CP1251 и сохраните копию исходных байтов. Не преобразовывайте один и тот же текст дважды: так можно получить нечитаемые символы, хотя JSON начнёт формироваться.
- Проверьте соединение с базой, а не только столбецУ MySQL проверьте кодировку базы, таблицы, колонок и клиентского соединения. В приложении с PDO MySQL используйте согласованный
charset=utf8mb4в DSN при подключении (для поддерживаемой конфигурации). Прежде чем переводить существующие поля, сделайте резервную копию базы и проверку нескольких исходных записей. Глобальная команда перевода всех таблиц без анализа фактических байтов может испортить данные.
- Преобразуйте только подтверждённые строкиЕсли установлено, что конкретная строка действительно CP1251, примените к ней
mb_convert_encoding($value, "UTF-8", "Windows-1251")при импорте или в отдельной миграции. Сравните русские буквы с оригиналом до и после. Если исходная кодировка неизвестна, остановитесь и получите исходный файл или описание поставщика; использование автоматического определения для массового исправления опасно. - Переменная $legacyText — именно текст исходной Windows-1251PHPПеременная $legacyText — именно текст исходной Windows-1251.
JSON_THROW_ON_ERRORтребует обработки JsonException в реальном приложении.<?php // ТОЛЬКО если источник достоверно имеет кодировку Windows-1251: $utf8 = mb_convert_encoding($legacyText, 'UTF-8', 'Windows-1251'); $json = json_encode(['text' => $utf8], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
- Не используйте подмену битых символов как исправление хранилищаФлаги
JSON_INVALID_UTF8_SUBSTITUTEиJSON_INVALID_UTF8_IGNOREпозволяют получить JSON даже с повреждённой строкой, но первый заменяет символы, а второй теряет байты. Применяйте их только по согласованной политике приложения, где допустима потеря части текста, и записывайте факт очистки без исходных персональных данных. Не считайте это восстановлением правильной кодировки.
- Проверьте итоговый JSON и откатите неудачный импортПроверьте, что json_encode теперь возвращает строку, JSON повторно декодируется, а кириллица читается без потерь. Прогоните несколько старых и новых записей; если старые буквы искажены, верните тестовую копию из резервной копии и исправьте сам процесс импорта. Не меняйте колонку базы наугад, пока не выяснено, какие байты там реально находятся.
#Как проверить результат и что делать дальше
Успех — ошибочный текстовый элемент получает настоящую UTF-8 последовательность, json_encode создаёт корректный JSON и после декодирования сохраняется читаемый текст. Если ошибка остаётся, проверьте остальные поля и не переводите двоичные значения как кириллицу.