You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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]属性仅在容器负责实例化对象时生效。要实现预期效果,需从以下几点调整:

  1. 将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()
    ]);
    
  2. 简化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);
        }
    }
    
  3. 实现"无参调用"的两种可行方式
    方式一:从容器获取实例(符合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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 14:25:22