== и === в PHP: разница, подводные камни и лучшие практики
Тема запроса: «PHP == и ===: в чём разница и когда что использовать». Если вы только начинаете или уже пишете на PHP, понимание сравнения — обязательный шаг к надёжному коду. Нестрогое сравнение (==) выполняет приведение типов «на лету», а строгое (===) сравнивает и значение, и тип без неявных преобразований. Ошибки в этой области ведут к багам и уязвимостям, особенно при проверках входных данных и паролей.
Коротко: отличие == и ===
- == сравнивает значения с неявным приведением типов (loose comparison).
- === сравнивает значения и типы, без приведения (strict comparison).
var_dump(42 == "42"); // true (строка приводится к числу)
var_dump(42 === "42"); // false (разные типы: int vs string)
Как работает нестрогое сравнение (==) в PHP
При использовании == PHP старается «свести» операнды к общему типу. Главное помнить:
- Число и строка → строка приводится к числу: "123abc" станет 123, "abc" станет 0.
- Две строки → если обе «числовые строки», сравнение числовое; иначе — строковое.
- Булево и что-то ещё → всё приводится к булеву: 0, "", "0", [] считаются false.
var_dump("" == 0); // true (пустая строка → 0)
var_dump("0" == false); // true ("0" → false)
var_dump("abc" == 0); // true ("abc" → 0)
var_dump("0123" == 123); // true (числовое сравнение)
Такая магия приводит к неожиданностям и даже уязвимостям. Поэтому по умолчанию предпочитайте ===.
Опасный кейс: «магические» хеши и 0e...
Классическая уязвимость при сравнении хешей с ==: некоторые MD5/sha1 значения начинаются с «0e...», что выглядит как число в экспоненциальной форме и при нестрогом сравнении превращается в 0.
$storedHash = md5("240610708"); // "0e462097431906509019562988736854"
$givenHash = "0e123456789000000000000000000000"; // поддельное значение
var_dump($storedHash == $givenHash); // true (!) из-за численного сравнения 0 == 0
var_dump($storedHash === $givenHash); // false (строгое сравнение)
// Как правильно сравнивать хеши
if (hash_equals($storedHash, $givenHash)) {
// безопасное, тайминг-устойчивое сравнение строк
}
Вывод: для сравнения секретов (токены, подписи, хеши паролей) используйте hash_equals и не применяйте ==.
Списки и поиск: in_array, array_search и строгий флаг
По умолчанию in_array и array_search сравнивают нестрого. Это может дать ложные совпадения.
$list = [0, 1, 2];
var_dump(in_array("0", $list)); // true ("0" == 0)
var_dump(in_array("0", $list, true)); // false (строгое сравнение)
var_dump(array_search(false, ["0", "1", "2"])); // 0 (нашёлся "0")
var_dump(array_search(false, ["0", "1", "2"], true)); // false (не найдено)
Рекомендация: почти всегда передавайте третий аргумент true, чтобы включить строгое сравнение.
switch в PHP тоже сравнивает нестрого
Оператор switch использует == при сравнении со значениями case. Будьте осторожны со строками «0», пустыми строками и булевыми.
$value = "0";
switch ($value) {
case false: echo "false"; break; // сработает (!), т.к. "0" == false
case 0: echo "zero"; break;
case "0": echo "string zero"; break;
}
// Выведет: false
Решение: избегайте таких неоднозначностей в switch или приводите тип явно перед сравнением.
Массивы: == против ===
У массивов == проверяет равенство пар ключ=>значение без учёта порядка. === дополнительно учитывает порядок и типы.
$a = ["x" => 1, "y" => 2];
$b = ["y" => 2, "x" => 1];
var_dump($a == $b); // true
var_dump($a === $b); // false (другой порядок)
Строковое vs числовое сравнение строк
Если обе строки — числовые, сравнение будет числовым; иначе — лексикографическим.
var_dump("2" < "10"); // false (числовое: 2 < 10 → true, но оба операнда строки? В PHP это всё равно числовое → true)
var_dump("2" < "10"); // true на самом деле, т.к. обе — числовые строки
var_dump("a2" < "10"); // false ("a2" не числовая — сравнение строковое)
Чтобы избежать сюрпризов, приводите тип явно: (int) $s, (float) $s или валидируйте is_numeric($s).
Сравнение float: не используйте ==
Из-за двоичной природы float точные совпадения редки. Сравнивайте с допуском (epsilon).
$a = 0.1 + 0.2; // 0.30000000000000004
$b = 0.3;
$eps = 1e-12;
var_dump(abs($a - $b) < $eps); // true
Полезные функции и приёмы
- is_numeric, is_int, is_bool — проверяйте тип прежде чем сравнивать.
- hash_equals — для сравнения секретов и хешей.
- filter_var — валидируйте ввод перед приведением типов.
- Тернарный оператор ?: и null coalescing ?? не заменяют строгое сравнение — будьте внимательны к «пустым» значениям.
$v = "0";
var_dump(empty($v)); // true ("0" считается пустой)
var_dump($v === "0"); // true — точная проверка на строку "0"
Оператор «космический корабль»
Удобен для сортировок: возвращает -1, 0 или 1, и учитывает приведение аналогично обычным сравнениям. Лучше явно приводить типы перед использованием.
usort($rows, function ($a, $b) {
return (int)$a['age'] (int)$b['age'];
});
Чек-лист лучших практик
- По умолчанию используйте === и !==.
- Всегда включайте строгий флаг в in_array/array_search: in_array($x, $arr, true).
- Секреты сравнивайте через hash_equals, не через == или ===.
- Перед сравнением чисел приводите строку к числу и валидируйте is_numeric.
- Для float используйте сравнение с допуском.
- Будьте осторожны со switch и «пустыми» значениями ("", "0", 0, false, null).
- Для массивов учитывайте, что порядок влияет на ===, но не на ==.
Итоги
Нестрогое сравнение в PHP удобно, но коварно. Используйте === как «дефолтный» инструмент, а к == обращайтесь только там, где вы на 100% контролируете типы и ожидаемое приведение. Это снижает количество скрытых багов и повышает безопасность вашего приложения.
Хотите системно прокачать основы и научиться писать надёжный PHP‑код с базой данных и практикой? Посмотрите курс «PHP и MySQL с Нуля до Гуру 3.0 — практикум для уверенного старта».
-
Создано 15.04.2026 17:01:15
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.