<? phpukraine СПІВБЕСІДИ
Пошук по платформі
CORE PHP · MIDDLE

Чим match відрізняється від switch?

`match` (PHP 8.0+) — це вираз, який повертає значення й порівнює строго через `===` без провалювання між гілками, а якщо жодна умова не збіглася і немає `default` — кидає `UnhandledMatchError`; `switch` — оператор, який порівнює нежорстко через `==`, потребує `break` і при відсутності збігу мовчки нічого не робить.

`match` — це просто коротший `switch`, чи різниця глибша?
Чому `match ($_GET['page'])` з гілкою `1 => ...` не спрацьовує, хоча в URL стоїть `page=1`?
Що станеться, якщо жодна гілка `match` не збіглася і `default` немає?
Чому в `switch` треба писати `break`, а в `match` — ні? І що буде, якщо все-таки написати?
match switch PHP 8.0 UnhandledMatchError строге порівняння вираз enum

Головна відмінність не в синтаксисі, а в тому, що це різні мовні категорії. switch — оператор: він передає керування в гілку, а результат ви мусите самі кудись покласти, тому кожен case закінчується присвоєнням у тимчасову змінну і break. match, що зʼявився в PHP 8.0, — вираз: він обчислюється у значення, тому пишеться там, де очікується значення — праворуч від =, у return, в аргументі виклику, у тілі стрілочної функції, у рядковій інтерполяції через {}. Звідси й дрібна деталь синтаксису, на якій спотикаються: після закриваючої фігурної дужки match ставиться крапка з комою, бо це закінчення виразу, а не блоку.

Друга відмінність — семантика порівняння. switch порівнює тему з кожним case нежорстко, через ==, з усіма приведеннями типів; match — строго, через ===, тобто збіг має бути і за значенням, і за типом. PHP 8.0 прибрав найгидкішу пастку switch, змінивши порівняння числа з нечисловим рядком (0 == 'foo' тепер false), але сам == нікуди не подівся: '1' == 1, '1e2' == '100', '0' == false, null == false — усе це в switch досі дає збіг. Практичний наслідок видно в прикладі коду: значення '1', що прийшло з query-рядка, потрапляє в case 1 у switch і не потрапляє в гілку 1 => у match. Це не баг match, а причина №1 регресій при механічній заміні однієї конструкції на іншу — тип теми треба нормалізувати явно ((int) $page, Status::from($raw)).

Третя відмінність — поведінка на невідомому значенні. switch без відповідного case і без default мовчки не робить нічого; це зручно рівно доти, доки на цьому «нічого» випадково не почала триматися логіка. match зобовʼязаний повернути значення, тому відсутність збігу для нього — помилковий стан: інтерпретатор кидає \UnhandledMatchError. Важлива для співбесіди деталь: цей клас успадковує \Error, а не \Exception, тож catch (Exception $e) його не перехопить — потрібні catch (\UnhandledMatchError), catch (\Error) або \Throwable. Найцінніше це на enum: match по кейсах enum без default дає вичерпність, яку перевіряє статичний аналізатор на етапі CI, а рантайм страхує помилкою. Дописаний «щоб не падало» default => null вимикає обидва рівні захисту — і забутий новий кейс перетворюється з голосної помилки на тихий null, який спливе за три екрани звідси.

Решта відмінностей дрібніші, але їх теж питають. Провалювання (fallthrough) в match немає взагалі: кілька значень в одній гілці перелічуються комою (200, 201, 204 =>), а break там не пишеться і не парситься — гілка є виразом, а не блоком інструкцій. Через це ж обмеження гілка не може містити кілька інструкцій: багатокрокова логіка або витягується в метод, або лишається в switch. Для діапазонів і складених умов є ідіома match (true), де кожна гілка — булевий вираз; вона читається краще за драбину if/elseif, бо повертає значення, але порядок гілок стає критичним, а умови після першого збігу не обчислюються взагалі. Щодо продуктивності: коли всі умови — літерали одного типу (int або string), обидві конструкції компілюються в таблицю переходів (ZEND_SWITCH_LONG/ZEND_SWITCH_STRING для switch, ZEND_MATCH для match), тож обирати між ними за швидкістю немає сенсу.

Межі варто назвати чесно. match — не патерн-матчинг: у PHP немає ні деструктуризації, ні guard-умов, ні матчингу за типом (instanceof доводиться писати руками всередині match (true)). Порівняння через === для звичайних обʼєктів означає ідентичність екземпляра, а не рівність вмісту, тому два еквівалентні DateTimeImmutable у match не збігатимуться — з enum це працює лише тому, що його кейси є синглтонами. І якщо match розростається до пари десятків гілок, це вже не питання вибору між ним і switch: там просилася або мапа array<string, callable>, або поліморфізм із окремим класом на кожен випадок.

declare(strict_types=1);

$page = '1';                          // усе, що приходить з HTTP, — рядок

switch ($page) {                      // switch порівнює через ==
    case 1:                           // '1' == 1 → true, гілка спрацює
        $bySwitch = 'перша сторінка';
        break;                        // без break провалиться в default
    default:
        $bySwitch = 'інша';
}
echo $bySwitch;                       // 'перша сторінка'

$byMatch = match ($page) {            // match порівнює через ===
    1 => 'перша сторінка',            // '1' !== 1 → гілка НЕ спрацює
    '1' => 'перша сторінка (рядок)',
    default => 'інша',
};                                    // match — вираз, тому крапка з комою
echo $byMatch;                        // 'перша сторінка (рядок)'

$status = 503;

// match повертає значення: присвоюємо одразу, тимчасова змінна не потрібна
$label = match (true) {               // умови перевіряються згори вниз
    $status >= 500 => 'помилка сервера',
    $status >= 400 => 'помилка клієнта',
    $status >= 200 => 'успіх',
    default => 'невідомо',
};
echo $label;                          // 'помилка сервера'

try {
    // default немає; кілька значень в одній гілці — через кому, без fallthrough
    echo match ($status) {
        200, 201, 204 => 'тіло можна кешувати',
        301, 302 => 'редірект',
    };
} catch (\UnhandledMatchError $e) {   // це Error, а не Exception
    echo $e->getMessage();            // у повідомленні — незіставлене значення
}
Що `match` порівнює через `===` (тип і значення), а `switch` — через `==` з приведенням типів; це не стилістична, а семантична різниця.
Що `match` — вираз: його результат можна присвоїти, повернути з `return`, передати аргументом, покласти в тіло стрілочної функції; `switch` — оператор, тому в кожній гілці доводиться писати присвоєння у тимчасову змінну.
Що провалювання (fallthrough) в `match` немає взагалі: кілька значень групуються комою `200, 201, 204 =>`, а `break` там не потрібен і навіть неможливий.
Що при відсутності збігу `match` без `default` кидає `\UnhandledMatchError`, який успадковує `Error`, а не `Exception` — тобто `catch (Exception)` його не спіймає.
Що тіло гілки `match` — рівно одне вираження, тому багатокрокова логіка з кількома інструкціями лишається за `switch`, окремим методом або `if`.
Що `match` зʼявився в PHP 8.0 і що на enum він дає перевірювану вичерпність: без `default` статичний аналізатор бачить пропущений case, а рантайм ловить його `UnhandledMatchError`.
Називати `match` «синтаксичним цукром над switch»: цукор не змінює семантику, а тут змінюються і порівняння, і поведінка при відсутності збігу.
Механічно замінювати `switch` на `match` у коді, що працює з даними з HTTP, БД чи CSV: там значення — рядки, і гілка `1 =>` після заміни перестає спрацьовувати, бо `'1' !== 1`.
Вважати, що в PHP 8 `switch` став строгим: змінилося лише порівняння числа з нечисловим рядком (`0 == 'foo'` тепер `false`), а `==` з усіма іншими приведеннями (`'1' == 1`, `'1e2' == '100'`, `'0' == false`) у `switch` лишився.
Писати `break` або кілька інструкцій через `;` усередині гілки `match` — це помилка парсингу, бо гілка є виразом, а не блоком.
Ловити `UnhandledMatchError` через `catch (Exception $e)` — не спрацює; потрібен `catch (\UnhandledMatchError)`, `catch (\Error)` або `\Throwable`.
Дописувати `default => null` «щоб не падало» в `match` по enum: це вимикає перевірку вичерпності, і новий case мовчки почне повертати `null` замість того, щоб зламатися голосно.
ПОРАДА

Одна фраза, яка закриває питання: «`switch` — оператор із `==` і провалюванням, `match` — вираз із `===` і без провалювання, а на невідоме значення `switch` мовчить, `match` кидає `UnhandledMatchError`». Далі додайте головний практичний наслідок: `match` по enum без `default` перетворює забутий новий case із тихого бага на помилку.

оновлено 3 вересня 2026 · ліцензія CC-BY-SA-4.0 Знайшли неточність? Напишіть →
ПЕРЕВІРТЕ СЕБЕ

`match` без `default` зобовʼязаний повернути значення, тому відсутність збігу — це помилка, а не «нічого не сталось»: PHP кидає `\UnhandledMatchError` (нащадок `\Error`). Саме тому `catch (Exception $e)` його не перехопить, а на enum ця поведінка працює як страховка від забутого нового кейса.