Match и switch: новый синтаксис
switch — классика
Существует с PHP 4, активно используется.
switch ($status) {
case 'active':
$label = 'Активен';
break;
case 'pending':
$label = 'Ожидает';
break;
default:
$label = 'Неизвестно';
}
Проблемы:
breakможно забыть — баг.- Сравнение через
==, не===. - Приводит типы —
'0'и0совпадут. - Не возвращает значение — нужна переменная.
- Многословно.
match — современная замена
PHP 8.0+:
$label = match ($status) {
'active' => 'Активен',
'pending' => 'Ожидает',
default => 'Неизвестно',
};
Что лучше:
- Возвращает значение — присваивание напрямую.
- Строгое сравнение
===. - Без
break— нельзя забыть. UnhandledMatchError, если ни один case не совпал.- Читается короче.
Синтаксис
$result = match ($value) {
$pattern1 => $result1,
$pattern2 => $result2,
default => $default,
};
$value — любое выражение.
$pattern — значение или список значений через запятую.
default — если ничего не совпало (опционально).
Несколько значений
$type = match ($code) {
200, 201, 204 => 'Успех',
400, 422 => 'Ошибка клиента',
500, 502, 503 => 'Ошибка сервера',
default => 'Другое',
};
Без default
$status = match ($code) {
200 => 'OK',
404 => 'Not Found',
};
// code = 500 → UnhandledMatchError
Если не покрыты все случаи — исключение.
Строгое сравнение
$value = '1';
switch ($value) {
case 1:
echo 'switch: 1'; // сработает! ==
break;
}
match ($value) {
1 => print('match: 1'), // не сработает ===
default => print('match: default'),
};
Match не приводит типы.
Match как выражение
В аргументе:
function render(int $code): void
{
echo match ($code) {
200 => 'OK',
404 => 'Not Found',
default => 'Error',
};
}
В return:
function getMessage(string $status): string
{
return match ($status) {
'active' => 'Активен',
'pending' => 'Ожидает',
default => 'Неизвестно',
};
}
Match с true
Условная логика — замена if/elseif:
$grade = match (true) {
$score >= 90 => 'Отлично',
$score >= 75 => 'Хорошо',
$score >= 60 => 'Удовлетворительно',
default => 'Неудовлетворительно',
};
Часто используют для сложных условий.
Match и enums
Идеально сочетаются:
enum Status: string
{
case Active = 'active';
case Pending = 'pending';
case Blocked = 'blocked';
}
$label = match ($status) {
Status::Active => 'Активен',
Status::Pending => 'Ожидает',
Status::Blocked => 'Заблокирован',
};
Со strict types — типобезопасно.
Match и функции
Вызов функции как результат:
$result = match ($action) {
'create' => createUser($data),
'update' => updateUser($id, $data),
'delete' => deleteUser($id),
default => throw new InvalidArgumentException('Unknown action'),
};
Match с исключением
$value = match ($type) {
'int' => 42,
'string' => 'hello',
default => throw new InvalidArgumentException("Unknown type: $type"),
};
throw как выражение (PHP 8.0+).
Многострочные результаты
$result = match ($action) {
'sum' => function () use ($a, $b) {
return $a + $b;
}(),
'diff' => $a - $b,
default => 0,
};
Обычно делают чище — через отдельные функции.
Match в шаблонах
HTML через match:
echo match ($status) {
'active' => '<span class="badge success">Активен</span>',
'pending' => '<span class="badge warning">Ожидает</span>',
'blocked' => '<span class="badge danger">Заблокирован</span>',
};
Не забывай htmlspecialchars, если данные из БД.
switch — когда нужен
Match не всегда заменяет switch:
- Fall-through (проваливание case):
switch ($day) {
case 1:
case 2:
case 3:
case 4:
case 5:
echo 'Рабочий';
break;
default:
echo 'Выходной';
}
В match — просто 1, 2, 3, 4, 5 => 'Рабочий'.
- Сложные сравнения —
switch (true)эмулирует:
switch (true) {
case $a > 5 && $b < 10:
// ...
break;
case $x === 'active':
// ...
break;
}
В match — match(true) { ... } чище.
- Несколько инструкций на case — в switch естественно:
switch ($action) {
case 'create':
validate($data);
save($data);
log('created');
break;
}
В match — только одно выражение. Для нескольких действий оборачивают в функцию или IIFE.
Вывод: match — по умолчанию. switch — для fall-through или сложных блоков.
Проблема с case в switch
Проваливание — классический баг:
switch ($status) {
case 'active':
echo 'Активен'; // забыл break — сработает и следующее
case 'pending':
echo 'Ожидает';
break;
}
active выведет и «Активен», и «Ожидает».
Производительность
Match быстрее switch в большинстве случаев.
Причина: компилируется в набор сравнений без интерпретации.
Практика
<?php
declare(strict_types=1);
enum Role: string
{
case Admin = 'admin';
case Editor = 'editor';
case Viewer = 'viewer';
}
function getPermissions(Role $role): array
{
return match ($role) {
Role::Admin => ['read', 'write', 'delete', 'manage'],
Role::Editor => ['read', 'write'],
Role::Viewer => ['read'],
};
}
function getColorByStatus(string $status): string
{
return match ($status) {
'active', 'paid' => 'green',
'pending', 'processing' => 'yellow',
'cancelled', 'failed' => 'red',
default => 'gray',
};
}
$role = Role::Editor;
echo implode(', ', getPermissions($role)) . "\n";
echo getColorByStatus('active') . "\n";
// Match(true) для диапазонов
$score = 85;
$grade = match (true) {
$score >= 90 => 'A',
$score >= 80 => 'B',
$score >= 70 => 'C',
$score >= 60 => 'D',
default => 'F',
};
echo $grade; // B
Итог
match— заменаswitchв 90% случаев.- Возвращает значение, строгое сравнение, без
break. default— опционально, но обычно нужен.UnhandledMatchError— если ни один case не совпал.match (true)— для условий.switch— для fall-through и сложных блоков.- Match + enums — идеальная пара.
Дальше — named arguments и spread.