Перечисления в PHP: от switch до Enum (полное руководство)

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему «Enumerations on PHP» — это вопрос на миллион?

Если вы когда-нибудь гуглили «enumerations on PHP», то знаете: до выхода PHP 8.1 эта тема была настоящим квестом. Разработчики изобретали велосипеды с константами, абстрактными классами и хитрыми трейтами. Stack Overflow пестрил вопросами вроде «Как эмулировать Enum в PHP?», а ответы собирали сотни апвоутов.

Но времена изменились. Начиная с PHP 8.1, перечисления (Enum) стали нативной частью языка. И это не просто синтаксический сахар — это мощный инструмент для написания чистого, безопасного и самодокументируемого кода.

В этой статье мы разберём всё: от классического подхода с константами до современных Enum, с реальными примерами и лучшими практиками. Поехали!

Проблема: как жили PHP-разработчики до PHP 8.1

До появления нативных перечислений у нас было два основных пути: константы класса и «костыли» с абстрактными классами. Рассмотрим оба.

Подход №1: Константы класса (самый популярный)

class OrderStatus {
    const PENDING = 'pending';
    const PROCESSING = 'processing';
    const SHIPPED = 'shipped';
    const DELIVERED = 'delivered';
    const CANCELLED = 'cancelled';
}

// Использование
$status = OrderStatus::PENDING;
if ($status === OrderStatus::PROCESSING) {
    echo 'Заказ обрабатывается';
}

Плюсы: простота, понятность, автодополнение в IDE.

Минусы: отсутствие типизации. Любой может передать строку 'invalid_status' — и PHP не ругнётся.

Подход №2: Абстрактный класс + приватный конструктор

Продвинутые разработчики шли дальше:

abstract class OrderStatus {
    private function __construct() {}
    
    public static function pending(): self {
        return new class extends OrderStatus {};
    }
    public static function processing(): self {
        return new class extends OrderStatus {};
    }
    // ... остальные статусы
}

Работало, но код раздувался. И это всё равно была эмуляция, а не настоящая поддержка языка.

Нативные Enum в PHP 8.1: революция

С выходом PHP 8.1 проблема «enumerations on PHP» была решена раз и навсегда. Теперь перечисления — это полноценные типы, которые:

  • Поддерживают строгую типизацию
  • Имеют встроенные методы (name, value, cases())
  • Могут реализовывать интерфейсы
  • Поддерживают трейты
  • Интегрируются с match и switch

Базовый синтаксис Enum в PHP

enum OrderStatus {
    case Pending;
    case Processing;
    case Shipped;
    case Delivered;
    case Cancelled;
}

// Использование
$status = OrderStatus::Pending;
echo $status->name; // 'Pending'

Всё просто и элегантно. Но это только начало.

Типы Enum: чистые и со значениями

PHP поддерживает два типа перечислений:

1. Pure Enum (чистые перечисления)

Без привязки к конкретным значениям. Идеально для флагов, состояний, типов.

enum UserRole {
    case Admin;
    case Editor;
    case Viewer;
}

function checkAccess(UserRole $role): bool {
    return match($role) {
        UserRole::Admin => true,
        UserRole::Editor => true,
        UserRole::Viewer => false,
    };
}

2. Backed Enum (перечисления со значениями)

Когда нужно сопоставить enum с конкретным значением (строкой или целым числом).

enum OrderStatus: string {
    case Pending = 'pending';
    case Processing = 'processing';
    case Shipped = 'shipped';
    case Delivered = 'delivered';
    case Cancelled = 'cancelled';
}

// Получаем значение
$status = OrderStatus::Shipped;
echo $status->value; // 'shipped'

// Создаём из значения
$status = OrderStatus::from('processing'); // OrderStatus::Processing
$status = OrderStatus::tryFrom('unknown'); // null (без исключения)

Важно: from() выбрасывает исключение, если значение не найдено. tryFrom() возвращает null — используйте его, когда не уверены в корректности данных.

Продвинутые возможности Enum в PHP

Методы внутри Enum

Да, у перечислений могут быть свои методы! Это не просто набор констант — это полноценные объекты.

enum OrderStatus: string {
    case Pending = 'pending';
    case Processing = 'processing';
    case Delivered = 'delivered';
    
    public function label(): string {
        return match($this) {
            self::Pending => 'Ожидает обработки',
            self::Processing => 'В обработке',
            self::Delivered => 'Доставлен',
        };
    }
    
    public function canCancel(): bool {
        return match($this) {
            self::Pending, self::Processing => true,
            default => false,
        };
    }
}

// Использование
$status = OrderStatus::Processing;
echo $status->label(); // 'В обработке'
echo $status->canCancel() ? 'Можно отменить' : 'Нельзя отменить';

Реализация интерфейсов

Enum могут реализовывать интерфейсы — это даёт невероятную гибкость.

interface HasColor {
    public function color(): string;
}

enum Status: string implements HasColor {
    case Success = 'success';
    case Warning = 'warning';
    case Error = 'error';
    
    public function color(): string {
        return match($this) {
            self::Success => '#28a745',
            self::Warning => '#ffc107',
            self::Error => '#dc3545',
        };
    }
}

function renderBadge(HasColor $status): string {
    return "<span style='color: {$status->color()}'>{$status->value}</span>";
}

Метод cases() — итерация по всем значениям

Часто нужно получить список всех возможных вариантов. Метод cases() возвращает массив всех кейсов.

enum Day: int {
    case Monday = 1;
    case Tuesday = 2;
    case Wednesday = 3;
    case Thursday = 4;
    case Friday = 5;
    case Saturday = 6;
    case Sunday = 7;
    
    public static function workingDays(): array {
        return array_filter(self::cases(), fn($day) => $day->value <= 5);
    }
}

// Все дни
foreach (Day::cases() as $day) {
    echo $day->name . ' (' . $day->value . ')<br>';
}

// Только рабочие
$workingDays = Day::workingDays();

Enum vs. match: идеальная пара

Одно из главных преимуществ Enum — идеальная интеграция с конструкцией match. Вместо громоздких switch-case:

// До Enum
function getDiscount(string $userType): float {
    switch ($userType) {
        case 'vip': return 0.2;
        case 'regular': return                

Блоги

Рекомендаций по книгам