构造函数注入是依赖注入的起点,需明确类型提示(如pdo、loggerinterface),避免无类型参数或可空默认值;耗时操作不可放在构造函数中;容器无法自动解析pdo等第三方类,须手动绑定工厂;shared()用于无状态服务,notshared()用于有请求上下文的对象;函数级依赖应直接传参或用闭包预绑定,而非调用resolve。

__construct() 是你第一个该盯住的地方
别从“什么是依赖注入”开始学,直接看类的构造函数。只要它接收一个对象参数且类型明确(比如 PDO、LoggerInterface),那就是依赖注入在发生。这种写法不靠框架、不靠容器,只靠 PHP 自身的类型提示和手动传参,是所有高级用法的起点。
- 如果构造函数里写的是
function __construct($db),没有类型提示,那容器根本没法自动解析,反射也抓不到该塞什么进去 - 如果写成
function __construct(?PDO $db = null),等于默认允许空值,后续运行时容易触发Call to a member function on null - 构造函数里别做耗时操作:比如连数据库、读配置文件、发 HTTP 请求——这些动作应该在调用方准备好实例后再传进来,而不是让容器在
build()时卡住
容器报错 Entry "App\Service\UserService" cannot be resolved: Entry "PDO" cannot be resolved 怎么办
这不是你代码写错了,是容器不知道怎么造 PDO。PHP-DI 或 Symfony DI 不会自动猜你要用哪个 DSN、用户名和密码。它只认注册规则。
- 必须显式绑定:用 $container->set(PDO::class)->factory(...) 告诉它怎么创建
- 别指望“自动发现”能搞定第三方类,PDO、Redis、Monolog\Logger 这些都得手写工厂闭包
- 如果用了接口抽象(如 CacheInterface),绑定必须写成 $container->set(CacheInterface::class)->to(RedisCache::class),不能只绑实现类
shared() 和 notShared() 选哪个
这决定对象是单例还是每次新建,不是凭感觉,得看对象有没有状态。
- 数据库连接、缓存客户端、日志器通常要 shared():复用连接、避免重复初始化开销
- 请求上下文类(如 Request、Session)、DTO、表单验证器必须 notShared():每个请求/每次调用都该是干净的新实例
- 混用风险高:比如把 notShared() 的 UserService 注入到 shared() 的 ApiController 里,会导致后者持有一个可能已过期的用户服务实例
函数里也能用依赖注入,但别硬套类那一套
函数没构造函数,也没$this,强行塞进容器反而绕远路。更自然的做法是:
- 直接把依赖当参数传:比如 function sendWelcomeEmail(User $user, Mailer $mailer),测试时换 mock 更快
- 用闭包预绑定:把 $mailer 和 $logger 捕获进一个闭包,后续调用不用重复传
- 避免写 function sendWelcomeEmail(User $user) { $mailer = resolve(Mailer::class); ... }:这又把容器耦合进业务逻辑,而且 resolve() 在函数里调用,生命周期难控制
真正难的不是写容器,是判断哪些类该被注入、哪些该自己管生命周期、哪些压根不该进容器。比如一个只做数组排序的工具函数,加个 ArraySorter 容器绑定,纯属给自己找事。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











