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
28 августа 2014 09:25

Сергей Морозов (morozov.livejournal.com)
28 августа 2014, 10:10

В качестве придирки: функция не работает для строк типа "+2" (возвращает FALSE, хотя при приведении к целочисленному типу строка превращается в int(2)). И последняя круглая скобка лишняя.

Евгений Степанищев (bolknote.ru)
28 августа 2014, 10:22, ответ предназначен Сергей Морозов (morozov.livejournal.com):

Она и не должна работать для строки «+2», с чего бы? Тут ситуация ровно как с нулём — он не должен потеряться, плюс — тоже. Скобку исправил, спасиюо!

http://steelice.ru (инкогнито)
28 августа 2014, 10:32

У is_number (особенно если обрабатываются входные данные) не хватает trim().
Причём trim можно использовать только в правой части. Тогда и необходимость явно приводить $var в string отпадает.

Сергей Морозов (morozov.livejournal.com)
28 августа 2014, 10:35

Ну если задача функции — убедиться в том, что строка содержит десятичное число, то «+2» — это десятичное число (например, градусов тепла), точно так же, как и «-2» — это десятичное число.

Евгений Степанищев (bolknote.ru)
28 августа 2014, 10:39, ответ предназначен Сергей Морозов (morozov.livejournal.com):

Вопрос определений. В исходной задачи часто требуется перевести в число то, что должно бы иметь тип int/float, но почему-либо записано в строковой форме (например, пришло из GET/POST-запроса). «+2» тут не может иметь такой тип, так как «плюс» нужно будет *восстанавливать* из данных (пусть это и делается без проблем), тогда как в строке он просто записан.

Евгений Степанищев (bolknote.ru)
28 августа 2014, 10:41, ответ предназначен http://steelice.ru

У is_number (особенно если обрабатываются входные данные) не хватает trim().
Причём trim можно использовать только в правой части. Тогда и необходимость явно приводить $var в string отпадает.
Если вам нужен trim, то очевидно, что пришло не число в строковой форме, а строка, содержащая число и пробельные символы.

Горбунов Олег (инкогнито)
28 августа 2014, 10:44, ответ предназначен Евгений Степанищев (bolknote.ru):

Если отрицательные значения не нужны, а нужны именно положительные целочисленные значения (например, идентификаторы из базы данных) то есть ctype_digit() (и ctype_xdigit() для тех самых шестнадцатеричных значений)

Евгений Степанищев (bolknote.ru)
28 августа 2014, 10:45, ответ предназначен Горбунову Олегу

0777 пройдёт ctype_digit().

Ваше имя или адрес блога (можно OpenID):

Текст вашего комментария, не HTML:

Кому бы вы хотели ответить (или кликните на его аватару)