为什么自定义用户事件侦听器会停止事件传播?

Why do custom User Event listeners stop event propagation?

我正在尝试使用自定义事件侦听器连接到 Friends of Symfony User Bundle 事件。有很多为 FOS 用户包创建事件侦听器的示例,我在创建侦听器、接收事件和执行自定义代码方面都没有遇到任何问题。我正在监听 FOSUserEvents::REGISTRATION_CONFIRMED 事件并发送一些关于新确认用户的通知电子邮件。

但是我的方法有一个问题。即:

如果没有我的事件侦听器,用户会在访问确认页面后登录并重定向到他们的个人资料页面。然而,我的事件监听器的存在停止了正常的事件传播,并且用户没有登录或被重定向离开确认页面。

这是预期的行为吗?我在文档中看到相互矛盾的信息。

FOS 用户包文档在这一点上含糊不清。虽然它们似乎从代码示例中暗示 here 自定义事件停止正常传播,或者至少替换给定事件的正常操作。

虽然 Symfony 事件文档 here 描述了为阻止事件传播而存在的 stopPropagation 函数,但仅在必要时才这样做。

那么 User Bundle 自定义事件侦听器会阻止传播到内置的正常事件吗?如果是的话,这附近有什么吗?因此不需要修改请求或响应的自定义代码可以简单地监听事件,而不会影响 User Bundle 附带的正常操作。

回答我自己的问题:

FOS 用户捆绑包自定义事件侦听器对捆绑包附带的事件侦听器没有影响。它们都将被处理,并且 运行 正如您阅读 symfony 文档所期望的那样。

值得注意的是,我在上面引用的 User Bundle 文档对自定义事件的用途略有含糊。如果您检查调度每个 events - after the "pre-action" events are dispatched, and event listeners are processed (including any custom ones you register), the controller examines the event object extracting specific properties that control how the action is complete. In the example given in the documentation 的用户包中的控制器代码,RESETTING_RESET_SUCCESS 事件的自定义侦听器正在使用 event->setResponse。这是 ResettingController 中 resetAction 的相关代码部分:

$dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_SUCCESS, $event);

$userManager->updateUser($user);

if (null === $response = $event->getResponse()) {
    $url = $this->container->get('router')->generate('fos_user_profile_show');
    $response = new RedirectResponse($url);
}

$dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_COMPLETED, new FilterUserResponseEvent($user, $request, $response));

return $response;

自定义侦听器唯一可以影响内置控制器操作的方法是在事件中传回 RedirectResponse 对象,然后控制器将使用该对象作为操作的 return 值 -将最终用户重定向到您选择的 URL.

所以最后一个谜是为什么我在 REGISTRATION_CONFIRMED 事件上的自定义事件侦听器在访问电子邮件确认 link 后中断了自动登录。该问题的根本原因是 Symfony 本身存在一个严重的竞争条件错误。该错误已于去年年底修复,但我正在处理一个客户的应用程序,他们仍在 运行 使用具有该错误的旧版本的 symfony。简而言之,使用 swiftmailer 发送电子邮件, 将电子邮件假脱机存储到内存中,可能会导致在写出会话文件之前对 HTTP 响应进行 returned 的竞争。随后重定向到客户端将导致他们加载较旧的 - 未登录 - 会话文件,在下一个页面加载时看起来没有登录。

我将 post 在第二个答案中提供有关错误的详细信息,因为堆栈溢出显然害怕在一个答案中出现太多 link。

我之前回答中提到的 symfony 错误已经在 2.3.22、2.5.7 和 2.6 及更高版本中得到修复。如果您对这里的 github 问题感到好奇:

错误报告:https://github.com/symfony/symfony/issues/6417

修复: https://github.com/symfony/symfony/pull/12341

如果您无法将 symfony 升级到固定版本,解决方法是阻止 swiftmailer 假脱机到内存(即在响应返回到浏览器之前立即发送)。在您的 swiftmailer 配置中注释掉这一行:

#    spool:     { type: memory }