PHP: полный мануал с примерами, функциями и ООП
Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Дерево руководства
Глава 94 · От основ к практике
Iconv: кодировки, символы и MIME-заголовки
Полный раздел Iconv: известные байты → читаемый текст, 11 документированных API-страниц, все настройки/константы, потери и ошибки.
Импорт товара: увидеть буквы вместо чужих байтов
Поменять кодировку байтов, сохранив текст
Старая выгрузка партнёра содержит слово Чай в известной Windows-1251. Сайт хранит текст в UTF-8.
- 1Вход Windows-1251↓
Исходная кодировка известна из договора обмена, а не угадана по имени файла.
- 2iconv↓
Декодирует исходные байты и кодирует те же символы в UTF-8.
- 3Новые байты UTF-8↓
Слово остаётся Чай, но его бинарное представление и длина в байтах меняются.
- 4Проверка результата
Отказ или потеря символов должны быть обработаны явно, до сохранения выгрузки.
Строка PHP не хранит автоматически название кодировки. TRANSLIT и IGNORE могут менять или терять данные; это отдельная политика приложения.
Вы получили выгрузку от старой учётной системы. В её договоре сказано: текст Windows-1251. Ваш сайт работает в UTF-8. Задача — перевести байты один раз на входе, затем обрабатывать обычный UTF-8. Iconv — семейство функций для такого преобразования, подсчёта/поиска символов в выбранной кодировке и работы с MIME-заголовками.
- Система поставщика: d7 e0 e9
- Договор сообщает Windows-1251
- iconv читает буквы Ч а й и записывает их в UTF-8
- Приложение получает d0 a7 d0 b0 d0 b9 → Чай
PHP-строка хранит байты, а не прикреплённый к ним ярлык кодировки. Запись $raw называет переменную; iconv(...) вызывает функцию; три значения в скобках — её аргументы; = сохраняет возвращённую строку. Запись "\xD7" задаёт конкретный байт, чтобы опыт не зависел от кодировки файла редактора.
<?php
declare(strict_types=1);
$raw = "\xD7\xE0\xE9";
$name = iconv('Windows-1251', 'UTF-8', $raw);
if ($name === false) { echo "Выгрузка не прочитана\n"; }
else { echo 'Товар: ', $name, "\n"; }
Ожидаемый результат
Товар: Чай
В примере кодировка известна заранее. Проверка === false нужна, потому что iconv может отказать; она не путает отказ с пустым текстом. Здесь нет файла, формы, HTTP или БД — только видимая проверка преобразования.
Кодировка — договор о том, какие байты означают какие символы. UTF-8 умеет представлять весь Unicode; старые однобайтные таблицы имеют более узкий набор. Обратное преобразование в Windows-1251 может отказать на emoji даже при корректном исходном UTF-8. Не подменяйте это «починкой» текста удалением неподходящих букв.
Если сайт, API, БД и файлы уже согласованно используют UTF-8, дополнительный iconv на каждом шаге обычно не нужен. Храните один внутренний формат; преобразуйте только на известной внешней границе. Начальные понятия: переменная, аргументы и результат функции, условие. Подробный контракт первого вызова — iconv.
Установка: расширение PHP и системная библиотека — два слоя
Расширение iconv — интерфейс PHP к реализации Iconv, которая выполняет преобразование. Поэтому совпадение версии PHP ещё не означает одинаковые списки кодировок и правила приближённой замены. Современные POSIX-системы часто содержат iconv в libc; если её нет, используется отдельная библиотека, например GNU libiconv. На Windows поддержка определяется составом выбранной сборки.
- Нужный PHP/SAPI запущен
- extension_loaded("iconv") подтверждает расширение
- ICONV_IMPL / ICONV_VERSION показывают backend
- Пробная конвертация проверяет именно необходимую пару
| Официальный шаг setup | Практическое значение |
|---|---|
| Requirements | Требуется реализация Iconv: системная либо внешняя. GNU libiconv — один вариант, не единственное допустимое окружение. |
| Installation | При сборке PHP расширение включено по умолчанию. --without-iconv отключает, --with-iconv[=DIR] выбирает поддержку/расположение внешней библиотеки. Это configure-параметры сборки, не значения php.ini. |
| Готовые сборки/пакеты | Проверьте документацию вашего поставщика PHP и фактическую availability. Не ищите произвольную iconv.dll и не добавляйте extension=iconv вслепую: расширение может быть встроено. |
| CLI и web | Команда php может запускать другую версию/ini, чем FPM, Apache или IIS. Проверять надо тот процесс, где выполняется импорт. Закрытую диагностику после проверки убирают. |
| Имена кодировок | Зависят от реализации. Например, официальный Manual отдельно оговаривает ISO8859-1 вместо ISO-8859-1 на некоторых IBM AIX. Проверяйте реальные имена своей системы. |
<?php
declare(strict_types=1);
var_dump(extension_loaded('iconv'));
var_dump(defined('ICONV_IMPL'), defined('ICONV_VERSION'));
$converted = iconv('Windows-1251', 'UTF-8', "\xD7\xE0\xE9");
echo $converted === 'Чай' ? "Учебная пара кодировок работает\n" : "Пара не прошла проверку\n";
Ожидаемый результат
bool(true)
bool(true)
bool(true)
Учебная пара кодировок работает
Успех подтверждает данную пару и данные в этой среде. В проверенном PHP 8.5.11 используется libiconv 1.19. На другой машине сначала проверьте availability, затем переходите к вызовам; не публикуйте подробности окружения в открытом endpoint.
У PHP Iconv нет универсальной iconv_list() для всех имён. mb_list_encodings относится к другому backend и не подтверждает поддержку тех же псевдонимов в Iconv. Консольная программа iconv, если установлена, также может оказаться другой реализацией, чем PHP-расширение. Не делайте заключение только по команде из shell.
Вся эта глава проверена на одной сборке PHP 8.5.11 с project ini и libiconv 1.19. Платформенные различия подтверждаются документацией; запусков на glibc/AIX здесь не заявлено.
Четыре константы: диагностика реализации и флаги MIME
Константа читается без знака $, например ICONV_MIME_DECODE_STRICT. Здесь две константы сообщают, чем работает расширение, а две выбирают режим MIME-декодера. Первые не делают преобразований, вторые не относятся к обычному iconv.
| Константа | Контракт |
|---|---|
| ICONV_IMPL (string) | Название реализации Iconv; формат значения зависит от сборки. Не разбирать его как обязательное фиксированное слово. |
| ICONV_VERSION (string) | Версия используемой реализации, не PHP и не поддерживаемого Unicode. |
| ICONV_MIME_DECODE_STRICT (int, 1) | Строгий разбор MIME encoded-word по RFC 2047; только режим mime_decode / mime_decode_headers. |
| ICONV_MIME_DECODE_CONTINUE_ON_ERROR (int, 2) | Продолжать разбор при ошибке; возможны необработанные фрагменты. Объединяется со STRICT через |. |
<?php
declare(strict_types=1);
$mode = ICONV_MIME_DECODE_STRICT | ICONV_MIME_DECODE_CONTINUE_ON_ERROR;
echo $mode, "\n";
echo iconv_mime_decode('=?UTF-8?B?0KfQsNC5?=', $mode, 'UTF-8'), "\n";
Ожидаемый результат
3
Чай
| — побитовое ИЛ: бит 1 и бит 2 включены одновременно. Это один аргумент mode, а не два последовательных вызова. Успех на корректной теме не отменяет более мягкую политику при повреждённом входе.
Для отчёта о совместимости сохраните PHP_VERSION, ICONV_IMPL, ICONV_VERSION и пример конкретных входных байтов без секретов. Одна только строка версии не заменяет проверку требуемой операции. Политика continue нужна для отображения проблемной корреспонденции; бизнес-решение о приёме данных формулируется отдельно.
Байт, кодовая точка, графема: выбрать правильную единицу
Пусть поле формы ограничено десятью символами, экспорт — двадцатью байтами, а карточка — шестью видимыми знаками. Эти три требования различны. В UTF-8 символ может занимать несколько байтов, а видимый знак — несколько кодовых точек. Iconv умеет считать и выделять символы кодировки, но не выполняет весь набор правил отображения текста.
- e занимает 1 UTF-8 байт
- Отдельный акцент U+0301 занимает 2 байта
- Вместе: strlen=3, iconv_strlen=2
- Одна воспринимаемая графема требует инструмента Intl
| Требование | Подход |
|---|---|
| Лимит сетевого пакета | strlen по байтам; не считать число символов достаточным. |
| Количество/позиция символов | iconv_strlen / iconv_strpos / iconv_substr с явной кодировкой. |
| Превью без разрыва графемы | Intl grapheme_*; для emoji/комбинируемых букв одного iconv_substr недостаточно. |
| Одинаковость составного и разложенного текста | Unicode-нормализация по согласованной политике; iconv не решает её автоматически. |
| Подстрока ASCII-протокола по байтовому offset | Обычный substr/strpos уместен. Главное — не передавать байтовый индекс в символьный API. |
Например, UTF-8 «Чай» занимает 6 байт и 3 символа. substr($name,0,1) оставит половину первой кириллической буквы; последующий iconv справедливо сообщит о повреждении. Исследуйте происхождение байтов, а не подавляйте ошибку конвертера. Подробнее: iconv_strlen, iconv_substr, iconv_strpos, iconv_strrpos.
Ошибка источника, узкий целевой алфавит и опасное «исправление»
Сбой конвертации не всегда означает испорченный файл. Есть как минимум три причины: неверно названа исходная кодировка; входная последовательность действительно повреждена; правильный символ не представим в целевой кодировке. Сначала установите причину, затем выбирайте политику обработки. Переименовать charset или дописать IGNORE — не универсальное решение.
- Сохраняем исходные байты и договор источника
- Проверяем, что API вызван с нужными именами
- Отдельно проверяем целевую представимость
- При отказе сохраняем безопасный ID партии и причину
- Повторяем импорт после исправления источника/договора
| Ситуация | Разумное действие |
|---|---|
| Повреждён UTF-8 | Отклонить/изолировать запись; не превращать незаметно AB и A[ошибочный байт]B в одинаковое значение. |
| Emoji при экспорте в Windows-1251 | Согласовать UTF-8 или явное правило потери для конкретного неключевого поля. Не терять символ молча. |
| Неизвестное имя charset | Исправить договор/конфигурацию; кодировку нельзя выбирать из любого пользовательского ввода. |
| Буквы уже испорчены двойной конвертацией | Найти место первого неверного декодирования. Обратимость не гарантируется, если там уже появились ? или удалённые байты. |
| Notice превращён в исключение | Учитывать установленный обработчик ошибок/фреймворк. false и диагностический канал — два аспекта отказа. |
| Необходим preview повреждённого письма | Continue MIME может сохранить часть исходной оболочки; показывать с HTML-экранированием и пометкой, не объявлять валидным бизнес-вводом. |
UTF-8-валидность и целевая представимость — разные проверки. mb_check_encoding может подтвердить корректность UTF-8, но не возможность записать emoji в UCS-2 или кириллицу в ASCII. Переносимые правила должны обходиться без зависимости от конкретной транслитерации; она может различаться между системными реализациями/окружениями.
В учебных отказах предупреждение перехватывается точечно и handler восстанавливается в finally. В рабочем коде не отключайте глобально E_WARNING/E_NOTICE ради чистого экрана: серверный журнал и понятная ошибка пользователю решают разные задачи. Не пишите весь импорт, пароль, токен или заголовок письма в диагностику. Смотрите контракт обработчиков ошибок и безопасного журнала.
Три настройки и их приоритет: не превращать глобальное состояние в магию
У расширения ровно три специальные ini-директивы. Все по умолчанию пусты, имеют режим INI_ALL и deprecated с PHP 5.6. INI_ALL означает доступность изменения на разных уровнях конфигурации PHP, а не обещание, что любой конкретный хостинг разрешит изменение во всех файлах.
| Директива | Назначение и современная альтернатива |
|---|---|
| iconv.input_encoding | Пустая строка по умолчанию. Старое значение входной кодировки; Manual рекомендует оставить пустым и использовать Core input_encoding. |
| iconv.output_encoding | Пустая строка по умолчанию. Цель output handler; вместо legacy override Core output_encoding. |
| iconv.internal_encoding | Пустая строка по умолчанию. Источник/значение по умолчанию для операций. Manual рекомендует default_charset; Core internal_encoding при явной настройке также участвует в fallback. |
- Непустой iconv.internal_encoding имеет приоритет
- Иначе читается непустой Core internal_encoding
- Затем default_charset
- Если всё пусто — UTF-8; явный аргумент функции предпочтительнее скрытого default
Для input/output действует аналогичная цепочка с соответствующим Core input_encoding/output_encoding. Она проверена по исходнику PHP 8.5.11; getter возвращает эффективный выбор, а ini_get конкретной директивы — её сырую строку. Все эти имена описывают настройки, они не прикрепляют метаданные к $text и не перекодируют значения автоматически.
Например, default_charset=UTF-8 не исправит Windows-1251 внутри переменной и не перекодирует сам по себе $_POST. А изменение internal_encoding внутри валидатора может повлиять на другую библиотеку в том же процессе. В долгоживущем worker состояние сохраняется между заданиями, пока приложение не восстановит его. Передача UTF-8 аргументом обычной текстовой операции гораздо понятнее такой зависимости.
Полные контракты: iconv_get_encoding читает; iconv_set_encoding показывает legacy-запись; ob_iconv_handler применяет конвертацию только на ступени вывода. Копировать старые образцы PHP.net с setter в новый код без учёта deprecated не следует.
BOM и большой файл: обработать границу, а не менять всё подряд
Иногда договор поставщика явно допускает UTF-8 BOM в начале файла. Это байты EF BB BF, обозначающие U+FEFF, а не часть заголовка CSV. Удалять их стоит только в известном месте для известного формата. str_replace по всей строке может изменить содержимое поля и уже не является обработкой сигнатуры.
<?php
declare(strict_types=1);
$csv = "\xEF\xBB\xBFname;qty\nЧай;2\n";
if (str_starts_with($csv, "\xEF\xBB\xBF")) { $csv = substr($csv, 3); }
$text = iconv('UTF-8', 'UTF-8', $csv);
if ($text === false) { throw new RuntimeException('Не UTF-8'); }
echo $text;
Ожидаемый результат
name;qty
Чай;2
Три байта удалены по точной проверенной сигнатуре, поэтому обычный substr здесь уместен. Это не определитель любой кодировки и не удаление всех BOM/невидимых символов. Для UTF-16 нужны свой договор и отдельное преобразование.
Для маленькой ограниченной выгрузки удобна одна конвертация строки. Большой файл нельзя безопасно «ускорить», читая произвольные 1024 байта и каждый раз вызывая iconv: многобайтный символ может попасть на границу порций. Потоковый фильтр convert.iconv.* предназначен для работы в системе streams; его создание, предупреждения, завершение и неполная последняя последовательность тоже требуют проверки.
<?php
declare(strict_types=1);
$stream = fopen('php://temp', 'w+b');
if ($stream === false) { throw new RuntimeException('Не создан поток'); }
try {
fwrite($stream, "\x27\x04\x30\x04\x39\x04"); // Чай в UTF-16LE, без BOM.
rewind($stream);
$filter = stream_filter_append($stream, 'convert.iconv.UTF-16LE/UTF-8', STREAM_FILTER_READ);
if ($filter === false) { throw new RuntimeException('Нет фильтра'); }
$text = stream_get_contents($stream);
if ($text === false) { throw new RuntimeException('Ошибка чтения'); }
echo $text, "\n";
} finally { fclose($stream); }
Ожидаемый результат
Чай
Опыт демонстрирует подключение фильтра, не заявляет ограниченную память всей программы: stream_get_contents здесь всё равно собирает конечную строку. Для большого файла потребителю надо постепенно обрабатывать результат; строки CSV могут содержать переносы внутри кавычек, поэтому простой fgets не заменяет полноценный CSV-парсер.
Исходные данные в примере записаны в временный поток и закрываются; внешних файлов и сети нет. Полный API streams — отдельная категория справочника; эта дополнительная иллюстрация не заменяет его контракт. Проверяйте ошибки и после чтения, не только успешное создание фильтра, особенно если вход завершается половиной символа.
Как выбрать функцию и пройти практику дальше
| Потребность | Куда перейти |
|---|---|
| Прочитать файл в известной старой кодировке | iconv |
| Посчитать символы / сделать превью | iconv_strlen · iconv_substr |
| Найти первое / последнее совпадение | iconv_strpos · iconv_strrpos |
| Сформировать тему письма | iconv_mime_encode |
| Прочитать одно поле / блок заголовков | iconv_mime_decode · iconv_mime_decode_headers |
| Разобраться с defaults старого проекта | iconv_get_encoding · iconv_set_encoding |
| Понять конвертацию всего output buffer | ob_iconv_handler |
Практический маршрут: получите вымышленную Windows-1251 строку товара → переведите в UTF-8 → проверьте символьный/байтовый лимит → подготовьте короткое превью → отдельно сделайте MIME-тему уведомления. Кодировка не должна решать остальные задачи формы, CSV или отправки письма. На каждой границе остаётся свой контракт.
<?php
declare(strict_types=1);
$rawName = "\xD7\xE0\xE9";
$name = iconv('Windows-1251', 'UTF-8', $rawName);
if ($name === false) { throw new RuntimeException('Ошибка импорта'); }
$length = iconv_strlen($name, 'UTF-8');
if ($length === false || $length === 0 || $length > 40) {
throw new RuntimeException('Недопустимая длина названия');
}
if (str_contains($name, "\r") || str_contains($name, "\n")) {
throw new RuntimeException('Название не должно создавать новую строку темы');
}
$subject = iconv_mime_encode('Subject', 'Заказ: ' . $name,
['input-charset' => 'UTF-8', 'output-charset' => 'UTF-8']);
if ($subject === false) { throw new RuntimeException('Тема не подготовлена'); }
echo iconv_mime_decode($subject, ICONV_MIME_DECODE_STRICT, 'UTF-8'), "\n";
Ожидаемый результат
Subject: Заказ: Чай
Это предложенное решение мини-задачи, а не отправка письма. Попробуйте пустое название, строку длиннее 40 символов и вход с CR/LF: каждому отказу соответствует свой этап. Для проверки повреждённого UTF-8 смените договор источника явно; Windows-1251 и UTF-8 имеют разные допустимые последовательности.
Дальнейшие темы: расширенные многобайтные операции, JSON-интеграция и структура ответа, полный маршрут безопасной формы. Текст в HTML дополнительно экранируется, а подготовленная MIME-тема передаётся почтовому компоненту по его собственному контракту.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
iconvПеревести текст между двумя известными кодировками и явно обработать невозможность перевода.
PHP 4.0.5+ · iconv
iconv(string $from_encoding, string $to_encoding, string $string): string|false| Параметр / результат | Что передать и проверить |
|---|---|
| from_encoding | Название реальной кодировки входных байтов; автоматического определения нет. Набор имён определяется системной библиотекой. |
| to_encoding | Целевая кодировка; поддерживаемые //TRANSLIT и //IGNORE добавляются здесь. |
| string | Входная PHP-строка, в том числе бинарные байты/NUL. Не передавайте уже ошибочно перекодированную строку как исходник. |
| Возврат | Новая строка, включая ""; false при ошибке. Исходная строка не изменяется. |
| Версии | API с PHP 4.0.5. Поведение преобразования зависит также от реализации Iconv, не только от версии PHP. |
- Windows-1251: d7 e0 e9
- iconv знает исходную таблицу и целевую UTF-8
- UTF-8: d0 a7 d0 b0 d0 b9 → Чай
Поставщик прислал название товара в Windows-1251, а приложение хранит текст в UTF-8. Первые два аргумента описывают старую и новую кодировки; третий содержит исходные байты. Функция возвращает новую строку и не меняет переменную с исходником. Слово «исходная» здесь принципиально: PHP не угадывает кодировку по внешнему виду букв.
<?php
declare(strict_types=1);
$supplierBytes = "\xD7\xE0\xE9";
$name = iconv('Windows-1251', 'UTF-8', $supplierBytes);
if ($name === false) { throw new RuntimeException('Не удалось прочитать название'); }
echo $name, "\n";
echo bin2hex($supplierBytes), ' -> ', bin2hex($name), "\n";
Ожидаемый результат
Чай
d7e0e9 -> d0a7d0b0d0b9
Слева три байта старого формата, справа шесть байт UTF-8. bin2hex только показывает байты; само преобразование делает iconv. Проверка === false отделяет ошибку от допустимых пустой строки и строки "0".
<?php
declare(strict_types=1);
$line = "SKU-7;Чай;2\r\n";
$export = iconv('UTF-8', 'Windows-1251', $line);
if ($export === false) { throw new RuntimeException('Строка не представима в формате обмена'); }
echo bin2hex($export), "\n";
echo iconv('Windows-1251', 'UTF-8', $export);
Ожидаемый результат
534b552d373bd7e0e93b320d0a
SKU-7;Чай;2
Экспорт пока остаётся переменной, запись файла или отправка идут отдельным шагом. Обратное преобразование подтверждает конкретную строку примера, но не доказывает, что выбранная исходная кодировка верна для любого файла поставщика.
Если целевой алфавит не содержит символа, обычное преобразование отказывает. Суффикс //TRANSLIT у целевой кодировки просит приблизительную замену, иногда несколькими символами. //IGNORE разрешает отбросить непредставимые знаки. Это осознанная потеря данных, а не лечение неизвестной кодировки: для фамилии, реквизитов, артикула, подписи или пароля такое правило обычно недопустимо.
<?php
declare(strict_types=1);
$priceLabel = 'A€B';
echo iconv('UTF-8', 'ASCII//IGNORE', $priceLabel), "\n";
echo iconv('UTF-8', 'ASCII//TRANSLIT', $priceLabel), "\n";
Ожидаемый результат
AB
AEURB
Точный результат TRANSLIT проверен на libiconv 1.19 в данной PHP 8.5.11. Другой backend/окружение может дать иную замену или отказ. Нельзя строить переносимый идентификатор или правило равенства людей на таком результате.
<?php
declare(strict_types=1);
$diagnostic = null;
set_error_handler(static function (int $level, string $message) use (&$diagnostic): bool {
$diagnostic = $level; // Только тип ошибки, без содержимого входной строки.
return true;
}, E_NOTICE | E_WARNING);
try { $text = iconv('UTF-8', 'UTF-8', "A\xFFB"); }
finally { restore_error_handler(); }
echo $text === false && $diagnostic !== null ? "Импорт отклонён\n" : "Принят\n";
Ожидаемый результат
Импорт отклонён
Обработчик ограничен одним вызовом и обязательно снят. В приложении запишите безопасный ID пакета и причину, а не весь текст клиента. Фреймворк может преобразовать notice в ErrorException: тогда учитывается его политика ошибок, а не добавляется @.
Неверное/незавершённое многобайтное слово и непредставимый символ обычно дают E_NOTICE и false; неизвестная пара кодировок — E_WARNING и false. Точные сообщения/поддержка суффиксов зависят от backend. Значения неверного PHP-типа в строгом вызове дают TypeError. Имена кодировок задавайте из доверенного договора/списка: utf8mb4 — имя MySQL, для iconv используйте UTF-8. PHP 8.5 также отклоняет чрезмерно длинные имена кодировок предупреждением и false.
Все байты входа и результата находятся в памяти одновременно; для больших файлов нужен продуманный потоковый конвейер, а не чтение всего файла. Конвертация не экранирует HTML, не нормализует Unicode, не проверяет содержание CSV/JSON и не гарантирует отсутствие управляющих символов. Альтернативы: mb_convert_encoding с его отдельными правилами подстановки; mb_check_encoding для проверки заявленной кодировки; нормализация и графемы относятся к Intl.
Рядом: iconv_strlen, iconv_substr, iconv_get_encoding.
Первый опыт с байтами и кодировкой · Ошибки, потери и границы гарантий.
iconv_strlenИзмерить текст в символах выбранной кодировки, отделив их от байтов и графем.
PHP 5+ · iconv
iconv_strlen(string $string, ?string $encoding = null): int|false| Параметр / результат | Что передать и проверить |
|---|---|
| string | Строка в заявленной кодировке. |
| encoding | Явное имя или null → эффективная internal_encoding. Nullable с PHP 8.0. |
| Возврат | Количество символов, 0 для пустой строки; false при ошибке кодировки. |
| Ошибки | Notice/warning и false при ошибке конвертера; TypeError при неподходящем PHP-типе в strict_types. |
- Чай в UTF-8 занимает 6 байт
- strlen → 6
- iconv_strlen с UTF-8 → 3
- Видимые графемы — отдельная модель
Ограничение названия товара «до 40 символов» нельзя проверять strlen: кириллица в UTF-8 занимает несколько байт на букву. iconv_strlen считает символы указанной кодировки. Это всё ещё не число видимых знаков: буква с отдельным ударением/акцентом состоит из нескольких кодовых точек.
<?php
declare(strict_types=1);
$name = 'Чай';
echo strlen($name), " bytes\n";
echo iconv_strlen($name, 'UTF-8'), " characters\n";
echo iconv_strlen("e\u{0301}", 'UTF-8'), " code points\n";
Ожидаемый результат
6 bytes
3 characters
2 code points
Комбинирующий акцент U+0301 здесь указан явно. Если продукт считает то, что человек воспринимает одной буквой, нужен grapheme_strlen из Intl, а не замена лимита байтов лимитом кодовых точек.
<?php
declare(strict_types=1);
$name = 'Чай с мятой';
$length = iconv_strlen($name, 'UTF-8');
if ($length === false) { throw new RuntimeException('Неверная кодировка'); }
$accepted = $length <= 20 && strlen($name) <= 64;
echo $accepted ? "Название подходит\n" : "Слишком длинно\n";
var_dump(iconv_strlen('', 'UTF-8'));
Ожидаемый результат
Название подходит
int(0)
20 — правило продукта по символам, 64 — условный байтовый бюджет внешнего протокола; это разные ограничения. Пустая строка допустима для функции, но форма отдельно может требовать непустое название.
Значение encoding=null/пропуск берётся из эффективной внутренней настройки Iconv; nullable появился в PHP 8.0. Передача UTF-8 делает библиотечную функцию независимой от чужих ini_set. Невалидная последовательность или неизвестная кодировка дают false и диагностическое сообщение. Подсчёт проходит строку: при проверке нескольких условий сохраните результат, не считайте заново в каждой итерации.
Рядом: iconv_substr, mb_strlen.
Первый опыт с байтами и кодировкой · Ошибки, потери и границы гарантий.
iconv_strposНайти первое совпадение и получить позицию в символах, включая корректный результат 0.
PHP 5+ · iconv
iconv_strpos(string $haystack, string $needle, int $offset = 0, ?string $encoding = null): int|false| Параметр / результат | Что передать и проверить |
|---|---|
| haystack / needle | Где искать / что искать; обе строки в одной encoding, регистр учитывается. |
| offset | Старт в символах. 0 — начало; отрицательные с PHP 7.1 отсчитываются от конца. |
| encoding | null/пропуск → внутреннее значение; nullable с PHP 8.0. |
| Возврат | Индекс первого совпадения от начала всей строки, либо false. Пустая needle даёт false в PHP 8.5.11. |
| Ошибки | PHP 8.5: ValueError для offset вне допустимого диапазона; ошибка кодировки — false и warning/notice. |
- Чай, кофе, чай
- кофе начинается на символе 5
- Возвращается 5, не число предшествующих байтов
Нужно найти первое вхождение слова в русском названии, затем работать с позицией как с номером символа. iconv_strpos возвращает такой номер, начиная с нуля. Обычный strpos возвращает байтовое смещение; смешивать эти две системы координат нельзя.
<?php
declare(strict_types=1);
$title = 'Чай, кофе, чай';
var_dump(iconv_strpos($title, 'Чай', 0, 'UTF-8'));
var_dump(iconv_strpos($title, 'кофе', 0, 'UTF-8'));
var_dump(iconv_strpos($title, 'сок', 0, 'UTF-8'));
Ожидаемый результат
int(0)
int(5)
bool(false)
Ноль — успешный поиск в начале. Проверяйте !== false; условие if ($position) пропустит найденное первое слово. Поиск чувствителен к регистру: Чай и чай различаются.
<?php
declare(strict_types=1);
$labels = 'чай|кофе|чай';
$first = iconv_strpos($labels, 'чай', 0, 'UTF-8');
if ($first === false) { throw new RuntimeException('Первой метки нет'); }
$second = iconv_strpos($labels, 'чай', $first + 1, 'UTF-8');
echo $first, '/', $second, "\n";
echo iconv_strpos($labels, 'чай', -3, 'UTF-8'), "\n";
Ожидаемый результат
0/9
9
offset — точка, с которой начинается просмотр; возвращаемая позиция всё равно отсчитывается от начала всей строки. +1 допускает перекрывающиеся совпадения; для неперекрывающихся прибавьте длину непустой искомой строки.
Отрицательный offset поддерживается с PHP 7.1 и отсчитывается от конца; в PHP 8.5 смещение за пределы длины вызывает ValueError, ровно в конце поиск непустой строки возвращает false. В отличие от современного strpos, пустая needle у iconv_strpos в проверенной PHP 8.5.11 возвращает false. Для пользовательского поиска определите отдельное правило пустого запроса. Обе строки должны быть в encoding; функция не перекодирует needle из иной кодировки и не приводит Unicode к одной форме.
Историческая фраза некоторых страниц о преобразовании числовой needle в код символа не подходит современному строгому коду: параметр имеет тип string. Передавайте строку явно; не рассчитывайте на int→символ. Ошибки входной последовательности дают false с диагностикой, отсутствие совпадения — false без ошибки. Альтернативы: mb_strpos и байтовый strpos, если ищется ASCII-разделитель, а результат используется именно как байтовая позиция.
Рядом: iconv_strrpos, iconv_substr.
Первый опыт с байтами и кодировкой · Ошибки, потери и границы гарантий.
iconv_strrposНайти последнее вхождение строки и безопасно отделить суффикс по символьной позиции.
PHP 5+ · iconv
iconv_strrpos(string $haystack, string $needle, ?string $encoding = null): int|false| Параметр / результат | Что передать и проверить |
|---|---|
| haystack / needle | Строка и искомая строка в общей кодировке. |
| encoding | Третий аргумент — кодировка, не offset; null → internal, nullable с PHP 8.0. |
| Возврат | Позиция последнего совпадения в символах, начиная с 0; false при отсутствии/ошибке. |
| Ограничения | Регистрозависимый поиск; пустая needle даёт false в PHP 8.5.11; диагностические ошибки кодировки нужно различать с обычным отсутствием. |
- чай / набор / 2026
- Последний разделитель начинается на 11
- iconv_substr(...,0,11) оставляет чай / набор
Когда в названии несколько одинаковых разделителей, iconv_strrpos выбирает последний. Здесь нет аргумента offset: третьим идёт encoding. Для отсечения суффикса найденную позицию удобно передать iconv_substr с той же кодировкой.
<?php
declare(strict_types=1);
$label = 'чай / набор / 2026';
var_dump(iconv_strrpos($label, ' / ', 'UTF-8'));
var_dump(iconv_strrpos('чай', 'чай', 'UTF-8'));
Ожидаемый результат
int(11)
int(0)
Результат — начало последнего совпадения, отсчитанное от начала строки. Значение 0 также является успехом.
<?php
declare(strict_types=1);
$label = 'чай / набор / 2026';
$position = iconv_strrpos($label, ' / ', 'UTF-8');
$visible = $position === false ? $label : iconv_substr($label, 0, $position, 'UTF-8');
echo $visible, "\n";
var_dump(iconv_strrpos($label, 'нет', 'UTF-8'));
Ожидаемый результат
чай / набор
bool(false)
Это фиксированный формат метки, а не парсер пути/URL. При отсутствии разделителя оставляем весь текст. Оба этапа используют символы, поэтому кириллица не сдвигает границу.
encoding=null разрешён с PHP 8.0 и использует внутреннюю настройку. Пустая needle возвращает false в PHP 8.5.11; отсутствующее совпадение — тоже false. Неподходящая кодировка/последовательность сопровождает false диагностикой. Нет режима без учёта регистра, offset и Unicode-нормализации. Для другой семантики смотрите mb_strrpos; его сигнатура отличается, механически переносить аргументы нельзя.
Рядом: iconv_strpos, iconv_substr.
Первый опыт с байтами и кодировкой · Ошибки, потери и границы гарантий.
iconv_substrИзвлечь фрагмент без разрезания многобайтного символа и правильно понять отрицательные границы.
PHP 5+ · iconv
iconv_substr(string $string, int $offset, ?int $length = null, ?string $encoding = null): string|false| Параметр / результат | Что передать и проверить |
|---|---|
| string | Строка в encoding. |
| offset | Нулевой индекс символа; отрицательное значение — от конца. В PHP 8.5 слишком далёкие позиции ограничиваются началом/концом. |
| length | null/пропуск — остаток; положительное — максимум символов; 0 — пусто; отрицательное исключает столько символов в конце. Nullable с PHP 8.0. |
| encoding | null/пропуск → internal; nullable с PHP 8.0. |
| Возврат | Фрагмент в исходной кодировке, включая ""; false и диагностика при ошибке кодировки. |
- Чай с мятой
- offset=0, length=3
- Окно охватывает Ч, а, й
- Результат Чай в той же UTF-8
Для короткого превью нужен начальный фрагмент текста без разрезания байтов кириллицы. offset задаёт первый символ, length — количество символов. Исходная строка остаётся прежней; функция возвращает новую в той же кодировке, а не обязательно в UTF-8.
<?php
declare(strict_types=1);
$text = 'Чай с мятой';
echo iconv_substr($text, 0, 3, 'UTF-8'), "\n";
echo iconv_substr($text, -5, null, 'UTF-8'), "\n";
echo iconv_substr('абвг', 1, -1, 'UTF-8'), "\n";
Ожидаемый результат
Чай
мятой
бв
Нулевой offset начинает с первой буквы, -5 берёт старт за пять символов до конца, null означает всё до конца. Отрицательная length отбрасывает указанное число символов с конца, а не идёт назад.
<?php
declare(strict_types=1);
$description = 'Чай с мятой';
$limit = 5;
$length = iconv_strlen($description, 'UTF-8');
if ($length === false) { throw new RuntimeException('Неверный текст'); }
$preview = $length > $limit ? iconv_substr($description, 0, $limit, 'UTF-8') . '…' : $description;
echo $preview, "\n";
var_dump(iconv_substr('абвг', 4, null, 'UTF-8'));
var_dump(iconv_substr('абвг', 99, null, 'UTF-8'));
Ожидаемый результат
Чай с…
string(0) ""
string(0) ""
Многоточие добавляет ещё один знак к пяти выбранным. Это ограничение по кодовым точкам, не по словам/графемам: составной emoji или буква с комбинирующим знаком всё ещё может оказаться разделённой.
Нулевой length даёт пустую строку; слишком большой length ограничивается концом; слишком отрицательный offset начинается с начала. Если отрицательная length оставила конец раньше начала, получается пустой фрагмент. В PHP 7.0.11 отдельно изменилось поведение offset, равного длине; в PHP 8.0 length и encoding стали nullable. Не смешивайте байтовые позиции strpos с символьным offset этой функции.
В PHP 8.5 реализация сначала подсчитывает символы, затем извлекает диапазон: многократный iconv_substr($text,$i,1) в цикле может повторно просматривать длинную строку и давать квадратичную суммарную работу. Для краткого превью один вызов подходит; для полного разбора нужен алгоритм одного прохода/специализированный Unicode-инструмент. mb_substr — близкий по задаче API, grapheme_substr из Intl режет по графемам.
Рядом: iconv_strlen, iconv_strpos, iconv_strrpos.
Первый опыт с байтами и кодировкой · Ошибки, потери и границы гарантий.
iconv_mime_encodeПодготовить MIME-поле с не-ASCII текстом и корректными переносами, не выдавая его за готовое письмо.
PHP 5+ · iconv
iconv_mime_encode(string $field_name, string $field_value, array $options = []): string|false| Параметр / результат | Что передать и проверить |
|---|---|
| field_name | Имя поля без двоеточия, например фиксированное Subject. Не произвольное имя из запроса. |
| field_value | Значение в input-charset. CR/LF пользовательского ввода отклоняются заранее. |
| options | Ассоциативный массив пяти ключей из таблицы ниже; регистр ключей значим. |
| Возврат | Полное поле "Name: ...", возможно folded; false при ошибке. Не только текст значения. |
| Ошибки | Notice/warning + false при ошибке преобразования/лимита строки; TypeError для неверных типов параметров. |
- Заказ готов в UTF-8
- UTF-8 байты упакованы как encoded-word B/Q
- Добавлены Subject: и переносы при необходимости
- Декодер восстанавливает Subject: Заказ готов
Письмо о заказе должно показать русскую тему даже там, где заголовки передаются в ASCII-совместимом формате. iconv_mime_encode создаёт целое поле, включая имя Subject и двоеточие. Внутри используются encoded-word по RFC 2047: метка кодировки, B/Q и закодированные байты. Это не шифрование и не защита содержимого.
<?php
declare(strict_types=1);
$field = iconv_mime_encode('Subject', 'Заказ готов', [
'input-charset' => 'UTF-8',
'output-charset' => 'UTF-8',
]);
if ($field === false) { throw new RuntimeException('Тема не закодирована'); }
echo $field, "\n";
echo iconv_mime_decode($field, ICONV_MIME_DECODE_STRICT, 'UTF-8'), "\n";
Ожидаемый результат
Subject: =?UTF-8?B?0JfQsNC60LDQtyDQs9C+0YLQvtCy?=
Subject: Заказ готов
Первая строка — фактическое содержимое поля для передачи: Subject — его имя, UTF-8 — кодировка исходного текста, B — упаковка байтов в Base64, заключительное ?= закрывает encoded-word. Вторая строка показывает, что прочитает почтовая программа после декодирования. Само письмо пока никуда не отправляется. Если API отправки принимает только значение темы, нельзя передать ему целое поле Subject:; иначе имя продублируется.
| Ключ options (регистр важен) | Значение и стандартное поведение |
|---|---|
| scheme | Строка B (base64, по умолчанию) либо Q (вариант quoted-printable для encoded-word). Для программного выбора используйте эти точные значения. |
| input-charset | Строка: кодировка обоих аргументов field_name и field_value. По умолчанию эффективная iconv.internal_encoding. |
| output-charset | Строка: кодировка байтов внутри encoded-word. По умолчанию тоже internal_encoding, а не output_encoding. |
| line-length | Целое, по умолчанию 76: длина строк поля при folding. Слишком малый бюджет может не вместить служебный синтаксис и дать false. |
| line-break-chars | Строка, по умолчанию CRLF ("\r\n"): разделитель при переносах. Интерпретируется как ASCII независимо от input-charset. Для почтового wire-формата сохраняйте CRLF. |
<?php
declare(strict_types=1);
$subject = 'Чай с мятой: заказ №42';
$field = iconv_mime_encode('Subject', $subject, [
'scheme' => 'Q', 'input-charset' => 'UTF-8', 'output-charset' => 'UTF-8',
'line-length' => 60, 'line-break-chars' => "\r\n",
]);
if ($field === false) { throw new RuntimeException('Тема не закодирована'); }
$lines = explode("\r\n", $field);
foreach ($lines as $index => $line) {
echo 'Строка ', $index + 1, ': ', $line, "\n";
echo 'Байт: ', strlen($line), "\n";
}
echo 'После чтения: ', iconv_mime_decode($field, ICONV_MIME_DECODE_STRICT, 'UTF-8'), "\n";
Ожидаемый результат
Строка 1: Subject: =?UTF-8?Q?=D0=A7=D0=B0=D0=B9=20=D1=81=20=D0=BC?=
Байт: 57
Строка 2: =?UTF-8?Q?=D1=8F=D1=82=D0=BE=D0=B9:=20=D0=B7=D0=B0=D0=BA?=
Байт: 59
Строка 3: =?UTF-8?Q?=D0=B0=D0=B7=20=E2=84=9642?=
Байт: 39
После чтения: Subject: Чай с мятой: заказ №42
В выводе видны сами строки поля, их длины и восстановленная тема. Пробел в начале продолжения связывает его с тем же полем Subject. PHP-файл следует сохранить в UTF-8, иначе обещание input-charset не совпадёт с байтами. Лимит 60 выбран для наглядности, стандартное значение функции — 76. Точное разбиение здесь относится к проверенной сборке, а смысл темы должен сохраняться при любом корректном разбиении.
<?php
declare(strict_types=1);
$subject = "Готово\r\nBcc: injected@example.invalid";
if (str_contains($subject, "\r") || str_contains($subject, "\n")) {
echo "Тема должна быть одной логической строкой\n";
} else {
$field = iconv_mime_encode('Subject', $subject,
['input-charset' => 'UTF-8', 'output-charset' => 'UTF-8']);
echo $field === false ? "Ошибка кодировки\n" : "Тема подготовлена\n";
}
Ожидаемый результат
Тема должна быть одной логической строкой
Имя поля фиксировано разработчиком, входные CR/LF отклоняются до кодирования. Разрешённые переносы создаёт сама функция. Это один слой безопасного формирования темы; адреса, вложения и полноценную отправку должен собирать почтовый компонент с отдельными правилами.
Функция возвращает false при невозможном преобразовании/неумещающемся заголовке и сопровождает отказ warning/notice. Неизвестные ключи не проверяются как схема настроек: опечатка output-encoding может просто не примениться. Реализация PHP 8.5 смотрит только первый символ scheme: B/b выбирает B, Q/q — Q, другие начала оставляют стандартное B. Даже недокументированное Qgarbage выберет Q: это не валидация полного значения options. Проверяйте собственную конфигурацию и используйте документированные типы. Неверный тип самого options в строгом вызове вызывает TypeError.
Не используйте encoded-word как универсальный формат для HTTP-заголовка, тела письма, имени файла вложения или адреса: там иные правила. Не нужно вручную base64_encode всего поля и добавлять метки — библиотека должна учитывать границы слов/строк. mb_encode_mimeheader работает с иным контрактом, включая параметры отступа; механическая замена без проверки длины/префикса опасна.
Рядом: iconv_mime_decode, iconv_mime_decode_headers.
Первый опыт с байтами и кодировкой · Ошибки, потери и границы гарантий.
iconv_mime_decodeВосстановить читаемый текст одного MIME-поля с явным выбором строгого или резервного разбора.
PHP 5+ · iconv
iconv_mime_decode(string $string, int $mode = 0, ?string $encoding = null): string|false| Параметр / результат | Что передать и проверить |
|---|---|
| string | Одно поле MIME или его значение; допускает encoded-word и folding. |
| mode | 0 либо комбинация двух флагов STRICT и CONTINUE_ON_ERROR через |. |
| encoding | Кодировка результата; null → internal, nullable с PHP 8.0. |
| Возврат | Декодированная строка либо false при ошибке; continue может оставить неразобранные фрагменты. |
| Границы | Не mail-body parser, не HTML-экранирование, не проверка подписи/адреса. |
- =?UTF-8?Q?…?= сообщает UTF-8 и способ Q
- Декодер извлекает исходные байты
- Переводит их в целевую encoding
- Возвращается обычный текст для дальнейшей обработки
Почтовый сервис вернул тему вида =?UTF-8?Q?...?=. Это не сломанный русский текст: перед нами отдельный синтаксис MIME. iconv_mime_decode читает метку кодировки в encoded-word, извлекает байты и переводит результат в явно заданную кодировку. Не нужно сначала угадывать её через mb_detect_encoding: внешняя упаковка обычно состоит из ASCII и сама содержит нужное описание.
<?php
declare(strict_types=1);
$field = 'Subject: =?UTF-8?Q?=D0=A7=D0=B0=D0=B9?=';
$decoded = iconv_mime_decode($field, ICONV_MIME_DECODE_STRICT, 'UTF-8');
if ($decoded === false) { throw new RuntimeException('Неверная тема'); }
echo $decoded, "\n";
Ожидаемый результат
Subject: Чай
Имя Subject остаётся в результате, если было во входе. Можно декодировать и только значение. Q-encoded слово — не произвольная строка для quoted_printable_decode: нужно также разобрать оболочку и применить заявленную кодировку.
<?php
declare(strict_types=1);
$value = "=?UTF-8?B?0KfQsNC5?=\r\n =?UTF-8?B?INGBINC80Y/RgtC+0Lk=?=";
echo iconv_mime_decode($value, ICONV_MIME_DECODE_STRICT, 'UTF-8'), "\n";
Ожидаемый результат
Чай с мятой
Пробел, который является частью фразы, находится внутри второго encoded-word. Служебные пробелы/перенос между соседними encoded-word не следует вручную удалять или добавлять: декодер разбирает структуру.
| mode | Что меняется |
|---|---|
| 0 | Стандартный более снисходительный разбор. Режим оставлен ради распространённых нестрогих заголовков. |
| ICONV_MIME_DECODE_STRICT (1) | Следовать RFC 2047 строже. Это не проверка всех правил письма, адреса или подлинности отправителя. |
| ICONV_MIME_DECODE_CONTINUE_ON_ERROR (2) | Продолжить даже при некорректном поле/фрагменте. Результат может сохранить недекодированное содержимое. |
| STRICT | CONTINUE_ON_ERROR (3) | Битовое ИЛ объединяет оба поведения. Цифру 3 лучше не писать вместо понятных именованных флагов. |
<?php
declare(strict_types=1);
$broken = 'Subject: =?NO-SUCH?B?QQ==?= tail';
$fallback = iconv_mime_decode($broken,
ICONV_MIME_DECODE_STRICT | ICONV_MIME_DECODE_CONTINUE_ON_ERROR, 'UTF-8');
echo $fallback === $broken ? "Фрагмент оставлен без декодирования\n" : "Иной результат\n";
Ожидаемый результат
Фрагмент оставлен без декодирования
Это пример аварийного просмотра, не правило приёма бизнес-данных. Нельзя считать строковый результат доказательством, что заголовок полностью корректен; continue допускает частичный разбор. В интерфейсе результат всё равно экранируют для HTML.
Параметр encoding — целевая кодировка результата, null/пропуск использует effective internal_encoding; nullable с PHP 8.0. Источник каждого encoded-word может иметь свою метку. При отказе без continue возвращается false, возможны warning/notice о неизвестном charset или повреждённом формате. Функция не анализирует MIME-дерево тела/вложений и не превращает From в подтверждённую личность.
Рядом: iconv_mime_encode, iconv_mime_decode_headers.
Первый опыт с байтами и кодировкой · Ошибки, потери и границы гарантий.
iconv_mime_decode_headersРазобрать блок MIME-заголовков в массив, сохранив повторения и явно обработав неоднозначность.
PHP 5+ · iconv
iconv_mime_decode_headers(string $headers, int $mode = 0, ?string $encoding = null): array|false| Параметр / результат | Что передать и проверить |
|---|---|
| headers | Строка блока заголовков с переносами; тело после пустой строки не разбирается. |
| mode | 0; STRICT; CONTINUE_ON_ERROR; их комбинация через |. |
| encoding | Целевая кодировка значений, null → internal; nullable с PHP 8.0. |
| Возврат | Имя поля → строка; повторяющееся точное имя → массив строк. Имена сохраняют регистр; false при ошибке. |
| Ошибки / ограничения | Диагностика при ошибке формата/кодировки. Не валидатор письма, доверия отправителю или числа Subject. |
- X-Tag: tea и X-Tag: mint
- Один ключ X-Tag → две строки
- x-tag может быть отдельным ключом
- Нормализуем имена, сохраняя все значения
Из нескольких строк заголовка требуется получить тему и служебные поля как массив. iconv_mime_decode_headers разделяет имена/значения и декодирует encoded-word. foreach затем перебирает этот массив как обычные данные. За первой пустой строкой начинается тело письма: этот API не предназначен для его разбора.
<?php
declare(strict_types=1);
$raw = "Subject: =?UTF-8?B?0KfQsNC5?=\r\nX-Order: 42\r\n\r\nBody: ignored";
$headers = iconv_mime_decode_headers($raw, ICONV_MIME_DECODE_STRICT, 'UTF-8');
if ($headers === false) { throw new RuntimeException('Заголовки не прочитаны'); }
echo $headers['Subject'], '/', $headers['X-Order'], "\n";
var_dump(array_key_exists('Body', $headers));
Ожидаемый результат
Чай/42
bool(false)
Возвращается ассоциативный массив, не объект сообщения. Ключи из примера фиксированы, но реальные заголовки могут отсутствовать/повторяться: следующий опыт показывает, почему прямого $headers["Subject"] недостаточно.
<?php
declare(strict_types=1);
$raw = "X-Tag: tea\r\nX-Tag: mint\r\nx-tag: sale\r\nSubject: One\r\nsubject: Two\r\n";
$decoded = iconv_mime_decode_headers($raw, ICONV_MIME_DECODE_STRICT, 'UTF-8');
if ($decoded === false) { throw new RuntimeException('Неверный блок'); }
$fields = [];
foreach ($decoded as $name => $values) {
$key = strtolower($name); // В этом формате имена полей ASCII.
foreach ((array) $values as $value) { $fields[$key][] = $value; }
}
echo implode(', ', $fields['x-tag']), "\n";
echo count($fields['subject'] ?? []) === 1 ? "Одна тема\n" : "Неоднозначная тема\n";
Ожидаемый результат
tea, mint, sale
Неоднозначная тема
Одинаково написанное имя повторяется массивом; имя с другим регистром может стать отдельным ключом. (array) превращает одну строку в одноэлементный массив, чтобы общий цикл сохранил все значения. HTTP/email-имена обычно сравнивают без регистра, поэтому array_change_key_case без накопления мог бы затереть одно значение.
Сохранён порядок значений повторённого точного ключа; исходный общий порядок разных полей после группировки уже не является полным wire-представлением. Не используйте этот преобразованный массив вместо исходных байтов при проверке DKIM/подписи. Для строгого бизнес-правила отдельно проверьте обязательные поля, количество значений и допустимое содержание. Ни mode=STRICT, ни удачное чтение Subject не подтверждают происхождение письма.
mode имеет те же два флага, что iconv_mime_decode; continue допускает частичный результат, поэтому не подходит как единственная проверка приёма. encoding задаёт выходную кодировку и nullable с PHP 8.0. Неверный блок/конвертация может дать false и диагностику. Для больших входных писем ограничьте размер заголовочного блока ещё на этапе чтения; функция получает уже целую строку и создаёт массив.
Рядом: iconv_mime_decode, iconv_mime_encode.
Первый опыт с байтами и кодировкой · Ошибки, потери и границы гарантий.
iconv_get_encodingДиагностировать эффективные кодировки текущего процесса, не принимая настройки за анализ данных.
PHP 4.0.5+ · iconv
iconv_get_encoding(string $type = "all"): array|string|false| Параметр / результат | Что передать и проверить |
|---|---|
| type | all либо input_encoding / output_encoding / internal_encoding. |
| Возврат | all → массив трёх строк; один известный ключ → строка; неизвестный → false. |
| Ошибки / версии | API с PHP 4.0.5; не изменяет настройки. Deprecated с 5.6 именно iconv.*_encoding-директивы, getter не объявлен deprecated. |
- ini_get читает сырой iconv override
- Пустой override передаёт выбор общим настройкам
- iconv_get_encoding сообщает эффективное значение
На одном сервере код читает русское название правильно, на другом считает иначе. Сначала нужно увидеть эффективную настройку текущего PHP-процесса. iconv_get_encoding ничего не перекодирует: он возвращает строку настройки либо массив трёх значений. Это удобно при разборе старого проекта, который опускает аргумент encoding.
<?php
declare(strict_types=1);
echo iconv_get_encoding('internal_encoding'), "\n";
var_dump(iconv_get_encoding('not_a_setting'));
echo implode(', ', array_keys(iconv_get_encoding('all'))), "\n";
Ожидаемый результат
UTF-8
bool(false)
input_encoding, output_encoding, internal_encoding
UTF-8 — результат настройки этой лаборатории, не обещание любого сервера. Неизвестный ключ возвращает false. Массив содержит входную, выходную и внутреннюю настройку, но не список поддерживаемых кодировок.
<?php
declare(strict_types=1);
$old = ini_get('internal_encoding');
try {
ini_set('internal_encoding', 'ISO-8859-1');
echo ini_get('iconv.internal_encoding') === '' ? "Iconv override пуст\n" : "Iconv override задан\n";
echo iconv_get_encoding('internal_encoding'), "\n";
echo iconv_strlen("\xE9"), "\n";
} finally { ini_set('internal_encoding', $old); }
Ожидаемый результат
Iconv override пуст
ISO-8859-1
1
В опыте iconv.internal_encoding изначально пуст, как в проектной ini. Используется общий internal_encoding, затем настройка восстанавливается. Если legacy override непуст, он имеет приоритет: поэтому одной проверки default_charset при диагностике недостаточно.
Допустимы all (по умолчанию), input_encoding, output_encoding, internal_encoding. В PHP 8.5 сравнение type нечувствительно к регистру, но в коде удобнее придерживаться документированных имён. Для одного ключа результат string, для all — array, для неизвестного — false; проверка только is_array не подходит всем режимам. Неподходящий PHP-тип в strict_types вызывает TypeError.
В PHP 8.5 эффективное значение берётся в порядке: непустое iconv.*_encoding → соответствующее Core *_encoding → default_charset → UTF-8. getter показывает именно это итоговое значение; ini_get показывает сырую строку выбранной директивы. Настройка не переписывает уже существующие переменные и не проверяет реальную кодировку их байтов. Полное пояснение трёх deprecated-директив — в разделе конфигурации.
Рядом: iconv_set_encoding, iconv_strlen, ob_iconv_handler.
Первый опыт с байтами и кодировкой · Ошибки, потери и границы гарантий.
iconv_set_encodingПонять и заменить legacy-изменение общей кодировки, не принимая успешную запись за проверку конвертера.
PHP 4.0.5+ · iconv
iconv_set_encoding(string $type, string $encoding): bool| Параметр / результат | Что передать и проверить |
|---|---|
| type | input_encoding, output_encoding или internal_encoding. Другие значения возвращают false. |
| encoding | Имя кодировки; пустое значение снимает override. Поддержка backend при записи не проверяется. |
| Возврат | true — настройка записана; false — запись не выполнена. Данные не преобразуются. |
| Ошибки / версии | С PHP 4.0.5; запись iconv.*_encoding deprecated с 5.6 и даёт E_DEPRECATED. PHP 8.5 также проверяет максимальную длину имени. |
- setter запоминает строку encoding
- Последующие вызовы без encoding читают её
- Существующие строки остаются теми же байтами
- Явный encoding в вызове снимает эту зависимость
В старом приложении можно встретить iconv_set_encoding перед обработкой текста. Вызов меняет общую настройку Iconv для оставшегося выполнения запроса/процесса. Другой код, опустивший encoding, начнёт зависеть от этого изменения. Для нового библиотечного кода проще и безопаснее передавать нужную кодировку явно в конкретную функцию.
<?php
declare(strict_types=1);
$old = ini_get('iconv.internal_encoding');
$deprecations = 0;
set_error_handler(static function (int $level, string $message) use (&$deprecations): bool {
if ($level === E_DEPRECATED && str_contains($message, 'iconv.internal_encoding')) {
++$deprecations; return true;
}
return false;
});
try {
$changed = iconv_set_encoding('internal_encoding', 'UTF-8');
$effective = iconv_get_encoding('internal_encoding');
} finally {
iconv_set_encoding('internal_encoding', $old);
restore_error_handler();
}
var_dump($changed);
echo $effective, "\n";
echo $deprecations > 0 ? "Устаревшая директива диагностирована\n" : "Нет диагностики\n";
Ожидаемый результат
bool(true)
UTF-8
Устаревшая директива диагностирована
Перехватывается только известное предупреждение этой директивы; исходная настройка восстанавливается в finally. Это лабораторный приём, не совет отключить E_DEPRECATED в рабочем проекте.
<?php
declare(strict_types=1);
$old = ini_get('iconv.internal_encoding');
set_error_handler(static function (int $level, string $message): bool {
return $level === E_DEPRECATED && str_contains($message, 'iconv.internal_encoding');
});
try {
var_dump(iconv_set_encoding('unknown_key', 'UTF-8'));
var_dump(iconv_set_encoding('internal_encoding', 'NO-SUCH-CHARSET'));
echo iconv_get_encoding('internal_encoding'), "\n";
} finally {
iconv_set_encoding('internal_encoding', $old);
restore_error_handler();
}
Ожидаемый результат
bool(false)
bool(true)
NO-SUCH-CHARSET
Setter сохраняет имя, но не создаёт конвертер и не проверяет возможность работы с ним. Следующий настоящий вызов может отказать. Поэтому ввод charset не принимают произвольно от посетителя и не проверяют одним setter.
type выбирает только input_encoding, output_encoding или internal_encoding, без префикса iconv.; all здесь не разрешён. encoding — строка имени; пустая строка убирает legacy override и включает fallback. Неизвестный type возвращает false. В PHP 8.5 имя длиной 64 байта и более отклоняется предупреждением и false; это внутренний предел имени, не длина преобразуемого текста. Ошибочный PHP-тип даёт TypeError при strict_types.
Предпочтительная миграция: убрать зависимость отдельной операции от глобальной настройки, передав UTF-8 явно; затем проверить общую конфигурацию приложения и убрать iconv.* overrides. Замена всех вызовов на ini_set(default_charset,...) без анализа тоже меняет общий контекст и может затронуть вывод. Настройки процесса устанавливают в одном известном месте, а не внутри каждого валидатора.
Рядом: iconv_get_encoding, iconv, ob_iconv_handler.
Первый опыт с байтами и кодировкой · Ошибки, потери и границы гарантий.
ob_iconv_handlerПреобразовать вывод на границе legacy-ответа и не спутать внутренний обработчик с обычной функцией.
PHP 4.0.5+; internal alias в PHP 8.5.11 · iconv
PHP 8.5: ob_start("ob_iconv_handler"); исторический договор: (string $contents, int $status): string| Параметр / результат | Что передать и проверить |
|---|---|
| Подключение | Специальное имя передаётся ob_start; прямой callable в проверенной PHP 8.5.11 отсутствует. |
| contents / status | Историческая callback-модель: байты порции и битовые флаги output control, поставляемые системой. Не параметры ручного вызова. |
| Настройки | Effective internal_encoding → output_encoding; legacy iconv overrides deprecated, предпочитайте Core/единый UTF-8. |
| Результат | Преобразованные байты идут следующему буферу/клиенту при flush; ob_get_clean возвращает исходное содержимое своего буфера. |
| Ошибки / окружение | Конфликты обработчиков, отправленный вывод, ошибка charset/порции. CLI не проверяет реальный HTTP charset. Страница API существует с PHP 4.0.5. |
- Шаблон echo → UTF-8
- Буфер собирает текст
- Alias переводит в Windows-1251
- Клиент должен читать именно Windows-1251
Старый HTML-шаблон печатает UTF-8, но конкретный legacy-потребитель требует Windows-1251. Обработчик выходного буфера позволяет преобразовать весь вывод на границе ответа. Начните с модели: echo пишет в буфер; при передаче буфера наружу выполняется конвертация; потребитель получает новые байты. Это более широкое действие, чем iconv одной выбранной строки, поэтому для нового приложения обычно проще держать весь HTML в UTF-8.
- echo печатает UTF-8 во внутренний ob_iconv_handler
- ob_end_flush передаёт его результат во внешний обычный буфер
- Внешний ob_get_clean забирает уже сконвертированные байты
- bin2hex показывает результат без браузера и HTTP
<?php
declare(strict_types=1);
$oldInternal = ini_get('internal_encoding');
$oldOutput = ini_get('output_encoding');
ini_set('internal_encoding', 'UTF-8');
ini_set('output_encoding', 'Windows-1251');
ob_start();
ob_start('ob_iconv_handler');
echo 'Привет';
$active = in_array('ob_iconv_handler', ob_list_handlers(), true);
ob_end_flush();
$converted = ob_get_clean();
ini_set('internal_encoding', $oldInternal);
ini_set('output_encoding', $oldOutput);
var_dump($active, function_exists('ob_iconv_handler'));
echo bin2hex($converted), "\n";
Ожидаемый результат
bool(true)
bool(false)
cff0e8e2e5f2
Пример начинается до любого вывода, iconv.* overrides в проектной ini пусты. Меняются только настройки этого изолированного процесса, затем восстанавливаются. Внешний буфер позволяет исследовать преобразование в CLI; работа HTTP-заголовков этим не проверяется.
<?php
declare(strict_types=1);
$oldInternal = ini_get('internal_encoding');
$oldOutput = ini_get('output_encoding');
ini_set('internal_encoding', 'UTF-8');
ini_set('output_encoding', 'Windows-1251');
ob_start('ob_iconv_handler');
echo 'Чай';
$raw = ob_get_clean();
ini_set('internal_encoding', $oldInternal);
ini_set('output_encoding', $oldOutput);
echo bin2hex($raw), "\n";
echo $raw === 'Чай' ? "Получено исходное содержимое буфера\n" : "Другой результат\n";
Ожидаемый результат
d0a7d0b0d0b9
Получено исходное содержимое буфера
ob_get_clean возвращает исходное содержимое своего буфера, а обработанный вывод при clean не становится его возвращаемым значением. Поэтому попытка использовать его как iconv-обёртку часто ничего не преобразует. Для одной строки проще явный iconv.
Исторические contents и status относятся к договору output handler: содержимое — строка текущей порции; status — битовые флаги этапа обработки, а не HTTP-код и не просто true/false. Их передаёт система буферизации. Для пользовательских callable-обработчиков результат возвращается в следующую ступень вывода; у этого alias внутренний механизм не вызывают вручную.
Источник берётся из эффективной internal_encoding, назначение — output_encoding. Настройте их до запуска/первого вывода, согласуйте Content-Type/charset реального ответа. PHP 8.5 пытается обновить charset у текстового/стандартного Content-Type; если вывод уже отправлен, обработчик может отказать. Следующий пример проверен отдельным HTTP-запросом к локальному PHP-серверу: заголовок и байты ответа согласованы. Это не проверка всех SAPI, сжатия или настроек прокси.
Теперь откроем результат в браузере. Этот небольшой PHP-файл отдаёт страницу старому потребителю, который ожидает Windows-1251. Сам файл остаётся в UTF-8. Сохраните его как example.php, запустите php -S 127.0.0.1:8080 в его каталоге и откройте http://127.0.0.1:8080/example.php. Нужны расширение iconv и пустые устаревшие настройки iconv.internal_encoding/iconv.output_encoding: иначе они перекроют общие настройки ниже.
- В файле: Заказ №42 в UTF-8
- echo направляет HTML в буфер
- ob_end_flush передаёт буфер обработчику Iconv
- Ответ: байты Windows-1251 + Content-Type с тем же charset
- Браузер читает указанный charset и показывает Заказ №42
<?php
declare(strict_types=1);
ini_set('internal_encoding', 'UTF-8');
ini_set('output_encoding', 'Windows-1251');
header('Content-Type: text/html; charset=Windows-1251');
ob_start('ob_iconv_handler');
echo "<h1>Заказ №42</h1>\n";
echo "<p>Чай с мятой — 2 упаковки.</p>\n";
ob_end_flush();
Ожидаемый результат
HTTP 200
Content-Type: text/html; charset=Windows-1251
На странице:
Заказ №42
Чай с мятой — 2 упаковки.
header задаёт описание ответа до его отправки. ob_start включает буферизацию, echo наполняет буфер, ob_end_flush передаёт результат наружу через обработчик. Настройки действуют в данном запросе. Сам заголовок не конвертирует текст: если убрать обработчик, браузер прочитает UTF-8-байты как Windows-1251 и покажет неверные буквы. Для нового сайта согласуйте UTF-8 во всех частях и не добавляйте такую конвертацию без требования внешнего потребителя.
Заказ №42
Чай с мятой — 2 упаковки.
Если увидели непонятные символы, сравните три вещи: кодировку сохранённого PHP-файла, фактические байты тела и charset заголовка. В инструментах браузера заголовок находится в Network → запрос example.php → Response Headers. Изменение одной надписи charset без изменения байтов проблему не исправляет. Подробнее: как работают буферы вывода.
Не включайте конвертацию для бинарного файла, уже закодированного фрагмента, UTF-8 JSON и смешанного потока. Не складывайте несколько одинаковых обработчиков либо ob_iconv_handler с mb_output_handler: предусмотрена проверка конфликтов. Порционный flush может разрезать многобайтную последовательность; этот обработчик не обещает сохранять недописанный символ между произвольными порциями. Для небольшого legacy-текста можно завершить цельный буфер; для больших потоков нужен подходящий потоковый конвертер с проверкой ошибок. Ошибка преобразования выдаёт диагностику и может лишить ответ данных — частично отправленный HTTP-ответ уже нельзя надёжно «откатить».
Рядом: iconv, iconv_get_encoding, iconv_set_encoding.
Первый опыт с байтами и кодировкой · Ошибки, потери и границы гарантий.
Источники и обсуждения
- PHP: book.iconv
- PHP: iconv.setup
- PHP: iconv.requirements
- PHP: iconv.installation
- PHP: iconv.configuration
- PHP: iconv.constants
- PHP: ref.iconv
- PHP: function.iconv
- PHP: function.iconv-get-encoding
- PHP: function.iconv-mime-decode
- PHP: function.iconv-mime-decode-headers
- PHP: function.iconv-mime-encode
- PHP: function.iconv-set-encoding
- PHP: function.iconv-strlen
- PHP: function.iconv-strpos
- PHP: function.iconv-strrpos
- PHP: function.iconv-substr
- PHP: function.ob-iconv-handler
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


