Introduction
Enumerations, or enums, are a powerful tool for creating type-safe and readable code. They define a set of named constants, restricting a variable to a specific set of values. While PHP didn’t natively support enums for a long time, several techniques can be used to achieve similar functionality. Since PHP 8.1, native enums are available. However, understanding the workarounds used in earlier versions provides valuable insight and can still be useful in certain scenarios. This tutorial will cover these approaches, starting with older methods and finishing with the modern, native enum feature.
The Need for Enumerations
Consider a scenario where you need to represent the days of the week in your code. Without enums, you might use integer constants:
const SUNDAY = 0;
const MONDAY = 1;
const TUESDAY = 2;
// ... and so on
While this works, it lacks clarity and type safety. It’s easy to accidentally assign an invalid integer value to a variable representing a day of the week. Enums solve this by providing a defined set of possible values, improving code readability and reducing the risk of errors.
Emulating Enums with Constants
The simplest approach is to use constant values, as shown in the example above. However, as your application grows, managing numerous constants can become cumbersome and lead to namespace collisions if you’re not careful. Additionally, IDE auto-completion isn’t always as effective with simple constants.
Abstract Classes for Enhanced Control
A more robust solution involves using abstract classes. This approach provides a basic structure for defining an enum-like type and can be extended to include validation logic.
abstract class DaysOfWeek
{
const SUNDAY = 0;
const MONDAY = 1;
const TUESDAY = 2;
const WEDNESDAY = 3;
const THURSDAY = 4;
const FRIDAY = 5;
const SATURDAY = 6;
}
$today = DaysOfWeek::MONDAY;
echo $today; // Output: 1
While this improves organization, it still lacks validation and doesn’t prevent assigning arbitrary integer values.
Implementing Validation with Reflection
To address the validation issue, you can leverage PHP’s Reflection API. Reflection allows you to inspect classes and their constants at runtime. This enables you to create methods that verify whether a given value is a valid enum member.
abstract class BasicEnum {
private static $constCacheArray = NULL;
private static function getConstants() {
if (self::$constCacheArray == NULL) {
self::$constCacheArray = [];
}
$calledClass = get_called_class();
if (!array_key_exists($calledClass, self::$constCacheArray)) {
$reflect = new ReflectionClass($calledClass);
self::$constCacheArray[$calledClass] = $reflect->getConstants();
}
return self::$constCacheArray[$calledClass];
}
public static function isValidName($name, $strict = false) {
$constants = self::getConstants();
if ($strict) {
return array_key_exists($name, $constants);
}
$keys = array_map('strtolower', array_keys($constants));
return in_array(strtolower($name), $keys);
}
public static function isValidValue($value, $strict = true) {
$values = array_values(self::getConstants());
return in_array($value, $values, $strict);
}
}
abstract class DaysOfWeek extends BasicEnum {
const SUNDAY = 0;
const MONDAY = 1;
const TUESDAY = 2;
const WEDNESDAY = 3;
const THURSDAY = 4;
const FRIDAY = 5;
const SATURDAY = 6;
}
echo DaysOfWeek::isValidValue(0); // true
echo DaysOfWeek::isValidValue(7); // false
In this example, the BasicEnum
class provides static methods for validating enum names and values using reflection. The DaysOfWeek
class extends BasicEnum
and defines the specific enum members. Caching the reflection results improves performance, especially when working with multiple enums.
Leveraging SplEnum
(PECL)
The PECL SplEnum
extension provides a more native-like implementation of enums in PHP. However, it’s important to note that it’s not bundled with PHP and requires separate installation. Also, as of the last update of information, a DLL for this extension might not be available.
If you can install and use the SplEnum
extension, you can create enums like this:
// Requires the SplEnum extension
// Requires installation via PECL
class MyEnum extends SplEnum {}
MyEnum::create("VALUE1");
MyEnum::create("VALUE2");
echo MyEnum::VALUE1->value; // Output: VALUE1
Native Enums in PHP 8.1 and Later
PHP 8.1 introduced native enum support, providing a clean and efficient way to define and use enums.
enum DaysOfWeek: int {
case SUNDAY = 0;
case MONDAY = 1;
case TUESDAY = 2;
// ... and so on
}
$today = DaysOfWeek::MONDAY;
echo $today->value; // Output: 1
echo $today->name; // Output: Monday
Native enums offer several advantages:
- Type Safety: The compiler enforces that only valid enum values can be assigned.
- Readability: Enums improve code clarity by using meaningful names instead of magic numbers.
- Performance: Native enums are generally more efficient than emulated solutions.
- IDE Support: Modern IDEs provide excellent auto-completion and type checking for native enums.
Choosing the Right Approach
- PHP 8.1 or later: Use native enums for the best performance, type safety, and readability.
- Older PHP versions (without SplEnum): The abstract class with reflection approach is a good balance between functionality and complexity.
- If you can install the PECL SplEnum extension: Consider it as an alternative, but be aware of potential installation and availability issues.