is_numeric
Функция is_numeric одна из традиционно неверно понимаемых программистами на ПХП. Её, обычно, понимаю как «посмотреть содержится ли в переменной что-то похожее на число». Например, таким образом проверяют пришло ли число в строке для последующего его преобразования в численный тип.
Беда в том, что понятия числа у ПХП и человека различаются. Посмотрите, все эти вызовы рассматриваемой функции вернут «true»:
var_dump(is_numeric("123e123")); // true — десятичное число в экспоненциальной записи
var_dump(is_numeric("0123")); // true — восьмеричное число
var_dump(is_numeric("0XDeadBeef")); // true — шестнадцатеричное число
var_dump(is_numeric(NAN)); // true — специальное численное значение «не число»
Особенно не везёт восьмеричным числам, которые во многих языках (и в ПХП) тоже начинаются с нуля: формально выглядят как обычное десятичное (только начинается с нуля), проходят все проверки, а после преобразования теряют ведущий ноль и это может быть важно.
С незапамятных времён существует хак, который позволяет убедиться, что перед нами именно десятичное число, пусть и в строковой форме:
function is_number($var) { return (string) (int) $var === (string) $var; }
Принцип очень простой — переменная преобразуется в целый тип (можно преобразовывать и в тип с плавающей точкой, если это нужно), потом берётся строковое представление получившегося числа. На этом этапе восьмеричные, шестнадцатеричные и прочие виды записи чисел будут преобразованы в целочисленный тип, строковое представление которого — десятичное число. Справа же пришедшее остаётся в первозданном виде.
Далее мы сравниваем левую и правую часть и, если они совпадают, значит к нам пришло именно десятичное число, так как никаких преобразований с ним не произошло.
Добавлено позднее: в документации к функции написано, что она так же понимает двоичную форму записи введёную недавно (число должно начинаться с «0b»), к сожалению, это неправда — в коде поддержки нет, баг заведён и в нём написано, что это проблема документации, а не функции.
Интересно ещё, что is_numeric пропускает любое количество пробельных символов слева, но плохо относится к ним справа:
var_dump(is_numeric("\f\f\t0xBadBabe")); // true
var_dump(is_numeric("1\t")); //false
В качестве придирки: функция не работает для строк типа «+2» (возвращает FALSE, хотя при приведении к целочисленному типу строка превращается в int(2)). И последняя круглая скобка лишняя.
Комментарий для morozov.livejournal.com:
Она и не должна работать для строки «+2», с чего бы? Тут ситуация ровно как с нулём — он не должен потеряться, плюс — тоже. Скобку исправил, спасиюо!
У is_number (особенно если обрабатываются входные данные) не хватает trim().
Причём trim можно использовать только в правой части. Тогда и необходимость явно приводить $var в string отпадает.
Ну если задача функции — убедиться в том, что строка содержит десятичное число, то «+2» — это десятичное число (например, градусов тепла), точно так же, как и «-2» — это десятичное число.
Комментарий для morozov.livejournal.com:
Вопрос определений. В исходной задачи часто требуется перевести в число то, что должно бы иметь тип int/float, но почему-либо записано в строковой форме (например, пришло из GET/POST-запроса). «+2» тут не может иметь такой тип, так как «плюс» нужно будет восстанавливать из данных (пусть это и делается без проблем), тогда как в строке он просто записан.
Комментарий для http://steelice.ru:
Если вам нужен trim, то очевидно, что пришло не число в строковой форме, а строка, содержащая число и пробельные символы.
Комментарий для Евгения Степанищева:
Если отрицательные значения не нужны, а нужны именно положительные целочисленные значения (например, идентификаторы из базы данных) то есть ctype_digit() (и ctype_xdigit() для тех самых шестнадцатеричных значений)
Комментарий для Горбунов Олег:
0777 пройдёт ctype_digit().