Починил 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, и как только выйдут новые версии поддерживаемых веток интерпретаторов, мы, наконец, перестанем спотыкаться об этот баг.
Комментарии 12
Вы меня извините за снобизм, но ЗАЧЕМ? Разве существуют кодировки кроме utf8?
а потом однажды решили выйти на мир, и локализовать продукт на 11 языков.
моя команда занималась переписыванием 1.5 MLoC кода на жабе и JS 11 месяцев в отдельной utf8 ветке. продукт в это время продолжал развиваться, поэтому последние 9 месяцев я лично каждый день синхронизировал изменения в обе стороны. проще было бы вообще выкинуть нахрен все кишки, и написать заново, но нельзя
но ничего. ничего таки не сломали. но если бы изначально не экономили на спичках, не пришлось бы почти год тратить на переписывание.
Ну и у нас не на спичках экономия, как можно наверное понять из предыдущего моего комментария.
звучит разумно и от этого ещё больнее :)
испытываю отвращение к любым кодировкам кроме utf8
как и к любым таймзонам кроме utc
а вы в браузер тоже cp1251 показываете? ведь для того же json надо уже конвертировать, например.
Зачем?
—
— по содержимому можно надёжно определить, что текст закодирован в
— уже упомянутая обратная совместимость с ASCII
Наконец, как вы живёте без Unicode смайликов? ?
Следующий аргумент:
Готов поспорить, что не всегда. И не понимаю зачем это нужно сейчас.
Почему это вообще для наскакое-то достоинство?