php - 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"
}
解决方案
感谢 Dries Vints 和 Jonas Staudenmeir 在 GitHub 上提供答案。
来自 DriesVints:
好吧,考虑一下。“更新”事件发生在您的模型更新后。因此,您对模型所做的任何更改都必然会被 isDirty 调用获取。$user->active 返回 true 的事实确实是因为它从原始更新更改为 true。原始更改未清除或任何内容。由于您不断引用传递给更新方法的同一个对象,这是预期的行为。
来自乔纳斯·施陶登迈尔
这是因为在调用 syncOriginal() 之前触发了更新的事件。