Enumy (PHP 8.1)
🎯 Po tej lekcji zdefiniujesz backed enum, użyjesz from() i tryFrom() bezpiecznie i dodasz metodę do enuma.
Przed PHP 8.1 programiści używali zestawów stałych, żeby reprezentować skończony zestaw wartości: STATUS_NOWE = 1, STATUS_REALIZACJA = 2, STATUS_DOSTARCZONE = 3. Problem? Nic nie stało na przeszkodzie, żeby przekazać do funkcji liczbę 99 — PHP tego nie sprawdzał. Enum rozwiązuje ten problem: tworzy typ, który akceptuje tylko wartości z góry zdefiniowanego zbioru.
Pure enum — enum bez wartości
Najprostszy enum to lista nazwanych przypadków bez żadnych dodatkowych wartości:
<?php
declare(strict_types=1);
enum Status
{
case Aktywny;
case Nieaktywny;
case Zablokowany;
}
$stan = Status::Aktywny;
echo $stan->name; // wynik: Aktywny
// Porównanie:
$stanB = Status::Aktywny;
var_dump($stan === $stanB); // wynik: bool(true)
// Niedozwolone — PHP odmówi:
// new Status(); // Fatal Error: Cannot instantiate enum
// $stan = Status::Usunieto; // Fatal Error: undefined enum case
Właściwość ->name zwraca nazwę case'a jako string. Jest dostępna zarówno w pure, jak i backed enum.
Backed enum — enum z wartościami
Backed enum przechowuje dodatkową wartość (ang. backing value) — string lub int. Deklarujesz typ po dwukropku:
<?php
declare(strict_types=1);
enum StatusZamowienia: string
{
case Nowe = 'nowe';
case WRealizacji = 'w_realizacji';
case Dostarczone = 'dostarczone';
case Anulowane = 'anulowane';
}
$status = StatusZamowienia::WRealizacji;
echo $status->name; // wynik: WRealizacji
echo $status->value; // wynik: w_realizacji
Właściwość ->value zwraca wartość backing (string lub int). Jest dostępna tylko w backed enum.
from() i tryFrom()
Backed enum umożliwia tworzenie case'a na podstawie wartości backing. Masz do wyboru dwie metody:
<?php
declare(strict_types=1);
enum StatusZamowienia: string
{
case Nowe = 'nowe';
case WRealizacji = 'w_realizacji';
case Dostarczone = 'dostarczone';
}
// from() — rzuca \ValueError gdy wartość nie istnieje
$s1 = StatusZamowienia::from('nowe');
echo $s1->name; // wynik: Nowe
// tryFrom() — zwraca null gdy wartość nie istnieje (bezpieczna)
$s2 = StatusZamowienia::tryFrom('w_realizacji');
echo $s2?->name; // wynik: WRealizacji
$s3 = StatusZamowienia::tryFrom('nieznany');
var_dump($s3); // wynik: NULL
// from() z nieznana wartością → wyjątek:
// StatusZamowienia::from('nieznany'); // rzuca \ValueError
from(), gdy wartość pochodzi z Twojego kodu i powinna zawsze być prawidłowa (nieoczekiwany błąd to naprawdę błąd). Użyj tryFrom(), gdy wartość pochodzi z zewnątrz (formularz, baza danych, API) i może być nieprawidłowa — sprawdź null przed użyciem.cases() — lista wszystkich case'ów
cases() zwraca tablicę wszystkich case'ów enuma. Przydatne np. do budowania listy wyboru:
<?php
declare(strict_types=1);
enum StatusZamowienia: string
{
case Nowe = 'nowe';
case WRealizacji = 'w_realizacji';
case Dostarczone = 'dostarczone';
case Anulowane = 'anulowane';
}
foreach (StatusZamowienia::cases() as $status) {
echo $status->name . ' => ' . $status->value . PHP_EOL;
}
// wynik:
// Nowe => nowe
// WRealizacji => w_realizacji
// Dostarczone => dostarczone
// Anulowane => anulowane
Metody w enumach
Enum może mieć metody — zarówno zwykłe (dla konkretnego case'a przez $this), jak i statyczne:
<?php
declare(strict_types=1);
enum StatusZamowienia: string
{
case Nowe = 'nowe';
case WRealizacji = 'w_realizacji';
case Dostarczone = 'dostarczone';
case Anulowane = 'anulowane';
public function etykieta(): string
{
return match($this) {
self::Nowe => 'Nowe zamówienie',
self::WRealizacji => 'W realizacji',
self::Dostarczone => 'Dostarczone',
self::Anulowane => 'Anulowane',
};
}
public function czyAktywne(): bool
{
return match($this) {
self::Nowe, self::WRealizacji => true,
default => false,
};
}
}
$status = StatusZamowienia::WRealizacji;
echo $status->etykieta(); // wynik: W realizacji
echo $status->czyAktywne() ? 'aktywne' : 'zakończone'; // wynik: aktywne
$zakonczone = StatusZamowienia::Dostarczone;
echo $zakonczone->czyAktywne() ? 'aktywne' : 'zakończone'; // wynik: zakończone
Enum implementujący interfejs
Enum może implementować interfejsy — to przydatne do typowania:
<?php
declare(strict_types=1);
interface MaEtykiete
{
public function etykieta(): string;
}
enum Priorytet: int implements MaEtykiete
{
case Niski = 1;
case Sredni = 2;
case Wysoki = 3;
public function etykieta(): string
{
return match($this) {
self::Niski => 'Niski',
self::Sredni => 'Średni',
self::Wysoki => 'Wysoki',
};
}
}
echo Priorytet::Wysoki->etykieta(); // wynik: Wysoki
echo Priorytet::Wysoki->value; // wynik: 3
const) i implementować interfejsy — ale nie może mieć zwykłych właściwości instancji (np. public string $opis;). Każdy case to singleton — nie przechowuje stanu. Wszystkie dane enuma muszą wynikać z jego wartości backing lub const.Int backed enum
Backed enum może też używać int zamiast string:
<?php
declare(strict_types=1);
enum Priorytet: int
{
case Niski = 1;
case Sredni = 5;
case Wysoki = 10;
}
$p = Priorytet::from(5);
echo $p->name; // wynik: Sredni
echo $p->value; // wynik: 5
var_dump(Priorytet::tryFrom(99)); // wynik: NULL
Enumy to jeden z najpotężniejszych dodatków PHP 8.1. Zastępują błędogenne stałe klasowe i zapewniają pełne bezpieczeństwo typów. W kolejnej lekcji poznasz przestrzenie nazw — mechanizm organizowania klas w większych projektach.
Sprawdź się
Co zwraca StatusZamowienia::tryFrom('nieistniejaca_wartosc') dla backed enum?
tryFrom() to bezpieczna wersja from(). Gdy podana wartość nie pasuje do żadnego case enuma, tryFrom() zwraca null zamiast rzucać wyjątek. Używaj jej gdy dane przychodzą z zewnątrz i mogą być nieprawidłowe.
Jak odczytać wartość backing (np. 'nowe') z obiektu backed enum $status = StatusZamowienia::Nowe?
W backed enum właściwość ->value daje wartość backing (string lub int przypisaną w deklaracji: case Nowe = 'nowe'). Właściwość ->name daje nazwę case'a jako string ('Nowe'), dostępna zarówno w pure jak i backed enum.
Czy można stworzyć obiekt enuma przez new StatusZamowienia()?
Enumy w PHP 8.1 nie są klasami w tradycyjnym sensie — nie możesz zrobić new StatusZamowienia(). Wartości enum uzyskujesz przez StatusZamowienia::Nowe, from() lub tryFrom().
Ćwiczenie
Zdefiniuj backed enum RolUzytkownika: string z case'ami Admin = 'admin', Redaktor = 'redaktor', Czytelnik = 'czytelnik'. Dodaj metodę mozeDodawac(): bool zwracającą true dla Admin i Redaktor. Sprawdź tryFrom() dla 'admin', 'redaktor' i 'brak'. Wylistuj wszystkie role przez cases().
Pokaż rozwiązanie
<?php
declare(strict_types=1);
enum RolUzytkownika: string
{
case Admin = 'admin';
case Redaktor = 'redaktor';
case Czytelnik = 'czytelnik';
public function mozeDodawac(): bool
{
return match($this) {
self::Admin, self::Redaktor => true,
self::Czytelnik => false,
};
}
}
var_dump(RolUzytkownika::tryFrom('admin')); // wynik: enum(RolUzytkownika::Admin)
var_dump(RolUzytkownika::tryFrom('redaktor')); // wynik: enum(RolUzytkownika::Redaktor)
var_dump(RolUzytkownika::tryFrom('brak')); // wynik: NULL
$rola = RolUzytkownika::Admin;
echo $rola->mozeDodawac() ? 'może' : 'nie może'; // wynik: może
foreach (RolUzytkownika::cases() as $r) {
echo $r->value . ': ' . ($r->mozeDodawac() ? 'może' : 'nie może') . PHP_EOL;
}
// wynik:
// admin: może
// redaktor: może
// czytelnik: nie może