PHP 8 представил нам новое выражение — match — очень сильный функционал, который зачастую будет отличной альтернативой оператору switch. Я говорю здесь «зачастую», потому что как match, так и switch имеют свою область применения, часто не пересекающихся друг с другом. Итак, давайте рассмотрим различия между ними. Для начала сравним их. Вот один из примеров применения оператора switch в сфере разработки crm:
switch($clientStatus) {
case 'Open':
case 'Not assigned':
$discount = null;
break;
case 'First buyer':
$discount = 10;
break;
case 'Second buyer':
$dicount = 20;
break;
case 'VIP':
$discount = 30;
break;
default:
$discount = 5;
break;
}
А вот как будет выглядеть назначение скидки по статусу клиента в match:
$discount = match($clientStatus) {
'Open', 'Not assigned' => null,
'First buyer' => 10,
'Second buyer' => 20,
'VIP' => 30,
default => 5
}
Как вы могли увидеть из этих двух примеров, match значительно короче в написании, потому что:
- Ему не требуется ключевое слово break;
- Он может объединять несколько условий в одно через запятую;
- Он возвращает значение, поэтому вы можете сохранить его в переменной.
Поэтому, с синтаксической точки зрения, match значительно проще в написании. Однако, если мы копнём глубже, то можем увидеть куда больше отличий.
Match - это выражение
Match принято называть выражением (expression), тогда как switch - оператором (statement). Между этими двумя понятиями в теории лежит большая разница. Выражение совмещает в себе значение и вызов функции и его можно назначить переменной. Другими словами, оно возвращает какое-то значение. Поэтому мы можем хранить результат вызова match в какой-то переменной. В случае со switch это не возможно.
Отсутствие приведения типов
При сравнении значений match сравнивает и типы значений. Иными словами, при сравнении используется оператор ===, тогда как в switch используется ==.
Поэтому, у вас могут случится ситуации, когда вы захотите, чтобы PHP автоматически приводил типы к нужному значению. Именно это в первую очередь и объясняет, почему вы не можете заменить match вместо switch во всём коде.
$clientType = "2";
$redirectUrl = match($clientType) {
2 => '/discount',
3 => '/buy',
default => '/',
};
// $redirectUrl = '/'
Неизвестные значения выбрасывают ошибку
Если вы не укажете ключевое слово default и когда match не может найти никаких совпадений в перечислении, PHP выбросит исключение UnhandledMatchError в процессе исполнения. И снова мы видим больше ограничений, но всё это защищает разработчиков от возможных появлений багов.
$clientType = 5;
$redirectUrl = match($clientType) {
2 => '/discount',
3 => '/buy',
};
// UnhandledMatchError
Выражение должно умещаться в одной строке
Вы можете написать только одно выражение. Т.е. такое выражение у вас вызовет ошибку:
$a = 5;
$discount = match($clientStatus) {
'Open', 'Not assigned' => if ($a > 5) {
4
} else {
6
},
'First buyer' => 10,
}
В этом случае вам потребуется как минимум использовать тернатный оператор.
Совмещение условий
Я ранее отмечал, что у match отсутствует ключевое слово break; это также означает, что match не разрешает совмещать условия. Но с другой стороны вы можете писать несколько условий в одну строчку, разделяя их запятыми:
$discount = match($clientStatus) {
'First buyer', 'Second buyer', 'VIP' => 10,
}
Сложные условия и производительность
Когда обсуждалось внедрение match в сообществе, некоторые разработчики говорили, что нет никакой необходимости его внедрения, т.к. то же самое можно сделать используя обычные массивы. Посмотрите на пример ниже, когда мы хотим найти значения, где ключи - это поиск по regex. Здесь мы используем нотацию массива:
$message = [
$this->matchRegex($line) => 'match A',
$this->matchesOtherRegex($line) => 'match B'
][$search] ?? 'no match';
Но здесь есть загвоздка - подобный подход запустит все функции regex во время построения массива. Match, с другой стороны будет запускать всё построчно, а это более производительно.
Выбрасывание исключений
Так как мы говорим о PHP 8, то исключение можно выбросить непосредственно в самом match:
$discount = match($clientStatus) {
'Open', 'Not assigned' => throw new DomainException('This type of client not allowed here!'),
'First buyer' => 10,
'Second buyer' => 20,
'VIP' => 30,
default => 5
}
Итак, что использовать - switch или match?
Если сделать краткий итог, то можно сказать, что match - это более строгая и более современная версия его младшего брата switch.
Есть случаи, когда match предлагает большую гибкость, особенно когда мы говорим о более сложных условиях в несколько строк и жонглирование типами. С другой стороны, проверка по regex, которую я приводил выше, ограничения, которые гарантируют меньше ошибок, существенно сделают жизнь разработчиков проще.
Често признаться, раньше я редко использовать switch, ввиду его сложного синтаксиса. И эти проблемы решил match. Конечно, он ещё не совершеннен, поэтому есть случаи, когда вы будете использовать switch, а когда match.