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

CMS*
Хотел разместить в «пишем 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 — отлично. Нет — не нам выбирать… Кроме того, при реализации второго варианта нужно будет переписывать самый минимум готового кода.

Может быть свежий взгляд со стороны подскажет более изящное решение?
0
31 марта 2009, 10:25
3
Bal 2,5

комментарии (46)

НЛО прилетело и опубликовало эту надпись здесь
+1
Bal #
>Это извращение! Зачем хранить данные в разных кодировках???

Там, где этими вопросами занимаюсь я, у меня _всюду_ utf-8 и проблема просто не встаёт.

Но в некоторых случаях заказчик хочет странного.

Фреймворк используется далеко не только на моих личных ресурсах.

Так что хотелось бы не идеологию кодировок обсудить, а удобство тех или иных костылей :)
0
akzhan #
СУБД крайне корректно работают с utf8. Используйте у себя везде utf-8, а кодировку базы пусть выбирает клиент.
0
Bal #
Вот прямо таки все СУБД и сразу всюду корректно работают? Поддержка UTF-8 может быть вообще не вкомпилена даже в банальный mysql. Если поддержка этой кодировки есть у вас и у меня, это ещё не обозначает, то она будет на любом сервере. Увы, я и сегодня нередко такое встречаю на практике.
0
akzhan #
На всех современных хостингах поддержка utf-8 встроена в MySQL.

Например, в .masterhost utf-8 — внутренняя кодировка my sql.

А если свой сервер или VPS — вам все карты в руки.
+2
slesar #
Самое изощреннейшее решение: mb_string. И такие замечательнейшие функции как mb_substr, mb_strlen, mb_strtoupper и т.д. и т.п. Читайте мануал или, хотя бы, гуглом пользуйтесь.
0
Bal #
Это вообще не в тему.

На системах, где есть mb_string, обычно никаких проблем с использованием internal_encoding == 'utf-8'. И проблемы нет как класса.

Вопрос же стоит именно на системах, где нет mb_string.
0
slesar #
Что за системы без mb_string? PHP 3?

Напишите свои строковые функции для UTF-8. Там ничего сложного нет, посмотрите в Гугле как работает утф.

UTF-8 использовать надо хотя бы для того, чтобы забыть какой был зоопарк с кодировками. Это единственно правильная кодировка для нынешнего веба (и не только веба).

Срок жизни у него огромный, рассчитан на расширение. Так что если сейчас не перейдете на УТФ, через год будете жалеть. Или опять задумаетесь и через два будете жалеть еще больше.
+2
Bal #
>Что за системы без mb_string? PHP 3?

Нет. Собранные без ./configure --enable mbstring

Только не говорите, что никогда не видели таких. Сегодня это едва ли не каждый второй недорогой виртуальный хостинг :)

Именно на таких системах я обычно разворачиваю фреймворк под windows-1251.

>Напишите свои строковые функции для UTF-8.

Это будет первый вариант из предложенных мной. Я пока больше ко второму склоняюсь. В первом случае больше переписывать и выше оверхед :)

>UTF-8 использовать надо хотя бы для того, чтобы забыть какой был зоопарк с кодировками.

Вот, не поленился найти тему: balancer.ru/support/2004/02/t25205--Perevod-Forumov-Aviabazy-na-UTF-8.html

Получается, что я уже пять лет, как перешёл на UTF-8 :) Так что не нужно мне доказывать очевидные вещи ;) _Я_ давно уже на utf-8 перешёл. Но не все _чужие_ проекты позволяют сделать это с лёгкостью.
0
slesar #
ИМХО проще заплатить за хостинг на 1 доллар больше (взять нормальный) чем столько дописывать. Ведь в обоих случаях будет оверхед.
+2
Bal #
Выбираю не я.

Более того, я не хочу геморроя с выбором хостинга.

Ибо потом будут претензии «а у нас опять сайт недоступен, что ты за хостинг нам присоветовал»?

Мне проще поставить один раз на том, что они выбрали и забыть.



По существу вопроса, а не по идеологии, есть что-нибудь?
0
slesar #
Я бы выбрал первый вариант, переделывать придется не так много, но зато если вдруг появится mb_string — можно с легкостью перейти на него — вставить внутрь своих функций.
0
Bal #
Накладных расходов больше. И переделывать пусть не намного больше, но зато по всему коду. А во втором варианте — фактически только в лоадере PHP-шаблонов и самих шаблонах, а их пока немного. Основная часть — на Smarty.

Из этих двух я больше склоняюсь к варианту второму.

Но хочется убедиться, что я не упускаю какой-то более простой третий.

А в тех случаях, когда потребуется переход на mb_string — проще будед сделать это через mbstring.func_overload
НЛО прилетело и опубликовало эту надпись здесь
0
Kaaboeld #
Полностью поддерживаю предыдущего оратора, Автор — ты сам себе враг © народная мудрость.
Хотелось бы услышить доводы, почему действительно не перевести весь проект на utf8?
«Позволяет система включить utf8 — отлично. Нет — не нам выбирать… » А кому если не нам, сейчас сложнее найти где нет utf… — так что это не аргумент.
А так же очень интересна предыстория «как так вышло», если можно с подробностями, заранее благодарю.
+1
Bal #
>Хотелось бы услышить доводы, почему действительно не перевести весь проект на utf8?

Потому что проектов _много_. И в очень разных условиях. И платформу я могу в общем случае выбирать сам только на своих проектах. Они — все в utf-8.
0
Wave #
Объясняешь заказчику: выбранное вами — устарело\неэффективно\etc.
Да, я могу сделать, чтобы нормально работало в этих условиях. Но это дополнительное время, а следовательно, дополнительные деньги.
Моё время стоит столько-то.
Нормальный хостинг стоит столько-то.
При том: нормальный хостинг позволит в будущем избежать подобных же проблем.
При том: решение на чистом UTF-8 гораздо более перспективно (что ты будешь делать, когда настанет эпоха PHP6? Там ведь нативная кодировка UTF-8).

Заказчик далеко не всегда прав, хотя бы потому, что не является специалистом. И хороший специалист — это тот, который помимо прочего может объяснить заказчику, почему нужно так, а не иначе.
А совсем неадекватных заказчиков лучше избегать. Вот представь, некто захотел посмотреть твоё портфолио. Увидел свежие проекты в 1251, koi8 — закрыл портфолио со словами «некомпетентен, пользуется устаревшими технологиями». И кто виноват?

Впрочем, решение всё равно за тобой.
0
Bal #
>Объясняешь заказчику: выбранное вами — устарело\неэффективно\etc.

Он прекрасно это понимает. (В случае с koi8-сервером). Поэтому старая система медленно, но верно переписывается на новый код, который сможет переключиться на utf8 потом одним параметром в конфиге.

Но объём кода и его разнообразие там такие, что процесс идёт уже два года. И продлится ещё с пол-года — год.

А вопрос решать нужно уже сегодня :)



Ну и про варианты с cp1251 я тоже уже указывал. Я не собираюсь за те же деньги ещё и ответственность в выборе хостера брать :)

>Вот представь, некто захотел посмотреть твоё портфолио.

Ну, это, слава Богу, давно не актуально. Уже много лет не я ищу заказчиков, а заказчики обращаются ко мне. И вопрос портфолио мне просто неинтересен :)
0
Psih #
кодировка ну PHP проекта ну никак не зависит от системы. Весь нынешний софт прекрасно работает с чистым UTF-8 даже если в самой системе нету локалей в UTF-8.

ставте mb_string, сделайте что бы mb_overload переопределял стандартные PHP функции и живите спокойно. Подстраиваться под всех и вся в итоге приведёт вас к разбитому корыту. Есть предел плясок с бубном. Мой последний небольшой проектик имеет жесткие рамки: PHP 5.2+ с mysqli (да да да, я не пользуюсь более устаревшей морально и физически mysql), MySQL 4.1+ (рекомендую 5-ку). Заказчику условия диктовать не позволяю. Хотят что-бы работало на чём-то старом — пусть доплачивают за гемморой, я не собираюсь работать с неподдерживаемыми более библиотеками и софтом.
0
Bal #
_У меня_ на сервере все проекты под php-5.2.8-r2, mysql-5.0.70-r1

utf-8 всюду ещё начиная с mysql-4.1.10-alpha, едва там появилась поддержка этой кодировки.

Вопрос в ином.

>Заказчику условия диктовать не позволяю.

Даже когда заказчик обеспечит тебе 50% твоего годового дохода при непыльной работе? :)



Кроме того, бывают чисто объективные случаи.

Только один пример.

Коммерческий высоко нагруженный сайт. Системная кодировка — koi8-r. Огромная масса старого, много лет назад написанного кода, который работает только в этой кодировки. Процесс неторопливого переписывания этого кода идёт уже более двух лет :) Как только всё будет переписано (а это — ещё с год работы), система перейдёт полностью на UTF-8. Пока же она не только работает в устаревшей кодировке, более того, она работает под FreeBSD 4(!) :) И ничего тут сходу не сделать. Далеко не все проекты пишутся с нуля или состоят из десятка страничек.
0
Psih #
Учитывая какой это гемморой и что один раз заказчику засвербит в одном месте — я такое делать отказываюсь всегда. Даже за большие деньги, пусть идут в нормальные компании, которые способны сделать это быстро и правильно. Мне моё спокойствие важнее. И разбираться в багах 5+ летней давности, многие из которых просто не лечатся без установки новых версий (а то и существенно более новых) я не хочу. Проще найти 2-х других заказчиков, от которых и так иногда покоя нету при всём при том что фрилансом занимаюсь по личному желанию иногда.
0
Bal #
К сожалению, я не могу позволить себе отказываться от ряда подобных проектов :)



Давайте, всё же, попробуем обсудить не идеологию, ибо я итак понимаю, что к чему, а техническое решение.
0
Psih #
Базы как правило поддерживают utf-8, можно всё перевести на неё.
все файлы на FS можно перекодировать тем же iconv
В PHP ставим mb_string и включаем mb_func_overload: читать тут lv.php.net/manual/en/mbstring.overload.php
0
Bal #
>Базы как правило поддерживают utf-8

Да. Но далеко не всегда. Как минимум пару раз в год я натыкаюсь на хостинги, например, с windows-1251-only. Вообще, с mysql-4.0, который не поддерживает utf-8.

>все файлы на FS можно перекодировать тем же iconv

Так и делается.

>В PHP ставим mb_string

Он на хостингах часто отсутствует. И мне в ряде случаев требуется интеграция со старым кодом в одном процессе. Со строковыми функциями, работающими по умолчанию в системной кодировке.
0
akzhan #
На хостингах mb_string присутствует практически всегда.

Я вообще не знаю сегодня ни одного хостера без mb_string или без PHP 5.x

Вопрос в крайнем случае решается сменой хостера, а не геморроем в разработке.
0
Bal #
>На хостингах mb_string присутствует практически всегда.

Это не так.

>Я вообще не знаю сегодня ни одного хостера без mb_string или без PHP 5.x

Даже PHP4 Only есть до сих пор. Хотя вот от них я отказался строго — поддержка PHP4 обходится несравнимо дороже. А вот хостинги без mbstring — не редкость.
НЛО прилетело и опубликовало эту надпись здесь
0
Bal #
>такие проекты переписывают с нуля

Да, если там сотня страниц десятка категорий :)

>Можно перенести данные… но зачем переносить старую логику?

Потому если сайт активно писался 6 лет, нельзя его переписать за неделю. Значит будет выбор:

1. Заниматься двойной работой, параллельно переписывая сайт с нуля и занимаясь поддержкой и непрерывным расширением чужого страшного кривого кода.

2. Сразу писать новые компоненты системы на новом коде, но обеспечивая их интероперабельность со старым, попутно переписывая старый код на новый по мере надобности в модификациях.

Я не люблю заниматься двойной работой и поддержкой чужого кода низкого качества и поэтому выбираю второй путь :)
НЛО прилетело и опубликовало эту надпись здесь
0
Bal #
Я же написал — потому что старый тоже нужно поддерживать и расширять. Ни за неделю, ни за месяц такой проект не напишешь. Если ничем другим не заниматься — с полгода где-то.

Живой нагруженный проект полгода без поддержки и расширений — это нонсенс :)

Значит придётся в этом старом проекте (сложность которого, кстати, превысила возможности писавшего его программиста) разбираться и работать с ним. Параллельно с написанием нового.

Но это — во много раз больше работы (особенно — правка старого кода, представляющего из себя крайне нестройную систему костылей), чем если для старого проекта писать модули новой уже системы.

Во втором случае можно вообще лошадей не гнать, а заниматься понемногу сносом старых компонентов и заменой их на новые. И внедрением новых. Уже правильных и красивых :)
0
kvf77 #
Сказки венского леса прямо. Даже сложный проект можно было бы за два года 5 раз переписать целиком — а вы три требуете — такое ощущение, что вы просто доите клиента, разводя его на бабки.
0
Bal #
>Даже сложный проект можно было бы за два года 5 раз переписать целиком

Да. Если заниматься только этим процессом и ничем более. А реально же приходится поддерживать/расширять и старый код и делать то же самое уже и для нового.

Неужели нет опыта постоянной поддержки и развития сложных живых проектов сроком хотя бы больше 2-3 лет? Принципы работы там будут совсем иные, чем в режиме — «написал, сдал, с глаз долой из сердца вон».
–1
kvf77 #
Есть — потому и говорю, что вы рассказываете сказки.
+3
loat #
Где-то прочитал: «Единственной кодировкой должна быть UTF-8 а использование других следует приравнять к разжиганию межнациональной розни и карать соответствующей статьёй УК.»
0
runcore #
получив подобный проект в руки, нужно просто волевым усилием убедить руководство в критической необходимости перевода проекта на единую кодировку (utf8 например), а потом перекодировать всю базу и все исходники, и будет вам счастье

а ваш «зоопарк с кодировками» это… это просто неправильно
0
Bal #
>нужно просто волевым усилием убедить руководство в критической необходимости перевода проекта на единую кодировку

Увы, это далеко не всегда возможно.
0
runcore #
нет ничего невозможного )
а иначе, вы просто так и будете заниматься постоянным разгребанием багов с некорректной конвертацией одного в другое.
потому что нет единственного рабочего решения. есть только набор костылей, как у вас. которые можно сделать работоспособными, но при малейшей модификации системы, надо будет опять подставлять костыли.
0
Bal #
>нет ничего невозможного )

Бывает и такое :) См., например, проект, упомянутый в bal.habrahabr.ru/blog/55973/#comment_1500835

Бывает, что даже в новых заказах проект разворачивается, скажем, на сервере, где есть только windows-1251 (ситуация достаточно частая).

>а иначе, вы просто так и будете заниматься постоянным разгребанием багов с некорректной конвертацией одного в другое.

А багов-то, как раз, и нет. Впорос в удобстве того или иного костыля :)
+2
Psih #
Это может в системе нету локали на utf-8, но это ничуть не мешает использовать UTF-8 в скриптах и базе данных.
0
Bal #
На упомянутом «koi8-r-проекте»:

1. mysql поддерживает utf8.

2. Настройки php приходится ориентировать не на мой код, который может адаптироваться, а на старый, который работает _только_ с koi8-r. Более того, приходится использовать смешанный код — и мне приходится делать include старого кода, и в старом коде часть костылей реализуется уже моим кодом.

3. Приходится использовать массу файловых данных в koi8-r.



Поверьте, я уже два года причёсываю эту систему, подготавливая к полному переходу на utf-8. Но _пока_ это невозможно.

У нас уже есть готовый новый сервер, весь из себя юникодны, который готов принять обновлённую систему. Но пока процесс не завершён.
–1
whiplash #
под кат
0
Bal #
Спасибо. Поправил.
0
Xobb #
автор кажется прикалуется над комментаторами и не взирая ни на что хочет чтоб все согласились с ним и решали проблему как он.
0
Bal #
Нет, меня интересует нет ли третьего вариант решения, кроме двух предложенных.

Судя по всему, тут третьего варианта в рамках задачи предложить никто не может.
0
bubuq #
Проблема в самой первой строчке:

1. Все тексты в PHP-коде лежат...

Пока тексты от кода не отделены, щастя не будет.

0
Bal #
>Пока тексты от кода не отделены, щастя не будет.

Угу. И где хранить, например, заголовок безбэкендового класса, состоящего из одного шаблона? Организовывать фейковый бэкенд для одних заголовоков? Жирно будет :D

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.