Backend Development

Learn About PHP 8 Enumerations

Learn About PHP 8 Enumerations

In PHP 8.1 an important feature have been added which is the Enums or Enumerations. In this article we will check this feature in detail.

 

 

You may have seen the Enum structure before in languages such as C++, Java, etc. Enum abbreviated as Enumeration is an special structure with a fixed number of possible values. In PHP 8.1 release comes the Enum feature which will enhance the language capabilities for dealing more complex problems.

To express the enum type let’s check this code:

function printPostStatusMessage(int $status)
{
    if($status === 1) {
        echo "post is draft";
    } else if($status === 2) {
        echo "post is published";
    } else {
        echo "post is expired";
    }
}

Suppose we have a CMS system where we have this function. In this function we display a status message of the Post depending on the parameter $status. $status is an integer which have one of these values (1, 2, 3).

As you see the issue here of the $status parameter is hard-coded with (1, 2, 3) values. From clean coding standards this is code a smile that need to be resolved

So to make our function a bit more clean prior to PHP 8.1 we tend to create a class of constants like so:

class PostStatus
{
    const PUBLISHED = 2;
    const DRAFT = 1;
    const EXPIRED = 3;
}

And now update the function to check for the class constants:

function printPostStatus(int $status)
{
    if($status === PostStatus::DRAFT) {
        echo "post is draft";
    } else if($status === PostStatus::PUBLISHED) {
        echo "post is published";
    } else {
        echo "post is expired";
    }
}

The function now is some kind clean.

To standardize this process in PHP 8.1 whenever we encounter a value that can be a set of fixed values we can use the enum instead of class.

 

Enum Terminology:

enum <Enum Name> 
{
    case value1;
    case value2;
    ...
    ...
}

Enums declared in the same way as class/trait/interface using the enum keyword followed by the <enum name> and then a set of parenthesis and a list of case statements for each value of the enum.

So From PHP 8.1 enum is a reserved keyword and you can use it only for declaring enums.

 

Enum Types

There are two types of enums (UnitEnum, BackedEnum)

  • UnitEnum: This type of enum internally implements UnitEnum interface. an example of this enum:
enum AdStatus
{
    case Active;
    case Unactive;
    case Expired;
}

In the UnitEnum we don’t have to declare a type for the enum and also each enum value doesn’t have a scalar value.

To access any enum value we can use the :: operator as in class constant:

var_dump(AdStatus::Expired);

// enum(AdStatus::Expired)

When referencing any enum value like AdStatus::Expired the return will be of type enum, for this i used var_dump(). however to get the enum string value there is ->name property on the enum value:

print(AdStatus::Expired->name);   // Expired

print(AdStatus::Unactive->name);  // Unactive

To get an array of case statements of the UnitEnum we can use the cases() static method. This method declared on the UnitEnum interface:

var_dump(AdStatus::cases());
array(3) {
  [0]=>
  enum(AdStatus::Active)
  [1]=>
  enum(AdStatus::Unactive)
  [2]=>
  enum(AdStatus::Expired)
}

 

  • BackedEnum: This type of enum internally implements BackedEnum interface. This enum must have a type either (string or int) and each enum value must have a scalar value according to the enum type.

let’s use the PostStatus class example above:

enum PostStatus : int
{
    case Published = 2;
    case Draft = 1;
    case Expired = 3;
}

As you see the PostStatus enum is of type int declared after the enum name and each enum have a scalar value like:

case Published = 2;

Like the UnitEnum you can access any enum case using :: operator:

var_dump(PostStatus::Published);

To get the enum name string value there is ->name property

echo(PostStatus::Published->name);   // Published

In addition to that for BackedEnum we can retrieve the enum value using the ->value property of any case:

echo(PostStatus::Published->value);   // 2

In BackedEnum interface also there are two methods from(), tryFrom() which accepts a scalar value and returns the enum instance.

BackedEnum::from() terminology

public static BackedEnum::from(int|string $value): static

For example:

var_dump(PostStatus::from(3));   // enum(PostStatus::Expired)
var_dump(PostStatus::tryFrom(2));   // enum(PostStatus::Published)

The difference between from() and tryFrom() methods is the handling of return value in case no matching case found.

from() throw a ValueError if no matching case:

var_dump(PostStatus::from(5));

// PHP Fatal error:  Uncaught ValueError: 5 is not a valid backing value for enum PostStatus

tryFrom() return null if no matching case:

var_dump(PostStatus::tryFrom(5));   // NULL

 

From the above here are the rules for the backed enum:

  • Scalar type must be specified in the Enum declaration. Only string or int is allowed.
enum UserRoles : string
{
    case Admin = 'admin';
    case User = 'user';
}
enum Foo : object    // not allowed
{
    
}

PHP Fatal error:  Enum backing type must be int or string, object given
  • All cases must have values.
enum UserRoles : string
{
    case Admin = 'admin';
    case User;               // must have value
}

PHP Fatal error:  Case User of backed enum UserRoles must have a value
  • Not mixing scalar types:
enum UserRoles : string
{
    case Admin = 'admin';
    case User = 2;             // not allowed must be string as the backing type is string
}
  • Unique cases:
enum UserRoles : string
{
    case Admin = 'admin';
    case Admin = 'user';   // case must be unique
}

PHP Fatal error:  Cannot redefine class constant UserRoles::Admin
  • Also unique values:
enum UserRoles : string
{
    case Admin = 'admin';
    case User = 'admin';      // must be different value
}

 

Checking Enum Type

As enums similar to classes we can use any of the functions that detect class type with enums such as gettype(), is_a(), is_object(), get_class(), get_debug_type(available in PHP 8.0) and instanceof operator:

enum PostStatus : int
{
    case Published = 2;
    case Draft = 1;
    case Expired = 3;
}


var_dump(is_object(PostStatus::Draft));           // true
var_dump(get_class(PostStatus::Draft));           // string(10) "PostStatus"
var_dump(get_debug_type(PostStatus::Draft));      // string(10) "PostStatus"
var_dump(is_a(PostStatus::Draft, PostStatus::class));   // true

var_dump(PostStatus::Published instanceof PostStatus);   // true

 

Now we can refactor our previous printPostStatus function to accept PostStatus enum like so:

enum PostStatus : int
{
    case Published = 2;
    case Draft = 1;
    case Expired = 3;
}

function printPostStatus(PostStatus $status)
{
    if($status === PostStatus::Draft) {
        echo "post is draft";
    } else if($status === PostStatus::Published) {
        echo "post is published";
    } else {
        echo "post is expired";
    }
}

printPostStatus(PostStatus::Expired);    // post is expired

 

Class Magic Constants With Enums

All class based magic constants supported with Enums too like:

  • __CLASS__ magic constant that refers to the name of the Enum from within the Enum.
  • __FUNCTION__ in Enum method context.
  • __METHOD__ in Enum method context.
  • ::class constant that refers to the name of the Enum itself.

 

5 1 vote
Article Rating

What's your reaction?

Excited
1
Happy
0
Not Sure
0
Confused
0

You may also like

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments