PHP两个相互通信的类触发无限循环内存耗尽该如何解决
错误原因
你当前的写法属于构造函数循环依赖:实例化Foo的时候会在构造函数中new Bar,实例化Bar的时候构造函数又会new Foo,无限递归创建对象,最终耗尽PHP分配的内存触发致命错误。
常用解决方案
方案1:依赖注入(通用首选)
不要在构造函数内直接new对方实例,而是通过构造函数参数、setter方法将已经初始化的对方实例注入。如果是框架项目,直接使用框架自带的DI容器即可自动处理循环依赖的实例化,不需要手动编写初始化逻辑。
class UserStatusController { // 注入CouponController实例 public function __construct(CouponController $couponController) { $this->couponController = $couponController; } // 注:原代码里private属性是实例属性,不能用self::$badStatus调用,此处已修正 private $badStatus = "blocked"; public function hasBadStatus(int $id): bool { $user = User::where(['id' => $id])->get(); return $user->status == $this->badStatus; } public function actualizeStatus(int $id, string $status): bool { if ($status == $this->badStatus) { // 注:原代码调用deleteCoupon未传用户ID,此处已补全参数 $this->couponController->deleteCoupon($id); return false; } User::where(['id' => $id])->update(['status' => $status]); $this->couponController->createCoupon($id); return true; } } class CouponController { // 注入UserStatusController实例 public function __construct(UserStatusController $userStatusController) { $this->userStatusController = $userStatusController; } public function createCoupon(int $id): bool { if ($this->userStatusController->hasBadStatus($id)) { return false; } // 创建优惠券逻辑 return true; } public function deleteCoupon(int $id): bool { // 删除优惠券逻辑 return true; } }
方案2:懒加载(适配成本最低)
不在构造函数初始化对方实例,第一次用到的时候再创建并缓存为单例,避免重复初始化触发递归。不需要大幅修改原有代码,适合快速修复问题:
class UserStatusController { private $couponController = null; private $badStatus = "blocked"; // 首次调用时才初始化CouponController private function getCouponController() { if ($this->couponController === null) { $this->couponController = new CouponController(); } return $this->couponController; } public function hasBadStatus(int $id): bool { $user = User::where(['id' => $id])->get(); return $user->status == $this->badStatus; } public function actualizeStatus(int $id, string $status): bool { if ($status == $this->badStatus) { $this->getCouponController()->deleteCoupon($id); return false; } User::where(['id' => $id])->update(['status' => $status]); $this->getCouponController()->createCoupon($id); return true; } } class CouponController { private $userStatusController = null; // 首次调用时才初始化UserStatusController private function getUserStatusController() { if ($this->userStatusController === null) { $this->userStatusController = new UserStatusController(); } return $this->userStatusController; } public function createCoupon(int $id): bool { if ($this->getUserStatusController()->hasBadStatus($id)) { return false; } // 创建优惠券逻辑 return true; } public function deleteCoupon(int $id): bool { // 删除优惠券逻辑 return true; } }
方案3:事件中间层解耦(适合复杂项目)
抽离公共事件层,两个类不用直接持有对方实例,通过事件调度完成交互,彻底解耦后扩展性更强,后续新增关联逻辑不需要修改原有类代码:
// 事件定义 class UserStatusUpdated { public $userId; public $newStatus; public function __construct(int $userId, string $newStatus) { $this->userId = $userId; $this->newStatus = $newStatus; } } // 简易事件调度器,框架项目可直接使用自带的事件系统 class EventDispatcher { private $listeners = []; public function listen(string $event, callable $listener) { $this->listeners[$event][] = $listener; } public function dispatch(object $event) { $eventName = get_class($event); if (isset($this->listeners[$eventName])) { foreach ($this->listeners[$eventName] as $listener) { $listener($event); } } } } // 改造后的用户状态类,只负责状态更新和事件触发 class UserStatusController { public function __construct(EventDispatcher $dispatcher) { $this->dispatcher = $dispatcher; } private $badStatus = "blocked"; public function hasBadStatus(int $id): bool { $user = User::where(['id' => $id])->get(); return $user->status == $this->badStatus; } public function actualizeStatus(int $id, string $status): bool { User::where(['id' => $id])->update(['status' => $status]); // 触发状态变更事件 $this->dispatcher->dispatch(new UserStatusUpdated($id, $status)); return true; } } // 改造后的优惠券类,只需要监听事件完成对应操作 class CouponController { public function __construct(EventDispatcher $dispatcher, UserStatusController $userStatusController) { $this->userStatusController = $userStatusController; // 监听用户状态变更事件 $dispatcher->listen(UserStatusUpdated::class, function(UserStatusUpdated $event) { if ($event->newStatus == "blocked") { $this->deleteCoupon($event->userId); } else { $this->createCoupon($event->userId); } }); } public function createCoupon(int $id): bool { if ($this->userStatusController->hasBadStatus($id)) { return false; } // 创建优惠券逻辑 return true; } public function deleteCoupon(int $id): bool { // 删除优惠券逻辑 return true; } }
内容的提问来源于stack exchange,提问作者Tenarius
相关产品推荐
相关产品推荐

