构造函数中仅允许初始化依赖和轻量级预处理,必须调用 parent::__construct() 以完成服务容器绑定等关键初始化,否则 $this->session 等核心服务为 null;应优先使用参数注入而非 Services::xxx();禁止执行重定向、数据库查询等耗时或输出操作。

构造方法里只适合写「初始化依赖」和「轻量级预处理」,其他逻辑一律不该放——否则要么报错,要么拖慢整个请求链路。
必须调用 parent::__construct() 才能用服务
CI4 的控制器继承自 CodeIgniter\Controller,它在父类构造函数里完成服务容器绑定、请求/响应对象挂载、会话初始化等关键动作。漏掉这句,$this->session、$this->db、$this->request 全是 null 或未定义。
- 错误写法:
public function __construct() { helper('url'); }—— 之后调用site_url()会报 undefined function - 正确写法:
public function __construct() { parent::__construct(); helper('url'); } - 注意:即使你用的是构造函数参数注入(如
RequestInterface $request),也仍需显式调用parent::__construct(),因为参数注入只是补充手段,不是替代方案
优先用参数注入,而不是手动 Services::xxx()
CI4 支持通过类型提示自动注入核心接口实例,比在构造函数里反复调用 Services::session() 更安全、更易测。
- 推荐:
public function __construct(SessionInterface $session, UserModel $userModel)—— 容器自动解析并传入 - 不推荐:
$this->session = Services::session(); $this->userModel = new UserModel();—— 手动 new 破坏容器生命周期管理,测试时无法 mock - 限制:标量参数(如
string $apiVersion)无法自动注入,必须改用闭包绑定或配置项读取
禁止在构造函数里做耗时操作或跳转
构造函数在路由匹配后、中间件执行前就运行,此时响应头尚未发送,但一旦执行重定向、数据库查询、远程 API 调用,就会导致不可控副作用。
- 典型错误:
if (!session()->get('logged_in')) { return redirect()->to('/login'); }—— 构造函数里 return 不会中断流程,redirect 无效,还会引发 headers already sent - 正确做法:把权限检查移到中间件,或在 action 方法开头判断
- 性能陷阱:在构造函数里查数据库、读大文件、解密配置——每个请求都执行一遍,放大延迟
需要共享数据?别硬塞进构造函数
想让所有方法都能访问用户信息或配置项,很多人习惯在构造函数里赋值给 $this->currentUser。但这容易造成状态污染,且无法响应 session 变更。
- 更稳的方案:在基类控制器中定义 getter 方法,每次调用时动态获取
session()->get('user_id') - 如果真要缓存,用
static属性 + 懒加载,避免每次实例化都重复计算 - 特别注意:构造函数里不能调用
$this->response->setJSON()或任何输出方法,此时输出缓冲未就绪
最常被忽略的一点:构造函数执行完,中间件才开始跑。所以你以为“已经鉴权完毕”,其实中间件可能刚准备校验 token——顺序错位会导致逻辑断层。


















