PHP is_numeric or preg_match 0-9 validation
Asked Answered
N

12

47

This isn't a big issue for me (as far as I'm aware), it's more of something that's interested me. But what is the main difference, if any, of using is_numeric over preg_match (or vice versa) to validate user input values.

Example One:

<?php
    $id = $_GET['id'];
    if (!preg_match('/^[0-9]*$/', $id)) {
        // Error
    } else {
        // Continue
    }
?>

Example Two:

<?php
    $id = $_GET['id'];
    if (!is_numeric($id)) {
        // Error
    } else {
        // Continue
    }
?>

I assume both do exactly the same but is there any specific differences which could cause problems later somehow? Is there a "best way" or something I'm not seeing which makes them different.

Norrisnorrv answered 4/10, 2011 at 14:48 Comment(0)
M
74

is_numeric() tests whether a value is a number. It doesn't necessarily have to be an integer though - it could a decimal number or a number in scientific notation.

The preg_match() example you've given only checks that a value contains the digits zero to nine; any number of them, and in any sequence.

Note that the regular expression you've given also isn't a perfect integer checker, the way you've written it. It doesn't allow for negatives; it does allow for a zero-length string (ie with no digits at all, which presumably shouldn't be valid?), and it allows the number to have any number of leading zeros, which again may not be the intended.

[EDIT]

As per your comment, a better regular expression might look like this:

/^[1-9][0-9]*$/

This forces the first digit to only be between 1 and 9, so you can't have leading zeros. It also forces it to be at least one digit long, so solves the zero-length string issue.

You're not worried about negatives, so that's not an issue.

You might want to restrict the number of digits, because as things stand, it will allow strings that are too big to be stored as integers. To restrict this, you would change the star into a length restriction like so:

/^[1-9][0-9]{0,15}$/

This would allow the string to be between 1 and 16 digits long (ie the first digit plus 0-15 further digits). Feel free to adjust the numbers in the curly braces to suit your own needs. If you want a fixed length string, then you only need to specify one number in the braces.

Minimize answered 4/10, 2011 at 14:54 Comment(6)
So with the Regex how would you rectify the problem of the leading zeros etc? This could cause problems for me after all, since this is the method I've been using. If I just want to validate that it was a whole number (4,8,15,16,23,42,108 etc), no leading zeros, no decimal points, no negatives, how would this be achieved?Norrisnorrv
Thank you Spudley, that is a very clear explanation - definitely have a better understanding of their differences. Looks like I'll be going back and adjusting some of my code! :)Norrisnorrv
@Spudley, what should use for decimal values?Dehiscent
@er.irfankhan11: the answer depends on things like how many decimal places you want to allow, etc. A clue: Use \. or [.] for the decimal dot, and similar rules to the above for the digits after it. If you need more detail, please ask it as a separate question.Minimize
Probably one of the best explanations I ever seen in a forum. To this day I suck with regular expressions. It's gibberish to me! I've seen people help others with regex, but they usually lack explanation. Thanks for this solid answer. Now I don't have to feel intimidated by them anymore. Thanks!Fay
PHP's is_numeric will return true for 1337e0 which is not a number. It might be considered a number in a more niche field though for programmers it's not only overkill it forces people to ask this question.Terminal
B
12

According to http://www.php.net/manual/en/function.is-numeric.php, is_numeric alows something like "+0123.45e6" or "0xFF". I think this not what you expect.

preg_match can be slow, and you can have something like 0000 or 0051.

I prefer using ctype_digit (works only with strings, it's ok with $_GET).

<?php
  $id = $_GET['id'];
  if (ctype_digit($id)) {
      echo 'ok';
  } else {
      echo 'nok';
  }
?>
Brescia answered 4/10, 2011 at 14:57 Comment(3)
So ctype_digit just checks for rounded numbers, what about if it's a positive or negative number? How does it work with that?Norrisnorrv
It works only with positive numbers, I think it can be useful for an id (I don't have negative Ids in my code): var_dump(ctype_digit('-5')); // returns falseBrescia
I, too, would probably use ctype_digit() for this task. I should note, though, that ctype_digit() will allow 00000004; so if you don't want zero left-padding to be possible, you can cast as an int explicitly $id = (int)$id (after the ctype_digit() check).Calibre
L
7

is_numeric() allows any form of number. so 1, 3.14159265, 2.71828e10 are all "numeric", while your regex boils down to the equivalent of is_int()

Lavinialavinie answered 4/10, 2011 at 14:51 Comment(1)
Not sure how it worked back '11. But now is_int and is_integer actually returns false for any given stringPacket
L
5

is_numeric would accept "-0.5e+12" as a valid ID.

Lillia answered 4/10, 2011 at 14:51 Comment(0)
W
5

Not exactly the same.

From the PHP docs of is_numeric:

'42' is numeric
'1337' is numeric
'1e4' is numeric
'not numeric' is NOT numeric
'Array' is NOT numeric
'9.1' is numeric

With your regex you only check for 'basic' numeric values.

Also is_numeric() should be faster.

Whiz answered 4/10, 2011 at 14:51 Comment(3)
So using is_numeric to check against say a post entry on a forum which all have unique IDs assigned by auto_increment, it may cause problems since the database just has integers, no decimals, symbols etcNorrisnorrv
@Joe Mottershaw: if that's the case you would use is_int()Whiz
@JoeMottershaw: or perhaps better you would type cast it to an int: $id = (int) $_REQUEST['id'];Whiz
U
5

is_numeric checks whether it is any sort of number, while your regex checks whether it is an integer, possibly with leading 0s. For an id, stored as an integer, it is quite likely that we will want to not have leading 0s. Following Spudley's answer, we can do:

/^[1-9][0-9]*$/

However, as Spudley notes, the resulting string may be too large to be stored as a 32-bit or 64-bit integer value. The maximum value of an signed 32-bit integer is 2,147,483,647 (10 digits), and the maximum value of an signed 64-bit integer is 9,223,372,036,854,775,807 (19 digits). However, many 10 and 19 digit integers are larger than the maximum 32-bit and 64-bit integers respectively. A simple regex-only solution would be:

/^[1-9][0-9]{0-8}$/ 

or

/^[1-9][0-9]{0-17}$/

respectively, but these "solutions" unhappily restrict each to 9 and 19 digit integers; hardly a satisfying result. A better solution might be something like:

$expr = '/^[1-9][0-9]*$/';
if (preg_match($expr, $id) && filter_var($id, FILTER_VALIDATE_INT)) {
    echo 'ok';
} else {
    echo 'nok';
}
Unction answered 30/10, 2013 at 5:21 Comment(0)
S
3

is_numeric checks more:

Finds whether the given variable is numeric. Numeric strings consist of optional sign, any number of digits, optional decimal part and optional exponential part. Thus +0123.45e6 is a valid numeric value. Hexadecimal notation (0xFF) is allowed too but only without sign, decimal and exponential part.

Sojourn answered 4/10, 2011 at 14:52 Comment(0)
L
2

You can use this code for number validation:

if (!preg_match("/^[0-9]+$/i", $phone)) {
        $errorMSG = 'Invalid Number!';
        $error    = 1;
    }
Laevorotation answered 14/11, 2017 at 12:42 Comment(2)
Why in the world would you need a case-insensitive flag for a completely numeric character class? Can you show me an example of an uppercase number and a lower case number?Calibre
Stop being so rude and scientist. Just say numbers don't have any case, so, please remove the i.Alvardo
R
1

If you're only checking if it's a number, is_numeric() is much much better here. It's more readable and a bit quicker than regex.

The issue with your regex here is that it won't allow decimal values, so essentially you've just written is_int() in regex. Regular expressions should only be used when there is a non-standard data format in your input; PHP has plenty of built in validation functions, even an email validator without regex.

Rogerson answered 4/10, 2011 at 14:51 Comment(1)
I don't think is_int() does what you think it does -- it actually checks the type of variable being checked; it isn't a string validator.Minimize
A
1

PHP's is_numeric function allows for floats as well as integers. At the same time, the is_int function is too strict if you want to validate form data (strings only). Therefore, you had usually best use regular expressions for this.

Strictly speaking, integers are whole numbers positive and negative, and also including zero. Here is a regular expression for this:

/^0$|^[-]?[1-9][0-9]*$/

OR, if you want to allow leading zeros:

/^[-]?[0]|[1-9][0-9]$/

Note that this will allow for values such as -0000, which does not cause problems in PHP, however. (MySQL will also cast such values as 0.)

You may also want to confine the length of your integer for considerations of 32/64-bit PHP platform features and/or database compatibility. For instance, to limit the length of your integer to 9 digits (excluding the optional - sign), you could use:

/^0$|^[-]?[1-9][0-9]{0,8}$/

Ahriman answered 9/6, 2014 at 14:51 Comment(1)
In my projects, I never expect an "id" to be zero.Calibre
T
1

Meanwhile, all the values above will only restrict the values to integer, so i use

/^[1-9][0-9\.]{0,15}$/

to allow float values too.

Toothbrush answered 24/3, 2015 at 8:47 Comment(0)
D
0

You can use filter_var() to check for integers in strings

<?php
$intnum = "1000022";
if (filter_var($intnum, FILTER_VALIDATE_INT) !== false){
    echo $intnum.' is an int now';
}else{
    echo "$intnum is not an int.";
}
// will output 1000022 is an int now
Doily answered 16/12, 2021 at 18:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.