Be careful when Integer Typecasting / Type Juggling in PHP

There I was, minding my own business checking to see if a string was an integer or not. That’s so simple! We’ll use PHP’s built-in is_int() function. So easy.

$var = "5";
echo is_int($var)?(int)$var:0;

But, wait a second, this outputs a “0” instead of the expected “5”.

Alright, you might say,” of course, Adam, your $var is surrounded by quotes. It’s obviously a string.” Yes, I absolutely agree. The problem is I have form inputs that should pass back numbers (like a year or user id) that were behaving like strings. What’s a guy to do?

To solve this I cast the variable as an integer and tested to see if it was greater than zero. (See PHP’s Type Juggling or, Can Someone Explain PHP Type Juggling on StackOverflow)

Test 1: If casting “5” as integer is greater than zero, then output the variable else zero;

$var = "5";
echo ((int)$var > 0)?(int)$var:0;

Hooray, test 1 works. It output 5.

Test 2: If casting “John Smith” as integer is greater than zero, then output the variable else zero;

$var = "John Smith";
echo ((int)$var > 0)?(int)$var:0;

Hooray, test 2 works. It output zero because it was not an integer.

I’m so glad we solved that. Now, we can go home and rest our heads at night.

Except … something went wrong!

via GIPHY

In an odd place somewhere deep within the code, an input gets passed along that could be either an email or a user id. So I tested if the input was a valid email or (in the case below) a valid integer. And, it all works swimmingly.

If it passes an email, then it results in zero.

$var = "john.q.smith@emaildomain.com";
echo ((int)$var > 0)?(int)$var:0;

If it passes a user id, then it casts the variable as an integer and it’s a happy day.

$var = "5983024";
echo ((int)$var > 0)?(int)$var:0;

But when we pass an email that starts with a number, then it truncates the variable to the beginning number. In the example below, the variable outputs the integer “999”. Ouch!

$var = "999jane.m.smith@emaildomain.com";
echo ((int)$var > 0)?(int)$var:0;

In this same way, decimal numbers also get truncated.

$var = "89.78";
echo ((int)$var > 0)?(int)$var:0;

Shoot! Not even rounded. Just truncated. This will not do at all.

Don’t lose hope. There’s a solution!

This is the best solution I could come up with. We compare the character count (“strlen()”) of the variable both as it is and when cast an integer. Also, we see if the variable cast an integer is greater than zero, as we did before.

$var = "999jane.m.smith@emaildomain.com";
echo ( strlen($var) == strlen((int)$var) && (int)$var > 0) ? (int)$var : 0;

Finally, this works (crossing my fingers) for now.

As a side note, I found an article detailing how Type Juggling might be used for PHP exploits. It could be a security loophole if not properly used.

Oh, and I put this test into a function for future use.

Leave a Reply

Your email address will not be published. Required fields are marked *