103

I have the following code

    $page = $_GET['p'];

    if($page == "")
    {
        $page = 1;
    }
    if(is_int($page) == false)
    {
        setcookie("error", "Invalid page.", time()+3600);
        header("location:somethingwentwrong.php");
        die();
    }
    //else continue with code

which I am going to use for looking at different "pages" of a database (results 1-10, 11-20, etc). I can't seem to get the is_int() function to work correctly, however. Putting "1" into the url (noobs.php?p=1) gives me the invalid page error, as well as something like "asdf".

Austin Gayler
  • 4,038
  • 8
  • 37
  • 60

14 Answers14

222

Using is_numeric() for checking if a variable is an integer is a bad idea. This function will return TRUE for 3.14 for example. It's not the expected behavior.

To do this correctly, you can use one of these options:

Considering this variables array :

$variables = [
    "TEST -1" => -1,
    "TEST 0" => 0,
    "TEST 1" => 42,
    "TEST 2" => 4.2,
    "TEST 3" => .42,
    "TEST 4" => 42.,
    "TEST 5" => "42",
    "TEST 6" => "a42",
    "TEST 7" => "42a",
    "TEST 8" => 0x24,
    "TEST 9" => 1337e0
];

The first option (FILTER_VALIDATE_INT way) :

# Check if your variable is an integer
if ( filter_var($variable, FILTER_VALIDATE_INT) === false ) {
  echo "Your variable is not an integer";
}

Output :

TEST -1: -1 (type:integer) Your variable is an integer ✔
TEST 0 : 0 (type:integer) is an integer ✔
TEST 1 : 42 (type:integer) is an integer ✔
TEST 2 : 4.2 (type:double) is not an integer ✘
TEST 3 : 0.42 (type:double) is not an integer ✘
TEST 4 : 42 (type:double) is an integer ✔
TEST 5 : 42 (type:string) is an integer ✔
TEST 6 : a42 (type:string) is not an integer ✘
TEST 7 : 42a (type:string) is not an integer ✘
TEST 8 : 36 (type:integer) is an integer ✔
TEST 9 : 1337 (type:double) is an integer ✔

The second option (CASTING COMPARISON way) :

# Check if your variable is an integer
if ( strval($variable) !== strval(intval($variable)) ) {
  echo "Your variable is not an integer";
}

Output :

TEST -1: -1 (type:integer) Your variable is an integer ✔
TEST 0 : 0 (type:integer) is an integer ✔
TEST 1 : 42 (type:integer) is an integer ✔
TEST 2 : 4.2 (type:double) is not an integer ✘
TEST 3 : 0.42 (type:double) is not an integer ✘
TEST 4 : 42 (type:double) is an integer ✔
TEST 5 : 42 (type:string) is an integer ✔
TEST 6 : a42 (type:string) is not an integer ✘
TEST 7 : 42a (type:string) is not an integer ✘
TEST 8 : 36 (type:integer) is an integer ✔
TEST 9 : 1337 (type:double) is an integer ✔

The third option (CTYPE_DIGIT way) (positive numbers and 0 only):

# Check if your variable is an integer
if ( ! ctype_digit(strval($variable)) ) {
  echo "Your variable is not an integer";
}

Output :

TEST -1: -1 (type:integer) # Your variable is not an integer ✘
TEST 0 : 0 (type:integer) is an integer ✔
TEST 1 : 42 (type:integer) is an integer ✔
TEST 2 : 4.2 (type:double) is not an integer ✘
TEST 3 : 0.42 (type:double) is not an integer ✘
TEST 4 : 42 (type:double) is an integer ✔
TEST 5 : 42 (type:string) is an integer ✔
TEST 6 : a42 (type:string) is not an integer ✘
TEST 7 : 42a (type:string) is not an integer ✘
TEST 8 : 36 (type:integer) is an integer ✔
TEST 9 : 1337 (type:double) is an integer ✔

The fourth option (REGEX way) :

# Check if your variable is an integer
if ( ! preg_match('/^-?\d+$/', $variable) ) {
  echo "Your variable is not an integer";
}

Output :

TEST -1: -1 (type:integer) Your variable is an integer ✔
TEST 0 : 0 (type:integer) is an integer ✔
TEST 1 : 42 (type:integer) is an integer ✔
TEST 2 : 4.2 (type:double) is not an integer ✘
TEST 3 : 0.42 (type:double) is not an integer ✘
TEST 4 : 42 (type:double) is an integer ✔
TEST 5 : 42 (type:string) is an integer ✔
TEST 6 : a42 (type:string) is not an integer ✘
TEST 7 : 42a (type:string) is not an integer ✘
TEST 8 : 36 (type:integer) is an integer ✔
TEST 9 : 1337 (type:double) is an integer ✔
Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
Cam CHN
  • 3,377
  • 1
  • 19
  • 12
  • 11
    Pay attention with FILTER_VALIDATE_INT (your preferred way) 0 returns false! – Niksac Jul 27 '15 at 14:39
  • 17
    Use filter_var($int, FILTER_VALIDATE_INT) !== false instead. – Niksac Jul 27 '15 at 15:15
  • 1
    I think there is a ! missing at `if(filter_var($variable, FILTER_VALIDATE_INT) !== false) echo "its not an integer` ,right? – Adam Jun 28 '16 at 09:36
  • 3
    or the text should be changed to ` it is an iteger` – Adam Jun 28 '16 at 09:37
  • Great answer, I've been burned by trying is_numeric had to change it, hopefully your comments will save some people trouble ... – Mike Q Jun 06 '17 at 13:30
  • Validation is always very difficult to cater to all examples. In the above example, using "FILTER_VALIDATE_INT" does not return as an integer the number: "09" perhaps because "09" is not an integer? Well, yes and no....in dates, "09" is a formatting issue rather than a numeric vs string issue. So validating a number as a date, could lead to some trouble when using: "fliter_validate_int" which sees "09" as a string. Using REGEX solved the problem and returned: 09 as an integer (regardless of its type casting). – Danny F Feb 19 '18 at 16:41
  • It would be good if you added these cases: '00' '01' '12.00' Different implementations will return different answers. I'd suggest that all three are integers, in the common man sense of the word... – Dan Sandberg Jul 05 '18 at 22:31
  • 5
    The 'CTYPE_DIGIT way' and 'REGEX way' both fail for negative integers. The 'REGEX way' can be fixed by checking if there is a leading negative sign (`-?`), but I don't believe that `ctype_digit` can be used in a concise way to check for negative integers. Also, the posted code cannot produce the corresponding outputs. – Dave F Jul 13 '18 at 01:25
  • TYPO in first solution: ```if ( filter_var($variable, FILTER_VALIDATE_INT) !== false ) { echo "Your variable is not an integer"; }``` must be ```if ( filter_var($variable, FILTER_VALIDATE_INT) === false ) { echo "Your variable is not an integer"; }```! – David Vielhuber Sep 20 '18 at 20:44
  • What about `is_int()`? – Connor Leech Jul 19 '19 at 14:03
  • This answer could be improved by directly answering the question about `is_int()` . Starting the answer by saying that `is_numeric` is a bad idea is confusing. – Liam Jun 27 '23 at 17:04
36

All $_GET parameters have a string datatype, therefore, is_int will always return false.

You can see this by calling var_dump:

var_dump($_GET['p']); // string(2) "54"

Using is_numeric will provide the desired result (mind you, that allows values such as: 0x24).

27

When the browser sends p in the querystring, it is received as a string, not an int. is_int() will therefore always return false.

Instead try is_numeric() or ctype_digit()

Michael Berkowski
  • 267,341
  • 46
  • 444
  • 390
20

Just for kicks, I tested out a few of the mentioned methods, plus one I've used as my go to solution for years when I know my input is a positive number or string equivalent.

I tested this with 125,000 iterations, with each iteration passing in the same set of variable types and values.

Method 1: is_int($value) || ctype_digit($value)
Method 2: (string)(int)$value == (string)$value
Method 3: strval(intval($value)) === strval($value)
Method 4: ctype_digit(strval($value))
Method 5: filter_var($value, FILTER_VALIDATE_INT) !== FALSE
Method 6: is_int($value) || ctype_digit($value) || (is_string($value) && $value[0] === '-' && filter_var($value, FILTER_VALIDATE_INT) !== FALSE)

Method 1: 0.0552167892456
Method 2: 0.126773834229
Method 3: 0.143012046814
Method 4: 0.0979189872742
Method 5: 0.112988948822
Method 6: 0.0858821868896

(I didn't even test the regex, I mean, seriously... regex for this?)

Things to note:
Method 4 always returns false for negative numbers (negative integer or string equivalent), so is a good method to consistently detect that a value is a positive integer.
Method 1 returns true for a negative integer, but false for a string equivalent of a negative integer, so don't use this method unless you are certain your input will never contain a negative number in string or integer form, and that if it does, your process won't break from this behavior.

Conclusions
So it seems that if you are certain that your input will not include a negative number, then it is almost twice as fast to use is_int and ctype_digit to validate that you have an integer. Using Method 1 with a fallback to method 5 when the variable is a string and the first character is a dash is the next fastest (especially when a majority of the input is actual integers or positive numbers in a string). All in all, if you need solid consistency, and you have no idea what the mix of data is coming in, and you must handle negatives in a consistent fashion, filter_var($value, FILTER_VALIDATE_INT) !== FALSE wins.

Code used to get the output above:

$u = "-10";
$v = "0";
$w = 0;
$x = "5";
$y = "5c";
$z = 1.44;

function is_int1($value){
    return (is_int($value) || ctype_digit($value));
}

function is_int2($value) {
    return ((string)(int)$value == (string)$value);
}

function is_int3($value) {
    return (strval(intval($value)) === strval($value));
}

function is_int4($value) {
    return (ctype_digit(strval($value)));
}

function is_int5($value) {
    return filter_var($value, FILTER_VALIDATE_INT) !== FALSE;
}

function is_int6($value){
    return (is_int($value) || ctype_digit($value) || (is_string($value) && $value[0] === '-' && filter_var($value, FILTER_VALIDATE_INT)) !== FALSE);
}

$start = microtime(TRUE);
for ($i=0; $i < 125000; $i++) {
  is_int1($u);
  is_int1($v);
  is_int1($w);
  is_int1($x);
  is_int1($y);
  is_int1($z);
}
$stop = microtime(TRUE);
$start2 = microtime(TRUE);
for ($j=0; $j < 125000; $j++) {
  is_int2($u);
  is_int2($v);
  is_int2($w);
  is_int2($x);
  is_int2($y);
  is_int2($z);
}
$stop2 = microtime(TRUE);
$start3 = microtime(TRUE);
for ($k=0; $k < 125000; $k++) {
  is_int3($u);
  is_int3($v);
  is_int3($w);
  is_int3($x);
  is_int3($y);
  is_int3($z);
}
$stop3 = microtime(TRUE);
$start4 = microtime(TRUE);
for ($l=0; $l < 125000; $l++) {
  is_int4($u);
  is_int4($v);
  is_int4($w);
  is_int4($x);
  is_int4($y);
  is_int4($z);
}
$stop4 = microtime(TRUE); 

$start5 = microtime(TRUE);
for ($m=0; $m < 125000; $m++) {
  is_int5($u);
  is_int5($v);
  is_int5($w);
  is_int5($x);
  is_int5($y);
  is_int5($z);
}
$stop5 = microtime(TRUE); 

$start6 = microtime(TRUE);
for ($n=0; $n < 125000; $n++) {
  is_int6($u);
  is_int6($v);
  is_int6($w);
  is_int6($x);
  is_int6($y);
  is_int6($z);
}
$stop6 = microtime(TRUE); 

$time = $stop - $start;  
$time2 = $stop2 - $start2;  
$time3 = $stop3 - $start3;  
$time4 = $stop4 - $start4;  
$time5 = $stop5 - $start5;  
$time6 = $stop6 - $start6;  
print "**Method 1:** $time <br>";
print "**Method 2:** $time2 <br>";
print "**Method 3:** $time3 <br>";
print "**Method 4:** $time4 <br>";  
print "**Method 5:** $time5 <br>";  
print "**Method 6:** $time6 <br>";  
  • This comparison makes sense if you wish to optimize for performance, but that is not always the case. Many people will find the regex method `preg_match('/^[0-9]+$/', $value)` easier to understand, and it performs as well as some of the other methods here. – Liam Jun 27 '23 at 17:14
  • OP may prefer a solution that only allows _positive integers_ , such as 1, 2, 3. Allowing negative integers and zeros could be problematic. Speed might not matter. – Liam Jun 27 '23 at 17:20
9

/!\ Best anwser is not correct, is_numeric() returns true for integer AND all numeric forms like "9.1"

For integer only you can use the unfriendly preg_match('/^\d+$/', $var) or the explicit and 2 times faster comparison :

if ((int) $var == $var) {
    // $var is an integer
}

PS: i know this is an old post but still the third in google looking for "php is integer"

job3dot5
  • 862
  • 7
  • 7
  • 1
    This is wrong, because this condition will be true even when e.g. $var="9". It should be if ((int) $var === $var). $a == $b Equal TRUE if $a is equal to $b after type juggling. $a === $b Identical TRUE if $a is equal to $b, and they are of the same type. – valerij vasilcenko Apr 17 '15 at 10:46
  • notice that `(int) $var` will return zero if the string can't be made into a number, so: `(int) 'banana' === 0` – Ignacio Segura Oct 31 '16 at 09:21
  • Downvoted as comments reveal this answer is not right. – EAmez Feb 07 '20 at 09:01
  • @IgnacioSegura so is it safe if used after a validator like `if (is_numeric($var) && ((int) $var == $var)) {...}`? – Guney Ozsan May 23 '20 at 13:01
  • @GuneyOzsan what you suggest sounds right, but to be honest, I stopped coding in PHP two years ago and I forgot a lot of stuff. Sorry I could not be more helpful. – Ignacio Segura May 24 '20 at 15:14
  • @IgnacioSegura no worries. I wanted to keep discussion alive because that part was still open. Someone else may comment later on. Thanks anyway! – Guney Ozsan May 26 '20 at 08:10
4

doctormad's solution is not correct. try this:

$var = '1a';
if ((int) $var == $var) {
    var_dump("$var is an integer, really?");
}

this prints

1a is an integer, really?"

use filter_var() with FILTER_VALIDATE_INT argument

$data = Array('0', '1', '1a', '1.1', '1e', '0x24', PHP_INT_MAX+1);
array_walk($data, function ($num){
$is_int = filter_var($num, FILTER_VALIDATE_INT);
if ($is_int === false)
var_dump("$num is not int");
});

this prints

1a is not int 
1.1 is not int
1e is not int
0x24 is not int
9.2233720368548E+18 is not int
Aleksandr
  • 49
  • 1
  • 1
3

I had a similar problem just now!

You can use the filter_input() function with FILTER_VALIDATE_INT and FILTER_NULL_ON_FAILURE to filter only integer values out of the $_GET variable. Works pretty accurately! :)

Check out my question here: How to check whether a variable in $_GET Array is an integer?

Community
  • 1
  • 1
maxxon15
  • 1,559
  • 4
  • 22
  • 35
2

When i start reading it i did notice that you guys forgot about abvious think like type of to check if we have int, string, null or Boolean. So i think gettype() should be as 1st answer. Explain: So if we have $test = [1,w2,3.45,sasd]; we start test it

foreach ($test as $value) {
        $check = gettype($value);
        if($check == 'integer'){
            echo "This var is int\r\n";
        }
        if($check != 'integer'){
            echo "This var is {$check}\r\n";
        }
}

And output:

> This var is int 
> This var is string 
> This var is double 
> This var is string

I think this is easiest way to check if our var is int, string, double or Boolean.

Piotr Mirosz
  • 846
  • 9
  • 20
2

Values $_GET are always strings – that's what GET paramters come as. Therefore, is_int($_GET[...]) is always false.

You can test if a string consists only of digits(i.e. could be interpreted as a number) with is_numeric.

phihag
  • 278,196
  • 72
  • 453
  • 469
2

You could try using a casting operator to convert it to an integer:

$page = (int) $_GET['p'];

if($page == "")
{
    $page = 1;
}
if(empty($page) || !$page)
{
    setcookie("error", "Invalid page.", time()+3600);
    header("location:somethingwentwrong.php");
    die();
}
//else continue with code
Drewdin
  • 1,732
  • 5
  • 23
  • 35
1
$page = (isset($_GET['p']) ? (int)$_GET['p'] : 1);
if ($page > 0)
{
  ...
}

Try casting and checking if it's a number initially.

Brad Christie
  • 100,477
  • 16
  • 156
  • 200
  • The call to `is_int()` here is redundant. The variable `$page` will always be an integer here, due to the previous line of code. – Hammerite Jun 20 '11 at 20:16
  • @netcoder/@Hammerite: Touche, forgot that a cast will supply the default value. Should be checking if `>0`. My apologies. – Brad Christie Jun 20 '11 at 20:20
0

In my opinion the correct function should look as following:

    public function checkIfValueIsInt($value): bool
{
    if (is_bool($value)) {
        return false;
    }

    if (0 === filter_var($value, FILTER_VALIDATE_INT) || filter_var($value, FILTER_VALIDATE_INT)) {
        return true;
    }

    return false;
}

given set of tests is validated correctly using it:

        [
            true,
            false,
        ],
        [
            false,
            false,
        ],
        [
            'false',
            false,
        ],
        [
            'true',
            false,
        ],
        [
            'some random string',
            false,
        ],
        [
            [],
            false,
        ],
        [
            ['some string', 1],
            false,
        ],
        [
            (object) [],
            false,
        ],
        [
            2.222,
            false,
        ],
        [
            rand(0, 99999),
            true,
        ],
        [
            1,
            true,
        ],
        [
            0,
            true,
        ],
        [
            -1,
            true,
        ],

the reason I added is_bool check is that

true

value is handle incorrectly by:

0 === filter_var($value, FILTER_VALIDATE_INT) || filter_var($value, FILTER_VALIDATE_INT)
Tomasz Wu
  • 29
  • 3
0

Check int and float types:

private function isInteger(mixed $val): bool
{
    if (!is_scalar($val) || is_bool($val)) {
        return false;
    }

    return !$this->isFloat($val) && preg_match('~^((?:\+|-)?[0-9]+)$~', $val) === 1;
}

private function isFloat(mixed $val): bool
{
    if (!is_scalar($val) || is_bool($val)) {
        return false;
    }

    return gettype($val) === "double" || preg_match("/^([+-]*\\d+)*\\.(\\d+)*$/", $val) === 1;
}

}

Basil
  • 21
  • 6
-2

I prefer to use this simple util function: Note that this will not work with numbers that are bigger that int, as PHP converts those to string

function is_int_($number) : bool {
    return is_numeric($number) && gettype($number + 0) === "integer";
}
Hef
  • 606
  • 6
  • 16
  • It returns true for `[ 2 => 2.56 3 => 9.0 4 => "0" 5 => "1" 6 => "2.56" 7 => "9.0"]` – Hmerman6006 Feb 26 '23 at 12:31
  • @Hmerman6006 You are right. I was unaware of how the `modulo` operator worked in `PHP` as I had copied the this function straight from one of my `JS` functions. It's now fixed. the `fmod` function was the way to go. – Hef Feb 26 '23 at 19:03
  • It is now incorrectly showing `[0, 4, 9.0, "0", "1", "9.0"]` as false and correctly `[2.56, "2.56", "hey", null, []]` as false. Reason is that `fmod` returns `The floating point remainder of num1/num2`: see the documentation. Try making the right side of the identity `0.0`. Then it gives the correct result. The string numbers though is a grey area, because technically they are strings but Php is very loose on type conversion. – Hmerman6006 Feb 27 '23 at 11:39
  • It struggles with large numbers `23450999999999977777777777777777777777555555555552333333333.78`. – Hmerman6006 Feb 27 '23 at 11:47
  • @Hmerman6006 I have now changed it to use the `gettype` function, as the whole modulo and operator thing was a mess. This solution does not work with large numbers, because `PHP` automatically converts those to `string`. – Hef Feb 27 '23 at 15:08