A enum is a type composed of a closed set of valid cases.
Imagine you’re programming the status of a blog post. You have three options: Draft, Published, and Archived.
The old way (Chaos):
class Post {
public const STATUS_DRAFT = 'draft';
public const STATUS_PUBLISHED = 'published';
public string $status; // Can be any string
}
$post->status = 'pubished'; // ❌ Typo that PHP swallows without complaintThe modern way (Enums): An Enum is a special object type that only allows a limited, predefined set of values. It’s a closed list.
Basic Enums (Pure Enums)
We define an Enum using the enum keyword.
enum PostStatus {
case DRAFT;
case PUBLISHED;
case ARCHIVED;
}Now we can use PostStatus as a Data Type in our functions.
class Post {
// Total safety! We only accept valid values
public PostStatus $status;
}
$post = new Post();
$post->status = PostStatus::PUBLISHED; // ✅ Correct
// $post->status = 'published';
// ❌ FATAL ERROR: Must be of type PostStatus, string givenNo more manually validating if the string is correct. PHP does it for you.
Backed Enums
Sometimes you need to save that Enum to a database. The database doesn’t understand PHP objects; it understands strings ('draft') or numbers (1).
For that, we use Backed Enums. We assign a scalar value (int or string) to each case.
// We declare this Enum is "backed" by strings
enum PostStatus: string {
case DRAFT = 'draft';
case PUBLISHED = 'published';
case ARCHIVED = 'archived';
}Automatic Conversion
- From Enum to value: Use the
->valueproperty.
echo PostStatus::DRAFT->value; // 'draft'- From value to Enum: Use the
::from()or::tryFrom()method.
$status = PostStatus::from('published'); // Returns the PostStatus::PUBLISHED objectUse tryFrom() when the value comes from an untrusted input.
If it doesn’t exist, tryFrom() returns null; from() throws a ValueError, useful when an invalid value represents a failure you don’t want to hide.
Enums with Methods
PHP enums can have methods, allowing you to keep some of their behavior together with the type.
They are classes after all. They can have internal logic. This is great for encapsulating presentation logic.
enum PostStatus: string {
case DRAFT = 'draft';
case PUBLISHED = 'published';
case ARCHIVED = 'archived';
// Method to get a color for the frontend
public function color(): string {
return match($this) {
self::DRAFT => 'gray',
self::PUBLISHED => 'green',
self::ARCHIVED => 'red',
};
}
// Method to get a readable label
public function label(): string {
return match($this) {
self::DRAFT => 'In Editing',
self::PUBLISHED => 'Visible',
self::ARCHIVED => 'Hidden',
};
}
}Look at how clean your code is in the view:
$status = PostStatus::PUBLISHED;
echo "<span class='badge-{$status->color()}'>{$status->label()}</span>";
// Output: <span class='badge-green'>Visible</span>No more giant if/else blocks in your HTML templates. The Enum knows how to render itself.
Listing All Cases (cases)
If you need to populate a <select> with all possible options, enums provide the static method ::cases().
foreach (PostStatus::cases() as $option) {
echo "<option value='{$option->value}'>{$option->label()}</option>";
}