PHP-DI分层类条件注入的冗余配置优化方案问询
优化PHP-DI分层依赖注入配置的方案
方案1:利用PHP-DI自动注入+命名服务+动态参数绑定
核心思路是通过命名服务区分不同类型的底层加载器,再让上层加载器通过自动注入+参数指定依赖的命名服务,避免手动编写所有组合。
步骤:
给底层加载器绑定命名服务
先把XmlChildLoader和SqlChildLoader注册为命名服务,让容器能通过名称快速获取:return [ // 命名服务:区分不同类型的ChildLoader 'xml.child_loader' => DI\autowire(XmlChildLoader::class), 'sql.child_loader' => DI\autowire(SqlChildLoader::class), // 自动绑定基础依赖(如果容器没自动处理的话) DOMDocument::class => DI\create(DOMDocument::class), PDO::class => DI\create(PDO::class) ->constructor('mysql:host=localhost;dbname=test', 'user', 'pass'), ];让ParentLoader通过参数动态依赖命名服务
给XmlParentLoader和SqlParentLoader的构造函数添加类型提示,同时在容器配置中指定它们依赖的ChildLoader对应的命名服务:// 假设XmlParentLoader构造函数是:public function __construct(ChildLoader $childLoader) 'xml.parent_loader' => DI\autowire(XmlParentLoader::class) ->constructorParameter('childLoader', DI\get('xml.child_loader')), 'sql.parent_loader' => DI\autowire(SqlParentLoader::class) ->constructorParameter('childLoader', DI\get('sql.child_loader')),根据配置动态绑定MyService的依赖
读取配置文件中的类型(比如$config['model_type'] = 'xml'),然后动态绑定MyService需要的ParentLoader:$loaderType = $config['model_type']; // 比如'xml'或'sql' return [ MyService::class => DI\autowire(MyService::class) ->constructorParameter('parentLoader', DI\get("{$loaderType}.parent_loader")), ];
优势:
- 新增
GrandChildLoader时,只需给对应类型的GrandChildLoader注册命名服务,再修改上层ChildLoader的配置绑定该命名服务即可,无需重复编写所有组合。 - 充分利用PHP-DI的自动注入,减少手动构造对象的代码。
方案2:工厂模式+DI容器集成
通过工厂类封装加载器的创建逻辑,把依赖组合的逻辑从DI配置中剥离,让容器只负责注入工厂的依赖,新增类型/层级时只需修改工厂。
步骤:
实现LoaderFactory类
工厂类负责根据类型创建对应的ParentLoader,并自动注入所需的底层加载器(容器会自动注入工厂依赖的所有实现类):class LoaderFactory { private XmlChildLoader $xmlChildLoader; private SqlChildLoader $sqlChildLoader; private XmlParentLoader $xmlParentLoader; private SqlParentLoader $sqlParentLoader; // 容器自动注入所有依赖的加载器实例 public function __construct( XmlChildLoader $xmlChildLoader, SqlChildLoader $sqlChildLoader, XmlParentLoader $xmlParentLoader, SqlParentLoader $sqlParentLoader ) { $this->xmlChildLoader = $xmlChildLoader; $this->sqlChildLoader = $sqlChildLoader; $this->xmlParentLoader = $xmlParentLoader; $this->sqlParentLoader = $sqlParentLoader; } public function createParentLoader(string $type): ParentLoader { return match($type) { 'xml' => new XmlParentLoader($this->xmlChildLoader), 'sql' => new SqlParentLoader($this->sqlChildLoader), default => throw new InvalidArgumentException("Unsupported loader type: {$type}"), }; } }在DI配置中注册工厂并绑定MyService
让容器自动注入工厂,然后在MyService的配置中调用工厂方法创建对应类型的ParentLoader:return [ LoaderFactory::class => DI\autowire(), MyService::class => DI\factory(function (Container $container, array $config) { $factory = $container->get(LoaderFactory::class); $parentLoader = $factory->createParentLoader($config['model_type']); return new MyService($parentLoader); }), ];
优势:
- 所有加载器的组合逻辑集中在工厂类中,新增层级(如
GrandChildLoader)时,只需修改工厂类的创建逻辑,无需修改大量DI配置。 - 完全利用自动注入,容器负责管理所有加载器的依赖,工厂只负责组装。
方案3:PHP-DI标签绑定+自动装配(进阶)
如果需要更灵活的扩展,可以用PHP-DI的标签绑定功能,把同类型的加载器打上标签,然后通过标签批量获取,再动态组装。
示例配置:
return [ // 给加载器打标签 DI\tagged('loader.xml') => [ DI\autowire(XmlChildLoader::class), DI\autowire(XmlParentLoader::class), ], DI\tagged('loader.sql') => [ DI\autowire(SqlChildLoader::class), DI\autowire(SqlParentLoader::class), ], // 工厂通过标签获取同类型的加载器并组装 LoaderFactory::class => DI\autowire() ->constructorParameter('xmlLoaders', DI\get(DI\tagged('loader.xml'))) ->constructorParameter('sqlLoaders', DI\get(DI\tagged('loader.sql'))), ];
这种方式适合加载器层级较多的场景,新增类型时只需给新的加载器打对应标签,工厂通过标签批量获取并组装。
内容的提问来源于stack exchange,提问作者jairhumberto
相关产品推荐
相关产品推荐

