Хранитель Атлантиды
Скажу сразу: я очень рад, что Unicode повсеместно проник в массы, прочно поселившись и на веб-серверах. Это помогло, наконец-то, забыть раз и навсегда о проблемах с кодировками и веселых вечерах распутывания абракадабры, которая могла получиться, когда текст, отправленный пользователем, сначала кодировался броузером, затем пропускался через интерпретатор и, наконец, приземлялся в базе данных, у которой также могло быть свое мнение на счет того, в какой кодировке это следует все хранить. Обратное преобразование шло примерно по такой же схеме, плюс тут могли вмешиваться свои веселые эпизоды, вроде хостера, которому приспичило обновить СУБД до более новой версии с попутным перекодированием данных в таблицах. Именно это однажды произошло с Атлантидой и ситуацию спасло только то, что перед обновлением была таки сделана резервная копия.
Прошел десяток лет, UTF-8 стал стандартом де-факто повсюду, благодаря чему подобные сценарии сегодня могут разве что присниться в страшных снах — в случае правильно настроенного сервера, разумеется. Однако, поддержка legacy-кода, который был порожден в «доперестроечную» эпоху, может нести свои сюрпризы.
Как известно, в PHP переход на Unicode происходил с диким скрипом и скрежетом. На одном этапе это даже вылилось в необходимость писать аналоги функций, которые бы правильно работали с ним, поскольку разработчики, очевидно, никак не рассчитывали, что для кодирования одной буквы когда-нибудь понадобится больше одного байта. Одной из жертв подобного поведения стали и функции (un)serialize(), призванные упаковывать переданный им массив в строку. Так, развернув на более-менее подходящей версии компонентов старую копию форума на не менее старинном Invision Board 2.1, я убедился, что оно пытается как-то работать, но половина контента при этом не выводится, а просматривать темы вообще можно было только в текстовом режиме, поскольку в обычном сил у скрипта хватало лишь на то, чтобы вывести девственно чистую страницу с тремя уведомлениями о делении на ноль.
Можно было бы оставить все это дело и так, тем более, что от бэкапа мне нужен был не сам форум, а тексты с него, но выработанное с годами чувство перфекционизма не позволяло вот так просто пустить все на самотек, поэтому я вновь начал расковыривать чужой код. Достаточно скоро выяснилось, что по какой-то причине система не загружает собственные настройки, в числе которых есть и такие вещи, как количество сообщений на страницу по умолчанию и прочее, из-за чего, собственно, и происходило, кроме прочего, и деление на ноль.
Invision Board, вообще, хранит свои настройки в виде отдельных записей в таблице СУБД, но для скорости работы кэширует их в массиве, который потом сохраняется в отдельной таблице. Вот с ним-то и возникла проблема, поскольку через serialize() он был пропущен в те времена, когда о существовании Unicode лишь смутно догадывались, а вот попытка распаковать его на системе, где UTF-8 уже включен по умолчанию, приводила к тому, что все кириллические символы начинали занимать вдвое больше места. Это, естественно, не сходилось с данными в упакованном массиве и интерпретатор уже не мог его распаковать.
Например, вот строка, упакованная старой версией PHP на предыдущей системе:
Как видим, все хорошо, данные занимают 16 байт, поскольку кодировка однобайтовая. Однако, попытка распаковать их на системе с UTF приведет к тому, что 13 байт кириллицы превратятся уже в 26, но при этом в сериализованных данных указано, что их надо запихнуть в прежние 16, что, разумеется, не получается. В системе с Unicode, таким образом, эта строка должна иметь такой вид:
Пришлось потратить еще немного времени, но я нашел функцию, отвечающую за обновление кэша настроек, какие данные из таблицы она в него помещает, и просто сделал это вручную. Этого оказалось достаточно, чтобы 90% функционала форума тут же начало работать как следует. Даже админка, хоть и выводит вверху предупреждения, в целом работает адекватно.
В целом я рад полученному интересному опыту. Всегда здорово, когда получается не только найти ошибку, но и понять, что именно ее вызывает и устранить ее. Ну и заодно стоит отдать должное создателям Invision Board, которая ухитряется хоть как-то, но работать даже без примерно 95% данных о собственной конфигурации.
Прошел десяток лет, UTF-8 стал стандартом де-факто повсюду, благодаря чему подобные сценарии сегодня могут разве что присниться в страшных снах — в случае правильно настроенного сервера, разумеется. Однако, поддержка legacy-кода, который был порожден в «доперестроечную» эпоху, может нести свои сюрпризы.
Как известно, в PHP переход на Unicode происходил с диким скрипом и скрежетом. На одном этапе это даже вылилось в необходимость писать аналоги функций, которые бы правильно работали с ним, поскольку разработчики, очевидно, никак не рассчитывали, что для кодирования одной буквы когда-нибудь понадобится больше одного байта. Одной из жертв подобного поведения стали и функции (un)serialize(), призванные упаковывать переданный им массив в строку. Так, развернув на более-менее подходящей версии компонентов старую копию форума на не менее старинном Invision Board 2.1, я убедился, что оно пытается как-то работать, но половина контента при этом не выводится, а просматривать темы вообще можно было только в текстовом режиме, поскольку в обычном сил у скрипта хватало лишь на то, чтобы вывести девственно чистую страницу с тремя уведомлениями о делении на ноль.
Можно было бы оставить все это дело и так, тем более, что от бэкапа мне нужен был не сам форум, а тексты с него, но выработанное с годами чувство перфекционизма не позволяло вот так просто пустить все на самотек, поэтому я вновь начал расковыривать чужой код. Достаточно скоро выяснилось, что по какой-то причине система не загружает собственные настройки, в числе которых есть и такие вещи, как количество сообщений на страницу по умолчанию и прочее, из-за чего, собственно, и происходило, кроме прочего, и деление на ноль.
Invision Board, вообще, хранит свои настройки в виде отдельных записей в таблице СУБД, но для скорости работы кэширует их в массиве, который потом сохраняется в отдельной таблице. Вот с ним-то и возникла проблема, поскольку через serialize() он был пропущен в те времена, когда о существовании Unicode лишь смутно догадывались, а вот попытка распаковать его на системе, где UTF-8 уже включен по умолчанию, приводила к тому, что все кириллические символы начинали занимать вдвое больше места. Это, естественно, не сходилось с данными в упакованном массиве и интерпретатор уже не мог его распаковать.
Например, вот строка, упакованная старой версией PHP на предыдущей системе:
Как видим, все хорошо, данные занимают 16 байт, поскольку кодировка однобайтовая. Однако, попытка распаковать их на системе с UTF приведет к тому, что 13 байт кириллицы превратятся уже в 26, но при этом в сериализованных данных указано, что их надо запихнуть в прежние 16, что, разумеется, не получается. В системе с Unicode, таким образом, эта строка должна иметь такой вид:
Пришлось потратить еще немного времени, но я нашел функцию, отвечающую за обновление кэша настроек, какие данные из таблицы она в него помещает, и просто сделал это вручную. Этого оказалось достаточно, чтобы 90% функционала форума тут же начало работать как следует. Даже админка, хоть и выводит вверху предупреждения, в целом работает адекватно.
В целом я рад полученному интересному опыту. Всегда здорово, когда получается не только найти ошибку, но и понять, что именно ее вызывает и устранить ее. Ну и заодно стоит отдать должное создателям Invision Board, которая ухитряется хоть как-то, но работать даже без примерно 95% данных о собственной конфигурации.