Рекурсивное iconv для массивов: UTF‑8 в проекте на «1С‑Битрикс»
Зачем это рядом с «1С‑Битрикс»
Сайт на UTF‑8, а из обмена, старых выгрузок или контура партнёра приходит дерево массивов в Windows‑1251/KOI8. Пакетно прогнать строки через iconv() без ручного обхода уровней — типовая задача агентов импорта и тестовых скриптов с prolog_before.php.
Идея заметки с pushorigin.ru
Исходный пример — рекурсивный обход: для каждого значения либо углубиться, либо вызвать iconv; ключи тоже переводить, потому что в PHP массив хранит строковые индексы в той же кодировке.
Суффиксы //IGNORE и //TRANSLIT
Сначала пробуют …//IGNORE: несовместимые байты отбрасываются. Если результат пустой, в старой шпаргалке брали //TRANSLIT, чтобы хоть символически переложить латиницей. На проде лучше явно логировать «битые» фрагменты, а не молча терять текст.
Рефакторинг: одна функция на строку + рекурсия
Ниже — тот же смысл, но с declare(strict_types=1), вынесенной конвертацией скаляра и рекурсией по вложенности. Так проще тестировать и подменять реализацию (например, на mb_convert_encoding для пары CP1251 ↔ UTF‑8).
declare(strict_types=1);
final class EncodingTree
{
public static function iconvTree(array $data, string $from, string $to): array
{
$out = [];
foreach ($data as $key => $value) {
$newKey = self::iconvString((string)$key, $from, $to);
if (is_array($value)) {
$out[$newKey] = self::iconvTree($value, $from, $to);
} else {
$out[$newKey] = is_string($value)
? self::iconvString($value, $from, $to)
: $value;
}
}
return $out;
}
private static function iconvString(string $s, string $from, string $to): string
{
$first = @iconv($from, $to . '//IGNORE', $s);
if ($first !== false && $first !== '') {
return $first;
}
$second = @iconv($from, $to . '//TRANSLIT', $s);
return $second !== false ? $second : $s;
}
}Вариант с «картой» значений через array_map
Если нужен более функциональный стиль, вложенный список можно обойти той же рекурсией, а на одном уровне применить array_map к значениям. Ключи по-прежнему обрабатывайте отдельно: «чистый» array_map по array_values не трогает ключи.
/** одна и та же iconvString(), что выше */
private static function mapLevel(array $row, string $from, string $to): array
{
$keys = array_map(
static fn(string $k): string => self::iconvString($k, $from, $to),
array_keys($row)
);
$vals = array_map(
static function ($v) use ($from, $to) {
return is_array($v)
? self::iconvTree($v, $from, $to)
: (is_string($v) ? self::iconvString($v, $from, $to) : $v);
},
array_values($row)
);
return array_combine($keys, $vals);
}Если после конвертации двух разных ключа получилась одинаковая строка, последний выиграет — так же, как при ручном присваивании в цикле. Для критичных данных проверяйте коллизии до объединения.
Когда смотреть на mb_convert_encoding
Для распространённых однобайтовых наборов внутри одного языка PHP часто используют mb_convert_encoding; iconv даёт тонкий контроль через суффиксы целевой кодировки. Выбирайте API по тому, что должно произойти с невалидными последовательностями.
Краткий чеклист
- Обходите и ключи, и значения — иначе индексы останутся в старой кодировке.
- Выносите преобразование одной строки в отдельный метод и покройте его парой тестов на «битых» байтах.
array_mapудобен для уровня значений; контролируйте уникальность ключей послеiconvключей.- В проектах на «1С‑Битрикс» итог почти всегда UTF‑8 под настройки сайта и ядра.
Не хотите копаться сами?
Починю за 1-3 дня. Без предоплаты — оплата по результату.
15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии