实例化带参构造PHP类报错、依赖注入可用原因及最优方案咨询
两种写法的差异原因
- 手动执行
new EmailService()实例化时,PHP本身不会自动填充类构造方法的必填参数,你必须显式传入EntityManagerInterface和EmailFactory两个实例,所以会触发参数不足的报错。 - 将
EmailService $emailService作为方法参数时,是框架的依赖注入容器(DI容器) 完成了实例化逻辑:容器已经提前注册了EntityManagerInterface、EmailFactory、EmailService的实例化规则,调用方法前会自动按依赖关系构造好EmailService实例传入,不需要你手动传递构造参数,因此可以正常运行。
这类场景的最佳实践
1. 优先使用构造注入而非方法注入/手动实例化
如果PaymentService的多个方法都需要用到EmailService,直接将EmailService注入到PaymentService的构造方法中,统一管理依赖,避免重复声明:
class PaymentService { private EmailService $emailService; private string $senderEmail; // 将需要的依赖全部声明在构造方法中,由容器自动注入 public function __construct(EmailService $emailService, string $emailNoReply) { $this->emailService = $emailService; $this->senderEmail = $emailNoReply; } public function sendPaymentEmail(User $user) { return $this->emailService->sendPaymentEmail($this->senderEmail, $user, 'customer_home'); } }
2. 不要在业务代码中直接调用容器获取服务
原代码中$this->container->get('twig')->getGlobals()['email_no_reply']的写法不推荐,会导致类的依赖不透明,也会提升单元测试的成本。需要什么依赖就直接在构造方法中声明,不需要依赖容器本身。
3. 业务代码中不要手动实例化带依赖的服务类
所有需要依赖的服务都交给DI容器管理,你只需要在使用的地方声明注入即可,容器会自动处理依赖的嵌套实例化逻辑,不需要你关心服务的构造参数是什么、怎么来的,刚好匹配你不想手动传递参数的需求。
4. 仅单元测试场景可手动实例化
只有在写单元测试需要mock依赖的场景下,才需要手动传入构造参数实例化服务:
// 单元测试示例,业务代码请勿使用 $mockEm = $this->createMock(EntityManagerInterface::class); $mockFactory = $this->createMock(EmailFactory::class); $emailService = new EmailService($mockEm, $mockFactory);
内容的提问来源于stack exchange,提问作者Lucifer
相关产品推荐
相关产品推荐

