PHP命令模式需显式定义Command接口(含execute()和建议的undo())、隔离Receiver与Client、Invoker依赖接口而非具体类,且命令对象须可序列化以支持队列与跨进程执行。

PHP 实现命令模式不是靠框架自动注入或魔术方法,而是靠显式定义接口、分离接收者与调用逻辑、手动组装对象链。它不解决“怎么跑起来”的问题,而是解决“怎么让执行时机、撤销、日志、排队这些能力可插拔”的问题。
Command 接口必须声明 execute(),建议加 undo()
只定义 execute() 能跑通,但实际项目中几乎立刻会遇到回滚需求。如果初期没预留 undo(),后期补会导致所有 ConcreteCommand 类批量修改,破坏开闭原则。
-
execute()是必选,负责触发接收者动作 -
undo()建议从第一版就定义(哪怕先留空),避免后续扩展时重构成本飙升 - 不要在接口里加
log()或validate()—— 这些属于横切关注点,应由 Invoker 或装饰器处理
Receiver 类不能直接暴露给 Client,必须通过 ConcreteCommand 中转
常见错误是让 Client new 一个 Receiver 后直接传给 Invoker,这等于绕过命令封装。Receiver 的职责只是干活,它不该知道“自己被谁、何时、以什么上下文调用”。
- Client 只创建
ConcreteCommand,并把Receiver实例传进去(构造注入) -
ConcreteCommand持有Receiver引用,但不暴露其方法;execute()内部才调用$this->receiver->doSomething() - Receiver 类本身可以是任意已有业务类(比如
UserRepository、PaymentGateway),无需为模式改造
Invoker 必须持有 Command 接口类型变量,不能依赖具体类
如果 Invoker 的属性写成 private $command = new SendEmailCommand();,那就彻底废了命令模式的意义——完全失去替换、组合、延迟执行的能力。
- 正确写法:
private Command $command;(PHP 8+)或private $command;+ 类型断言 - Invoker 的
run()方法只调用$this->command->execute(),不做任何分支判断 - 需要支持队列?加个
public function queue(Command $cmd): void方法即可,不碰具体实现
真实场景下容易漏掉的三个点
命令模式在 PHP Web 请求中不像 CLI 那么直观,很多团队落地失败是因为忽略了运行时约束:
- 命令对象通常不能带未序列化资源(如 PDO 实例、文件句柄),否则无法存入 Redis 队列或跨进程执行
- Undo 操作必须幂等且能反向补偿,比如
createUser()的 undo 不是deleteUser(),而是deactivateUser()+ 清理缓存 - 日志记录不应放在
execute()里,而应在 Invoker 的run()前后统一埋点,否则每个命令重复写 log 逻辑
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











