本文目录导读:

我来详细介绍PHP状态机流转控制的实现方案。
基础状态机实现
简单状态机类
<?php
class StateMachine
{
private string $currentState;
private array $transitions = [];
private array $callbacks = [];
public function __construct(string $initialState)
{
$this->currentState = $initialState;
}
/**
* 定义状态转换
* [
* 'pending' => ['approved', 'rejected'],
* 'approved' => ['processing'],
* 'processing' => ['completed', 'failed']
* ]
*/
public function defineTransitions(array $transitions): void
{
$this->transitions = $transitions;
}
/**
* 尝试执行状态转换
*/
public function transition(string $newState): bool
{
if (!$this->canTransition($newState)) {
throw new \InvalidArgumentException(
"Cannot transition from {$this->currentState} to {$newState}"
);
}
$oldState = $this->currentState;
// 执行前置回调
$this->executeCallbacks('before', $oldState, $newState);
// 更新状态
$this->currentState = $newState;
// 执行后置回调
$this->executeCallbacks('after', $oldState, $newState);
return true;
}
/**
* 检查是否允许转换
*/
public function canTransition(string $newState): bool
{
if (!isset($this->transitions[$this->currentState])) {
return false;
}
return in_array($newState, $this->transitions[$this->currentState]);
}
/**
* 注册回调
*/
public function on(string $event, callable $callback): void
{
$this->callbacks[$event][] = $callback;
}
private function executeCallbacks(string $type, string $oldState, string $newState): void
{
$eventName = "$type: $oldState -> $newState";
if (isset($this->callbacks[$eventName])) {
foreach ($this->callbacks[$eventName] as $callback) {
call_user_func($callback, $oldState, $newState);
}
}
}
public function getCurrentState(): string
{
return $this->currentState;
}
}
实际业务场景示例
订单状态机
<?php
class OrderStateMachine
{
private StateMachine $stateMachine;
private array $orderData;
const STATE_PENDING = 'pending';
const STATE_PAID = 'paid';
const STATE_SHIPPED = 'shipped';
const STATE_COMPLETED = 'completed';
const STATE_CANCELLED = 'cancelled';
const STATE_REFUNDED = 'refunded';
public function __construct(array $orderData)
{
$this->orderData = $orderData;
$this->stateMachine = new StateMachine($orderData['status'] ?? self::STATE_PENDING);
$this->setupTransitions();
$this->setupCallbacks();
}
private function setupTransitions(): void
{
$this->stateMachine->defineTransitions([
self::STATE_PENDING => [
self::STATE_PAID,
self::STATE_CANCELLED
],
self::STATE_PAID => [
self::STATE_SHIPPED,
self::STATE_REFUNDED
],
self::STATE_SHIPPED => [
self::STATE_COMPLETED,
self::STATE_REFUNDED
],
self::STATE_COMPLETED => [
self::STATE_REFUNDED
],
self::STATE_CANCELLED => [],
self::STATE_REFUNDED => []
]);
}
private function setupCallbacks(): void
{
// 支付成功回调
$this->stateMachine->on(
"after: pending -> paid",
function ($old, $new) {
$this->processPayment();
}
);
// 发货回调
$this->stateMachine->on(
"after: paid -> shipped",
function ($old, $new) {
$this->processShipping();
}
);
// 完成订单回调
$this->stateMachine->on(
"after: shipped -> completed",
function ($old, $new) {
$this->completeOrder();
}
);
}
private function processPayment(): void
{
// 处理支付逻辑
echo "处理支付...\n";
// 记录支付时间
$this->orderData['paid_at'] = date('Y-m-d H:i:s');
}
private function processShipping(): void
{
echo "订单发货...\n";
$this->orderData['shipped_at'] = date('Y-m-d H:i:s');
}
private function completeOrder(): void
{
echo "完成订单...\n";
$this->orderData['completed_at'] = date('Y-m-d H:i:s');
}
public function pay(): void
{
$this->stateMachine->transition(self::STATE_PAID);
}
public function ship(): void
{
$this->stateMachine->transition(self::STATE_SHIPPED);
}
public function complete(): void
{
$this->stateMachine->transition(self::STATE_COMPLETED);
}
public function cancel(): void
{
$this->stateMachine->transition(self::STATE_CANCELLED);
}
public function getStatus(): string
{
return $this->stateMachine->getCurrentState();
}
public function canTransition(string $targetState): bool
{
return $this->stateMachine->canTransition($targetState);
}
}
// 使用示例
$order = [
'id' => 1,
'amount' => 199.00,
'status' => 'pending'
];
$orderStateMachine = new OrderStateMachine($order);
// 支付
if ($orderStateMachine->canTransition('paid')) {
$orderStateMachine->pay();
echo "当前状态: " . $orderStateMachine->getStatus() . "\n";
}
// 发货
if ($orderStateMachine->canTransition('shipped')) {
$orderStateMachine->ship();
echo "当前状态: " . $orderStateMachine->getStatus() . "\n";
}
// 完成
if ($orderStateMachine->canTransition('completed')) {
$orderStateMachine->complete();
echo "当前状态: " . $orderStateMachine->getStatus() . "\n";
}
高级状态机模式
带验证和事件驱动的状态机
<?php
abstract class AbstractStateMachine
{
protected string $currentState;
protected array $transitionRules = [];
protected array $eventListeners = [];
abstract protected function getTransitions(): array;
abstract protected function getInitialState(): string;
public function __construct()
{
$this->currentState = $this->getInitialState();
$this->transitionRules = $this->getTransitions();
}
public function apply(string $state): void
{
if (!$this->canTransition($state)) {
throw new \RuntimeException(
sprintf('Invalid state transition: %s -> %s', $this->currentState, $state)
);
}
$from = $this->currentState;
// guard logic (条件检查)
$guardResult = $this->checkGuard($state);
if (!$guardResult['allowed']) {
throw new \RuntimeException($guardResult['message']);
}
// 执行状态转换
$this->currentState = $state;
// 触发事件
$eventName = sprintf('%s_to_%s', $from, $state);
$this->triggerEvent($eventName);
// 日志记录
$this->logTransition($from, $state);
}
protected function canTransition(string $state): bool
{
$allowedStates = $this->transitionRules[$this->currentState] ?? [];
return in_array($state, $allowedStates);
}
protected function checkGuard(string $targetState): array
{
$guardMethod = 'guard_' . $this->currentState . '_' . $targetState;
if (method_exists($this, $guardMethod)) {
return $this->$guardMethod();
}
return ['allowed' => true, 'message' => ''];
}
protected function triggerEvent(string $eventName): void
{
if (isset($this->eventListeners[$eventName])) {
foreach ($this->eventListeners[$eventName] as $listener) {
call_user_func($listener, $this);
}
}
}
public function on(string $eventName, callable $callback): void
{
$this->eventListeners[$eventName][] = $callback;
}
protected function logTransition(string $from, string $to): void
{
// 记录转换日志到数据库
$logEntry = [
'from' => $from,
'to' => $to,
'timestamp' => date('Y-m-d H:i:s'),
'user' => $_SESSION['user_id'] ?? null
];
// 存储到日志表
// Log::info('State transition', $logEntry);
}
public function getCurrentState(): string
{
return $this->currentState;
}
public function getAvailableTransitions(): array
{
return $this->transitionRules[$this->currentState] ?? [];
}
}
// 工作流状态机示例
class WorkflowStateMachine extends AbstractStateMachine
{
private array $workflowData;
private array $permissions = [];
public function __construct(array $data = [])
{
$this->workflowData = $data;
parent::__construct();
}
protected function getInitialState(): string
{
return 'draft';
}
protected function getTransitions(): array
{
return [
'draft' => ['submitted', 'cancelled'],
'submitted' => ['in_review', 'rejected'],
'in_review' => ['approved', 'rejected', 'returned'],
'approved' => ['in_progress'],
'in_progress' => ['completed', 'cancelled'],
'completed' => [],
'cancelled' => [],
'rejected' => ['draft'],
'returned' => ['draft']
];
}
// Guard 方法示例 (条件控制)
protected function guard_draft_submitted(): array
{
if (empty($this->workflowData['description'])) {
return ['allowed' => false, 'message' => 'Description is required for submission'];
}
if ($this->workflowData['amount'] < 0) {
return ['allowed' => false, 'message' => 'Amount cannot be negative'];
}
return ['allowed' => true, 'message' => ''];
}
protected function guard_in_review_approved(): array
{
// 检查审批权限
if (!$this->hasPermission($_SESSION['user_id'], 'approve_workflow')) {
return ['allowed' => false, 'message' => 'No permission to approve'];
}
return ['allowed' => true, 'message' => ''];
}
private function hasPermission($userId, $permission): bool
{
// 实现权限检查逻辑
return in_array($permission, $this->permissions[$userId] ?? []);
}
public function setWorkflowData(array $data): void
{
$this->workflowData = $data;
}
public function getUserPermissions(int $userId): void
{
// 从数据库加载用户权限
$this->permissions[$userId] = ['approve_workflow', 'review'];
}
}
使用数据库持久化状态
<?php
class DatabaseStateMachine extends AbstractStateMachine
{
private PDO $db;
private int $entityId;
private string $entityType;
public function __construct(PDO $db, string $entityType, int $entityId)
{
$this->db = $db;
$this->entityType = $entityType;
$this->entityId = $entityId;
$currentState = $this->loadCurrentState();
parent::__construct();
$this->currentState = $currentState;
}
protected function getTransitions(): array
{
return [
'new' => ['active', 'deleted'],
'active' => ['modified', 'archived', 'deleted'],
'modified' => ['active', 'archived'],
'archived' => ['deleted'],
'deleted' => []
];
}
protected function getInitialState(): string
{
return 'new';
}
private function loadCurrentState(): string
{
$stmt = $this->db->prepare(
"SELECT state FROM entity_states
WHERE entity_type = ? AND entity_id = ?
ORDER BY created_at DESC LIMIT 1"
);
$stmt->execute([$this->entityType, $this->entityId]);
$state = $stmt->fetchColumn();
return $state ?: 'new';
}
public function apply(string $state): void
{
parent::apply($state);
// 保存到数据库
$this->saveState($state);
}
private function saveState(string $state): void
{
$stmt = $this->db->prepare(
"INSERT INTO entity_states (entity_type, entity_id, state, created_at)
VALUES (?, ?, ?, NOW())"
);
$stmt->execute([$this->entityType, $this->entityId, $state]);
}
}
使用第三方库(推荐)
Symfony Workflow Component
<?php
use Symfony\Component\Workflow\Definition;
use Symfony\Component\Workflow\MarkingStore\MethodMarkingStore;
use Symfony\Component\Workflow\Transition;
use Symfony\Component\Workflow\Workflow;
use Symfony\Component\Workflow\Registry;
use Symfony\Component\EventDispatcher\EventDispatcher;
// 定义工作流
$transitions = [
new Transition('pay', 'pending', 'paid'),
new Transition('ship', 'paid', 'shipped'),
new Transition('complete', 'shipped', 'completed'),
new Transition('cancel', ['pending', 'paid'], 'cancelled'),
];
$places = ['pending', 'paid', 'shipped', 'completed', 'cancelled'];
$definition = new Definition($places, $transitions);
// 创建Workflow实例
$markingStore = new MethodMarkingStore(true, 'state');
$eventDispatcher = new EventDispatcher();
$workflow = new Workflow($definition, $markingStore, $eventDispatcher, 'order');
// 事件监听
$eventDispatcher->addListener('workflow.transition', function ($event) {
echo 'Transition from ' . $event->getTransition();
});
// 使用示例
class Order
{
private string $state;
public function getState(): string
{
return $this->state;
}
public function setState(string $state): void
{
$this->state = $state;
}
}
$order = new Order();
$order->setState('pending');
if ($workflow->can($order, 'pay')) {
$workflow->apply($order, 'pay');
echo "Order status: " . $order->getState(); // 输出: paid
}
最佳实践建议
1 状态机设计模式
<?php
interface StateInterface
{
public function proceed(): void;
public function cancel(): void;
public function getStatus(): string;
}
class PendingState implements StateInterface
{
private OrderState $context;
public function __construct(OrderState $context)
{
$this->context = $context;
}
public function proceed(): void
{
// 检查是否有足够库存等条件
if ($this->validatePayment()) {
$this->context->setState(new PaidState($this->context));
echo "Orders paid\n";
} else {
throw new \RuntimeException('Payment validation failed');
}
}
public function cancel(): void
{
$this->context->setState(new CancelledState($this->context));
echo "Order cancelled\n";
}
public function getStatus(): string
{
return 'pending';
}
private function validatePayment(): bool
{
// 验证逻辑
return true;
}
}
// 其他状态类类似...
class OrderState
{
private StateInterface $state;
public function __construct()
{
$this->state = new PendingState($this);
}
public function setState(StateInterface $state): void
{
$this->state = $state;
}
public function proceed(): void
{
$this->state->proceed();
}
public function cancel(): void
{
$this->state->cancel();
}
public function getStatus(): string
{
return $this->state->getStatus();
}
}
// 使用
$order = new OrderState();
$order->proceed(); // 输出: Orders paid
echo $order->getStatus(); // 输出: paid
2 注意事项
<?php
// 1. 状态转换的原子性
class TransactionalStateMachine extends AbstractStateMachine
{
private PDO $db;
public function applyWithTransaction(string $state): void
{
$this->db->beginTransaction();
try {
$this->apply($state);
// 其他业务逻辑
$this->db->commit();
// 触发事件在事务提交后
$eventName = sprintf('after_%s', $state);
$this->triggerEvent($eventName);
} catch (\Exception $e) {
$this->db->rollBack();
throw $e;
}
}
}
// 2. 状态机审计日志
class AuditTrailLogger
{
private PDO $db;
public function logTransition(
string $entityType,
int $entityId,
string $from,
string $to,
array $metadata = []
): void {
$stmt = $this->db->prepare("
INSERT INTO audit_log
(entity_type, entity_id, from_state, to_state, metadata, created_at)
VALUES (?, ?, ?, ?, ?, NOW())
");
$stmt->execute([
$entityType,
$entityId,
$from,
$to,
json_encode($metadata)
]);
}
}
// 3. 状态机的测试
class StateMachineTest extends PHPUnit\Framework\TestCase
{
public function testOrderFlow(): void
{
$order = new Order();
$machine = new OrderStateMachine($order);
$this->assertEquals('pending', $machine->getCurrentState());
$machine->pay();
$this->assertEquals('paid', $machine->getCurrentState());
// 测试非法转换
$this->expectException(\RuntimeException::class);
$machine->complete(); // 无法从 paid 直接到 completed
}
public function testInvalidTransition(): void
{
$machine = new OrderStateMachine(new Order());
$this->expectException(\RuntimeException::class);
$machine->apply('completed'); // 无效状态转换
}
}
性能优化
<?php
// 缓存状态转换配置
class OptimizedStateMachine extends AbstractStateMachine
{
private static array $cache = [];
public static function getTransitionsFor(string $entityType): array
{
if (!isset(self::$cache[$entityType])) {
// 从数据库或配置文件加载
self::$cache[$entityType] = [
'transitions' => include(__DIR__ . "/config/{$entityType}_transitions.php"),
'guards' => include(__DIR__ . "/config/{$entityType}_guards.php")
];
}
return self::$cache[$entityType];
}
protected function getTransitions(): array
{
return self::getTransitionsFor($this->entityType)['transitions'];
}
}
状态机模式在PHP中的应用要点:
- 清晰定义状态和转换规则
- 使用Guard/条件控制转换
- 事件驱动设计
- 事务性操作
- 完善的日志和审计
- 测试覆盖
- 考虑使用成熟库如Symfony Workflow
根据项目规模选择合适的实现方式:简单的可以用数组配置,复杂的建议使用专门的状态机库。