26 сентября 2006 г.

Apache + PHP + DB2: набор граблей для русского человека

Иногда размеренное течение жизни нарушается таким завихрением, что сразу и не понимаешь, пора тонуть или можно ещё побарахтаться. Вот, скажем, вчера. Обычная рутина. Разрабатывается некое приложение, хранящее некие данные в базе DB2. Доступ к ним, естественно, через web. HTTP-сервер - Apache. Сервер, с позволения сказать, приложений - PHP. Всё хорошо, приложение разрабатывается, данные из базы берутся, на странице показываются.

И вот я опрометчиво делаю странную вещь. Я решаю проверить, а как всё будет работать, если данные - на русском языке. Задумано, сделано. Посредством Control Center в базу вносится несколько записей с русскими буквами. Я обращаюсь к ним. Трах! Бах! На странице в нужных местах ничего нет.

Ну, думаю, у кого с первого раза такое получалось? Начинаю разбираться. Изучаю error.log сервера (сначала в этом месте я хотел написать "индейца". Для верности спросил у Лингвы. Ответ: апаш, бомж, хулиган, бандит, головорез. В общем, самое оно). И вижу запись, которая повергает меня в состояние ужаса.
PHP Warning: db2_fetch_array() [function.db2-fetch-array]: Fetch Failure in db2.inc on line NN
Должен признаться, что ни в Apache, ни в PHP я не силён. Так, постольку поскольку. Естественно, количество людей, использующих такую комбинацию софта, невелико. Подмножество тех, кому при этом небезразличен русский язык, наверное, пренебрежимо мало. Из них я знаю только одного человека. Да и то, я сам ему порекомендовал DB2. И с кем в этой ситуации мне было советоваться? В общем, стучусь к нему в асю, обрисовываю ситуацию, спрашиваю, ты как с этим справился? А он мне и отвечает: а я с русским ещё не работал. Называется, приплыли.

Ну что ж. Стали пытаться определить, где засада. В конце концов выяснил, что при запросе данных из БД возникает вот такая ошибка:
[IBM][CLI Driver][DB2/NT] SQLSTATE 01517: A character that could not be converted was replaced with a substitute character
Следом в сети был подсмотрен найденный кем-то эмпирически способ. Если запрашиваемые данные сконвертировать в blob, типа SELECT cast(column as blob) FROM..., то "иногда они возвращаются" ©. Проверил, сработало. Но понятно было, что это не решение проблемы. Например, потому, что данные в БД нужно ещё и класть. И тут этот метод точно не сработает.

Пришлось искать дальше. Функция PHP db2_server_info показала, что кодовая страница БД - 1208 (юникод). Естественно. Я её такой создавал. Ровно оттого, что мне в ней хранить данные на довольно большом количестве языков. Функция db2_client_info сообщила, что кодовые страницы что приложения, что коннектора - 1252. Я, в общем-то с этом тоже не спорю. Потому что всё происходит на сервере, а устанавливать ему какую-то русскую кодовую страницу как-то странно.

И вот тут до меня начало доходить.

Естественно, сервер берёт юникодовые данные, смотрит на клиента. А по клиенту видно, что кроме Latin-1 он ничего не соображает. Ну сервер-то в такой ситуации ничего сделать не может - не соображаешь, получи отлуп. Стал искать, где приложению или PHP можно сделать facelift. В смысле, разрешить понимать юникод. Не нашёл. Пришлось действовать топором. Всему runtime client-у DB2 было сказано: понимать юникод.

После того, как перезагрузил весь сервер (по другому никак, PHP иначе не смог прихватить изменения), проверил. И СРАЗУ ВСЕ МОИ ПЕЧАЛИ УШЛИ. Всё заработало как и должно. Никаких заклинаний (cast) при извлечении данных, никаких ошибок. Всё возвращается как надо. Надо будет, конечно, проверить поиск, сохранение и т.п., но тут я уже почему-то уверен, что всё будет в порядке. До очередного облома.

Что же останется в сухом остатке, если не читать всю лирику сверху?

Чтобы не было проблем с русским или ещё каким-нибудь языками в комбинации Apache - PHP - DB2, необходимо (речь о форточках, что будет на линухе, не знаю, но думаю, то же самое):
  • Базу данных создавать с поддержкой Unicode (UTF-8), кодовая страница 1208
  • Колонки БД, которые могут содержать символы не английского алфавита, нужно создавать вдвое большей длины
  • Если клиент (Apache + PHP) и сервер DB2 находятся на разных машинах, на обоих в командной строке сказать: db2set DB2CODEPAGE=1208. Если всё установлено на одной машине, то работы вдвое меньше
  • Перезагрузить сервер. Если сервер не один - достаточно того, на котором установлены Apache и PHP. Впрочем, и сервер базы данных на всякий случай тоже можно
  • Убедиться, что всё волшебным образом заработало
  • Для отладки я использовал функции error_log, db2_stmt_error и db2_stmt_errormsg. Чего и вам желаю

Комментариев нет: