<? phpukraine СПІВБЕСІДИ
Пошук по платформі
CORE PHP · JUNIOR ЧАСТО ПИТАЮТЬ

Що таке enum у PHP і чим backed enum відрізняється від pure?

Enum (PHP 8.1+) — тип із фіксованим переліком case-ів; pure enum не має скалярного значення, backed enum привʼязаний до int або string і вміє from()/tryFrom().

Навіщо enum, якщо є константи класу?
Що поверне tryFrom, якщо такого значення в enum немає?
Чому json_encode падає на вашому enum, а на сусідньому працює?
Можна в enum додати властивість, щоб зберігати стан кожного case?
enum PHP 8.1 backed enum match Eloquent

Enum зʼявився в PHP 8.1 і закриває стару проблему «магічних рядків»: замість набору констант класу ви отримуєте окремий тип із фіксованим переліком значень. Кожен case — це обʼєкт-одинак, створений один раз на процес, тому порівняння через === завжди коректне, а new OrderStatus(...) заборонено. Найважливіший практичний наслідок — типізація: сигнатура public function markAs(OrderStatus $status): void фізично не дає передати 'payed' з одруківкою, і перевірку робить не ваш if, а сам PHP.

Різниця pure і backed — у наявності скалярного значення. Pure enum (enum Weekday { case Mon; }) реалізує інтерфейс UnitEnum і має лише властивість name — рядок з іменем case-у. Backed enum оголошується з типом (enum OrderStatus: string), реалізує BackedEnum, додає властивість value і два статичні методи: from() повертає case або кидає \ValueError, tryFrom() повертає case або null. Правило просте: from() — для значень, у яких ви впевнені (константи, дані з власної бази), tryFrom() — для всього, що прийшло від користувача чи зовнішнього API. Метод cases() є в обох видах і повертає масив усіх case-ів у порядку оголошення — з нього роблять селекти, сідери й перевірки.

Enum — це майже повноцінний тип: у ньому можна оголошувати методи, статичні методи, константи, підключати трейти й реалізовувати інтерфейси. Чого не можна — властивостей і будь-якого стану: enum не має конструктора, всі case-и незмінні. Тому «додаткові дані» до case-у оформлюють методом, найчастіше через match ($this), як у прикладі з label(). match тут природний партнер enum: він порівнює строго через ===, а якщо жодна гілка не підійшла, кидає \UnhandledMatchError — це краще за мовчазний default, бо новий case ламає код одразу, а не в проді.

У Laravel backed enum стає типом атрибута моделі: protected function casts(): array { return ['status' => OrderStatus::class]; } (enum-cast доступний з Laravel 9, метод casts() — з Laravel 10; раніше те саме писали у властивості $casts). Читання дає обʼєкт enum, запис приймає обʼєкт і кладе в колонку value. Pure enum так кастити не можна — усередині Eloquent викликає tryFrom(). Так само enum підтримується в implicit route binding і у валідації через Rule::enum(OrderStatus::class).

Межі варто назвати чесно. json_encode серіалізує backed enum у його value автоматично, а на pure enum кидає виняток, бо представлення за замовчуванням у нього немає. У базі колонку зазвичай лишають звичайним varchar замість нативного ENUM MySQL — тоді додавання нового case-у не вимагає ALTER TABLE, а PostgreSQL взагалі не має сумісного синтаксису. І пам'ятайте, що значення backed enum — це частина контракту з базою та API: перейменувати case Paid можна майже безкарно, а змінити 'paid' на 'payed' — уже міграція даних.

enum OrderStatus: string          // backed enum: у кожного case є рядкове значення
{
    case Pending = 'pending';
    case Paid = 'paid';
    case Cancelled = 'cancelled';

    public const Default = self::Pending;   // константа-псевдонім, не новий case

    /** У методі $this — це сам case */
    public function label(): string
    {
        return match ($this) {              // match порівнює через ===
            self::Pending => 'Очікує оплати',
            self::Paid => 'Оплачено',
            self::Cancelled => 'Скасовано',
        };                                  // без default: новий case одразу зламає тест
    }
}

enum Weekday { case Mon; case Tue; }        // pure enum: значення немає

OrderStatus::from('paid');        // OrderStatus::Paid
OrderStatus::tryFrom('deleted');  // null — для даних ззовні
OrderStatus::from('deleted');     // \ValueError
OrderStatus::cases();             // [Pending, Paid, Cancelled]
OrderStatus::Paid->value;         // 'paid'   (лише в backed)
OrderStatus::Paid->name;          // 'Paid'   (є в обох)
Weekday::Mon->value;              // помилка: у pure enum немає value

OrderStatus::Paid === OrderStatus::from('paid'); // true: case — одинак
OrderStatus::Paid === 'paid';                    // false: обʼєкт != рядок

// Eloquent: у колонці лежить 'paid', у моделі — обʼєкт enum
final class Order extends Model
{
    protected function casts(): array
    {
        return ['status' => OrderStatus::class]; // працює тільки з backed enum
    }
}

$order->status->label();          // 'Оплачено'
$order->status = OrderStatus::Paid;
$order->save();                   // у базу піде 'paid'
Що enum зʼявився в PHP 8.1, а кожен case — це обʼєкт-одинак, тому порівнювати їх треба через === без жодних хитрощів.
Що pure enum реалізує UnitEnum і має лише властивість name, а backed enum реалізує BackedEnum, має ще value і методи from()/tryFrom().
Різницю from() і tryFrom(): from() кидає \ValueError на невідомому значенні, tryFrom() повертає null — саме його беруть для даних ззовні.
Що cases() повертає масив усіх case-ів по порядку оголошення і використовується для селектів, валідації та сідерів.
Що enum може мати методи, константи, статичні методи, трейти й реалізовувати інтерфейси, але не може мати власних властивостей і стану.
Практику Laravel: cast моделі на backed enum, match у методі label(), Rule::enum у валідації.
Казати, що enum — це просто набір констант: константи не дають типізації параметра, а enum дає (OrderStatus $status).
Використовувати from() для даних із запиту чи API: невідоме значення кине \ValueError і 500 замість валідаційної помилки.
Порівнювати case зі скаляром: OrderStatus::Paid === 'paid' завжди false, потрібне ->value.
Пробувати додати властивість у enum (public string $label) — це фатальна помилка, enum не має стану; значення дають через backed value або метод.
Кастити в Eloquent pure enum: cast працює лише з backed enum, бо всередині викликається tryFrom().
Очікувати, що json_encode серіалізує будь-який enum: pure enum кидає виняток, бо не має представлення за замовчуванням.
ПОРАДА

Сформулюйте одним реченням: pure enum — це іменований перелік, backed enum — той самий перелік плюс міст до бази й API через value. І одразу додайте, що з зовнішніми даними використовуєте tryFrom(), а не from().

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

Обидва види enum мають методи, константи та cases(); відрізняє їх саме наявність скалярного value і методів from()/tryFrom() у backed enum.