Урок 04

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:

  1. 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 => 'Рабочий'.

  1. Сложные сравнения — switch (true) эмулирует:
switch (true) {
    case $a > 5 && $b < 10:
        // ...
        break;
    case $x === 'active':
        // ...
        break;
}

В match — match(true) { ... } чище.

  1. Несколько инструкций на 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.