PHPDI通过addDefinitions()注册服务并支持自动注入,需用DI\get()声明依赖、DI\create()定义工厂函数实现懒加载;默认prototype模式,单例需显式设置;方法注入须调用container->call();接口绑定实现解耦,替换驱动只需修改定义。

PHPDI 容器怎么注册一个服务并让它自动注入?
PHPDI 的核心不是“手动 new”,而是声明「谁依赖谁」,让容器在需要时自动构造。注册服务的关键是用 addDefinitions() 或配置文件定义服务的创建方式,而不是直接调用构造函数。
常见错误是把工厂函数写成立即执行:new Database() —— 这会导致服务提前实例化,失去懒加载和生命周期控制。正确做法是传匿名函数(工厂):
container->addDefinitions([
'Database' => \DI\create()->constructor(\DI\get('Config'))
]);
-
\DI\get('Config')表示“等真正需要 Database 时,再从容器里取 Config 实例” - 如果
Config本身也是容器管理的服务,它会按需递归解析 - 不加
\DI\get()直接写字符串,PHPDI 会尝试自动反射构造函数,但参数名必须匹配已注册的服务名
为什么用 $container->get('UserService') 有时返回新实例,有时是单例?
默认行为取决于服务定义方式:没显式指定作用域就是“每次 get 都新建”。要复用同一个实例,必须显式标记为单例(scope="singleton" 或用 \DI\create()->method('setSingleton', true))。
容易踩的坑是误以为“类名注册一次就自动单例”——其实 PHPDI 默认是 prototype 模式。比如:
立即学习“PHP免费学习笔记(深入)”;
container->addDefinitions([
'Logger' => \DI\create(Logger::class)->method('setLevel', 'debug')
]);
每次 $container->get('Logger') 都会 new 一个新对象。要改成单例,得加 ->method('setSingleton', true) 或改用 \DI\autowire() + 注解 @Singleton(需启用注解扩展)。
如何让控制器里的方法参数自动被 PHPDI 注入?
PHPDI 不自动拦截方法调用,它只管构造函数和 setter 注入。想让普通方法(比如 handle())也享受依赖注入,得手动用 $container->call():
$controller = $container->get('UserController');
$container->call([$controller, 'handle'], [
'request' => $request,
'response' => $response,
]);
-
$container->call()会尝试从容器中解析未传入的参数(如UserService $service) - 如果方法参数类型提示是接口或类名,且该类型已在容器中定义,就会自动注入
- 别指望直接
$controller->handle()能触发注入——那是 Laravel Service Container 的魔法,PHPDI 不提供运行时 AOP 式拦截
替换底层实现(比如换数据库驱动)为什么改配置就行?
因为 PHPDI 把“类型绑定”和“实例创建”完全分离。你注册的是抽象(接口名或类名),不是具体实现。例如:
container->addDefinitions([
'PdoInterface' => \DI\create(MySQLPdo::class),
// 后续换成 PostgrePdo,只需改这一行
]);
所有依赖 PdoInterface 的服务(比如 QueryService)无需改代码,只要它的构造函数参数类型是 PdoInterface,就能自动拿到新实现。
注意点:接口名必须和类型提示完全一致(包括命名空间),否则反射失败;若用字符串键注册(如 'db'),则必须在构造函数里用对应字符串参数名,不能靠类型提示。
最常被忽略的是 autowire 和 explicit binding 的混用——一旦对某个类用了 \DI\autowire(),它就不再看同名的字符串键定义,优先走反射。调试时建议先关掉 autowire,用显式定义确保行为可预测。










