必须调用 parent::__construct(),否则 $this->request、$this->session 等核心服务为空;ci4 控制器基类在此完成服务绑定、中间件挂载及请求上下文初始化,跳过将导致上下文失联。

必须调用 parent::__construct(),否则 $this->request、$this->session 等核心服务全为空 —— 这不是可选项,是 CI4 的硬性执行前提。
为什么不能跳过 parent::__construct()
CI4 的控制器基类 Controller 在其构造函数中完成了关键初始化:注册服务容器绑定、挂载中间件钩子、初始化 $this->request / $this->response / $this->session 等属性。跳过它,等于让整个请求上下文“失联”。
常见错误现象:
-
$this->session->get()返回null或抛出 “Call to a member function get() on null” -
$this->request->getPost()报错 “Trying to get property 'getPost' of non-object” - 自定义中间件不触发,过滤器失效
两种合法调用方式:手动注入 vs 服务工厂
CI4 支持两种主流写法,本质都是依赖注入,但时机和可控性不同:
✅ 推荐方式:构造函数参数自动注入(需确保容器能解析)
- 框架会根据类型提示(如
Session、ConnectionInterface)从服务容器中自动取实例 - 必须仍调用
parent::__construct(),否则注入的服务无法与请求生命周期联动 - 示例:
public function __construct(Session $session, ConnectionInterface $db) { parent::__construct(); // ← 不可省略 $this->session = $session; $this->db = $db; }
✅ 替代方式:运行时通过 Config\Services 获取
- 适用于类型未被容器自动注册、或需动态选择服务变体的场景
- 不依赖构造函数参数,更灵活但失去类型安全和测试友好性
- 示例:
public function __construct() { parent::__construct(); $this->cache = \Config\Services::cache(); // ← 注意命名空间前缀 $this->email = \Config\Services::email(); }
容易踩的坑:服务获取时机与作用域混淆
服务容器返回的是单例(Singleton),但部分服务有请求级状态(如 Session、Request),它们在每次请求中是新实例 —— 容器只是负责“创建并返回当前请求对应的实例”。
- 不要在构造函数里保存
$this->request->uri这类瞬态值,它会在后续方法中变化 - 避免在构造函数中执行耗时操作(如查库、远程调用),这会阻塞所有请求入口
-
Services::database('custom-group')可传参切换连接组,但该调用本身不触发连接,首次查询才真正建立 - 若自定义服务未注册到容器,
Services::myService()会抛出InvalidArgumentException
扩展服务时必须重写 app/Config/Services.php
想用 Services::myLogger()?光写个类不够,得显式注册:
- 在
app/Config/Services.php的public static function myLogger()方法里返回实例 - 该方法名即服务名,且必须是
public static - 若服务依赖其他服务(如需要
CacheInterface),可在参数中声明,容器会自动注入 - 未注册的服务无法通过构造函数参数注入,只能靠
Services::getShared()手动拉取(不推荐)
构造函数不是初始化业务逻辑的万能入口,它是服务装配的交接点 —— 拿到对象后怎么用,才是你该专注的事。











