Lesson 41 of 100
Types15 min5 checksPHP 8.5

Strict Comparisons and Type Juggling

Learn when PHP's loose == coerces values, why === is usually safer, and how to write a search function that never relies on type juggling.

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 bool or null, both sides are converted to bool. So 0 == null is true because both become false.
  • When null meets a string, null becomes "" 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.

Example
<?php
var_dump(0 == "a");
var_dump("1" == "01");
var_dump("10" == "1e1");
var_dump(100 == "1e2");
Output
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].

Example
<?php
$values = [0, "0", "", null, false];
$result = [];
foreach ($values as $value) {
    $result[] = [$value == null, $value === null];
}
echo json_encode($result), PHP_EOL;
Output
[[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:

Example
<?php
$list = ["1e1", "abc", 7];
echo json_encode([in_array(10, $list), in_array(10, $list, true)]), PHP_EOL;
Output
[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.

  • $haystack is a list (sequential integer keys).
  • Return the index of the first element that is identical (===) to $needle.
  • Return -1 when nothing is identical, including when the list is empty.
  • Do not use ==, and do not call array_search() or in_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.

Before you start

Functions · Arrays · Types

You’ll practice
  • Compare values without unintended coercion
  • Implement and test a deterministic PHP 8.4 function
Your task

Make strictIndexOf() pass all 5 checks. Run your code as often as you like, then check your answer.

exercise.phpSaved in this browserPHP 8.5
CONSOLE & CHECKS● Ready

Ready when you are

Your output will appear here.
Make it work. Then make it pass.

PHP loads on your first run. Each check starts fresh. Your code stays in this browser; clearing site data removes your progress. A check or the task looks wrong? Report a problem with this exercise.