Pull to refresh

Нужен совет по кодировкам

Reading time3 min
Views2.5K
Хотел разместить в «пишем CMS», но говорит, что недостаточно кармы. Так что к себе, надеюсь, попадётся на глаза тому, с кем можно помозговать :(

Upd: я знаю, что правильно писать всё в utf-8. Более того, в моих личных проектах, или тех, что я пишу на заказ с нуля, всё только в этой кодировке и проблема вообще не встаёт. Поэтому не нужно в 10-й раз в комментариях писать банальность про юникод. Я это знаю. Вопрос про случаи, когда это невозможно

Upd2: спасибо за карму, топик перенёс

Исторически сложилось, что мой фреймворк работает не просто на системах с разными кодировками (utf-8, windows-1251, koi8-r), но нередко в смешанных условиях (БД отдаёт данные в utf-8, клиент должен получить в windows-1251, файлы лежат в koi8-r, клиент получает в utf-8, контент сайта отдаётся в koi8-r, но RSS отдаются в utf-8 и т.п. сочетания).

До какого-то момента всё было прекрасно:



1. Все тексты в PHP-коде лежат в utf-8, но при загрузке система переводит их во внутреннюю кодировку системы. Например:
class ... function title() { return ec("Тест"); }

где ec() — функция, осуществляющая перекодировку utf8->internal_charset

2. Все операции над текстом (upper/lower/substr/etc) осуществляются во внутренней кодировке сервера.

3. При выводе происходит преобразование internal_charset -> output_charset.

4. При загрузке данных из пользовательских файлов происходит перекодировка files_charset -> internal_charset

5. При загрузке данных из БД происходит перекодировка db_charset->internal_charset.

6. Все Smarty-шаблоны в utf-8 и при их загрузке перекодируются в internal_charset.

Всё прекрасно работало, пока мне не потребовались шаблоны на чистом PHP. Ну, с логикой всё понятно. Класс готовит блок данных. При рендеринге система распаковывает их в область видимости и делает include() нужному шаблону, перехватывая вывод. Потом использует результат.

И вот тут у меня случится первый затык. Для простоты рассмотрим конкретный пример.

Пусть internal_encoding, кодировка системы, у нас koi8-r. PHP-шаблон, единообразия ради, в utf-8. Без всякого преобразования сразу же получается каша: в utf-8 текст в PHP вставляются koi8-r данные.

Дальше я сделал очевидно, но не для меня тогда, неверное решение. Я волюнтаристски принял, что internal_encoding всегда utf-8. Плюсы были налицо: отпадает надобность в ec("") функциях, так как internal всегда такой же, как у основных шаблонов. В Smarty в {file...} или {include...} вместо своего типа файлов xfile:// [загрузчик которых которых наряду с прочим занимался перекодировкой] можно использовать обычные файлы, без замечания вставляются PHP-шаблоны. И, вообще, приятно жить в хоть как-то унифицированном мире :)

Понятно уже, где всплыл костыль? internal_charset != системной локали PHP. Не работают strtolower/strtoupper/substr…

И вот я теперь стою на перепутье. И прошу советов, как это можно разгрести :)

Первый вариант вижу лобовой. Сейчас частично ситуацию разруливаю им. Ввводим понятие системной кодировки. Т.е. системной локали. Меняем все strtolower() на свою u_lower(), где делаем iconv из внутренней кодировки фреймворка в системную, потом strtolower и обратно во внутреннюю. Плюсы — остаётся унифицированная кодировка фреймворка. По-прежнему нет надобности в ec(). Возможна более тонкая настройка на системах со глючными mb_string и т.п. Минусы — использование своих функций вместо стандартных. Лишняя нагрузка процессора. Небольшая, но если это будет где-то глубоко в цикле?

Второй вариант. internal_charset всегда равен системной локали, в общем случае не равен utf-8. PHP-шаблоны, как и прочее в системе — в utf-8. При загрузке PHP-шаблонов, данные скармливаемые им, предварительно перекодируются из internal в utf-8. Перехваченный вывод потом перекодируется из utf-8 в internal. Плюсы — система может пользоваться стандартными PHP-функциями без оверхеда. Минусы — при обращении из шаблона к другим данным, не поданным непосредственно, в шаблоне нужно перекодирование (скажем, $title я могу перекодировать, а вот $items[0]->title() будет уже в системной кодировке). Придётся использовать функции преобразования из системной кодировки в utf-8. Т.е. если основные данные сможем выводить как есть:
Привет, <?=$title?>
, то внутренние данные уже придётся выводить в чём-то типа
Купите <?dc($items->title())?>
, где dc() занимается преобразованием intrnal -> utf-8. И это тоже некоторый оверхед, особенно, если, опять же, в цикле.

Был в голове ещё какой-то вариант, сейчас вылетел, но он совсем уж безумный :)

Пока я склоняюсь больше ко второму. Всё же, юникод юникодом, но в системе лучше жить в системной кодировке. Позволяет система включить utf8 — отлично. Нет — не нам выбирать… Кроме того, при реализации второго варианта нужно будет переписывать самый минимум готового кода.

Может быть свежий взгляд со стороны подскажет более изящное решение?
Tags:
Hubs:
Total votes 16: ↑8 and ↓80
Comments46

Articles