PHP依赖注入无强制规范,但社区形成最佳实践:面向接口编程、优先构造函数注入、容器仅组装不侵入业务、善用自动装配、函数级也遵循依赖外传原则。

PHP依赖注入没有统一的强制规范,但社区已形成一套被广泛验证、高度一致的最佳实践。这些实践不是语法要求,而是解耦、可测、可维护代码的底层逻辑支撑。
面向接口编程,而非具体实现
声明依赖时始终使用接口或抽象类类型提示,避免硬编码具体类名。这为替换实现(如从FileLogger换成DatabaseLogger)提供零成本路径。
- 在构造函数、方法参数或属性中写
MessengerInterface $messenger,而不是Mailer $mailer - 容器配置中将接口映射到具体实现:
LoggerInterface::class => DI\get(FileLogger::class) - 接口定义应聚焦行为契约(如
send()),不暴露内部细节或技术选型
优先使用构造函数注入
这是最清晰、最易测试、最符合“对象创建即就绪”原则的方式。它让依赖关系一目了然,且不可变,避免后续状态不一致。
- 把所有必需依赖放在构造函数中,确保对象实例化后处于可用状态
- 避免在构造函数中做耗时操作(如连接数据库),应交由方法触发
- 若存在可选依赖,可用
= null默认值配合setter注入,但需明确注释其可选性
容器只负责组装,不侵入业务逻辑
业务类本身不应感知容器存在。绝不调用$container->get()或任何容器方法——那意味着你正在手动“拉取”依赖,违背了控制反转本质。
立即学习“PHP免费学习笔记(深入)”;
- 依赖应通过构造函数、方法参数或属性(仅限控制器等特殊场景)被动注入
- 容器配置(如
di.php)是独立文件,与业务代码物理隔离 - 测试时直接传入模拟对象,完全绕过容器,验证逻辑本身
善用自动装配,谨慎覆盖
现代容器(如PHP-DI)默认启用基于反射的自动装配:根据类型提示递归解析依赖树。多数情况下无需显式配置,减少冗余。
- 让容器自动处理
OrderService → PaymentGateway → Logger这类链式依赖 - 仅当需要定制行为时才手动配置:如标量参数(
database.host)、工厂逻辑、单例/原型作用域 - 避免为每个类都写完整绑定定义,那会抵消自动化的价值
函数级依赖也遵循相同逻辑
即使不写类,纯函数同样适用依赖注入思想。关键在于“不自己创建,由外部传入”。
- 工具函数接收依赖作为参数,而非内部
new Mailer() - 用闭包捕获上下文,形成预绑定函数,保持调用简洁
- 轻量函数式容器可用于脚本场景,但核心仍是显式传递或延迟解析,而非全局访问



















