оригинальная версия версия для слабовидящих контрастная версия выключить изображения включить изображения тёмная версия светлая версия RSS FEED K2 NEWS
☰☰
Суббота, 17 Октябрь 2026 00:00

Разница между == и === — классические грабли PHP

Если спросить любого PHP-разработчика, с чего начинается список личных «граблей», процентов восемьдесят вспомнят именно сравнение через два знака равенства. Выглядит безобидно: == сравнивает значения, === — ещё и типы. Казалось бы, мелочь. На практике эта мелочь регулярно превращается в баг, который не ловится на глаз и может жить в коде месяцами, пока не всплывёт на проде в самый неподходящий момент.

Разница между == и === — классические грабли PHP

Разберём классику жанра — сравнение строки с числом:

var_dump(0 == "abc");      // bool(false) — в PHP 8+
var_dump(0 == "0");        // bool(true)
var_dump("abc" == "0");    // bool(false)
var_dump("1" == "01");     // bool(true)
var_dump("10" == "1e1");   // bool(true)
var_dump(100 == "1e2");    // bool(true)

Отдельно нужно сказать про историческое поведение — в PHP до версии 8.0 сравнение 0 == "abc" возвращало true, потому что нечисловая строка при сравнении с числом приводилась к нулю. Это и есть тот самый печально известный баг, из-за которого строка-хэш, случайно начинающаяся с «0e» (например, "0e123456"), при сравнении через == с другой похожей строкой считалась равной нулю — а значит, и друг другу, даже будучи совершенно разными значениями. На этом строилась реальная уязвимость — обход проверки пароля или токена, если сравнение велось через == вместо ===. В PHP 8 это поведение исправили: сравнение числа со строкой теперь, наоборот, приводит число к строке, а не строку к числу, если строка не является числовой. Но это не повод расслабляться — множество серверов и легаси-проектов до сих пор крутятся на PHP 7.x, где старое поведение сохраняется.

Вот с чем конкретно спотыкаются чаще всего:

1. Проверка результата функции, которая может вернуть false или 0. Классический пример — strpos():

$str = "Привет, мир!";

if (strpos($str, "Привет") == false) {
    echo "Подстрока не найдена";
}

Проблема в том, что strpos() возвращает позицию вхождения подстроки, и если искомая строка находится в самом начале (позиция 0), функция вернёт 0. А 0 == false — это true. В итоге код ошибочно решает, что подстрока не найдена, хотя она буквально в начале строки. Правильная проверка — через ===:

if (strpos($str, "Привет") === false) {
    echo "Подстрока не найдена";
}

2. Сравнение id, пришедших из разных источников. Одно из значений пришло из базы данных как число (int), другое — из $_GET или формы как строка:

$userId = 5;              // число из базы
$requestId = "5";         // строка из $_GET['id']

var_dump($userId == $requestId);   // true — и это ок в большинстве случаев
var_dump($userId === $requestId);  // false — а вот тут можно словить баг на ровном месте

Здесь, наоборот, === может стать источником проблемы, если разработчик не подумал о приведении типов заранее — именно поэтому многие явно приводят тип через (int)$requestId перед строгим сравнением, а не полагаются на нестрогое ==.

3. Сравнение с null. Частая ошибка — проверять переменную на null через ==:

$value = 0;

if ($value == null) {
    echo "Значения нет"; // выполнится, хотя 0 — это вполне себе значение
}

0, "", "0", false, пустой массив [] — все эти значения при сравнении через == с null дадут true, хотя логически это разные состояния переменной. Для проверки именно на null правильнее использовать is_null($value) или строгое $value === null.

Практическое правило, которое стоит просто взять за привычку: используйте === по умолчанию везде, где сравниваете значения, и переключайтесь на == только тогда, когда осознанно хотите получить автоматическое приведение типов и понимаете, как именно PHP его выполнит в вашем случае. Такой подход резко сокращает число багов, которые проявляются только на специфичных входных данных и не ловятся при поверхностном тестировании.

Отдельно стоит упомянуть функцию in_array(), у которой по умолчанию тоже используется нестрогое сравнение:

$arr = ["admin", "editor", 0];

var_dump(in_array("moderator", $arr));        // true! "moderator" == 0 ⇒ true (до PHP 8)
var_dump(in_array("moderator", $arr, true));  // false — со строгим сравнением всё честно

Третий необязательный аргумент true включает строгий режим и для in_array(), и для родственной ей array_search() — про этот параметр забывают регулярно, а стоит держать его в голове как стандарт, а не как опцию «на всякий случай».

Если резюмировать: == и === — это не взаимозаменяемые операторы с разным уровнем строгости, это два разных способа мышления о сравнении данных. == спрашивает «похожи ли эти значения по смыслу», === — «одинаковы ли они вообще, включая тип». Пока оба значения гарантированно одного типа, разницы на практике нет. Но как только в цепочку попадают данные из внешних источников — форм, базы, API, — риск получить неожиданное true там, где логически должно быть false, резко возрастает, и единственная защита от этого — привычка писать === рефлекторно, а не по необходимости.

Спонсоры: