在Slim v4中用PHP-DI给服务工具类实现依赖注入遇阻
问题
在Slim v4项目中已完成PHP-DI容器定义配置,MailerInterface可在控制器中正常注入使用。现希望创建可全局调用的MailerService服务类,期望无需处理依赖创建,能通过无参构造实例化调用。按照PHP-DI文档给构造方法参数添加#[Inject]属性后仍无法实现,请问实现缺少什么?
容器配置代码
$containerBuilder = new ContainerBuilder(); $containerBuilder->useAttributes(true); $containerBuilder->addDefinitions([ MailerInterface::class => function (ContainerInterface $container) { $dsn = sprintf( '%s://%s:%s@%s:%s', "smtp", "username", "password", "hostname", "587" ); return new Mailer(Transport::fromDsn($dsn)); } ]);
控制器使用代码
<?php abstract class BaseController { protected MailerInterface $mailer; /* DI works in constructor as part of the controller */ public function __construct(MailerInterface $mailer) { $this->mailer = $mailer; } }
期望的服务类调用方式
$m = new MailerService(); $m->sendEmail($email);
当前MailerService实现
final class MailerService { private MailerInterface $mailer; public function __construct(#[Inject('MailerInterface')] MailerInterface $mailer) { $this->mailer = $mailer; } public function sendEmail(Email $email): void { $this->mailer->send($email); } }
解决方案
核心问题是直接用new MailerService()实例化时,PHP-DI容器并未参与对象创建过程,#[Inject]属性仅在容器负责实例化对象时生效。要实现预期效果,需从以下几点调整:
将MailerService注册到容器
必须让容器知晓MailerService的存在,才能自动解析其依赖:$containerBuilder->addDefinitions([ // 原有的MailerInterface定义 MailerInterface::class => function (ContainerInterface $container) { $dsn = sprintf( '%s://%s:%s@%s:%s', "smtp", "username", "password", "hostname", "587" ); return new Mailer(Transport::fromDsn($dsn)); }, // 添加MailerService的容器定义,容器会自动解析构造参数 MailerService::class => Autowire::create() ]);简化MailerService构造(可选)
由于已开启useAttributes(true),且MailerInterface已在容器中注册,无需手动添加#[Inject]属性,容器会根据类型提示自动注入:final class MailerService { private MailerInterface $mailer; public function __construct(MailerInterface $mailer) { $this->mailer = $mailer; } public function sendEmail(Email $email): void { $this->mailer->send($email); } }实现"无参调用"的两种可行方式
方式一:从容器获取实例(符合DI规范,推荐)// 先构建容器实例 $container = $containerBuilder->build(); // 从容器中获取MailerService,依赖会自动注入 $m = $container->get(MailerService::class); $m->sendEmail($email);方式二:添加静态工厂方法(模拟无参构造)
如果一定要贴近new MailerService()的调用形式,可通过静态方法让容器参与创建:final class MailerService { private MailerInterface $mailer; // 私有构造,强制通过静态方法创建 private function __construct(MailerInterface $mailer) { $this->mailer = $mailer; } public static function create(): self { // 假设容器实例可全局获取(比如通过Slim的App实例) $container = \Slim\App::getInstance()->getContainer(); return new self($container->get(MailerInterface::class)); } public function sendEmail(Email $email): void { $this->mailer->send($email); } } // 调用方式 $m = MailerService::create(); $m->sendEmail($email);注意:原生PHP的无参构造
new MailerService()不可能触发PHP-DI的依赖注入,因为该过程完全脱离容器控制,必须让容器参与对象创建才能自动注入依赖。
内容的提问来源于stack exchange,提问作者Purus
相关产品推荐
相关产品推荐

