switch
Часто условие сравнивает одну переменную с длинным списком значений: если день — 6, то «суббота», если 7 — «воскресенье», иначе «будни». Цепочка elseif с этим справится, но в каждом условии придётся повторять $day ===. Для такого выбора в PHP есть switch.
Представьте длинный коридор с дверями. На каждой двери табличка с номером, а у входа стоит вахтёр. Он смотрит на ваш билет, находит первую дверь с подходящим номером и впускает вас. Номер он сверяет нестрого: билет с цифрой 7, написанной от руки, для него то же, что напечатанное число 7.
Давайте посмотрим на switch в деле:
<?php
$day = 3;
switch ($day) {
case 6:
echo 'Saturday', "\n";
break;
case 7:
echo 'Sunday', "\n";
break;
default:
echo 'workday', "\n"; // workday
}Разберём по частям:
switch ($day)— значение, которое будут сравнивать;case 6:— метка, то есть указание «если значение равно6, начинай отсюда»;break;— выход изswitch: дальше выполняется код после закрывающей скобки;default:— метка для всех остальных значений, её можно не писать.
В нашем примере $day равен 3. Ни case 6, ни case 7 не совпали, поэтому PHP выполняет строки после default и печатает workday.
Нестрогое сравнение
switch сравнивает значение с каждой меткой через ==, а не через ===. Поэтому числовая строка, например из формы, попадает в case с числом:
$input = '7';
switch ($input) {
case 7:
echo 'Sunday', "\n"; // Sunday
break;
default:
echo 'unknown', "\n";
}Строка '7' совпала с числом 7, потому что '7' == 7 истинно: числовую строку == сравнивает как число. А вот нечисловая строка с числом не совпадёт: в PHP 8 'abc' == 0 ложно, и switch ('abc') в case 0 не попадёт.
Проваливание без break
Самое неожиданное в switch начинается после совпадения. Найдя подходящую метку, PHP выполняет все строки подряд до ближайшего break или до конца switch. Следующие метки он при этом не проверяет и проходит насквозь. Такое поведение называют проваливанием.
В нашем коридоре это выглядит так: войдя в свою дверь, вы попадаете в комнату, из которой есть проход в следующую. Вы идёте через комнаты подряд, пока кто-нибудь не скажет «стоп» — break. Нажмите «Следующий шаг» и следите за $kind в рамке {main}: после case 6 PHP не останавливается и дописывает к строке ещё и weekend.
1<?php2 3$day = 6;4$kind = '';5switch ($day) {6 case 6:7 $kind .= 'sat ';8 case 7:9 $kind .= 'weekend';10 break;11 default:12 $kind = 'weekday';13}14echo $kind, "\n";
В $day лежит 6 — это значение switch будет сравнивать с метками.
Здесь аналогия с коридором перестаёт работать. В настоящем коридоре у каждой двери проверили бы билет снова. switch сверяет метки только до первого совпадения, а дальше идёт насквозь, не глядя на номера.
Правило такое: switch выбирает, с какой метки начать, а заканчивает только на break или на закрывающей скобке. На собеседовании часто показывают switch без break и просят предсказать вывод. Проходите такую программу так же, как в виджете: от совпавшей метки строка за строкой до первого break.
Несколько значений в одном case
Иногда проваливание нужно. Когда несколько значений ведут к одному результату, их метки ставят подряд, без строк между ними:
$day = 7;
switch ($day) {
case 6:
case 7:
echo 'weekend', "\n"; // weekend
break;
default:
echo 'workday', "\n";
}$day равен 7, поэтому PHP начинает с метки case 7 и печатает weekend. При 6 он начал бы с пустой метки case 6, провалился в case 7 и дошёл до той же строки.
Частая ошибка
В ветке забыли break:
$role = 'admin';
switch ($role) {
case 'admin':
echo 'full access', "\n"; // ✗
case 'guest':
echo 'read only', "\n";
break;
}Программа печатает и full access, и read only: после первой ветки break нет, и PHP проваливается в case 'guest'. Сообщения об ошибке не будет, ведь для PHP это допустимый код. Исправление — break; в конце каждой ветки, кроме тех, что проваливаются намеренно.
Главное
switchсравнивает значение с каждой меткойcaseчерез==, поэтому строка'7'попадает вcase 7.- После совпавшей метки PHP выполняет все строки подряд до
break, не проверяя следующих меток. defaultсрабатывает, когда не совпала ни одна метка.- Забытый
breakне вызывает ошибки: программа молча выполняет лишние строки.