
Нужен совет по кодировкам
Хотел разместить в «пишем 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, но при загрузке система переводит их во внутреннюю кодировку системы. Например:
где 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. Т.е. если основные данные сможем выводить как есть:
Был в голове ещё какой-то вариант, сейчас вылетел, но он совсем уж безумный :)
Пока я склоняюсь больше ко второму. Всё же, юникод юникодом, но в системе лучше жить в системной кодировке. Позволяет система включить utf8 — отлично. Нет — не нам выбирать… Кроме того, при реализации второго варианта нужно будет переписывать самый минимум готового кода.
Может быть свежий взгляд со стороны подскажет более изящное решение?
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 — отлично. Нет — не нам выбирать… Кроме того, при реализации второго варианта нужно будет переписывать самый минимум готового кода.
Может быть свежий взгляд со стороны подскажет более изящное решение?
Comments 46
Only users with full accounts can post comments. Log in, please.