Евгений Степанищев

Я — эксперт в области разработки веб-приложений и безопасности в интернете. Каждый месяц мой блог посещают около 90 тысяч человек. Работаю техническим директором в «Системах документооборота», занимаюсь электронным правительством.

WDDX в PHP и русские символы

У меня ощущение, что никто никогда не пробовал гонять национальные символы через WDDX из PHP в другие языки и обратно. Задача: получить от демона Perl сериализованные данные и десериализовать их. Был выбран формат WDDX, вроде как хорошо описанный стандарт и всё такое. JSON сначала не подошёл, модуль в Perl что-то там дурил, теперь мы это победили. Тем временем в WDDX…

Всё было хорошо, пока в utf8-потоке от Perl не появились символы, которые не влезают в нижнюю половину ASCII. Я перебрал автоматом все кодировки, которые знает iconv, чтобы посмотреть в какой кодировке PHP WDDX умеет есть русские буквы. Ничего ему не понравилось. Потом я догадался сериализовать utf-8 символ через функцию из PHP. Получилось что-то странное. Залез в исходники.

В общем, оказалось, что какое-то время назад кто-то постил баг по этому поводу — UTF-8 в WDDX не передавались вообще. Решили проблему очень просто — взяли кодировку как однобайтовую ISO-8859-1, закодировали в utf-8 и в таком виде выпустили в свет. При получении делается обратное преобразование:
if (!strcmp(atts[i], EL_NAME) && atts[++i] && atts[i][0]) {
    char *decoded;
    int decoded_len;
    decoded = xml_utf8_decode(atts[i], strlen(atts[i]), &decoded_len, "ISO-8859-1");
    stack->varname = decoded;
    break;
}
Соответственно, когда PHP получает из Perl нормальный UTF-8, он его пытается рассмотреть как UTF-8, который надо превратить в ISO-8859-1, на выходе мы получаем вопросы. Чтобы этого не происходило перед wddx_deserialize надо сделать iconv('iso-8859-1', 'utf-8', $string), что решает проблему.

В итоге, мы перешли на JSON. Всем хорошего дня.
11 комментариев
25 июня 2008 17:13

Кодирование национальных символов в Cyrus IMAP

На днях пришлось столкнуться с кодированием русских символов в папках сервера Cyrus IMAP. Выглядит это примерно так:

folder.BC8EPQQ0BDUEOgRBBDAALQRBBDUEOgRABDUEQg-

Мне повезло, я догадался, что это модифицированная UTF-7, после раскодировки оказалось, что раскодированный текст находится в кодироке UCS-2 big endian. Получилась вот такая функция для перевода всего этого безобразия в UTF-8:
// Cyrus IMAP modified UTF-7 decode
function decodeCyrus($str)
{
    return preg_replace_callback('!&([a-z0-9+/=,]*)-!is', 'decodeCyrusHelper', $str);
}
 
function decodeCyrusHelper($m)
{
   if ($m[1] == '') return '&';
    return iconv('ucs-2be', 'utf-8', str_replace(',', '/', base64_decode($m[1])));
}
Способ использования: $decoded = decodeCyrus('folder.BC8EPQQ0BDUEOgRBBDAALQRBBDUEOgRABDUEQg-')
Комментировать
25 июня 2008 14:32