Himanshu SharmaSoftware engineer
Let’s talk

PHP · Lesson 11 of 54

Comparisons, == versus === and type juggling

Learn how PHP converts types when comparing, why === is safer than ==, how PHP 8 changed string-to-number comparison, and how strict_types works.

13 minThe basicsUpdated 22 October 2026

Awaiting technical review. This lesson is readable but not yet listed in search engines.

By the end of this lessonChoose between == and === correctly and predict how PHP compares values of different types.

Two kinds of equality

PHP has two equality operators, and choosing the wrong one is a classic source of bugs.

  • == is loose equality. If the two sides have different types, PHP converts them to a common type first, then compares.
  • === is strict equality, also called identity. It is true only when both the type and the value match.
php
<?php
var_dump(5 == "5");
var_dump(5 === "5");
var_dump(1.0 == 1);
var_dump(1.0 === 1);
var_dump(null == false);
var_dump(null === false);
result
bool(true)
bool(false)
bool(true)
bool(false)
bool(true)
bool(false)

The not-equal operators follow the same pattern: != is loose and !== is strict.

Type juggling

The automatic conversion that == performs is called type juggling. It can feel helpful, because form fields always arrive as strings and "5" == 5 just works. But the rules have corners:

php
<?php
var_dump("1" == "01");
var_dump("10" == "1e1");
var_dump(100 == "1e2");
var_dump("abc" == 0);
var_dump(null == 0);
var_dump([] == false);
result
bool(true)
bool(true)
bool(true)
bool(false)
bool(true)
bool(true)

When both strings look like numbers, == compares them as numbers, which is why "1" == "01" and "10" == "1e1" are true. That surprises people comparing codes or IDs: two different discount codes that happen to look like numbers can be considered equal. With ===, strings are compared character by character, so "1" === "01" is false.

What PHP 8 changed

Before PHP 8, comparing a number with a non-numeric string converted the string to a number. "abc" became 0, so 0 == "abc" was true, a long-standing trap that caused security bugs. PHP 8 reversed it: when one side is a number and the other is a non-numeric string, the number is turned into a string and they are compared as strings.

php
<?php
var_dump(0 == "abc");
var_dump(0 == "");
var_dump("1" == " 1");
var_dump(42 == "42abc");
result
bool(false)
bool(false)
bool(true)
bool(false)

Leading whitespace is still accepted in numeric strings (" 1"), but trailing text such as "42abc" makes a string non-numeric. You may still meet older code or tutorials that rely on the PHP 7 behaviour.

Use === by default

A simple rule removes almost all of these puzzles: use === and !== unless you specifically want conversion. Convert input on purpose first, then compare strictly:

php
<?php
$quantityFromForm = "3";      // form values are always strings
$quantity = (int) $quantityFromForm;

if ($quantity === 3) {
    echo "Three items\n";
}

$status = "paid";
if ($status !== "cancelled") {
    echo "Order is active\n";
}
result
Three items
Order is active

Loose comparison inside functions

Some built-in functions compare loosely by default. in_array() and array_search() take a third argument that switches them to strict comparison:

php
<?php
$allowedIds = [10, 20, 30];

var_dump(in_array("1e1", $allowedIds));
var_dump(in_array("1e1", $allowedIds, true));
var_dump(array_search("20", $allowedIds));
var_dump(array_search("20", $allowedIds, true));
result
bool(true)
bool(false)
int(1)
bool(false)

Passing true is a good habit, especially when the value comes from a user. switch also compares loosely, while match, covered in switch and match, compares strictly.

Comparing strings and arrays

<, > and <=> work on strings too. They compare byte by byte, so uppercase letters sort before lowercase ones. For human-friendly ordering use strcmp(), strcasecmp() (case-insensitive) or strnatcmp() (natural order, so "item2" comes before "item10"):

php
<?php
var_dump("apple" < "banana");
var_dump("Zebra" < "apple");
echo strcasecmp("Zebra", "apple") > 0 ? "Zebra after apple\n" : "Zebra first\n";
echo strnatcmp("item2", "item10") < 0 ? "item2 first\n" : "item10 first\n";
result
bool(true)
bool(true)
Zebra after apple
item2 first

Arrays are equal with == when they have the same key-value pairs, and identical with === when the order and types also match.

Strict types for functions

Type juggling also happens when you pass a value to a function with a typed parameter. By default PHP converts "5" to 5 for an int parameter. Putting declare(strict_types=1); as the very first statement of a file turns that off for calls made from that file, so a wrong type throws a TypeError instead:

php
<?php
declare(strict_types=1);

function double(int $number): int
{
    return $number * 2;
}

echo double(5) . "\n";
echo double("5") . "\n";
result
10

Fatal error: Uncaught TypeError: double(): Argument #1 ($number) must be of type int, string given, called in /path/to/script.php on line 10 and defined in /path/to/script.php:4
Stack trace:
#0 /path/to/script.php(10): double('5')
#1 {main}
  thrown in /path/to/script.php on line 4

Many modern projects, including frameworks, use strict types in every file. It catches mistakes early, at the cost of converting input explicitly. Parameter types are covered in Parameters, defaults, named arguments and types.

Common mistakes

  • Using == to compare codes, IDs or hashes that may look numeric. Use ===, or hash_equals() for secrets.
  • Forgetting the strict flag in in_array() and array_search().
  • Checking a function's result with == false when it can legitimately return 0, such as strpos(). Use === false.
  • Assuming PHP 7 rules when reading old answers online about 0 == "abc".

Exercise

strpos("PHP is fun", "PHP") returns 0, the position where "PHP" starts. Write code that prints "found" if the word is in the string and "not found" otherwise, and explain why if (strpos(...) == false) would give the wrong answer.

Show one possible answer
php
<?php
$position = strpos("PHP is fun", "PHP");

if ($position === false) {
    echo "not found\n";
} else {
    echo "found at $position\n";
}

var_dump($position == false);
result
found at 0
bool(true)

The text was found at position 0, but 0 == false is true, so a loose comparison would report "not found". Since PHP 8, str_contains() answers the simpler question directly and returns a real boolean.

Key points

  • == compares after converting both sides to a common type (loose). === also requires the same type (strict).
  • Since PHP 8, comparing a number with a non-numeric string compares them as strings, so 0 == 'abc' is false.
  • Functions such as in_array() and array_search() compare loosely unless you pass true as the strict argument.
  • declare(strict_types=1); makes PHP reject wrong scalar types passed to typed function parameters instead of converting them.

Check yourself

1. What is the result of '1' === 1?
2. In PHP 8, what is 0 == 'abc'?
3. What does in_array('1e1', [10]) return?
4. What does declare(strict_types=1) affect?