If you have prior programming experience with other languages, you’re likely familiar with Enums. Enums are a common construct allowing you to group constants together. Depending on the language, these constants can be integers, strings, or even objects.
Regrettably, PHP did not include this feature until recently. If you’ve been simulating enums in your code or using third-party libraries, you’ll be pleased to know that PHP 8.1 now includes this essential construct as a built-in feature.
The Old Way
Suppose you are writing a state machine and you need to define a variable for the state. Here’s how you might have done it before. This approach is based on constants which are public static member properties then uses the group method that returns an iterator to all defined constants. The type of the constants can be an integer, string, or object. Here is the skeleton of our enum:
Int Enum In PHP
In the int Enum approach, constants are of type int:
<?php
class MyState {
const state1 = 1;
const state2 = 2;
const state3 = 3;
public static function group() {
yield MyState::state1;
yield MyState::state2;
yield MyState::state3;
}
}
// Usage - not we use int for the enum
function doSomething(int $state) {
if ( $state = MyState::state1 ) {
// ...
}
foreach( MyState::group() as $state) {
// ....
}
}
doSometing(MyState::state1)
However, this method doesn’t prevent you from assigning values to $state that aren’t defined constants. This lack of inherent protection can potentially lead to errors and inconsistencies in your code. An incorrect state assignment may result in unexpected program behavior.
Debugging can be challenging as you only see the integer value and need to refer back to the MyState code to understand what it represents.
String Enum In PHP
In the String approach, constants are of type string:
<?php
class MyState {
const state1 = "state1";
const state2 = "state2";
const state3 = "state3";
public static function group() {
yield MyState::state1;
yield MyState::state2;
yield MyState::state3;
}
}
// Usage - note we use string for the enum
function doSomething(string $state) {
if ( $state === MyState::state1 ) {
// ...
}
// ...
}
doSometing(MyState::state1)
The String Enum makes debugging more straightforward. However, it doesn’t prevent you from using any string as a state, and it’s more memory-intensive than integer Enums. Thankfully, with PHP 8.1, these issues can be avoided by using built-in Enumerations.
Object as Enum In PHP
In the Object approach, constants are instances of a class:
<?php
class MyState {
public static MyState $state1;
public static MyState $state2;
public static MyState $state3;
public static function group() {
yield self::$state1;
yield self::$state2;
yield self::$state3;
}
public static function init() {
self::$state1 = new MyState('state1', false);
self::$state2 = new MyState('state2', false);
self::$state3 = new MyState('state3', true);
}
private function __construct(string $name, bool $isFinal) {
$this->name = $name;
$this->isFinal = $isFinal;
}
public function __toString() {
return sprintf('[%s] isFinal=%s', $this->name, $this->isFinal );
}
public string $name;
public bool $isFinal;
}
MyState::init();
// Usage
function doSomething(string $state) {
if ( $state = MyState::$state1 ) {
// ...
}
// ...
}
doSometing(MyState::$state1);
The object approach addresses the pitfalls of the last two methods. First, it prevents the use of any MyState except the predefined values as the class constructor is private and can only be used in MyState members. Second, you can easily understand the meaning of the state while debugging by examining the value of the name property or using strval, which returns the output of the __toString method.
Furthermore, you can add metadata to the state a use it in your code by adding properties to the instance of the class. In this case, we added whether the state is final. you can enrich it with methods ( i.e print, format and so on)
Using 3rd party libraries
Those approach are verbose and requires a non-standard way of defining and using enums. It also introduces additional complexity into your codebase.
Instead of writing your own version of Enum, Several libraries took the above ideas, extend them and provide own enum implementation – I will mention here the https://github.com/myclabs/php-enum by myclabs and https://github.com/spatie/enum by Spatie.
Here how it can be done with https://github.com/spatie/enum by Spatie
<?php
use Spatie\Enum\Enum;
/**
* @method static self View()
* @method static self Edit()
*/
class StatusEnum extends Spatie\Enum\Enum {
}
doAction(Action $action) {
...
}
doAction( Action::View() );
Here how it can be done https://github.com/myclabs/php-enum
<?php
/** Action enum */
final class Action extends MyCLabs\Enum\Enum {
private const View = 'View';
private const Edit = 'Edit';
}
doAction(Action $action) {
// ...
}
doAction( Action::View() );
These libraries offer a more structured way and robust way of defining enums, with methods for validation, iteration, and more. I.e It does not allow to change the value of the enum, mistakenly or deliberately as the enum are accessed with methods only. However, they add an external dependency to your project and is not longer necessary with the introduction of built-in Enums in PHP 8.1.
PHP 8.1 now includes Enums, a feature that allows grouping of constants, which can be integers, strings, or objects. Prior to this, PHP developers simulated enums or used third-party libraries. The mimic methods had limitations such as potential for errors, debugging difficulties, and memory-intensiveness. Third-party libraries offered a more structured approach but added an external dependency. The introduction of built-in Enums in PHP 8.1 eliminates these issues.