Type Juggling
what is Type Juggling?
In PHP, type juggling is an internal behaviour that results in the conversion of variables to other data types in specific contexts, such as when performing comparisons. While this is not inherently a security vulnerability, it can result in unexpected or undesired outcomes, potentially leading to security vulnerabilities depending on the specific web application.

PHP Loose vs. Strict Comparisons
Unlike other programming languages, PHP supports two distinct types of comparisons: loose comparisons, which are performed with two equal signs (==), and strict comparisons, which are performed with three equal signs (===). A loose comparison compares two values after type juggling, whereas a strict comparison compares two values and their corresponding data types. As an example, consider the following code snippet:
$a = 42;
$b = "42";
// loose comparison
if ($a == $b) { echo "Loose Comparison";}
// strict comparison
if ($a === $b) { echo "Strict Comparison";}

The behavior of type juggling in a comparison context is documented here. Here are some important cases:
| Operand 1 | Operand 2 | Behavior |
|---|---|---|
string |
string |
Numerical or lexical comparison |
null |
string |
Convert null to "" |
null |
anything but string |
Convert both sides to bool |
bool |
anything | Convert both sides to bool |
int |
string |
Convert string to int |
float |
string |
Convert string to float |

Type Juggling Examples
For example, consider the comparison 1 == "1HelloWorld" which evaluates to true. Since the first operand is an int and the second operand is a string, PHP converts the string to an integer. When converting "1HelloWorld" to an integer, the result is 1. Thus, the comparison evaluates to true after type juggling.

A potentially even more odd example is the result of min(-1, null, 1), which is null. The function min computes the minimum of the provided arguments and returns it. To do so, the function compares the different arguments. When evaluating null < 1, both sides are converted to booleans. The integer 1 is converted to true while null is converted to false. It holds that false < true. Furthermore, the same methodology is applied when evaluating null < -1. The integer -1 is also converted to true. Thus, overall it holds that null < 1 and null < -1. Thus, null is the minimum of the provided arguments.

As a final example, let us consider the comparison "00" == "0e123". Intuitively, this comparison should evaluate to false since the arguments are both strings, and the strings are obviously different. This is a special case in which PHP performs a numerical comparison of the two strings, resulting in a conversion to numbers. The e in the second argument is the scientific notation for floats, as we can see here. When both arguments are converted to numbers, the result is 0 for both sides. Thus, PHP evaluates the comparison as true.

Now let us have a look at the full behavior of a loose comparison, which can be found here:
true |
false |
1 |
0 |
-1 |
"1" |
"0" |
"-1" |
null |
[] |
"php" |
"" |
|
|---|---|---|---|---|---|---|---|---|---|---|---|---|
true |
✓ | ✗ | ✓ | ✗ | ✓ | ✓ | ✗ | ✓ | ✗ | ✗ | ✓ | ✓ |
false |
✗ | ✓ | ✗ | ✓ | ✗ | ✗ | ✓ | ✗ | ✓ | ✓ | ✗ | ✓ |
1 |
✓ | ✗ | ✓ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
0 |
✗ | ✓ | ✗ | ✓ | ✗ | ✗ | ✓ | ✗ | ✓ | ✗ | ✓ (< PHP 8.0.0) | ✓ (< PHP 8.0.0) |
-1 |
✓ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
"1" |
✓ | ✗ | ✓ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
"0" |
✗ | ✓ | ✗ | ✓ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ |
"-1" |
✓ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
null |
✗ | ✓ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | ✓ | ✓ | ✗ | ✓ |
[] |
✗ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✓ | ✗ | ✗ |
"php" |
✓ | ✗ | ✗ | ✓ (< PHP 8.0.0) | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ |
"" |
✗ | ✓ | ✗ | ✓ (< PHP 8.0.0) | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✓ |