== или ===: что спрашивают в первую очередь
Первый технический вопрос почти любого PHP-скрининга — и самый недооценённый кандидатами.
==сравнивает значения, приводя операнды к общему типу.===сравнивает и значение, и тип: если типы разные, результат сразуfalse, без всяких преобразований.
Вопрос 1: «Чем == отличается от ===?»
Что на самом деле проверяет интервьюер
Ответ «одно строгое, другое нестрогое» знает каждый — его недостаточно. Интервьюер хочет понять, держите ли вы в голове таблицу приведений и понимаете ли, что нестрогое сравнение — источник целого класса багов в авторизации и валидации. Хороший ответ звучит так: «=== сначала сравнивает типы, и только при их совпадении — значения; == сначала приводит операнды по правилам языка, а уже потом сравнивает».
Развёрнутый ответ с примерами
<?php
var_dump(0 == "hello");
var_dump("1" == "01");
var_dump("10" == "1e1");
var_dump(100 == "1e2");
var_dump(null == false);
var_dump([] == false);
var_dump("abc" == 0);
var_dump("1" === 1);
Вывод:
bool(false) bool(true) bool(true) bool(true) bool(true) bool(true) bool(false) bool(false)
Разберём строки. "1" == "01" — обе строки числовые, поэтому PHP сравнивает их как числа: 1 и 1, равны. То же с "10" == "1e1": 1e1 — корректная запись числа 10 в экспоненциальной форме. А вот "1" === 1 — сразу false: слева строка, справа int, дальше сравнение не идёт.
Вопрос 2: «Что изменилось в PHP 8 при сравнении строки с числом?»
Что проверяет интервьюер
Это вопрос-разделитель: он отличает тех, кто читал release notes, от тех, кто просто пишет код. До PHP 8 выражение 0 == "hello" давало true — строка приводилась к числу и превращалась в 0. Это ломало проверки вроде if ($password == 0) и служило вектором для обходов авторизации.
Правило PHP 8
Начиная с PHP 8, при сравнении числа со строкой язык смотрит на строку: если она числовая — сравнение идёт как числовое, если нет — число приводится к строке и сравниваются строки.
| Выражение | PHP 7 | PHP 8 |
0 == "hello" | true | false |
0 == "" | true | false |
"abc" == 0 | true | false |
"1" == "01" | true | true |
100 == "1e2" | true | true |
Обратите внимание: сравнение двух числовых строк между собой не изменилось — они по-прежнему сравниваются как числа. Именно поэтому "1" == "01" остаётся true и в современном PHP.
Где это стреляет в реальном коде
Нестрогое сравнение спрятано внутри функций, о которых кандидаты забывают: in_array(), array_search(), array_keys() с искомым значением, а также в switch. По умолчанию все они используют ==.
<?php
$roles = ['admin', 'editor'];
var_dump(in_array(0, $roles));
var_dump(in_array(null, $roles));
var_dump(in_array('admin', $roles, true));
Вывод:
bool(false) bool(false) bool(true)
В PHP 8 первые две проверки безопасны, а вот в PHP 7 in_array(0, $roles) возвращал true — и пользователь с ролью 0 внезапно оказывался администратором. Третий аргумент true включает строгий режим сравнения: используйте его всегда, когда ищете в массиве.
У switch строгого режима нет вовсе. Если нужен строгий разбор вариантов — берите match, он сравнивает через ===:
// switch сравнивает через ==, "0" совпадёт с 0
switch ($input) {
case 0:
// сюда попадёт и 0, и "0"
break;
}
// match сравнивает через ===, подмены не будет
$result = match ($input) {
0 => 'ноль',
'0' => 'строка ноль',
default => 'другое',
};
Типичные ошибки кандидатов
- Говорят «
==сравнивает значения,===— ссылки». Это про другие языки: в PHP===для объектов означает «тот же самый экземпляр», а для скаляров — «тот же тип и то же значение». - Утверждают, что в PHP 8 нестрогое сравнение стало безопасным. Нет:
"1" == "01","1e3" == "1000"иnull == falseпо-прежнемуtrue. - Забывают про третий аргумент
in_array()и про то, чтоswitchнестрогий. - Путают
==с оператором<=>(spaceship), который возвращает -1/0/1, или с??, который вообще проnull. - Не могут объяснить, почему
[] == falseистинно: пустой массив приводится кfalse, как и0,"","0",null.
Как ответить кратко
«
===сравнивает тип и значение, при разных типах сразуfalse.==сначала приводит операнды: две числовые строки сравниваются как числа, а с PHP 8 число, сравниваемое с нечисловой строкой, само превращается в строку — поэтому0 == "hello"теперьfalse. В рабочем коде я по умолчанию пишу===, вin_arrayпередаю третий аргументtrue, а вместоswitchпредпочитаюmatch».