Будущее e2 и УТФ-8
Я очень хочу, чтобы e2 перешёл уже, наконец, на УТФ-8 (сейчас он использует Виндоус-1251). При этом я в какой-то степени ощущаю себя разработчиком Виндоуса: мне нужно не просто сделать продукт, который был бы хорош сам по себе; мне нужно построить его на имеющейся довольно большой базе кода, научить автообновляться с предыдущей версии и заставить работать на имеющихся инсталляциях.
Идеально было бы сделать так, чтобы очередной e2 просто во время обновления сконвертировал всю базу и всё остальное в УТФ-8 так, чтобы никто не заметил, но зато начали работать любые символы.
К сожалению, я довольно плохо разбираюсь во взаимоотношениях MySQL’я с кодировками, а более всего меня бесит сам факт существования таких взаимоотношений: казалось бы, ну ты же база данных, вот и храни байты и вообще не думай о том, что на планете существуют кодировки, а если мне нужно будет регистронезависимый поиск или ещё что-нибудь такое, я тебе во время запроса напишу, как относиться к этим байтам.
Но почему-то всё очень сложно.
Есть идея пока оставить конвертацию старых данных и хотя бы просто написать движок, который при инсталляциях с нуля работал бы на УТФ-8. Можно даже не делать автообновления, а разрешить только восстановление из бекапа, в ходе которого уже писать всё в базу таким, как нужно. Но что делать с кодом? Если его сконвертить в УТФ-8 (что, конечно, очень просто), то уже после этого он, думаю, может сломаться из-за использования однобайтных функций работы со строками. Как можно безболезненно перейти на многобайтные функции? Существуют ли многобайтные версии всех однобайтных функций? Насколько это всё надёжно работает?
Короче, проблема, конечно, в том, что я мало знаю про это. Иначе мне было бы всё просто и очевидно. Поэтому я предлагаю тем, кто знает больше, а ещё лучше — у кого есть опыт перевода приложений на УТФ-8, поделиться со мной.
Мой сын ждет UTF, ему без иероглифов некомфортно, а движок нравиться.
Мультибайтные версии почти всех строковых функций существуют (http://php.net/manual/en/book.mbstring.php). Для этого нужно только проверить, собран ли PHP с поддержкой mbstring. Впрочем, редко где он есть без этого расширения
Означает ли это, что нужно внимательно пройтись по всему коду и поменять одни функции на другие?
Когда поймешь, как это можно сделать для MySQL — рассскажи, интересно. Для Оракла мы это делали, и по-человечески сделать не смогли, забили.
В MySQL всё просто: ALTER TABLE tbl_name CONVERT TO CHARACTER SET charset_name
http://dev.mysql.com/doc/refman/5.0/en/alter-table.html#id3080423
В базе можно, на самом деле, оставить все как есть в cp1251, и указать кодировку соединения utf-8 — само на лету перекодируется. Хотя, конечно, это как-то некузяво. :) Да, если не использовать строковые операции в sql-запросах (типа select * from table where name like ’%вася пупкин%’) и не рассчитывать на case insensivity, то, в общем-то, нет никакой разницы — главное, чтобы кодировки соединения и таблицы совпадали, иначе произойдет та самая перекодировка.
Хороший faq про кодировки в mysql: http://phpfaq.ru/charset
Что касается mbstring. Если использовать ini-настройку mbstring.func_overload, то все строковые функции автоматически «перегружаются» их mb_аналогами (за исключением preg_*, там надо использовать модификатор /u). Только надо не забыть указать правильные mbstring.detect_order и mbstring.internal_encoding.
Ох, вечно я пропускаю при изложении всю логическую цепочку. Я про 1251 на самом деле что имел в виду: я вижу два варианта. Первый — сразу при обновлении перекодировать в utf-8, но тут есть такая проблема, что кто-то, возможно, вручную дергает данные из базы для отображения на сайте (скажем, выводит блок «а вот что нового на блоге»), и такая перекодировка все сломает. Второй — предлагать сделать перекодировку в любой момент, и до тех пор, пока она не сделана, использовать set names cp1251 (а после — соответственно, set names utf8); несколько сложнее, зато обратная совместимость полная.
Есть опыт перевода большого проекта на UTF-8. Идея использовать mbstring.func_overload звучит заманчиво, но это довольно глючный хак. Придётся разбираться почему поломалась отсылка почты, почему неправильно считается длина двоичных строк; новые баги могут вылезти в любой момент, особенно при подключении сторонних библиотек.
Я как-то тоже в уличную магию не верю и версию с перегрузкой даже не рассматриваю :-)
Не дописал. Лучше внимательно пройтись по всему коду, и поменять одни функции на другие, кроме тех мест, где вы хотите работать с байтами, а не с символами. Особенно это актуально для strlen.
Ага, спасибо.
Для обратной совместимости лучше конечно сделать возможность выбора кодировки в которой работает система. Тогда после обновления старой версии нужно будет просто установить кодировку cp1251 и данные конвертировать не нужно. Но это явное усложнение, т. к. система должна будет уметь работать и с UTF и c cp1251. И по хорошему еще уметь конвертировать все данные при смене кодировки.
Не, поддерживать две кодировки я точно не хочу. Задача — переехать на УТФ-8 и забыть про Виндоус-1251 навсегда.
Мне кажется, что заранее кодировку надо указывать, как минимум, чтобы база могла построить индекс.
Но главная причина — та поза, в которой находится ситуация с кодировками. В идеальном мире, конечно, не пришлось бы указывать. А так нужно учитывать, что консоль, к примеру, у нас в кои, сайт — в utf, а какая-нибудь пропиертарь типа 1с понимает только 1251.
При автоматической конвертации следует учитывать два момента
Так что «просто ALTER TABLE» следует делать с осторожностью.
Есть опыт работы с php-проектом, использующим юникод. Кое-какие мысли по поводу PHP и UTF-8 я изложил в заметке (привожу ссылку, чтобы не повторяться):
http://written.ru/articles/technologies/site_building/php_and_utf
Для регулярных выражений следует использовать модификатор u, как написано выше.
При инициализации скрипта нужно вызывать mb_internal_encoding(«UTF-8»), а после соединения говорить базе, что бы она отдавала данные в UTF8 (SET NAMES UTF8).
В исходном коде нужно вручную найти все однобайтные аналоги функций из этого списка http://php.net/manual/en/ref.mbstring.php и заменить их на соответствующие им mb_*() при этом особое внимание обращать на регулярные выражения. По моему опыту проще всего оставить обычный preg_*(), только добавить в конец регекспа флаг u («/regexp/iu»), так можно избежать проблем и неожиданностей с mb_ ereg_*().
Потом нужно поменять кодировку посылаемых писем и отдаваемых браузеру страниц, вспомнить, если в коде еще места, где текст получается или отдается во вне в однобайтной кодировке и добавить соответсвующую обработку.
Далее нужно один раз сконвертировать содержимое базы средствами mysql (как подсказал Splurov) и все исходники движка (используя iconv например, она есть в любой *NIX системе).
При обновлении придется полностью заменять исходники на UTF версию, конвертить конфиги, базу и все остальное используя какой-нибудь умный скрипт (не видел структуры твоего движка, не могу ничего посоветовать).
Мороки много, зато, когда ты это сделаешь, можно будет писать в прямо в коде коде © 2002—2008 вместо &_copy;&_nbsp;2002&_mdash;2008 :)
P.S. Как у тебя можно офрмить код и html entieties? ( @код@? ©?)
Спасибо!
Можно заключить его в двойные диезы. А энтитис никак нельзя пока.
Ух ты, textile не работет и HTML тоже (я не поверил на слово:), зато парсер правильно оформил мой постскриптум!
А ты специально сделал так, что нажимая назад в браузере после отправки комментария я попадаю не на ту же страницу еще без комментария, а куда то совсем назад?