Enumerations(Enums) in PHP 8.1

PHP 8.1 added a bunch of new features and Enumerations are by far our favorite new feature. In this article, we’ll do a brief overview of why you should be excited too.

Why Use Enumerations?

Enumerations or enums for short allow us to define a new structure much like a class but that can only hold a set of allowed values.

In the past, this was handled by using public constants in a class.

class PublishedState {
    public const NotPublished = 0;
    public const Scheduled = 1;
    public const Published = 2;
    public const Deleted = 3;
}

When we need to set a variable to one of these states we’ll do the following.

$value = PublishedState::Published;

The problem with this method is that because we’re just setting a variable to a value we could, and did, do something like the following.

$value = 2 * PublishedState::Published;

This is a valid expression but causes us to have an invalid value of 4 in the $value variable.

Using enums we’ll define our values like the following.

enum PublishedState {
    case NotPublished;
    case Scheduled;
    case Published;
    case Deleted;
}

We can still set our values like so.

$value = PublishedState::Published;

var_dump($value); // enum(PublishedState::Published)

But it’s impossible to set it to an invalid case.

$value = 2 * PublishedState::Published;

// PHP Fatal error:  Uncaught TypeError: Unsupported operand types: PublishedState * int in Standard input code:8

It also has the added bonus of allowing us to pass these as a parameter to a function so we’re always sending valid data.

function aTestFunction(PublishedState $value): bool
{
    //something
}

Enum to Scalar

So the downside to enumerations over plain integers is that by default they have no scalar equivalent. This makes it a challenge to save the values to a database or datastore.

Thankfully we can define what the scalar equivalent is for each value. We might get these from our database or come up with them on our own. This is called a Backed Enum because it’s backed up by a value.

enum PublishedState:int {
    case NotPublished = 0;
    case Scheduled = 1;
    case Published = 2;
    case Deleted = 3;
}

We can also use string values instead of integers.

enum PublishedState:string {
    case NotPublished = 'A';
    case Scheduled = 'B';
    case Published = 'C';
    case Deleted = 'D';
}

When we’re using a Backed Enum we must create a unique scalar equivalent for ALL values.

enum PublishedState:string {
    case NotPublished = 'A';
    case Scheduled = 'A';
    case Published = 'C';
    case Deleted = 'D';
}
PHP Fatal error:  Duplicate value in enum PublishedState for cases NotPublished and Scheduled in /var/www/test.php on line 4
enum PublishedState:string {
    case NotPublished = 'A';
    case Scheduled;
    case Published = 'C';
    case Deleted = 'D';
}
PHP Fatal error:  Case Scheduled of backed enum PublishedState must have a value in /var/www/test.php on line 4

Now that we’ve defined our scalar equivalents we can grab it by using the value property.

echo PublishedState::Scheduled->value, PHP_EOL;
$state = PublishedState::Scheduled;
echo $state->value, PHP_EOL;
1
1

Scalar to Enum

When we need to get from the scalar value back to the enum we can use the from() method. This method takes the string or integer value and converts it back to the enum.

$state = PublishedState::from(1);
var_dump($state); // enum(PublishedState::Scheduled)

If a value is passed that doesn’t match the “allowed” values there will be an error.

$state = PublishedState::from(100);
PHP Fatal error:  Uncaught ValueError: 100 is not a valid backing value for enum "PublishedState" in /var/www/test.php:10

In order to make this safer PHP 8.1 gives us a tryFrom() function that will return null instead of throwing an error.

$state = PublishedState::tryFrom(100);
var_dump($state); // null

Methods and Interfaces

Enums may contain methods and are also able to implement interfaces.

enum PublishedState:int {
    case NotPublished = 0;
    case Scheduled = 1;
    case Published = 2;
    case Deleted = 3;

    public function isDeleted(): bool {
        return $this == PublishedState::Deleted;
    }
}
interface Visible {
    public function visible(): bool;
}

enum PublishedState:int implements Visible {
    case NotPublished = 0;
    case Scheduled = 1;
    case Published = 2;
    case Deleted = 3;

    public function visible(): bool {
        if ($this == PublishedState::Published) {
            return true;
        }

        return false;
    }
}

Helper Functions

An undocumented benefit of enums is the ability to create a class that can’t be initialized which makes it a good spot to put helper functions.

enum StringHelper {
    public static function slugGenerator(string $title): string {
        // something
    }
}

What You Need To Know

  • Enums are being added in PHP 8.1
  • They provide the ability to have a specific set of values
  • This gives us better data integrity