== или ===: что спрашивают в первую очередь

Первый технический вопрос почти любого 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 7PHP 8
0 == "hello"truefalse
0 == ""truefalse
"abc" == 0truefalse
"1" == "01"truetrue
100 == "1e2"truetrue

Обратите внимание: сравнение двух числовых строк между собой не изменилось — они по-прежнему сравниваются как числа. Именно поэтому "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».

Проверьте себя
1. Что вернёт в PHP 8 выражение var_dump(0 == "hello")?
Abool(false), число приводится к строке и сравниваются строки
Bbool(true), строка приводится к нулю
CTypeError
Dbool(true) только при выключенном strict_types
2. Почему "1" == "01" истинно даже в PHP 8?
AПотому что PHP обрезает ведущие нули у любых строк
BПотому что обе строки числовые и сравниваются как числа
CПотому что == сравнивает длину строк
DЭто ошибка, в PHP 8 выражение возвращает false
3. Как сделать поиск в массиве через in_array() строгим?
AИспользовать функцию in_array_strict()
BОбернуть искомое значение в (string)
CПередать третьим аргументом true
DНикак, нужно писать цикл вручную