Починил ucfirst в PHP
В отпуске дошли руки посмотреть и починить баг в функции ucfirst — по задумке она должна приводить в верхний регистр первый символ однобайтовой строки, учитывая текущую локаль. Всё бы хорошо, но с буквой «я» кодовой страницы 1251 это не срабатывало — она оставалась в нижнем регистре.
Интересно, что похожая функция — strtoupper тут работает прекрасно, то есть вот такой код на текущих версиях ПХП выдаёт false:
if (setlocale(LC_CTYPE, "ru_RU.cp1251") === false) {
throw new RuntimeException();
}
// буква «я» имеет в кодировке «1251» код 255
var_dump(ucfirst(chr(255)) === strtoupper(chr(255));
Поскольку эта беда уже второй раз ломает тесты в нашем продукте (программисты об этой особенности просто забывают), решил глянуть в исходники. Внутри ПХП обе функции — проблемная и рабочая, используют вызов toupper, в документации к которой человеческим языком сказано, что передаваемый параметр должен иметь тип unsigned char.
Функция strtoupper как раз использует unsigned char, а ucfirst — char (который signed). Документация к toupper такую вольность по отношению к типам осуждает — там прямым текстом написано, что могут быть проблемы, так как внутри входной параметр будет расширен до int, знак перенесётся на старший разряд и всё сломается.
Что, собственно, и происходит с буквой «я», код которой как раз получается отрицательным. Удивительно, что с другими «отрицательными» буквами всё каким-то чудом работает.
В общем, мой патч приняли в версию 7.3, и как только выйдут новые версии поддерживаемых веток интерпретаторов, мы, наконец, перестанем спотыкаться об этот баг.
Вы меня извините за снобизм, но ЗАЧЕМ? Разве существуют кодировки кроме utf8?
Причина более чем банальна — экономия денег. Однобайтовая кодировка — это почти в два раза меньший объём дорогущих SSD на сотнях серверов (или тысячах? я что-то давно не знаю сколько у нас их), вдвое меньший I/O, меньшие затраты на CPU (например, все взятия по индексу в UTF-8 становятся O(n), а не O(1)), ну и так далее.
на прошлой работе в проекте тоже поначалу была однобайтная кодировка. и в базе, и в UI, и везде. а что, продукт жил в США, так что всё, что за пределами Latin1, попросту выкидывалось как ненужное. зачем тратиться на utf8, если всё равно актуален только американский английский?
а потом однажды решили выйти на мир, и локализовать продукт на 11 языков. пы-дыщь! не вечно же стартапом сидеть, надо же и в ынторпрайз вырастать, миллиарды для инвесторов зарабатывать.
моя команда занималась переписыванием 1.5 MLoC кода на жабе и JS 11 месяцев в отдельной utf8 ветке. продукт в это время продолжал развиваться, поэтому последние 9 месяцев я лично каждый день синхронизировал изменения в обе стороны. проще было бы вообще выкинуть нахрен все кишки, и написать заново, но нельзя из-за объёма. и худшего кошмара, чем итоговый коммит на 600 тыщ строк, я за все 20 с лишним лет своей карьеры не припомню.
но ничего. ничего таки не сломали. но если бы изначально не экономили на спичках, не пришлось бы почти год тратить на переписывание.
Ну да, такое тоже бывает. Но в нашем случае вероятность такого крайне невелика — продукт сильно заточен под российское законодательство, отвязывать его — проблема куда более серьёзная, чем смена кодировки и куда менее понятная.
Ну и у нас не на спичках экономия, как можно наверное понять из предыдущего моего комментария.
звучит разумно и от этого ещё больнее :)
Да нам не особо и больно :) Не знаю почему у многих такое отвращение к этой кодировке ) На ПХП до сих приходится использовать костыли, чтобы работать с многобайтовыми кодировками, так что с cp1251 наоборот — даже проще :)
испытываю отвращение к любым кодировкам кроме utf8
как и к любым таймзонам кроме utc
а вы в браузер тоже cp1251 показываете? ведь для того же json надо уже конвертировать, например.
ЮТФ-8 — худшая кодировка, на мой взгляд ) Сделанная как раз с целью экономии на спичках и совместимости с легаси :)
Зачем?
Какая альтернатива UTF-8 для международных текстов? Всё остальное объективно хуже на мой взгляд:
Наконец, как вы живёте без Unicode смайликов? ?
UCS-4, конечно. Очевидный выбор, вроде. BOM не является обязательным, и в UTF-8 он может применяться точно так же, см. табличку:
Следующий аргумент:
Готов поспорить, что не всегда. И не понимаю зачем это нужно сейчас.
Почему это вообще для нас какое-то достоинство?
Спасибо вам за патч! =)
Рад, что смог ещё кому-то помочь :)