100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
PHP

Enums in PHP

PHP 8.1 enums provide a first-class way to define a fixed set of named values, with optional scalar backing, methods, interfaces, and constants attached.

Modern PHP FeaturesIntermediate9 min readJul 9, 2026
Analogies

Enums in PHP

Before PHP 8.1, developers simulated enumerations with class constants (class Status { const ACTIVE = 'active'; }), which offered no type safety — any string could be passed where a status was expected. PHP 8.1 introduced true enums via the enum keyword, producing a real type: an enum's cases are singleton instances of the enum type itself, so a parameter typed Status $status can only ever receive one of the enum's declared cases, and PHP enforces that at the type level, not just by convention. Enums can be 'pure' (cases have no underlying scalar value) or 'backed' (each case maps to a specific int or string value), and both kinds can implement interfaces, declare methods, and use traits, exactly like a class.

🏏

Cricket analogy: Simulating a status with class constants is like allowing any word to be scribbled on the scoreboard for 'out' or 'not out'; a true PHP 8.1 enum is like the umpire's official signal set, where only the recognized signals (declared cases) can ever be shown.

Pure enums and backed enums

A pure enum simply lists cases without any underlying value — useful when you only need distinct, comparable identities. A backed enum declares a scalar type (: string or : int) after the enum name, and every case must then specify a unique literal value of that type. Backed enums automatically gain a ->value property to read the underlying scalar, plus two static methods: from(), which converts a scalar to the matching case or throws a ValueError if none matches, and tryFrom(), which returns null instead of throwing. These are essential when persisting enum values to a database column or accepting them from an HTTP request.

🏏

Cricket analogy: A pure enum listing only case names is like naming fielding positions (Slip, Gully, MidOn) with no numeric value attached, just distinct identities; a backed enum is like assigning each position a jersey number, so from() converts number 4 straight to MidOn or throws if no fielder wears it.

php
<?php
declare(strict_types=1);

enum Suit
{
    case Hearts;
    case Diamonds;
    case Clubs;
    case Spades;
}

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

    public function isFinal(): bool
    {
        return match ($this) {
            self::Delivered, self::Cancelled => true,
            self::Pending, self::Shipped => false,
        };
    }
}

$status = OrderStatus::from('shipped'); // OrderStatus::Shipped
var_dump($status->isFinal());            // false

$unknown = OrderStatus::tryFrom('lost'); // null, no exception

Interfaces, constants, and static methods on enums

Enums can implement interfaces, which is the standard way to attach richer, polymorphic behaviour — for example, an enum could implement a HasLabel interface requiring a label(): string method, letting UI code call $status->label() uniformly across enum types. Enums can also declare their own constants and static factory methods, and — since enum cases are just class constants under the hood — SomeEnum::cases() (a built-in static method) returns an ordered array of every declared case, which is invaluable for populating select dropdowns or validating input against the full set of allowed values.

🏏

Cricket analogy: An enum implementing a HasLabel interface is like every fielding position implementing a common 'announce yourself' contract, so the stadium PA system calls the same method regardless of position and gets 'Silly Point' or 'Long On' back automatically.

php
<?php
declare(strict_types=1);

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

enum Priority: int implements HasLabel
{
    case Low = 1;
    case Medium = 2;
    case High = 3;

    public function label(): string
    {
        return match ($this) {
            self::Low => 'Low priority',
            self::Medium => 'Medium priority',
            self::High => 'High priority',
        };
    }
}

foreach (Priority::cases() as $priority) {
    echo "{$priority->value}: {$priority->label()}" . PHP_EOL;
}

Because each enum case is a genuine singleton object, comparing cases with === is always safe and correct — OrderStatus::Shipped === OrderStatus::Shipped is always true, and there is exactly one instance of each case in memory for the entire request lifecycle.

Enums cannot have public, mutable instance properties — only cases, constants, and methods. If mutable per-case state seems necessary, that is usually a sign the design should be a class (possibly with a static registry) rather than an enum, since enums model fixed identity, not mutable data.

  • Enum cases are true singleton instances of the enum type, giving compile-time-checked type safety that string/int constants never had.
  • Pure enums have no underlying scalar value; backed enums (: string or : int) expose ->value and gain from()/tryFrom().
  • from() throws a ValueError for an unmatched scalar; tryFrom() returns null instead.
  • Enums can implement interfaces, declare methods, use traits, and define constants, just like classes.
  • The built-in static cases() method returns an ordered array of every declared case.
  • Enum cases cannot hold mutable instance properties — they represent fixed, singleton identities, not mutable objects.

Practice what you learned

Was this page helpful?

Topics covered

#PHP#PHPProgrammingStudyNotes#Programming#EnumsInPHP#Enums#Pure#Backed#Interfaces#StudyNotes#SkillVeris