files 优先级最高,其次 classmap,最后 psr-4;Composer 内部调用顺序固定,与 composer.json 中书写顺序无关。

composer autoload 中 psr-4 和 classmap 谁优先?
ThinkPHP 默认使用 psr-4 加载应用类(如 app\controller\Index),但如果你手动加了 classmap 或 files,它们的加载顺序并不由“谁写在前面”决定,而是由 Composer 自动加载器的内部调用顺序固定:先 files,再 classmap,最后才是 psr-4 / psr-0。这意味着,即使你在 composer.json 里把 psr-4 放最上面,它依然排在最后执行。
所以想让某个类「被优先加载」——比如你重写了 think\App,又不想改框架源码,就得把它放进 files 或 classmap,而不是指望调整 psr-4 的顺序。
如何用 files 实现高优先级类覆盖?
files 是 Composer 加载器中最早执行的一环,适合放单文件函数、全局常量或关键类的硬覆盖。ThinkPHP 启动时会 require think\initializer\Bootstrap 等,但核心类如 think\App 是运行时 new 出来的,只要你在 files 里提前定义同名类,就能拦截实例化。
- 在项目根目录新建
override/think/App.php,内容为完整重写的think\App类(注意命名空间和类名必须完全一致) - 修改
composer.json的autoload.files字段:
{
"autoload": {
"files": [
"override/think/App.php"
],
"psr-4": {
"app\\": "app/",
"think\\": "thinkphp/library/think/"
}
}
}
然后运行 composer dump-autoload。此后所有对 think\App 的 new 或静态调用,都会走你覆盖的版本。
立即学习“PHP免费学习笔记(深入)”;
⚠️ 注意:files 里的文件是无条件一次性加载的,不能有依赖其他未加载类的代码;也不支持自动重载,改完必须重新 dump。
classmap 怎么用于局部类优先加载?
如果要覆盖的是多个类(比如整个 think\cache\driver 目录),用 files 就太碎,这时更适合 classmap。它比 psr-4 早执行,且支持目录扫描,生成映射表后按类名精确匹配,性能还略高。
- 把自定义 driver 放到
override/cache/driver/Redis.php,命名空间保持think\cache\driver - 在
composer.json中添加:
{
"autoload": {
"classmap": [
"override/cache/"
],
"psr-4": {
"app\\": "app/",
"think\\": "thinkphp/library/think/"
}
}
}
再执行 composer dump-autoload -o(加 -o 生成优化后的 classmap)。Composer 会在生成的 vendor/composer/autoload_classmap.php 中,把 think\cache\driver\Redis 映射到你的路径,覆盖原版。
⚠️ 关键点:classmap 扫描结果以生成的 autoload_classmap.php 为准,不是实时读取文件;删了类文件不重新 dump,旧映射还在,可能报错或加载失败。
为什么 vendor/thinkphp/library/think 不该进 psr-4 自定义路径?
常见错误是把 ThinkPHP 框架目录也加进自己的 psr-4,例如:
"think\\": "thinkphp/library/think/", "think\\": "override/think/" ← 错!重复 key,后者会覆盖前者,但 composer.json 不允许重复键
更隐蔽的写法是合并成一个路径数组,但 Composer 对同一命名空间只认最后一个值,实际等效于只加载 override/think/,导致框架基础类全挂。
正确做法只有两个:
- 用
files或classmap单点覆盖(推荐) - 用 Composer 的
repositories+patch方式替换整个包(适合长期维护)
直接改 psr-4 映射去“优先加载框架类”,本质是破坏自动加载契约,后续升级极易出问题。真正的优先级控制不在路径顺序,而在加载阶段选择。











