Enumerations, or enums, provide a type-safe way to represent a fixed set of possible values. This is extremely useful when dealing with states, options, or predefined categories where previously developers relied on constants, static classes, or strings.
Basic Enumerations
A basic enum defines a set of named cases. Unlike class constants, enums guarantee type-safety: you cannot pass arbitrary strings or numbers.
enum Status { case Draft; case Published; case Archived; } function printStatus(Status $status): void { echo "Current status: $status->name"; } printStatus(Status::Draft); // Current status: Draft printStatus(Status::Published); // Current status: Published
Backed Enumerations
Backed enums associate each case with a scalar value (either string or int). This is particularly useful when storing values in a database or interacting with external APIs.
enum Role: string { case Admin = 'admin'; case Editor = 'editor'; case User = 'user'; } // Convert enum to string: echo Role::Admin->value; // admin // Restore enum from string: $role = Role::from('editor'); echo $role->name; // Editor // Safe restoration (nullable): $role = Role::tryFrom('unknown'); var_dump($role); // NULL
Enumeration Methods
Enums can have methods, just like classes. This allows encapsulating logic directly in the enum itself.
enum TrafficLight { case Red; case Yellow; case Green; public function action(): string { return match($this) { self::Red => 'Stop', self::Yellow => 'Slow down', self::Green => 'Go', }; } } echo TrafficLight::Green->action(); // Go
This makes enums more than just constants - they can contain behavior.
Enumeration Static Methods
Static methods can also be defined inside enums, useful for custom lookup or factories.
enum Priority: int { case Low = 1; case Medium = 2; case High = 3; public static function fromNumber(int $num): ?self { return match($num) { 1 => self::Low, 2 => self::Medium, 3 => self::High, default => null, }; } } echo Priority::fromNumber(2)->name; // Medium
Static methods help convert external data into enums in a safe and clean way.
Enumeration Constants
Enums can declare constants, just like classes. This allows grouping helper constants with the enum definition.
enum Direction { case North; case East; case South; case West; public const DEFAULT = self::North; } echo Direction::DEFAULT->name; // North
This avoids scattering "default" values across the codebase.
Enumerations and Traits
Enums can use traits, enabling code reuse across multiple enums.
trait Printable { public function printName(): void { echo "Enum case: " . $this->name; } } enum Currency: string { use Printable; case USD = 'usd'; case EUR = 'eur'; case GBP = 'gbp'; } Currency::EUR->printName(); // Enum case: EUR
This is a powerful way to keep enums DRY and consistent.
Enum Values in Constant Expressions
Enums can be used in constant expressions. This means you can define constants that reference enum cases.
enum Size { case Small; case Medium; case Large; } class Box { public const DEFAULT_SIZE = Size::Medium; } echo Box::DEFAULT_SIZE->name; // Medium
This makes enums fully usable in configuration-like contexts.
Differences from Objects
While enums are internally implemented as objects, they are singletons per case. That means:
$draft1 = Status::Draft; $draft2 = Status::Draft; var_dump($draft1 === $draft2); // true (same singleton)
Enums behave more like "value types" than regular objects.
Value Listing
PHP provides built-in methods to list all enum cases.
foreach (Status::cases() as $case) { echo $case->name; } // Draft // Published // Archived
This is extremely handy for building dropdowns, validation, or iterating over all possible states.
Serialization
Enum cases serialize as their fully qualified name.
$serialized = serialize(Status::Published); echo $serialized; $unserialized = unserialize($serialized); var_dump($unserialized === Status::Published); // true
You don't need to write custom serialization logic - PHP handles it.
Why Enums Aren't Extendable
Unlike classes, enums cannot be extended. This is by design:
If you need composition, use traits or interfaces, but never inheritance.
Real-World Use Cases: User Roles and Permissions
Instead of string constants ('admin', 'editor'), use a backed enum:
enum UserRole: string { case Admin = 'admin'; case Editor = 'editor'; case Viewer = 'viewer'; public function canEdit(): bool { return match($this) { self::Admin, self::Editor => true, self::Viewer => false, }; } } $userRole = UserRole::Editor; var_dump($userRole->canEdit()); // bool(true)
This eliminates mistakes like if ($role === 'admn').
Real-World Use Cases: Order Status in E-commerce
An order typically flows through specific states. Enums model these perfectly.
enum OrderStatus { case Pending; case Paid; case Shipped; case Delivered; case Cancelled; public function isFinal(): bool { return match($this) { self::Delivered, self::Cancelled => true, default => false, }; } } $order = OrderStatus::Shipped; var_dump($order->isFinal()); // bool(false)
This ensures no invalid state (like Refunded) sneaks in without explicit definition.
Real-World Use Cases: HTTP Methods
Enums are great for protocol-related constants.
enum HttpMethod: string { case GET = 'GET'; case POST = 'POST'; case PUT = 'PUT'; case DELETE = 'DELETE'; } function sendRequest(HttpMethod $method, string $url): void { echo "Sending {$method->value} request to $url"; } sendRequest(HttpMethod::POST, "https://example.com/api"); // Sending POST request to https://example.com/api
Real-World Use Cases: Form Field Types
Useful for frontend-backend consistency.
enum FieldType: string { case Text = 'text'; case Email = 'email'; case Password = 'password'; case Checkbox = 'checkbox'; } function renderField(FieldType $type, string $name): string { return "<input type='{$type->value}' name='{$name}'>"; } echo renderField(FieldType::Email, 'user_email'); // <input type="email" name="user_email">
Real-World Use Cases: Feature Flags
Enums work well for feature toggles.
enum Feature: string { case DarkMode = 'dark_mode'; case BetaAccess = 'beta_access'; case Notifications = 'notifications'; } $enabledFeatures = [Feature::DarkMode, Feature::Notifications]; if (in_array(Feature::DarkMode, $enabledFeatures, true)) { echo "Dark mode enabled!"; }
Source: Orkhan Alishov's notes