#Когда возникает ошибка
При шаблоне с модификатором /u регулярное выражение PHP ожидает корректную последовательность UTF-8. Если в полученной строке есть недопустимые байты, preg_match() может вернуть false с ошибкой PREG_BAD_UTF8_ERROR. Не путайте false с целым 0: ноль означает, что совпадений нет при корректном выполнении.
#Причины и признаки
| Возможная причина | Как узнать | Что делать |
|---|---|---|
| Вход не в UTF-8Данные из старой базы, файла или внешней системы приходят, например, в Windows-1251. | mb_check_encoding($text, "UTF-8") возвращает false. | Выясните реальную кодировку источника и преобразуйте в UTF-8. |
Строка обрезана посередине символаКод режет сырые байты substr() и случайно оставляет половину многобайтного символа. | Исходная строка допустима, а обрезанный фрагмент — нет. | Измените разбиение на операции с учётом символов или границ UTF-8. |
| Другой код ошибки PCREШаблон содержит ошибку либо превышен лимит выполнения. | preg_last_error() выдаёт не PREG_BAD_UTF8_ERROR. | Исправляйте соответствующую проблему, не меняя кодировку вслепую. |
#Пошаговая диагностика и устранение
- Отличите отсутствие совпадения от ошибкиВ тестовой копии после вызова
preg_match()сохраните результат в переменную и проверьте строго:=== falseозначает сбой,=== 0— отсутствие совпадения,=== 1— совпадение. Рядом вызовитеpreg_last_error()илиpreg_last_error_msg(), чтобы получить код. Не сравнивайте простоif (!$result), иначе ошибку примете за нормальный ответ. - Убедитесь, что шаблон требует UTF-8Откройте PHP-файл в редакторе, найдите вызов
preg_matchи проверьте завершающий модификаторuв шаблоне. Для Unicode-категорий, таких как\p{L},/uобычно нужен. Удаление/uможет изменить смысл сопоставления, поэтому не используйте его как обычное исправление некорректной строки. - Определите источник строки и назначенную кодировкуПроследите, откуда взялось значение: файл CSV, HTTP-ответ, поле базы данных. Проверьте метаданные источника, параметры кодировки соединения с БД и действительный формат файла. Название кодировки нельзя достоверно угадать по одному набору байтов; проверка UTF-8 сообщает только допустимость UTF-8.
- Проверьте валидность байтов на копии данныхУбедитесь, что в PHP включено расширение mbstring (
php -mв терминале CLI, отдельно проверить web-PHP), и вызовитеmb_check_encoding($text, "UTF-8"). Если функции нет, не устанавливайте расширение на рабочем сервере без согласования: обратитесь к администратору. Не публикуйте текст, если в нём персональные сведения. - Преобразуйте только подтверждённую старую кодировкуЕсли документы или параметры источника подтверждают Windows-1251, примените
mb_convert_encoding($text, "UTF-8", "Windows-1251")в копии данных. Убедитесь, что русские буквы отображаются без замены на «?». Не используйте «автоматическое исправление» и не заменяйте неверные байты пустыми символами без разрешения: это может изменить проверяемое содержимое. - Исправьте место, где повреждается строкаЕсли на входе строка корректна, но после
substr, чтения ограниченного числа байтов либо сетевой передачи она стала неверной, найдите точное место обрезки. Для UTF-8 используйте операции с символами, напримерmb_substr, задав кодировку. Для бинарных данных/uвообще не должен применяться; разделите обработку текста и бинарного потока. - Запустите проверку и верните исходный вариант при сбоеВ отдельном тестовом файле проверьте корректную строку, некорректные байты и строку без совпадений. Убедитесь, что код различает три результата; затем прогоните регрессионные проверки на копии реальных данных. Если преобразование исказило содержимое, откатите исходные байты и уточните кодировку, вместо повторной конвертации уже повреждённого файла.
#Проверяемый пример и команды
<?php
$subject = "Привет";
$found = preg_match('/\p{L}+/u', $subject, $matches);
if ($found === false) {
throw new RuntimeException('Ошибка PCRE: ' . preg_last_error_msg());
}
echo ($found === 1 ? 'Совпадение' : 'Совпадений нет') . PHP_EOL;
// Дополнительная проверка — только если установлено mbstring:
if (function_exists('mb_check_encoding')) {
var_export(mb_check_encoding($subject, 'UTF-8'));
echo PHP_EOL;
}#Проверка исправления и действия при неудаче
Исправление выполнено, если preg_match для правильного текста возвращает 0 или 1, preg_last_error() равен PREG_NO_ERROR, а некорректные байты обрабатываются явно. Если ошибка повторяется, сохраните только диагностические коды и безопасный небольшой образец байтов для разработчика.