Работаю я над одним проектом, где в день проноситься до миллиона уникальных посетителей в день. Для этого проекта я с командой работаю над маленьким сателлитом, назначения которого не имеет столь весомого значения, сколько имеет значение проблема, с которой я сегодня столкнулся. И нам крупно повезло, что кол-во данных сейчас на сателлите мизерное и он пока находиться в стадии тестирования и нагрузка на нём растёт постепенно.
Проект пишется на 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> и <dt> вокруг скрытых полей и прочих мелких религиозных и языковых разногласий. Ни одному из нас не возник простейший вопрос, как устроен другой компонент — 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 к сожалению, особенно на мультиязычных проектах.