PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

PHP Enumeracje zaawansowane – backed enums, metody, interfejsy, Magento 2

by Henryk Tews / środa, 03 czerwca 2026 / Opublikowano w PHP

Enumy weszły do PHP w wersji 8.1 i są czymś więcej niż tylko typowaną listą stałych. Backed enums z wartościami string lub int, metody na enumach, implementacja interfejsów, cases jako argumenty funkcji – to narzędzia które eliminują całą klasę błędów związanych z magic strings i niezwalidowanymi wartościami. Pokażę wszystkie możliwości z praktycznymi przykładami z Magento 2.

Pure enum vs Backed enum

<?php

declare(strict_types=1);

// Pure enum - tylko nazwy, bez wartości
enum Direction
{
    case North;
    case South;
    case East;
    case West;
}

// Backed enum int - case ma przypisaną wartość int
enum HttpStatus: int
{
    case Ok                  = 200;
    case Created             = 201;
    case NoContent           = 204;
    case BadRequest          = 400;
    case Unauthorized        = 401;
    case Forbidden           = 403;
    case NotFound            = 404;
    case UnprocessableEntity = 422;
    case InternalServerError = 500;
}

// Backed enum string - case ma przypisaną wartość string
enum OrderStatus: string
{
    case Pending    = 'pending';
    case Processing = 'processing';
    case Complete   = 'complete';
    case Cancelled  = 'cancelled';
    case Closed     = 'closed';
    case Holded     = 'holded';
}

// Konwersja z/do wartości backing
$status = OrderStatus::from('complete');       // OrderStatus::Complete
$status = OrderStatus::tryFrom('unknown');     // null (nie rzuca wyjątku)
$value  = OrderStatus::Complete->value;        // 'complete'
$name   = OrderStatus::Complete->name;         // 'Complete'

// Lista wszystkich cases
$allStatuses = OrderStatus::cases();
// [OrderStatus::Pending, OrderStatus::Processing, ...]

Metody na enumach

<?php

declare(strict_types=1);

enum OrderStatus: string
{
    case Pending    = 'pending';
    case Processing = 'processing';
    case Complete   = 'complete';
    case Cancelled  = 'cancelled';
    case Holded     = 'holded';

    // Etykieta do wyświetlenia w UI
    public function label(): string
    {
        return match($this) {
            self::Pending    => 'Oczekujące',
            self::Processing => 'W realizacji',
            self::Complete   => 'Zakończone',
            self::Cancelled  => 'Anulowane',
            self::Holded     => 'Wstrzymane',
        };
    }

    // Kolor dla badge w UI
    public function color(): string
    {
        return match($this) {
            self::Pending    => 'yellow',
            self::Processing => 'blue',
            self::Complete   => 'green',
            self::Cancelled  => 'red',
            self::Holded     => 'gray',
        };
    }

    // Dozwolone przejścia statusów
    public function allowedTransitions(): array
    {
        return match($this) {
            self::Pending    => [self::Processing, self::Cancelled],
            self::Processing => [self::Complete, self::Holded, self::Cancelled],
            self::Holded     => [self::Processing, self::Cancelled],
            self::Complete,
            self::Cancelled  => [],
        };
    }

    public function canTransitionTo(self $newStatus): bool
    {
        return in_array($newStatus, $this->allowedTransitions(), true);
    }

    // Czy to status końcowy
    public function isFinal(): bool
    {
        return empty($this->allowedTransitions());
    }
}

// Użycie
$status = OrderStatus::Processing;
echo $status->label();  // "W realizacji"
echo $status->color();  // "blue"

if ($status->canTransitionTo(OrderStatus::Complete)) {
    // dozwolone przejście
}

// Walidacja przy zmianie statusu
$current = OrderStatus::Pending;
$next    = OrderStatus::Complete;

if (!$current->canTransitionTo($next)) {
    throw new \RuntimeException(
        "Niedozwolone przejście: {$current->label()} => {$next->label()}"
    );
}

Enumeracje implementujące interfejsy

<?php

declare(strict_types=1);

interface HasLabel
{
    public function label(): string;
}

interface HasIcon
{
    public function icon(): string;
}

// Enum może implementować wiele interfejsów
enum PaymentMethod: string implements HasLabel, HasIcon
{
    case CreditCard  = 'checkmo';
    case BankTransfer= 'banktransfer';
    case PayPal      = 'paypal_express';
    case CashOnDelivery = 'cashondelivery';

    public function label(): string
    {
        return match($this) {
            self::CreditCard     => 'Karta kredytowa',
            self::BankTransfer   => 'Przelew bankowy',
            self::PayPal         => 'PayPal',
            self::CashOnDelivery => 'Płatność przy odbiorze',
        };
    }

    public function icon(): string
    {
        return match($this) {
            self::CreditCard     => 'icon-credit-card',
            self::BankTransfer   => 'icon-bank',
            self::PayPal         => 'icon-paypal',
            self::CashOnDelivery => 'icon-cash',
        };
    }

    public function requiresOnlineCapture(): bool
    {
        return match($this) {
            self::CreditCard, self::PayPal => true,
            default                        => false,
        };
    }
}

// Funkcja akceptuje interfejs - działa z każdym enum który go implementuje
function renderPaymentBadge(HasLabel&HasIcon $method): string
{
    return sprintf(
        '%s',
        $method->icon(),
        $method->label()
    );
}

echo renderPaymentBadge(PaymentMethod::PayPal);

Stałe na enumach

<?php

declare(strict_types=1);

enum Priority: int
{
    case Low    = 1;
    case Medium = 5;
    case High   = 10;
    case Critical = 100;

    // Stałe na enumie (PHP 8.3+: można typować)
    const int DEFAULT = 5;
    const int MIN     = 1;
    const int MAX     = 100;

    // Stała może być casem
    const self FALLBACK = self::Low;

    public function isUrgent(): bool
    {
        return $this->value >= self::High->value;
    }
}

echo Priority::DEFAULT;          // 5
echo Priority::FALLBACK->label;  // błąd - to nie jest enum z metodą label
Priority::from(Priority::DEFAULT); // Priority::Medium

Enumeracje w Magento 2

<?php

declare(strict_types=1);

// Zastąp magic strings w obserwatorach i pluginach
enum MagentoEvent: string
{
    case OrderPlaced           = 'sales_order_place_after';
    case OrderCancelled        = 'order_cancel_after';
    case ProductSaved          = 'catalog_product_save_after';
    case CustomerLogin         = 'customer_login';
    case CheckoutSubmitAll     = 'checkout_submit_all_after';
}

// W etc/events.xml możesz odwoływać się do wartości:
// Nie możesz użyć PHP enum bezpośrednio w XML - ale w obserwatorze:
class OrderPlacedObserver implements \Magento\Framework\Event\ObserverInterface
{
    public function execute(\Magento\Framework\Event\Observer $observer): void
    {
        // Zamiast 'sales_order_place_after' jako string - masz IDE support
        $eventName = MagentoEvent::OrderPlaced->value;
    }
}

// Enum dla typów produktów
enum ProductType: string
{
    case Simple      = 'simple';
    case Configurable= 'configurable';
    case Bundle      = 'bundle';
    case Virtual     = 'virtual';
    case Downloadable= 'downloadable';
    case Grouped     = 'grouped';

    public function isPhysical(): bool
    {
        return match($this) {
            self::Simple, self::Configurable, self::Bundle, self::Grouped => true,
            self::Virtual, self::Downloadable => false,
        };
    }

    public function supportsQty(): bool
    {
        return $this !== self::Grouped;
    }
}

// Użycie w serwisie
class ProductProcessor
{
    public function process(\Magento\Catalog\Api\Data\ProductInterface $product): void
    {
        $type = ProductType::from($product->getTypeId());

        if ($type->isPhysical()) {
            $this->calculateShipping($product);
        }

        if (!$type->supportsQty()) {
            return; // grupowane nie mają własnego qty
        }

        $this->updateInventory($product);
    }
}

Enum w tablicach i kolekcjach

<?php

// Enum jako klucz tablicy (PHP 8.1+)
$labels = [
    OrderStatus::Pending    => 'Oczekuje na płatność',
    OrderStatus::Processing => 'Realizacja w toku',
    OrderStatus::Complete   => 'Wysłane',
];

echo $labels[OrderStatus::Pending]; // "Oczekuje na płatność"

// Filtrowanie kolekcji po enumie
$orders = $this->orderRepository->getList($searchCriteria)->getItems();

$pending = array_filter(
    $orders,
    fn($order) => OrderStatus::tryFrom($order->getStatus()) === OrderStatus::Pending
);

// Grupowanie po statusie
$grouped = [];
foreach ($orders as $order) {
    $status           = OrderStatus::tryFrom($order->getStatus()) ?? OrderStatus::Pending;
    $grouped[$status->value][] = $order;
}

Podsumowanie

Enumy PHP 8.1 to więcej niż typowane stałe. Backed enums eliminują magic strings i zapewniają bezpieczną konwersję z/do wartości bazowej. Metody na enumach przenoszą logikę związaną z danym typem do miejsca gdzie powinna być. Interfejsy na enumach umożliwiają polimorfizm. W Magento 2 enumy najlepiej sprawdzają się wszędzie tam gdzie dotychczas używałeś class-of-constants – statusy zamówień, typy produktów, kody metod płatności i wysyłki.

About Henryk Tews

Co możesz przeczytać następne

AI-assisted optymalizacja SQL – LLM + EXPLAIN + Blackfire, 5x przyspieszenie
PHP 8.5 RC1 – pipe operator w realnym kodzie, pułapki, readonly dziedziczenie potwierdzone
Podsumowanie 2025 – pipe operator, Hyvä mainstream, AI tooling obowiązkowy, 120 wpisów
  • Publikacje
  • O autorze
  • Kontakt

© 2026 Created by

GÓRA
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Zawsze aktywne
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Zarządzaj opcjami
  • Zarządzaj serwisami
  • Zarządzaj {vendor_count} dostawcami
  • Przeczytaj więcej o tych celach
Zobacz preferencje
  • {title}
  • {title}
  • {title}