01 · THE IDEAREAD → TRY → LEARN
Two kinds of equality
PHP has two families of equality operators. The loose operators == and != compare values after type juggling, which means PHP may convert one or both operands before deciding. The strict operators === and !== compare the type and the value, and no conversion happens. The manual itself advises preferring === and !== in most cases, because juggling is not always obvious, especially with ints, bools and strings.
What loose comparison does
The rules depend on the operand types:
- If both operands are numeric strings, or one is a number and the other a numeric string, the comparison is numeric. So
"10" == "1e1"is true even though the text differs. - If either side is
boolornull, both sides are converted tobool. So0 == nullis true because both becomefalse. - When
nullmeets a string,nullbecomes""and the two strings are compared.
Since PHP 8.0, comparing a number with a non-numeric string no longer turns the string into 0. That is why 0 == "a" is now false. The rest of the numeric-string behaviour is unchanged, as the first example shows.
var_dump(0 == "a");
var_dump("1" == "01");
var_dump("10" == "1e1");
var_dump(100 == "1e2");bool(false) bool(true) bool(true) bool(true)
The switch statement uses these same loose rules, so a case can match in ways you did not intend.
Seeing the difference with null
Values that look "empty" are a classic trap. The next example checks several of them against null, once loosely and once strictly. Each pair in the output is [loose, strict].
$values = [0, "0", "", null, false];
$result = [];
foreach ($values as $value) {
$result[] = [$value == null, $value === null];
}
echo json_encode($result), PHP_EOL;[[true,false],[false,false],[true,false],[true,true],[true,false]]
Only the real null passes the strict test. Note that "0" == null is false: null becomes "", and "0" is not equal to "". Yet 0 == null is true. The rules differ by type pair, which is why memorising results is a poor strategy and choosing === is a good one.
Built-in helpers hide the same choice
Functions such as in_array() and array_search() use loose comparison unless you pass true as the strict argument. The outcome can change completely:
$list = ["1e1", "abc", 7];
echo json_encode([in_array(10, $list), in_array(10, $list, true)]), PHP_EOL;[true,false]
Loosely, 10 matches the numeric string "1e1". Strictly, no element is the integer 10.
Other things to remember
<=>returns a negative integer, zero or a positive integer, and follows the same juggling rules as<and>. Use it only on comparable values.- Do not test floats for equality with either operator, because of how they are represented internally.
===on arrays requires the same key/value pairs, in the same order and with the same types.==only requires equal pairs.- Strict comparison of arbitrary values is always safe. The ordering operators are only reliable for comparable values.
Exercise contract
Write strictIndexOf(array $haystack, mixed $needle): int.
$haystackis a list (sequential integer keys).- Return the index of the first element that is identical (
===) to$needle. - Return
-1when nothing is identical, including when the list is empty. - Do not use
==, and do not callarray_search()orin_array()without strict mode.
The tests include a string "1" that loosely equals the int 1, a 0 and false that loosely equal null, a numeric string "1e1" that loosely equals 10, and a true needle that loosely matches any non-zero number. A loose implementation returns the wrong index on these, while a strict one passes them all.
Recap
Use === and !== by default. Reach for == only when you deliberately want coercion and know the rules. Pass the strict flag to search helpers, and test boundary values such as 0, "", null, false and numeric strings.
Functions · Arrays · Types
- Compare values without unintended coercion
- Implement and test a deterministic PHP 8.4 function