laravel死循环主因是手动指针操作、事件/观察者互相触发及boot()中隐式调用自身逻辑;foreach本身安全,问题在于循环体内引入了二次进入路径。

直接说结论:Laravel 中的死循环风险,90% 来自手动指针操作(如 next())、事件/观察者互相触发、或在 boot() 等钩子中隐式调用自身逻辑——不是 foreach 本身有问题,而是你让它“不知道什么时候停”。
别在 foreach 里调 next() 或 prev()
这是最典型的“以为能控制指针,结果指针早被 foreach 重置了”的错误。PHP 的 foreach 是值拷贝遍历,内部指针和你手动调的 next() 完全不共享状态;一旦走到最后一个元素,next() 返回 false,再访问 $next->cnpj 就爆 Trying to get property 'cnpj' of non-object。
- 永远用索引访问:
$finances[$i]和$finances[$i + 1],加边界判断$i - 如果数据来自 Eloquent Collection,先转为索引数组:
collect($finances)->values()->all(),避免 Laravel 集合的键名干扰 - 别信“我只跑两次,不会越界”——加空数组防御:
if (empty($finances)) { return; }
Collection 遍历时别混用 each() 和模型更新
在 each() 回调里调 $model->save() 或 $model->update(),可能意外触发模型事件(如 updating),而该事件监听器又去修改同一个集合,形成隐式递归。
-
each()只适合无副作用操作(发通知、打日志);有写操作,改用原生foreach+ 手动事务包裹 - 若必须批量更新字段,优先走查询构建器:
Model::whereIn('id', $ids)->update(['status' => 'processed']) - 检查模型的
$dispatchesEvents是否开启,临时禁用可破循环:$model->dispatchesEvents = [];
Boot、Observer、Notification 里禁止跨层触发
boot() 方法、Eloquent Observer 的 created()、或通知类的 toDatabase(),都容易成为死循环入口——比如 A 模型创建后发通知,通知里又创建 B 模型,B 的 observer 再触发 A 的更新……链路一闭合就卡死。
- 在
boot()里避免调用会再次触发boot()的方法(如static::creating()中又 new 本类实例) - Observer 中做条件守卫:
if ($model->isDirty('status') && $model->status === 'active') { ... },防止重复响应 - 通知类里别直接操作模型;改用队列任务延后执行,或加缓存标记:
Cache::has("notified_{$model->id}")
真正危险的从来不是循环语法本身,而是你在循环体里悄悄埋下的“另一条进入循环的路径”。检查每处 save()、notify()、dispatch(),它们是否可能绕回来再进一次当前逻辑——这才是死循环最常藏身的地方。











