Before PHP 8.1, “enums” were fakes — a class full of public consts with zero type safety. Pass the wrong string and nothing complained. PHP 8.1 enums fix that: typed, comparable, and able to hold methods.

A basic backed enum
Use a backed enum when you need a stored value (database, API, JSON):
enum Status: string {
case Draft = 'draft';
case Published = 'published';
case Archived = 'archived';
}
The : string means every case has a ->value. A pure enum (no backing) is just enum Status { case Draft; } when you only need the identity.
Attaching behavior
Enums are objects, so they can own methods:
enum Status: string {
case Draft = 'draft';
case Published = 'published';
public function label(): string {
return match ($this) {
self::Draft => 'Draft',
self::Published => 'Live',
};
}
}
echo Status::Published->label(); // "Live"
Type-checked call sites
The win is at the boundary — wrong values are rejected at compile/run time:
function publish(Status $status): void {
// $status is guaranteed to be a real Status case
}
publish(Status::Published); // ok
publish('published'); // TypeError
Gotchas
- Compare cases with
===, not==— backed enums can otherwise match on their value. - Read the stored value with
->value; for pure enums there is no value, use->name. - Enums are not instantiable with
new, and cases are singletons. matchover an enum with nodefaultis exhaustive-checked by static analyzers.
When to reach for them
Any finite set of states — order status, user roles, HTTP methods, priority levels. If you currently pass strings or ints that mean a fixed set of things, an enum is the cleaner contract.