PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

PHP Enums advanced – backed enums, methods, interfaces, Magento 2

by Henryk Tews / Wednesday, 03 June 2026 / Published in PHP

Enums arrived in PHP 8.1 and are much more than a typed list of constants. Backed enums with string or int values, methods on enums, interface implementation, cases as function arguments – these are tools that eliminate an entire class of errors related to magic strings and unvalidated values. I show all the capabilities with practical examples from Magento 2.

Pure enum vs Backed enum

<?php

declare(strict_types=1);

// Pure enum - names only, no values
enum Direction
{
    case North;
    case South;
    case East;
    case West;
}

// Backed int enum - case has an assigned int value
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 string enum - case has an assigned string value
enum OrderStatus: string
{
    case Pending    = 'pending';
    case Processing = 'processing';
    case Complete   = 'complete';
    case Cancelled  = 'cancelled';
    case Closed     = 'closed';
    case Holded     = 'holded';
}

// Conversion to/from backing value
$status = OrderStatus::from('complete');       // OrderStatus::Complete
$status = OrderStatus::tryFrom('unknown');     // null (does not throw)
$value  = OrderStatus::Complete->value;        // 'complete'
$name   = OrderStatus::Complete->name;         // 'Complete'

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

Methods on enums

<?php

declare(strict_types=1);

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

    public function label(): string
    {
        return match($this) {
            self::Pending    => 'Pending payment',
            self::Processing => 'Processing',
            self::Complete   => 'Complete',
            self::Cancelled  => 'Cancelled',
            self::Holded     => 'On hold',
        };
    }

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

    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);
    }

    public function isFinal(): bool
    {
        return empty($this->allowedTransitions());
    }
}

// Usage
$status = OrderStatus::Processing;
echo $status->label(); // "Processing"
echo $status->color(); // "blue"

if ($status->canTransitionTo(OrderStatus::Complete)) {
    // allowed transition
}

$current = OrderStatus::Pending;
$next    = OrderStatus::Complete;

if (!$current->canTransitionTo($next)) {
    throw new \RuntimeException(
        "Invalid transition: {$current->label()} => {$next->label()}"
    );
}

Enums implementing interfaces

<?php

declare(strict_types=1);

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

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

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     => 'Credit card',
            self::BankTransfer   => 'Bank transfer',
            self::PayPal         => 'PayPal',
            self::CashOnDelivery => 'Cash on delivery',
        };
    }

    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,
        };
    }
}

// Function accepts interface - works with any enum implementing it
function renderPaymentBadge(HasLabel&HasIcon $method): string
{
    return sprintf(
        '<span class="%s">%s</span>',
        $method->icon(),
        $method->label()
    );
}

echo renderPaymentBadge(PaymentMethod::PayPal);

Constants on enums

<?php

declare(strict_types=1);

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

    // Constants on enum (PHP 8.3+: can be typed)
    const int DEFAULT = 5;
    const int MIN     = 1;
    const int MAX     = 100;

    // Constant can be a case
    const self FALLBACK = self::Low;

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

Priority::from(Priority::DEFAULT); // Priority::Medium

Enums in Magento 2

<?php

declare(strict_types=1);

// Replace magic strings in observers and plugins
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;
    }
}

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;
        }

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

Enums in arrays and collections

<?php

// Enum as array key (PHP 8.1+)
$labels = [
    OrderStatus::Pending    => 'Awaiting payment',
    OrderStatus::Processing => 'Processing in progress',
    OrderStatus::Complete   => 'Shipped',
];

echo $labels[OrderStatus::Pending]; // "Awaiting payment"

// Filter collection by enum
$orders = $this->orderRepository->getList($searchCriteria)->getItems();

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

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

Summary

PHP 8.1 enums are more than typed constants. Backed enums eliminate magic strings and provide safe conversion to/from the backing value. Methods on enums move logic related to a given type to where it belongs. Interfaces on enums enable polymorphism. In Magento 2 enums work best everywhere you previously used class-of-constants – order statuses, product types, payment and shipping method codes.

About Henryk Tews

What you can read next

PHP 7.2 – object type hint, sodium instead of mcrypt, deprecations

© 2026 Created by

TOP
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 Always active
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.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
Zobacz preferencje
  • {title}
  • {title}
  • {title}