Yandex Metrika
sanches.free

Рекурсивное 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 дней гарантии