factory() 必须返回闭包,用于注册工厂逻辑而非直接创建实例;闭包接收 $container 和配置数组,在运行时动态实例化,支持多实例且避免循环依赖与环境失效问题。

factory() 辅助函数必须返回闭包,不能直接返回实例
CI4 的 factory() 是服务容器提供的快捷绑定方式,但它不是“创建实例”的函数,而是“注册工厂逻辑”的辅助方法。常见错误是写成 factory(new MyService()) —— 这会导致实例在注册时就被创建,且无法响应后续参数变化或环境差异。
-
factory()的参数必须是一个闭包(function () { ... }),闭包内部才执行实例化逻辑 - 闭包接收两个隐式参数:
$container(当前服务容器实例)和可选的配置数组,可用于动态读取配置 - 若闭包返回
null或非对象,容器会抛出InvalidArgumentException
构造函数含标量参数时,闭包里必须显式传值
当类如 FileLogger 需要 string $logLevel 和 string $filePath 时,容器无法自动推断这些值。靠 factory() 绑定时,必须在闭包中硬编码或从配置读取:
// 正确:闭包内构造并返回实例
$container->factory('FileLogger', function ($container) {
return new \App\Libraries\FileLogger(
config('App')->logLevel ?? 'error',
WRITEPATH . 'logs/app.log'
);
});
// 错误:直接 new,绕过容器生命周期管理
$container->factory('FileLogger', new \App\Libraries\FileLogger('debug', '/tmp/log'));
- 推荐从
config('App')或env()读取值,避免硬编码 - 路径建议用
WRITEPATH、APPPATH等常量,而非相对路径 - 不要在闭包里调用
service('xxx'),可能引发循环依赖
多实例场景下,factory() 比 set() 更安全
如果同一个类需要多个不同配置的实例(例如连接不同数据库的 Database 实例),用 set() 会覆盖前一个绑定;而 factory() 天然支持每次调用都生成新实例,适合瞬态服务。
-
set()默认注册为单例(共享实例),除非手动指定false第三个参数 -
factory()总是返回新实例,无需额外配置 - 若需控制是否共享,应改用
singleton()或显式调用getSharedInstance()
绑定位置必须在 Services.php 的静态方法内
CI4 要求所有服务绑定集中在 app/Config/Services.php 的静态方法中,不能在控制器或中间件里调用 $container->factory() —— 因为容器在请求开始前就已冻结注册表。
- 在
Services::fileLogger()方法里调用factory()是标准做法 - 方法名需与服务标识符一致(如
fileLogger()对应service('fileLogger')) - 方法体开头加
if ($getShared)判断,否则会破坏单例语义
Services.php 文件加载时就执行 —— 否则环境变量切换(如 dev/staging/prod)将失效。











