Skip to content

枚举概览

概述

枚举(Enum)是 PHP 8.1 引入的全新语言特性,为 PHP 提供了原生的枚举类型支持。在此之前,开发者通常使用类常量来模拟枚举行为。PHP 枚举提供了类型安全、不可变性和自文档化的值集合,是现代 PHP 类型系统的重要补充。

版本要求

枚举(Enum)是 PHP 8.1+ 专有特性,无法在更低版本中使用。

基础概念

为什么需要枚举

在 PHP 8.1 之前,模拟枚举的常见方式存在以下问题:

php
<?php

declare(strict_types=1);

// PHP 8.1 之前的模拟枚举
class Status
{
    public const PENDING = 'pending';
    public const APPROVED = 'approved';
    public const REJECTED = 'rejected';
}

function setStatus(string $status): void
{
    // 无法在编译时约束参数只能是 PENDING/APPROVED/REJECTED
    // 任何字符串都能传入
    setStatus('invalid_value');  // 不会报错
}

PHP 枚举解决了这些问题:

  • 类型安全:枚举值只能是预定义的 case 之一
  • 不可变:枚举实例创建后无法修改
  • 单例:每个 case 都是唯一的单例实例
  • 自文档化:IDE 可以自动补全所有可能的值

枚举与类常量的对比

特性类常量枚举
类型安全弱(需要手动验证)强(编译时检查)
值不可变常量本身不可变枚举实例不可变
可枚举所有值需要反射内置支持
方法支持静态方法实例方法 + 静态方法
可匹配(match)需要值对比直接匹配
可携带值不支持Backed Enum 支持
单例行为不具备每个 case 是单例

语法与代码

最简单的枚举

php
<?php

declare(strict_types=1);

enum Status
{
    case Pending;
    case Approved;
    case Rejected;
}

function handleStatus(Status $status): string
{
    return match ($status) {
        Status::Pending => '等待审核',
        Status::Approved => '已通过',
        Status::Rejected => '已拒绝',
    };
}

// 类型安全 - 只能传入枚举值
echo handleStatus(Status::Approved);  // 已通过

// 以下会报 TypeError
// handleStatus('pending');    // string 不是 Status 类型
// handleStatus(123);          // int 不是 Status 类型

枚举值是不可变的

php
<?php

declare(strict_types=1);

enum Color
{
    case Red;
    case Green;
    case Blue;
}

$red = Color::Red;

// 枚举实例是单例
var_dump($red === Color::Red);  // bool(true)

// 无法克隆枚举
// clone $red;  // Fatal error: Trying to clone an uncloneable object of type Color

枚举实例是单例

php
<?php

declare(strict_types=1);

enum Direction
{
    case Up;
    case Down;
    case Left;
    case Right;
}

// 同一个 case 永远返回同一个实例
$up1 = Direction::Up;
$up2 = Direction::Up;

var_dump($up1 === $up2);     // bool(true)
var_dump($up1 instanceof Direction);  // bool(true)

详细说明

枚举的内部实现

PHP 枚举本质上是特殊的类(class),具有以下特点:

  1. 枚举自动实现了 UnitEnumBackedEnum 接口
  2. 每个 case 是一个常量,值是该枚举类的单例实例
  3. 枚举不能被实例化(不能使用 new
  4. 枚举不能被继承(final class

枚举的类型层次

UnitEnum (interface)
├── 纯枚举(无值枚举)
│   例: enum Status { case Pending; }

BackedEnum (interface extends UnitEnum)
├── Int Backed Enum (int|string)
│   例: enum Status: string { case Pending = 'pending'; }

└── String Backed Enum
    例: enum Color: int { case Red = 1; }

枚举可以使用的方法

php
<?php

declare(strict_types=1);

enum Suit
{
    case Hearts;
    case Diamonds;
    case Clubs;
    case Spades;
}

$suit = Suit::Hearts;

// name 属性 - 获取 case 名称
echo $suit->name;  // "Hearts"

// value 属性 - 仅 Backed Enum 可用
// echo $suit->value;  // Fatal error: Unpopulated Enum

// cases() 静态方法 - 获取所有 case
foreach (Suit::cases() as $case) {
    echo $case->name . PHP_EOL;
}

枚举不能用 new 实例化

php
<?php

declare(strict_types=1);

enum Role
{
    case Admin;
    case Editor;
    case Viewer;
}

// 以下都是非法的
// new Role();              // Fatal error
// new Role('Admin');       // Fatal error
// $obj = new class extends Role {};  // Fatal error: enums may not extend

实战示例

场景一:状态机

php
<?php

declare(strict_types=1);

enum OrderStatus
{
    case Created;
    case Paid;
    case Shipped;
    case Delivered;
    case Cancelled;

    public function canTransitionTo(self $newStatus): bool
    {
        return match ($this) {
            self::Created => in_array(
                $newStatus,
                [self::Paid, self::Cancelled],
                true
            ),
            self::Paid => in_array(
                $newStatus,
                [self::Shipped, self::Cancelled],
                true
            ),
            self::Shipped => $newStatus === self::Delivered,
            self::Delivered, self::Cancelled => false,
        };
    }

    public function label(): string
    {
        return match ($this) {
            self::Created => '已创建',
            self::Paid => '已支付',
            self::Shipped => '已发货',
            self::Delivered => '已送达',
            self::Cancelled => '已取消',
        };
    }
}

场景二:替代类常量

php
<?php

declare(strict_types=1);

// 之前 - 使用类常量
class HttpMethod
{
    public const GET = 'GET';
    public const POST = 'POST';
    public const PUT = 'PUT';
    public const DELETE = 'DELETE';
}

// 之后 - 使用枚举
enum HttpMethod: string
{
    case Get = 'GET';
    case Post = 'POST';
    case Put = 'PUT';
    case Delete = 'DELETE';

    public static function fromRequest(string $method): self
    {
        return self::tryFrom(strtoupper($method))
            ?? throw new \InvalidArgumentException("Unsupported HTTP method: {$method}");
    }
}

注意事项

注意事项

  • 枚举是 PHP 8.1+ 专属特性,低版本无法使用
  • 枚举不能被实例化(new),只能通过 case 访问
  • 枚举不能被继承,枚举类隐式为 final
  • 纯枚举没有 value 属性,只有 Backed Enum 才有
  • 枚举 case 不能使用 new,不能使用 clone

小贴士

  • 在方法参数和返回值中使用枚举类型,获得编译时类型安全
  • 使用 match 表达式处理枚举分支,确保穷尽所有 case
  • 枚举可以定义方法,将行为与值绑定在一起

最佳实践

1. 优先使用枚举替代类常量集合

当一组值有明确的边界且需要类型安全时,使用枚举。

2. 为枚举添加语义方法

php
<?php

declare(strict_types=1);

enum UserRole
{
    case Admin;
    case Editor;
    case Viewer;

    public function hasPermission(string $permission): bool
    {
        return match ($this) {
            self::Admin => true,
            self::Editor => in_array($permission, ['read', 'write']),
            self::Viewer => $permission === 'read',
        };
    }
}

3. 使用 self 类型提示

php
<?php

declare(strict_types=1);

enum Status
{
    case Active;
    case Inactive;

    public function isTransitionValid(self $target): bool
    {
        return $this !== $target;
    }
}

进阶用法

调试与测试技巧

php
<?php
declare(strict_types=1);

// 单元测试辅助函数
function createTestResource(): mixed
{
    return match (true) {
        default => new stdClass(),
    };
}

// 调试输出函数
function debugOutput(mixed , string  = ''): void
{
     =  ? ": " : '';
     .= print_r(, true);
    fwrite(STDERR,  . "\n");
}

// 性能基准测试
function benchmark(callable , int  = 1000): float
{
     = hrtime(true);
    for ($i = 0; $i < $iterations; $i++) {
        $fn();
    }
    return (hrtime(true) - $start) / 1e9;
}

日志记录实践

php
<?php
declare(strict_types=1);

/**
 * 简易日志记录器
 */
class SimpleLogger
{
    private string $logFile;
    private string $level = 'INFO';

    public function __construct(string $logFile)
    {
        $this->logFile = $logFile;
    }

    public function info(string $message, array $context = []): void
    {
        $this->log('INFO', $message, $context);
    }

    public function warning(string $message, array $context = []): void
    {
        $this->log('WARNING', $message, $context);
    }

    public function error(string $message, array $context = []): void
    {
        $this->log('ERROR', $message, $context);
    }

    private function log(string $level, string $message, array $context): void
    {
        $timestamp = date('Y-m-d H:i:s');
        $contextStr = $context ? ' ' . json_encode($context, JSON_UNESCAPED_UNICODE) : '';
        $line = "[{$timestamp}] [{$level}] {$message}{$contextStr}\n";
        file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX);
    }
}

配置与环境检测

php
<?php
declare(strict_types=1);

// 环境检测工具
class EnvironmentChecker
{
    public static function checkRequirements(array $requirements): array
    {
        $results = [];
        foreach ($requirements as $name => $check) {
            $results[$name] = is_callable($check) ? $check() : false;
        }
        return $results;
    }

    public static function getSystemInfo(): array
    {
        return [
            'php_version' => PHP_VERSION,
            'os' => PHP_OS,
            'sapi' => PHP_SAPI,
            'memory_limit' => ini_get('memory_limit'),
            'max_execution_time' => ini_get('max_execution_time'),
            'loaded_extensions' => get_loaded_extensions(),
        ];
    }
}

常见问题排查

问题可能原因解决方案
连接超时网络问题/配置错误检查配置,增加超时时间
权限不足文件/目录权限使用 chmod/chown 修正
性能下降索引缺失/数据量大添加索引,优化查询
数据不一致并发冲突/事务残留使用锁机制和事务
内存溢出大数据集/未释放资源增大内存限制,分批处理

故障排除步骤

  1. 检查错误日志和异常信息
  2. 确认配置和环境是否正确
  3. 使用调试工具逐步排查
  4. 参考官方文档查找已知问题

版本兼容性说明

功能最低版本说明
基础功能PHP 8.1本文档基准版本
只读属性PHP 8.1public readonly 修饰符
枚举类型PHP 8.1enum 类型和 match 表达式
FiberPHP 8.1协程/轻量级并发
命名参数PHP 8.0foo(arg_name: value)
联合类型PHP 8.0`int
Null 安全运算符PHP 8.0$obj?->method()
析构器 promotionPHP 8.0__construct(public $x)
php
<?php
declare(strict_types=1);

// 版本兼容性检测
function ensureVersion(string $minVersion): void
{
    if (version_compare(PHP_VERSION, $minVersion, '<')) {
        throw new RuntimeException(
            sprintf('需要 PHP %s+, 当前版本: %s', $minVersion, PHP_VERSION)
        );
    }
}

ensureVersion('8.1.0');

参考链接