Last week David Walsh posted about validating numeric values and digits with PHP on his excellent blog. I posted my own comments about his particular post but they never appeared so I decided to post a better response here.
The point of David’s post was that he needed to be able to validate between finding a numeric value (which might be a floating point number and therefore contain a decimal point) and whether a number only contains digits.
His solution was this:
/* numeric, decimal passes */ function validate_numeric($variable) { return is_numeric($variable); } /* digits only, no dots */ function is_digits($element) { return !preg_match ("/[^0-9]/", $element); }
As I noted in my comment (which wasn’t published), I’m not sure why you’d wrap is_numeric() into his validate_numeric() function when all it’s doing is calling is_numeric(). Why not just call is_numeric() instead?
The is_digits() function on the other hand is actually pretty good. I initially thought there may be some issues with it but it works nicely but there is one simpler alternative.
is_integer – no good
In my comment I suggested that he could use the is_integer() function as long as the acceptable value was within the integer type range, and postive and negative values are acceptable. Of course, a digit only value might not be within the integer range, for example is_integer(12345678901234567890) would return false but does only contain numbers.
The problem with my initial assumption is that in PHP data submitted from a form that *does* only contain digits and *is* within the integer range will still return false from is_integer() because it will be considered to be a string. For example:
<form ...> <input type="text" name="foo" value="123" /> </form>
And then doing this when the form is submitted:
echo (int)is_integer((int)$_POST['foo']);
will print 0, meaning false. D’oh! Also an integer can contain – if it is a negative value so -123 will return true even though it contains more than just digits.
ctype_digit – not much better
The other alternative is the ctype_digit() function which saves having to mess around with regular expressions. ctype_digit checks if all of the characters in the passed in value are numerical. Only the numbers 0 to 9 are valid, and it doesn’t matter if the value is type cast as a string, which is what will happen from a form post.
However, ctype_digit it expects the input to be a string and will return false if the value passed in is an ineteger…
For example:
var_dump(ctype_digit(123)); var_dump(ctype_digit('123')); var_dump(ctype_digit(12345678901234567890)); var_dump(ctype_digit('12345678901234567890'));
These display:
bool(false) bool(true) bool(false) bool(true)
You should be able to use (string), strval() etc to cast the value as a string first and then pass it to ctype_digit but I’ve found if the number is too large it still doesn’t work.
For example, the first four below return true, but the last one returns false:
ctype_digit(strval(12345678901)); ctype_digit(strval(123456789012)); ctype_digit(strval(1234567890123)); ctype_digit(strval(12345678901234)); ctype_digit(strval(123456789012345));
Use the regexp
So my initial thought was just use is_integer() and someone else suggested to use ctype_digit but it would turn out that using the regular expression with preg_match is the best solution after all. Just remember when using regular expressions with PHP to use the preg* functions and not the ereg* functions because the latter are deprecated in PHP 6.