PHP 8.1 — Welcome Enumeration

Eugenio Carocci
Geek Culture
Published in
4 min readJul 4, 2021
Photo by SpaceX on Unsplash

This is a story of a software developer, a very normal one, that used to write code in Java, and enjoyed working with enumeration to define a closed set of data.

That same developer one day started to work with PHP and realized that they were missing, which grew quite a sadness in him :(

But, finally, that developer is no longer sad since version 8.1 of PHP added enumeration to the language.

How does it work?

Enums are similar to classes, it defines a new type, which has a fixed, limited number of possible allowed values.

This declaration creates a new simple enumerated type UserState, which has four valid values: UserState::Registered, UserState::Activated, UserState::Disabled, and UserState::Blocked.

A function may be type-checked against an enumerated type, in which case only values of that type may be passed.

By default, cases are not intrinsically backed by a scalar value. That is, User::Registered is not equal to 0. Instead, each case is backed by a singleton object of that name.

This type of case, with no related data, is called a Pure Case and an Enum that contains only Pure Cases is called a Pure Enum.

All Cases have a read-only property, name, that is the case-sensitive name of the case itself. That is largely an implementation artifact, but may also be used for debugging purposes.

Backed Enum

By default, Enumerated Cases have no scalar equivalent. However, there are cases where an Enumerated Case needs to be able to round-trip to a database or similar datastore, so having a built-in scalar (and thus trivially serializable) equivalent defined intrinsically is useful.

To define a scalar equivalent for an Enumeration, the syntax is as follows:

A case that has a scalar equivalent is called a Backed Case, as it is “Backed” by a simpler value. An Enum that contains all Backed Cases is called a “Backed Enum.” A Backed Enum may contain only Backed Cases. A Pure Enum may contain only Pure Cases.

A Backed Enum may be backed by types of int or string, and a given enumeration supports only a single type at a time. If an enumeration is marked as having a scalar equivalent, then all cases must have a unique scalar equivalent defined explicitly.

There are no auto-generated scalar equivalents (e.g., sequential integers). Value cases must be unique; two backed enum cases can’t have the same scalar equivalent.

Value Cases have an additional read-only property, value, which is the value specified in the definition.

In order to enforce the value property as read-only, a variable cannot be assigned as a reference to it.

Backed enums implement an internal BackedEnum interface, which exposes two additional methods:

  • from(int|string): self will take a scalar and return the corresponding Enum Case. If one is not found, it will throw a ValueError. This is mainly useful in cases where the input scalar is trusted and a missing enum value should be considered an application-stopping error.
  • tryFrom(int|string): ?self will take a scalar and return the corresponding Enum Case. If one is not found, it will return null. This is mainly useful in cases where the input scalar is untrusted and the caller wants to implement their own error handling or default-value logic.

Methods

Enums may contain methods and may implement interfaces. If an Enum implements an interface, then any type check for that interface will also accept all cases of that Enum.

Methods may be public, private, or protected, although in practice private and protected are equivalent as inheritance is not allowed.

Static Methods

Enumerations may also have static methods. The use for static methods on the enumeration itself is primarily for alternative constructors:

Static methods may be public, private, or protected, although in practice private and protected are equivalent as inheritance is not allowed.

Value listing

Both Pure Enums and Backed Enums implement an internal interface named UnitEnum. UnitEnum includes a static method cases()that returns a packed array of all defined Cases in the order of declaration.

Final Toughts

I hope that this article helped some of you figuring out the potential of enumerations, I think that this has been a great addition to the language and I’m curious to see how this new construct will be used to the libraries that each one of us use each day to create great products!

Thanks for reading and feel free to add comments about your experiences :)

--

--