Native enumerations in PHP
Enumerations, or Enums allow a developer to define a custom type that is limited to one of a discrete number of possible values. That can be especially helpful when defining a domain model, as it enables making invalid states unrepresentable. In other words, enums are a restricting layer on top of classes and class constants, intended to provide a way to define a closed set of possible values for a type.
Enums appear in many languages with a variety of different features. In PHP, enums are a special kind of object. The enum itself is a class and its possible cases are all single-instance objects of that class. That means enum cases are valid objects and may be used anywhere an object may be used, including type checks.
Basic enumeration
Enums are similar to classes and share the same namespaces as classes, interfaces, and traits. They are also autoloadable the same way. Let's see a basic enum and how we can define days of week using it:
enum DayOfWeek
{
case MONDAY;
case TUESDAY;
case WEDNESDAY;
case THURSDAY;
case FRIDAY;
case SATURDAY;
case SUNDAY;
}
Case keyword is used to delineate the specific values the enum accepts. A single value is referenced in the same way as class constant:
$sunday = DayOfWeek::SUNDAY;
All cases have a read-only property, name, that is the case-sensitive name of the case itself.
echo DayOfWeek::SUNDAY->name; // outputs "SUNDAY"
Enums are fully compatible with the type system. For that reason you can force parameters to only accept a value defined in an enum.
function isTodaySunday(DayOfWeek $dayOfWeek)
{
return $dayOfWeek === DayOfWeek::SUNDAY;
}
$result = isTodaySunday(DayOfWeek::SATURDAY); // false
$result = isTodaySunday(DayOfWeek::SUNDAY); // true
Backed enumeration
By default, enumerated cases have no scalar equivalent. They are simply singleton objects. To define a scalar equivalent for an enumeration, the syntax is as follows:
enum BackedDayOfWeek: string
{
case MONDAY = 'Monday';
case TUESDAY = 'Tuesday';
case WEDNESDAY = 'Wednesday';
case THURSDAY = 'Thursday';
case FRIDAY = 'Friday';
case SATURDAY = 'Saturday';
case SUNDAY = 'Sunday';
}
A case that has a scalar equivalent is called a backed case. An enum that contains all backed cases is called a backed enum. A backed enum may be backed by types of int or string, and a given enumeration supports only a single type at a time. Besides, backed enum must have a unique scalar equivalent defined explicitly for each case.
Backed cases have an additional read-only property, value, which is the value specified in the definition.
echo BackedDayOfWeek::SUNDAY->name; // outputs "SUNDAY"
echo BackedDayOfWeek::SUNDAY->value; // outputs "Sunday"
Backed enums implement an internal BackedEnum interface, which exposes two additional methods:
public static from(int|string $value): static
public static tryFrom(int|string $value): ?static
Both of them take a scalar and return the corresponding enum case. The difference comes when the provided value is not valid:
from: If case is not found, it will throw a ValueError.
tryFrom: If case is not found, it will return null.
Customizing enumeration
As said before, enums are similar to classes may therefore include constants, contain methods and implement interfaces. Enumerations may also have static methods. The use for static methods on the enumeration itself is primarily for alternative constructors.
enum BackedDayOfWeek: string
{
case MONDAY = 'Monday';
case TUESDAY = 'Tuesday';
case WEDNESDAY = 'Wednesday';
case THURSDAY = 'Thursday';
case FRIDAY = 'Friday';
case SATURDAY = 'Saturday';
case SUNDAY = 'Sunday';
public static function fromInteger(int $day): ?self
{
return match($day) {
1 => self::MONDAY,
2 => self::TUESDAY,
3 => self::WEDNESDAY,
4 => self::THURSDAY,
5 => self::FRIDAY,
6 => self::SATURDAY,
7 => self::SUNDAY,
default => null,
};
}
}
Above static method will generate enum object based on the day integer value:
echo BackedDayOfWeek::fromDayNumber(7)->name; // outputs "SUNDAY"
echo BackedDayOfWeek::fromDayNumber(7)->value; // outputs "Sunday"
echo BackedDayOfWeek::fromDayNumber(8); // default value, null
Finally, enums include a static method cases() that return packed array of all defined cases in the order of declaration.
$cases = BackedDayOfWeek::cases();
It will produce and assign to cases variable the list of allowed values:
[
BackedDayOfWeek::MONDAY,
BackedDayOfWeek::TUESDAY,
BackedDayOfWeek::WEDNESDAY,
BackedDayOfWeek::THURSDAY,
BackedDayOfWeek::FRIDAY,
BackedDayOfWeek::SATURDAY,
BackedDayOfWeek::SUNDAY,
]
Very useful if we need to iterate available enum values in our application.
Conclusion
Before PHP 8.1, the best way to get enums to a code base was to use an abstract class and manually verify the provided value. Concrete implementations were extending the abstract class defining allowed values as constants. With native enums, we don't need manual validations, third-party dependencies or workarounds. Everything can be done using native PHP in very easy and straightforward way.
0 Comments