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

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

    Может быть свежий взгляд со стороны подскажет более изящное решение?

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 46

    • UFO just landed and posted this here
        +1
        >Это извращение! Зачем хранить данные в разных кодировках???

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

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

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

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

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

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

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

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

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

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

            Срок жизни у него огромный, рассчитан на расширение. Так что если сейчас не перейдете на УТФ, через год будете жалеть. Или опять задумаетесь и через два будете жалеть еще больше.
              +2
              >Что за системы без 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
                ИМХО проще заплатить за хостинг на 1 доллар больше (взять нормальный) чем столько дописывать. Ведь в обоих случаях будет оверхед.
                  +2
                  Выбираю не я.

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

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

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



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

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

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

                      А в тех случаях, когда потребуется переход на mb_string — проще будед сделать это через mbstring.func_overload
                • UFO just landed and posted this here
            0
            Полностью поддерживаю предыдущего оратора, Автор — ты сам себе враг © народная мудрость.
            Хотелось бы услышить доводы, почему действительно не перевести весь проект на utf8?
            «Позволяет система включить utf8 — отлично. Нет — не нам выбирать… » А кому если не нам, сейчас сложнее найти где нет utf… — так что это не аргумент.
            А так же очень интересна предыстория «как так вышло», если можно с подробностями, заранее благодарю.
              +1
              >Хотелось бы услышить доводы, почему действительно не перевести весь проект на utf8?

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

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

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

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

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

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



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

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

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

              ставте mb_string, сделайте что бы mb_overload переопределял стандартные PHP функции и живите спокойно. Подстраиваться под всех и вся в итоге приведёт вас к разбитому корыту. Есть предел плясок с бубном. Мой последний небольшой проектик имеет жесткие рамки: PHP 5.2+ с mysqli (да да да, я не пользуюсь более устаревшей морально и физически mysql), MySQL 4.1+ (рекомендую 5-ку). Заказчику условия диктовать не позволяю. Хотят что-бы работало на чём-то старом — пусть доплачивают за гемморой, я не собираюсь работать с неподдерживаемыми более библиотеками и софтом.
                0
                _У меня_ на сервере все проекты под 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
                  Учитывая какой это гемморой и что один раз заказчику засвербит в одном месте — я такое делать отказываюсь всегда. Даже за большие деньги, пусть идут в нормальные компании, которые способны сделать это быстро и правильно. Мне моё спокойствие важнее. И разбираться в багах 5+ летней давности, многие из которых просто не лечатся без установки новых версий (а то и существенно более новых) я не хочу. Проще найти 2-х других заказчиков, от которых и так иногда покоя нету при всём при том что фрилансом занимаюсь по личному желанию иногда.
                    0
                    К сожалению, я не могу позволить себе отказываться от ряда подобных проектов :)



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

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

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

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

                        >В PHP ставим mb_string

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

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

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

                            Это не так.

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

                            Даже PHP4 Only есть до сих пор. Хотя вот от них я отказался строго — поддержка PHP4 обходится несравнимо дороже. А вот хостинги без mbstring — не редкость.
                  • UFO just landed and posted this here
                      0
                      >такие проекты переписывают с нуля

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

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

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

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

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

                      Я не люблю заниматься двойной работой и поддержкой чужого кода низкого качества и поэтому выбираю второй путь :)
                      • UFO just landed and posted this here
                          0
                          Я же написал — потому что старый тоже нужно поддерживать и расширять. Ни за неделю, ни за месяц такой проект не напишешь. Если ничем другим не заниматься — с полгода где-то.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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



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

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

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

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

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

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

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

                          Only users with full accounts can post comments. Log in, please.