Enums in PHP 8.1

Enums in PHP 8.1

Since PHP 8.1, we can use enumerations also called enums. Enum defines a custom type that contains a fixed set of related values.

Enum is declared using the enum keyword followed by the name. Each value is declared with the case keyword.

UserStatus.php

<?php

enum UserStatus
{
    case ACTIVE;
    case NOT_ACTIVE;
    case PENDING;
    case DELETED;
}

Enums can be used anywhere where types are accepted: parameters, return values, and properties.

User.php

<?php

class User
{
    private UserStatus $status;

    public function getStatus(): UserStatus
    {
        return $this->status;
    }

    public function setStatus(UserStatus $status): void
    {
        $this->status = $status;
    }
}

Only allowable values of enum type can be passed to a function or class method that accepts a parameter with enum type.

<?php

require_once 'UserStatus.php';
require_once 'User.php';

$user = new User();
$user->setStatus(UserStatus::ACTIVE);

Zero or more cases

Enum may have any number of case definitions, from zero to unlimited. Enum without any case is valid, but not very useful.

<?php

enum UserStatus
{
}

Case-sensitive name of the case

The name of the case is case-sensitive. The following example is valid but not recommended:

<?php

enum UserStatus
{
    case ACTIVE;
    case active;
}

Backed enums

Each case in enum may have a scalar value. Enum that contains case definitions with values is called backed enum.

<?php

enum UserStatus: string
{
    case ACTIVE = 'A';
    case NOT_ACTIVE = 'N';
    case PENDING = 'P';
    case DELETED = 'D';
}

There are some important notes for backed enums:

  • Type must be specified in enum declaration. Only string or int types are allowed. For example, the following example is wrong because float cannot be used as a type for enum values:
enum UserStatus: float // Error
{
}
  • All cases must have values. For example, the following example is wrong because NOT_ACTIVE doesn't have value:
<?php

enum UserStatus: string
{
    case ACTIVE = 'A';
    case NOT_ACTIVE; // Error
}
  • Values must be of the same type. For example, the following example is wrong because NOT_ACTIVE must have int value instead of string:
<?php

enum UserStatus: int
{
    case ACTIVE = 1;
    case NOT_ACTIVE = '2'; // Error
}
  • Values must be unique. For example, the following example is wrong because NOT_ACTIVE has value 1 which already used:
<?php

enum UserStatus: int
{
    case ACTIVE = 1;
    case NOT_ACTIVE = 1; // Error
}

Enum methods

Enum may have methods, just like class. It supports visibility modifiers such as public, protected or private.

UserStatus.php

<?php

enum UserStatus
{
    case ACTIVE;
    case NOT_ACTIVE;

    public function getLabel(): string
    {
        return match ($this) {
            self::ACTIVE => 'Active',
            self::NOT_ACTIVE => 'Not active',
        };
    }
}
<?php

require_once 'UserStatus.php';

echo UserStatus::ACTIVE->getLabel(); // Active

Static methods supported as well.

UserStatus.php

<?php

enum UserStatus
{
    case ACTIVE;
    case NOT_ACTIVE;

    public static function getLabel(self $value): string
    {
        return match ($value) {
            self::ACTIVE => 'Active',
            self::NOT_ACTIVE => 'Not active',
        };
    }
}
<?php

require_once 'UserStatus.php';

echo UserStatus::getLabel(UserStatus::ACTIVE); // Active

Enum constants

Enum may have constants, just like class. Enum constant may refer to an enum case as well.

<?php

enum UserStatus
{
    case ACTIVE;
    case NOT_ACTIVE;

    public const DEFAULT = self::ACTIVE;
    public const TOTAL_STATUSES = 2;
}

Enum properties is not allowed

Enum cannot contain properties. It is an important difference between enum and class.

<?php

enum UserStatus
{
    private string $status; // Error
}

Enum and interfaces

Just like class, enum can implement interfaces.

HasLabelInterface.php

<?php

interface HasLabelInterface
{
    public function getLabel(): string;
}

UserStatus.php

<?php

enum UserStatus implements HasLabelInterface
{
    case ACTIVE;
    case NOT_ACTIVE;

    public function getLabel(): string { return '...'; }
}

If you want to implement an interface in backed enum, after the enum name must come a type and after it implements keyword.

UserStatus.php

<?php

enum UserStatus : string implements HasLabelInterface
{
    case ACTIVE = 'A';
    case NOT_ACTIVE = 'N';

    public function getLabel(): string { return '...'; }
}

Enum name and value

Each case in enum has property name which contains the name of the case. The name property is read-only.

UserStatus.php

<?php

enum UserStatus
{
    case ACTIVE;
    case NOT_ACTIVE;
}
<?php

require_once 'UserStatus.php';

echo UserStatus::ACTIVE->name; // ACTIVE

In backed enum, each case also has property value which is read-only as well.

UserStatus.php

<?php

enum UserStatus : string
{
    case ACTIVE = 'A';
    case NOT_ACTIVE = 'N';
}
<?php

require_once 'UserStatus.php';

echo UserStatus::ACTIVE->value; // A

Enum has cases static method which allows retrieving all available cases of the enum.

<?php

require_once 'UserStatus.php';

foreach (UserStatus::cases() as $status) {
    // ACTIVE A
    // NOT_ACTIVE N
    echo $status->name.' '.$status->value.PHP_EOL;
}

Enum as array keys is not allowed

It is not possible to use enum as array keys.

UserStatus.php

<?php

enum UserStatus : string
{
    case ACTIVE = 'A';
    case NOT_ACTIVE = 'N';
}
<?php

require_once 'UserStatus.php';

$statuses = [
    UserStatus::ACTIVE => 'Active', // Error
    UserStatus::NOT_ACTIVE => 'Not active', // Error
];

Enum properties in constant expressions

Since PHP 8.2, enum properties such as name and value can be used in constant expressions. The following code is valid since PHP 8.2:

UserStatus.php

<?php

enum UserStatus : string
{
    case ACTIVE = 'A';
    case NOT_ACTIVE = 'N';

    public const LABELS = [
        self::ACTIVE->value => 'Active', // Valid
        self::NOT_ACTIVE->value => 'Not active', // Valid
    ];
}

Leave a Comment

Cancel reply

Your email address will not be published.