== vs === in PHP: Why Type Juggling Bites
== compares after converting types (0 == "a" surprises); === compares value AND type with no conversion. Famous traps table and why login code must use ===.
The one-line answer
<?php
var_dump(5 == "5"); // bool(true) - string "5" converted to 5
var_dump(5 === "5"); // bool(false) - int vs string: types differ
?>
The famous traps table
| Comparison | == result | === result | Why == says that |
|---|---|---|---|
| 0 == "abc" | false (PHP 8) / true (PHP 7!) | false | PHP 7 converted "abc" to 0 |
| "1" == "01" | true | false | Both look numeric → compared as numbers |
| "10" == "1e1" | true | false | 1e1 is scientific notation for 10 |
| 100 == "1e2" | true | false | Same scientific-notation juggling |
| 0 == false | true | false | false converts to 0 |
| null == false | true | false | Both are falsy |
| "" == null | true | false | Both are falsy |
0 == "abc" is TRUE — any non-numeric string converts to 0. PHP 8 changed this rule. Code that "worked" on PHP 7 by accident can flip behaviour after an upgrade — one more reason to write === from day one.Where it burns real applications: security
<?php
// OTP / token comparison - the classic vulnerability:
$saved_otp = "0e462097431906509019562988736854"; // hash-like string
$user_otp = "0e830400451993494058024219903391"; // different string!
var_dump($saved_otp == $user_otp); // bool(true) !!!
// Both look like scientific notation: 0e{digits} = 0 x 10^n = 0
// 0 == 0 -> ATTACKER GETS IN. This is the "magic hash" attack.
var_dump($saved_otp === $user_otp); // bool(false) - safe
?>
This is not theoretical — "magic hashes" (strings like 0e123...) have broken real login systems. Rule: OTPs, tokens, passwords, hashes — always === (and for password hashes specifically, password_verify(), and hash_equals() for tokens).
The strpos trap — where === is compulsory
<?php
$pos = strpos("Hello World", "Hello"); // returns 0 (found at position 0!)
if ($pos == false) { echo "Not found"; } // WRONG - prints "Not found"!
// because 0 == false is true
if ($pos === false) { echo "Not found"; } // CORRECT - stays silent
?>
Functions like strpos(), array_search() return 0 or false — meaningfully different values that == cannot tell apart. The PHP manual itself marks these functions with a big warning to use ===.
Rule of thumb
- Default to === (and !==) everywhere. It never surprises.
- Use == only when you consciously WANT type conversion — e.g., comparing form input "5" (string) to int 5 — and even there, better to cast first:
(int)$_POST['qty'] === 5. - Interview line: "== juggles types before comparing, so 0, '', null and false blur together and magic hashes break security; === compares value plus type with no conversion — the professional default."
One-line answer
<?php
var_dump(5 == "5"); // bool(true) - string "5" बदलकर 5 हुई
var_dump(5 === "5"); // bool(false) - int vs string: types अलग
?>
Famous traps table
| Comparison | == result | === result | == ऐसा क्यों कहता है |
|---|---|---|---|
| 0 == "abc" | false (PHP 8) / true (PHP 7!) | false | PHP 7 "abc" को 0 बना देता था |
| "1" == "01" | true | false | दोनों numeric दिखे → numbers की तरह compare |
| "10" == "1e1" | true | false | 1e1 = 10 की scientific notation |
| 100 == "1e2" | true | false | वही scientific-notation juggling |
| 0 == false | true | false | false बदलकर 0 होता है |
| null == false | true | false | दोनों falsy |
| "" == null | true | false | दोनों falsy |
0 == "abc" TRUE है — कोई भी non-numeric string 0 बन जाती है. PHP 8 ने यह rule बदला. जो code PHP 7 पर "संयोग से चलता" था वह upgrade के बाद behaviour पलट सकता है — पहले दिन से === लिखने की एक और वजह.असली applications में कहां जलाता है: security
<?php
// OTP / token comparison - classic vulnerability:
$saved_otp = "0e462097431906509019562988736854"; // hash-जैसी string
$user_otp = "0e830400451993494058024219903391"; // अलग string!
var_dump($saved_otp == $user_otp); // bool(true) !!!
// दोनों scientific notation दिखीं: 0e{digits} = 0 x 10^n = 0
// 0 == 0 -> ATTACKER अंदर. यही "magic hash" attack है.
var_dump($saved_otp === $user_otp); // bool(false) - safe
?>
यह theoretical नहीं — "magic hashes" (0e123... जैसी strings) असली login systems तोड़ चुकी हैं. Rule: OTPs, tokens, passwords, hashes — हमेशा === (और password hashes के लिए खासतौर पर password_verify(), tokens के लिए hash_equals()).
strpos trap — जहां === compulsory है
<?php
$pos = strpos("Hello World", "Hello"); // 0 return हुआ (position 0 पर मिला!)
if ($pos == false) { echo "Not found"; } // GALAT - "Not found" छापता है!
// क्योंकि 0 == false true है
if ($pos === false) { echo "Not found"; } // SAHI - चुप रहता है
strpos(), array_search() जैसे functions 0 या false return करते हैं — meaningfully अलग values जिन्हें == अलग कर ही नहीं सकता. PHP manual खुद इन functions पर === use करने की बड़ी warning लगाता है.
Rule of thumb
- हर जगह default === (और !==). यह कभी नहीं चौंकाता.
- == सिर्फ तब जब आपको जानबूझकर type conversion CHAHIYE — जैसे form input "5" (string) को int 5 से मिलाना — और वहां भी बेहतर है पहले cast करना:
(int)$_POST['qty'] === 5. - Interview line: "== compare से पहले types juggle करता है, जिससे 0, '', null और false आपस में घुल जाते हैं और magic hashes security तोड़ते हैं; === value plus type बिना conversion compare करता है — professional default."
Frequently Asked Questions
What is the difference between == and === in PHP?
== converts both sides to a common type before comparing values (type juggling), while === compares both value and type with no conversion — so 5 == "5" is true but 5 === "5" is false.
What is the magic hash vulnerability?
Strings like "0e12345..." look like scientific notation, so == converts them all to 0 and two different hashes compare as equal — letting attackers bypass OTP or hash checks; === (or hash_equals) prevents it.
Why must strpos() results be checked with ===?
strpos() returns 0 when the match is at the start and false when not found; 0 == false is true, so only $pos === false correctly detects the not-found case.