PHP 8.1+ 引入的 枚举(Enum) 类型

PHP 8.1+ 引入的 枚举(Enum) 类型

PHP 8.1+ 引入的 枚举(Enum) 类型,用于定义订单的各种状态,并实现了一个简单的状态机(State Machine),控制订单状态只能按照合理的业务流程进行变更。

1. 枚举定义部分(5 个订单状态)

case PendingPayment = 1;  // 待支付
case Paid           = 2;  // 已支付
case Shipped        = 3;  // 已发货
case Completed      = 4;  // 已完成
case Cancelled      = 5;  // 已取消

这是在定义一个名为 OrderStatus 的枚举(enum),它有 5 个可能的值:

  • OrderStatus::PendingPayment → 值是 1,代表“待支付”
  • OrderStatus::Paid → 值是 2,代表“已支付”
  • OrderStatus::Shipped → 值是 3,代表“已发货”
  • OrderStatus::Completed → 值是 4,代表“已完成”
  • OrderStatus::Cancelled → 值是 5,代表“已取消”

这些值通常对应数据库里 status 字段存储的数字(比如 1、2、3...),这样既节省空间,又方便查询。

使用枚举的好处是:代码里不会出现魔法数字(magic number),写 OrderStatus::Paid 比直接写 2 更清晰、更安全。

2. 状态流转规则(状态机核心)

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

这个方法是枚举上的一个实例方法,意思是:当前状态$this)能不能合法地变成某个新状态$newStatus)?

它返回 true 表示“允许变更”,返回 false 表示“不允许,禁止变更”。

我们用大白话 + 图表来解释每一种情况:

当前状态($this) 允许变成的新状态($newStatus) 不允许变成的状态(会返回 false) 业务含义解释
待支付 (PendingPayment) 已支付 (Paid)
已取消 (Cancelled)
不能直接变成 已发货、已完成 用户还没付钱,只能支付成功或取消订单
已支付 (Paid) 已发货 (Shipped)
已取消 (Cancelled)
不能变成 待支付、已完成 钱已收到,可以发货,或者用户退款取消
已发货 (Shipped) 已完成 (Completed) 不能变成其他任何状态 货物在路上,只能等用户签收后完成
已完成 (Completed) 不允许变成任何状态 所有变更都不允许 订单结束了,不能再改
已取消 (Cancelled) 不允许变成任何状态 所有变更都不允许 订单已取消,不能再改

举例说明如何判断

假设当前订单状态是 待支付(1):

$current = OrderStatus::PendingPayment;

$current->canTransitionTo(OrderStatus::Paid);      // 返回 true  → 允许变成“已支付”
$current->canTransitionTo(OrderStatus::Cancelled); // 返回 true  → 允许变成“已取消”
$current->canTransitionTo(OrderStatus::Shipped);   // 返回 false → 不允许直接变成“已发货”(没付钱怎么发货?)

假设当前是 已支付(2):

$current = OrderStatus::Paid;

$current->canTransitionTo(OrderStatus::Shipped);   // true  → 可以发货
$current->canTransitionTo(OrderStatus::Cancelled); // true  → 可以退款取消
$current->canTransitionTo(OrderStatus::PendingPayment); // false → 不能退回“待支付”

假设当前是 已完成(4):

$current = OrderStatus::Completed;

$current->canTransitionTo(任何状态); // 都返回 false → 彻底锁死,不能再改

为什么要有这个方法?(核心目的)

这正是你之前提到的规则:核心字段只允许状态驱动修改

在代码的其他地方(比如订单服务),修改状态前一定会先检查:

if (!$order->getStatus()->canTransitionTo($newStatus)) {
    throw new Exception("非法状态变更!");
}

这样就强制保证了:

  • 不会出现业务上不可能的状态跳转
  • 所有状态变更都是合法、可控的
  • 防止程序 bug 或恶意操作导致数据混乱

总结成一张状态流转图

待支付 (1)
   ├──→ 已支付 (2)
   │      ├──→ 已发货 (3)
   │      │      └──→ 已完成 (4)  (结束)
   │      └──→ 已取消 (5)  (结束)
   └──→ 已取消 (5)  (结束)

已完成 (4) 和 已取消 (5) 不能再变到任何状态

这就是一个典型的电商订单状态机。是生产环境中保护订单数据一致性的核心机制。



Don’t reinvent the wheel, library code is there to help.

欢迎关注公-众-号【TaonyDaily】、留言、评论,一起学习。

posted @ 2026-02-09 19:14  刘俊涛的博客  阅读(22)  评论(0)    收藏  举报