MySQL + PHP: charset и collation

Работаю я над одним проектом, где в день проноситься до миллиона уникальных посетителей в день. Для этого проекта я с командой работаю над маленьким сателлитом, назначения которого не имеет столь весомого значения, сколько имеет значение проблема, с которой я сегодня столкнулся. И нам крупно повезло, что кол-во данных сейчас на сателлите мизерное и он пока находиться в стадии тестирования и нагрузка на нём растёт постепенно.

Проект пишется на PHP 5.2.x + MySQL 5.0 Так как проект международный, база сателлита находиться в collation utf8_unicode_ci.

У сателлита есть 2 части: так называемая клиентская и административная. Клиентская доступна всем и дёргается постоянно, административная только администраторам, ею пользуются раз в неделю примерно пока.

Клиентская часть, из-за ожидаемой нагрузки, писалась полностью мною, без использования каких-либо фреймворков, каркасов и прочего. Благо её простейшие функции позволяли это сделать быстро. Естественно, в качестве MySQL клиента был выбран mysqli, всё как надо, сразу после соединения был выставлен нужный charset, строго как в мануале:

$conn->set_charset("utf8")

Сам mysqli был выбран потому-что проект новый и у него вроде-как получше с поддержкой UTF-8 всё устроено.

Административная часть писалась коллегой, на пару со мной, причём ответственность за качество кода лежит на мне. Для большей скорости написания мы использовали Zend Framework, который мы оба довольно не плохо освоили к этому моменту. К тому-же, административная часть имела куда больше функций и меньше нагрузки, нежели клиентская. Единственное разногласие, которое у нас было с коллегой, это использовать или нет Zend_Form или нет из-за очень сложной кастомизации самих форм и их декораторов, неразумного использования комбинации <dd> и &ltdt> вокруг скрытых полей и прочих мелких религиозных и языковых разногласий. Ни одному из нас не возник простейший вопрос, как устроен другой компонент — Zend_Db. Определит ли он сам charset и collation, который мы используем и нам нужен или будет использовать тот, что установлен по умолчанию. И вот, сегодня мы поняли что мы выстрелили себе в ногу примерно две недели назад — не то пуля летело медленно и наконец долетела, не то порох сырой ныл и сработал только сейчас.

Я всегда думал, что умный Zend_Db как-то сам узнает какой нужно charset и collation использовать, раз он сам узнаёт какие поля у таблицы и какие значения туда можно писать, а какие нет. Оказалось что нет… Я был не прав и ему об этом нужно грубо говорить (ну или клиенту вдолбить в настройки по умолчанию).

Поэтому в самом начале ему пришлось прописать следующие строку сразу после инициализации:

$db->query('SET CHARACTER SET utf8');

Теоретически, если следовать документации Configuring the Character Set and Collation for Applications, хватило-бы только SET NAMES ‘utf8’, но в таком случае collation остался бы utf8_general_ci — а нам этого не хочется. Поэтому, копнув немного глубже, в Connection Character Sets and Collations, было найдено SET CHARACTER SET utf8.

После данных изменений со стороны административной части на Zend Framework, замены $conn->set_charset(“utf8”); тоже на $conn->query(‘SET CHARACTER SET utf8’); и правок в базе данных всё заработало прекраснейшим образом.

Я вот понять не могу, почему у Zend Framework нигде об этой проблеме не написано? Зачем у mysqli есть метод mysqli::set_charset, если он меняет collation на верный? Почему в PHP мануале написано не использовать «старый дедовский способ с SET NAMES»?

Вообще, если посмотреть на то, что я встречаю в других проектах, которые попадают к нам на поддержку или консультации, много кто зарывается на этих charset и collation к сожалению, особенно на мультиязычных проектах.

6 thoughts on “MySQL + PHP: charset и collation

  1. Алексей ЗахлестинNo Gravatar

    Я наверное странное скажу, но определить какой использовать чарсет Zend_DB не может впринципе, потому что речь идёт о чарсете соединения. Он, строго говоря, может быть совершенно разным даже для разных пользователей. MySQL на ходу конвертирует все записи из чарсета базы в чарсет соединения.
    Часто встречаются случаи, когда база в utf8_unicode_ci, а соединение в latin1 или cp1251… при это всё корректно работает (если чарсет соединения совпадает с чарсетом выдаваемых документов) :)

  2. Сергей КуракинNo Gravatar Post author

    Алексей, это совсем не странное, и совершенно прав, это как должно быть и как есть на самом деле. Я ничего против этого механизма не имею, он просто замечателен.

    Но я смотрю на Zend Framework как на интегрированное средствоъплатформу для разработки WEB проектов, и если Zend_Db используется внутри проекта, то почему-бы им всем не работать в одном charset автоматически? Как к примеру у них устроено с Zend_Locale или Zend_Translate? Ведь у Zend_View есть понятие encoding, которое могло-бы быть установлено где-то ввиде ключа в Zend_Registry (может я его не нашёл где?)…

  3. Pingback: Zend_View и encoding | Сергей Куракин

  4. OldFornitNo Gravatar

    Дело в том, что чарсет можно передать как PDO-параметра соединения, поэтому и пишут – не использовать ;-)

    А вообще я и тут попропагандирую Posgres – с ним этой проблемы не возникает никогда. Вообще.

  5. БлоголётчикNo Gravatar

    А почему не исползовали встроенный в php PDO? Вроде будет как побыстрее чем zend?

  6. Сергей КуракинNo Gravatar Post author

    OldFornit – вполне может быть. Posgres? Может Postgres? Если это Postgres то да, в нём есть свои плюсы.

    Блоголётчик, не нравиться мне API PDO.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.