psr规范是codeigniter 4的核心骨架,psr-4决定类自动加载路径匹配,psr-12确保服务容器反射解析正确;控制器必须调用parent::__construct()启用ioc;services.php工厂方法须返回已初始化实例。

PSR 规范不是 CodeIgniter 4 的可选附加项,而是它整个自动加载、服务注册和错误处理机制的底层骨架。不理解 PSR-4,你就没法真正控制类怎么被找到;不遵循 PSR-12,你的服务注册代码很可能在 CI4 的容器里根本解析失败。
为什么 CI4 的 autoloader 只认 PSR-4 路径结构
CI4 的自动加载器完全依赖 Composer 的 autoload 配置,而该配置只识别 PSR-4 映射规则。如果你把一个控制器放在 app/Controllers/User.php 却声明命名空间为 App\MyModule\Controllers,框架启动时就会报 Class 'App\MyModule\Controllers\User' not found。
- 必须保证命名空间前缀(如
App\)与磁盘路径(如app/)严格对应 -
app/Models/UserModel.php对应的命名空间只能是App\Models\UserModel,多一层或少一层都会断 - 自定义服务类若放在
app/Services/,命名空间就必须是App\Services\*,否则Config\Services里注册的工厂方法无法被反射调用
PSR-12 不只是“写得好看”,它直接影响依赖注入解析
CI4 的服务容器使用 PHP 反射读取构造函数签名来决定注入什么。如果参数类型提示写成 UserRepository $repo(空格错位),或用了 public function __construct( UserRepository $repo )(括号外多余空格),某些 PHP 版本下反射会漏掉类型信息,导致注入失败并抛出 TypeError: Argument 1 must be of type UserRepository。
- 类型提示必须紧贴参数名:
UserRepository $repo✅,UserRepository $repo❌ - 构造函数参数不能用缩写注释替代类型提示,例如
/* @var UserRepository */ $repo不会被容器识别 - 默认值参数(如
string $mode = 'live')可以存在,但必须放在类型提示之后、无类型参数之前
控制器里不调 parent::__construct() 就等于关掉了整个 IoC 管道
这不是风格问题,是硬性机制:CI4 的 Controller 基类在 __construct() 中初始化了 $this->services 容器代理,并绑定请求上下文。跳过这步,你写的任何构造函数参数(哪怕类型正确)都不会被注入,全部为 null。
- 错误写法:
public function __construct(UserService $service) { $this->service = $service; }→$this->service永远是null - 正确顺序:先
parent::__construct(),再赋值;否则服务容器没准备好,反射解析直接跳过 - 如果需要前置逻辑(比如检查权限),必须放在
parent::__construct()之后,否则可能访问未初始化的$this->request或$this->session
服务注册文件 app/Config/Services.php 必须返回实例,不能只 new
CI4 的服务容器期望每个注册方法返回一个**已初始化的对象实例**。如果你在 customService() 里只写 new CustomService(),而该类构造函数又依赖其他服务(比如 Database),容器不会递归解析——它只负责调用你的工厂方法,不接管内部依赖。
- 正确做法:在工厂方法里显式获取依赖,例如
$db = \Config\Services::database(); return new CustomService($db); - 更推荐方式:用
singleton()注册,并让容器自动解析,前提是类构造函数参数全是容器已知类型 - 别在
Services.php里做耗时操作(如连接数据库),因为这个文件在每次请求都加载,会影响性能
CI4 的架构设计藏在细节里:命名空间路径、空格位置、父构造调用顺序、工厂方法返回值——这些地方出错,不会报语法错误,只会让依赖静默失效。调试时别急着查逻辑,先确认这四点是否全部合规。











