symfony工作流守卫是迁移前执行的php逻辑,用于动态拦截不合规流转,默认静默失败;必须配置在transition下guard字段,接收$subject、$workflow、$transition三参数,仅判断不修改状态。

Symfony 工作流的守卫(guard)不是“开关式”配置项,而是一段在状态迁移前执行的 PHP 逻辑,用于动态拦截不合规的流转。它不改状态、不抛异常、也不打日志——默认静默失败,所以设了却没效果,是最常见的排查盲区。
守卫必须写在 transitions 里,且只能是 callable
YAML 配置中,每个 transition 下可加 guard 字段,值必须是合法的 PHP callable 表达式,比如服务方法或内联函数:
- 推荐写法(调用服务方法):
guard: '@app.workflow_guard.can_publish' - 支持内联写法(仅限简单判断):
guard: 'is_object($subject) and $subject->getAuthor() === $user'(注意:$user 是当前 Security Token 用户,需启用上下文) - 不能写字符串如
'can_publish',也不能直接写类名,否则容器解析失败
守卫函数接收固定参数,必须严格签名
实际执行时,Workflow 会传入三个参数:$subject(当前实体)、$workflow(当前 workflow 实例)、$transition(触发的 transition 对象)。你的 guard 方法必须按此顺序声明:
- 正确示例(PHP 服务类方法):
{
if (!$subject instanceof Article) {
return false;
}
return $subject->getAuthor()->isVerified();
}
- 如果参数个数或类型错,guard 会静默返回 false,
can()和apply()都不会报错 - 别在 guard 里调用
$entityManager->flush()或修改 $subject 状态——它只做判断,不负责变更
调试守卫是否运行,得手动加日志或断点
因为 guard 失败不抛异常,最有效的方式是在函数开头加可观察信号:
- 开发环境用
error_log("guard fired for #{$subject->getId()}") - 或注入
LoggerInterface记录 trace 级日志 - 别依赖
var_dump()—— 它可能被缓冲或截断,尤其在 CLI 或异步上下文中 - 确认 guard 服务已注册为 public,并在 YAML 中打了正确的 tag:
tags: [{ name: 'workflow.guard' }](部分旧版需手动加)
守卫和 can() 的关系:先校验 transition 存在性,再跑 guard
$workflow->can($article, 'publish') 的执行流程是:
- 第一步:检查当前状态是否在
publish的from列表中 - 第二步:若通过,才调用 guard;guard 返回 true 才最终返回 true
- 所以
can()返回 false,可能是状态不对,也可能是 guard 拦截——得分开排查 - 测试时建议临时注释 guard,确认基础路径通了,再打开它加逻辑











