PHP 状态机流转控制

wen PHP项目 3

本文目录导读:

PHP 状态机流转控制

  1. 基础状态机实现
  2. 实际业务场景示例
  3. 高级状态机模式
  4. 使用数据库持久化状态
  5. 使用第三方库(推荐)
  6. 最佳实践建议
  7. 性能优化

我来详细介绍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中的应用要点:

  1. 清晰定义状态和转换规则
  2. 使用Guard/条件控制转换
  3. 事件驱动设计
  4. 事务性操作
  5. 完善的日志和审计
  6. 测试覆盖
  7. 考虑使用成熟库如Symfony Workflow

根据项目规模选择合适的实现方式:简单的可以用数组配置,复杂的建议使用专门的状态机库。

抱歉,评论功能暂时关闭!