首页 > 解决方案 > Laravel Observers - 更新无休止地触发自身并产生 bcrypt 错误

问题描述

我正面临一个我希望看到解释的错误。我有一个 UserObserver,每次更新用户并且active字段更新为 true 时,都会生成一个新密码并发送一封欢迎电子邮件。

函数看起来像这样。

    public function updated (User $user)
    {
        if ($user->active && $user->isDirty('active')) {
            $password = generatePassword();
            $user->password = bcrypt($password);
            $user->save();

            $user->notify(
                new UserWelcomeNotification(
                    $user->email,
                    $password,
                    new MailResource(Email::getMailInfo(23))
                )
            );
        }
    }

正如您在 if 语句中看到的,检查用户是否处于活动状态以及数据库字段是否已更改 ( isDirty())。如果这是真的,正在生成一个新密码,用 bcrypt 散列,然后通过通知发送给用户。(邮件)
正如预期的那样,密码更新再次触发该方法,但现在isDirty('active)应该返回 false。这不会发生,它会在所有迭代中返回 true。达到 PHP 最大执行时间后,出现以下错误:

[2019 年 1 月 11 日星期五 09:13:13] PHP 致命错误:app/src/vendor/laravel/framework/src/Illuminate/Hashing/BcryptHasher.php 中的最大执行时间超过了 60 秒

启用 xdebug 后会引发以下异常。(如预期)

PHP 错误:已达到“256”的最大函数嵌套级别,正在中止!在/home/ilyas/script/clockwork/app/src/vendor/laravel/framework/src/Illuminate/Support/Collection.php 1971 行

从这个可以轻松解决的问题中,我有两个问题。

为什么在达到最大执行时间后 bcrypt 会出错?

$user->isDirty('active')尽管观察者中的最后一次更新没有更新该active字段,但为什么在此循环中的每次更新后始终返回 true?

正如 Mozammil 所要求的那样$user->getDirty(),在第一次触发更新的方法时返回这个。

array(2) {
  'active' =>
  bool(true)
  'updated_at' =>
  string(19) "2019-01-11 11:27:13"
}

从第二次返回到超时:

array(3) {
  'password' =>
  string(60) "$2y$10$rlAbpelKnT/yp5zFhXcjwelEKkDEx5SfNJWqL1LiDltRnHYBLINmK"
  'active' =>
  bool(true)
  'updated_at' =>
  string(19) "2019-01-11 11:27:13"
}

标签: phplaravel

解决方案


感谢 Dries Vints 和 Jonas Staudenmeir 在 GitHub 上提供答案。

来自 DriesVints:

好吧,考虑一下。“更新”事件发生在您的模型更新后。因此,您对模型所做的任何更改都必然会被 isDirty 调用获取。$user->active 返回 true 的事实确实是因为它从原始更新更改为 true。原始更改未清除或任何内容。由于您不断引用传递给更新方法的同一个对象,这是预期的行为。

来自乔纳斯·施陶登迈尔

这是因为在调用 syncOriginal() 之前触发了更新的事件。

https://github.com/laravel/framework/issues/27138


推荐阅读