An enum (short for enumeration) is a type with a fixed set of possible values. Use it when something can only be one of a few named options: a status, a role, a suit of cards.
Enums arrived in PHP 8.1.
<?php
enum Status
{
case Pending;
case Paid;
case Cancelled;
}
$status = Status::Paid;
var_dump($status === Status::Paid);
echo $status->name;
bool(true)
Paid
Each case is a singleton object. Status::Paid is always the same value. ->name gives the case name as text.
Why not constants or strings?
// Strings: any typo or value is accepted
function ship(string $status) { ... }
ship("piad"); // no error, a silent bug
<?php
enum Status { case Pending; case Paid; }
function ship(Status $status): string
{
return "Shipping for " . $status->name;
}
echo ship(Status::Paid) . "\n";
try {
echo ship("paid"); // a string is not a Status
} catch (TypeError $e) {
echo "TypeError";
}
Shipping for Paid
TypeError
An enum is a type. A function that asks for a Status accepts only the cases of Status.
All cases
<?php
enum Suit
{
case Hearts;
case Spades;
}
foreach (Suit::cases() as $suit) {
echo $suit->name . " ";
}
Hearts Spades
Methods in enums
An enum can have methods, so the behaviour for each option lives next to it.
<?php
enum Status
{
case Pending;
case Paid;
case Cancelled;
public function label(): string
{
return match ($this) {
self::Pending => "Waiting for payment",
self::Paid => "Payment received",
self::Cancelled => "Order cancelled",
};
}
public function isFinal(): bool
{
return $this !== self::Pending;
}
}
echo Status::Paid->label() . "\n";
var_dump(Status::Pending->isFinal());
Payment received
bool(false)
match is a perfect partner: PHP can tell you if you forget a case (it throws UnhandledMatchError).
Constants and static methods
<?php
enum Level
{
case Low;
case High;
const DEFAULT = self::Low;
public static function fromScore(int $score): self
{
return $score >= 50 ? self::High : self::Low;
}
}
echo Level::DEFAULT->name . " " . Level::fromScore(80)->name;
Low High
Interfaces
An enum can implement an interface.
<?php
interface HasLabel
{
public function label(): string;
}
enum Role implements HasLabel
{
case Admin;
case Editor;
public function label(): string
{
return ucfirst(strtolower($this->name));
}
}
echo Role::Admin->label();
Admin
What enums cannot do
- They cannot be instantiated with
new. - They cannot hold changing state (no normal properties).
- They cannot extend a class.
Comparing
Use ===. Enum cases are the same object, so Status::Paid === Status::Paid is true.
The next lesson covers backed enums, which attach a string or number to each case, so you can store them in a database.
Replace "magic strings"
"draft", "published") is a candidate for an enum.