PHP Enum(枚举)用法实例详解:SplEnum与自定义实现

雨后青山
雨后青山
发布时间:2026-10-13 | 4人浏览过
原创

在PHP开发中,为了规范常量使用并提升代码的可读性与安全性,常需借助枚举(Enum)。本文详细解析了PHP环境下两种主流的枚举实现方法:利用核心扩展提供的 SplEnum 类,以及通过继承实现的自定义通用枚举类。

使用 SPL 标准扩展实现枚举

PHP 内核提供了对枚举支持的基础库,即 SplEnum 类。该类位于 PHP 的 SPL(Standard PHP Library)扩展中,它允许开发者定义一个具有单一取值范围的常量集。使用此方法不需要安装额外的第三方包,但要求运行环境已启用 SPL 扩展。

第1步:定义枚举类结构

SplEnum 类的结构较为简洁,主要包含以下核心特性:

  • 需要继承自 SplType 基类。
  • 通过定义类的静态常量来设定允许的枚举值。
  • 必须指定一个 __default 常量作为默认值。
<?php
class Month extends SplEnum {
  const __default = self::January;
  const January = 1;
  const February = 2;
  const March = 3;
  const April = 4;
  const May = 5;
  const June = 6;
  const July = 7;
  const August = 8;
  const September = 9;
  const October = 10;
  const November = 11;
  const December = 12;
}
?>

第2步:实例化与类型安全测试

在使用时,直接传入定义的常量进行初始化。若传入的值不在预定义的常量列表中,或者类型不匹配(视构造函数参数而定),将抛出 UnexpectedValueException 异常。

<?php
// 正确实例化,输出为对应的整数值
echo new Month(Month::June) . PHP_EOL;

try {
  // 错误尝试:传入无效值 13
  new Month(13);
} catch (UnexpectedValueException $uve) {
  // 捕获到类型或值不符的异常
  echo $uve->getMessage() . PHP_EOL;
}
?>

该段代码的预期输出结果为:

6
Value not a const in enum Month

通过抽象类构建通用自定义枚举

由于 SplEnum 的功能相对单一,在实际工程中,我们往往需要一个更具扩展性的枚举系统。可以通过定义一个抽象基类 Enum,让业务枚举类继承它,从而实现丰富的功能,如获取常量列表、深度比较等。

第1步:编写抽象基类 Enum

自定义的 Enum 抽象类负责统一处理验证逻辑。它利用反射(Reflection)技术在构造时动态读取子类中定义的所有常量。

<?php
abstract class Enum {
  const __default = null;
  private $value;
  private $strict;
  private static $constants = array();

  /**
   * 获取枚举的所有可用常量值
   */
  public function getConstList($includeDefault = false) {
    $class = get_class($this);
    if (!array_key_exists($class, self::$constants)) {
      self::populateConstants();
    }
    return $includeDefault ? array_merge(self::$constants[__CLASS_], array(
      "__default" => self::__default
    )) : self::$constants[__CLASS_];
  }

  /**
   * 构造函数:接收初始值,并进行严格的合法性校验
   */
  public function __construct($initialValue = null, $strict = true) {
    $class = get_class($this);
    if (!array_key_exists($class, self::$constants)) {
      self::populateConstants();
    }
    if ($initialValue === null) {
      $initialValue = self::$constants[$class]["__default"];
    }
    $temp = self::$constants[$class];
    // 校验传入的值是否存在于该类定义的常量中
    if (!in_array($initialValue, $temp, $strict)) {
      throw new UnexpectedValueException("Value is not in enum " . $class);
    }
    $this->value = $initialValue;
    $this->strict = $strict;
  }

  private function populateConstants() {
    $class = get_class($this);
    $r = new ReflectionClass($class);
    $constants = $r->getConstants();
    self::$constants = array(
      $class => $constants
    );
  }

  /**
   * 魔术方法:将枚举对象转换为字符串时返回其值
   */
  public function __toString() {
    return (string) $this->value;
  }

  /**
   * 判断两个枚举对象是否相等
   */
  public function equals($object) {
    if (!($object instanceof Enum)) {
      return false;
    }
    return $this->strict ? ($this->value === $object->value)
      : ($this->value == $object->value);
  }
}
?>

第2步:定义具体的业务枚举类

基于抽象基类,可以创建非常轻量的业务枚举。只需定义常量及默认值即可。

<?php
class MyEnum extends Enum {
  const HI = "Hi";
  const BY = "By";
  const NUMBER = 1;
  const __default = self::BY;
}
?>

第3步:实操应用与结果对照

接下来演示如何创建对象、获取默认值以及触发校验异常:

<?php
// 1. 手动指定有效常量
var_dump(new MyEnum(MyEnum::HI));

// 2. 再次指定另一个常量
var_dump(new MyEnum(MyEnum::BY));

// 3. 未传参,自动使用 __default (BY)
var_dump(new MyEnum());

// 4. 尝试传入非法值,触发异常
try {
  new MyEnum("I don't exist");
} catch (UnexpectedValueException $e) {
  var_dump($e->getMessage());
}
?>

上述操作的控制台输出结果如下:

object(MyEnum)#1 (2) {
 ["value":"Enum":private]=>
 string(2) "Hi"
 ["strict":"Enum":private]=>
 bool(true)
}
object(MyEnum)#1 (2) {
 ["value":"Enum":private]=>
 string(2) "By"
 ["strict":"Enum":private]=>
 bool(true)
}
object(MyEnum)#1 (2) {
 ["value":"Enum":private]=>
 string(2) "By"
 ["strict":"Enum":private]=>
 bool(true)
}
string(27) "Value is not in enum MyEnum"

总结

SplEnum 适用于简单且对性能敏感的底层场景,而自定义的抽象 Enum 则更适合复杂的业务模型开发。两者都实现了值的合法性约束,从而避免了魔法值(Magic Value)带来的隐患。以上就是PHP中Enum(枚举)用法的详细内容,更多关于PHP枚举设计的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表