Type juggling and Non-Strict Comparison Issues
What is Type Juggling?
PHP is a loosely typed language. This means that, by default, it doesn't require operands in an expression to be of the same (or compatible) types. For example, you can append a number to a string and expect it to work.
The output will be:
string(24) "This is example number 1"
PHP accomplishes this by automatically casting incompatible variable types into types that allow the requested operation to take place. In the case above, it will cast the integer literal 1 into a string, meaning that it can be concatenated onto the preceding string literal. This is referred to as type juggling. This is a very powerful feature of PHP, but it is also a feature that can lead you to a lot of hair-pulling if you are not aware of it, and can even lead to security problems.
Consider the following:
The intent appears to be that the programmer is checking that a variable has a value of 1. But what happens if $variable has a value of "1 and a half" instead? The answer might surprise you.
The result is:
Why has this happened? It's because PHP realised that the string "1 and a half" isn't an integer, but it needs to be in order to compare it to integer 1. Instead of failing, PHP initiates type juggling and, attempts to convert the variable into an integer. It does this by taking all the characters at the start of the string that can be cast to integer and casting them. It stops as soon as it encounters a character that can't be treated as a number. Therefore "1 and a half" gets cast to integer 1.
Granted, this is a very contrived example, but it serves to demonstrate the issue. The next few examples will cover some cases where I've run into errors caused by type juggling that happened in real software.
Reading from a file
When reading from a file, we want to be able to know when we've reached the end of that file. Knowing that
fgets() returns false at the end of the file, we might use this as the condition for a loop. However, if the data returned from the last read happens to be something that evaluates as boolean
false, it can cause our file read loop to terminate prematurely.
If the file being read contains a blank line, the
while loop will be terminated at that point, because the empty string evaluates as boolean
Instead, we can check for the boolean
false value explicitly, using strict equality operators:
Note this is a contrived example; in real life we would use the following loop:
Or replace the whole thing with:
Since PHP 7.0, some of the harmful effects of type juggling can be mitigated with strict typing. By including this
declare statement as the first line of the file, PHP will enforce parameter type declarations and return type declarations by throwing a
For example, this code, using parameter type definitions, will throw a catchable exception of type
TypeError when run:
Likewise, this code uses a return type declaration; it will also throw an exception if it tries to return anything other than an integer:
Switch statements use non-strict comparison to determine matches. This can lead to some nasty surprises. For example, consider the following statement:
This is a very simple statement, and works as expected when
$name is a string, but can cause problems otherwise. For example, if
$name is integer
0, then type-juggling will happen during the comparison. However, it's the literal value in the case statement that gets juggled, not the condition in the switch statement. The string
"input 1" is converted to integer
0 which matches the input value of integer
0. The upshot of this is if you provide a value of integer
0, the first case always executes.
There are a few solutions to this problem:
The value can be typecast to a string before comparison:
Or a function known to return a string can also be used:
Both of these methods ensure the value is of the same type as the value in the
if statement will provide us with control over how the comparison is done, allowing us to use strict comparison operators: