TP6是彻底重写的框架,不能通过composer update从TP5升级,必须新建项目迁移;命名空间、配置、路由、数据库查询、中间件等全部重构,存在大量不兼容变更。

ThinkPHP5 升级到 TP6 为什么直接 composer update 会失败
因为 TP6 不是 TP5 的简单迭代,而是彻底重写的框架,命名空间、核心类、配置结构、路由机制全部重构,composer update 默认只会拉取同主版本号的更新(如 5.1.x → 5.1.y),根本不会升级到 6.x。
常见错误现象:Class 'think\App' not found、Call to undefined method think\Request::param()、所有中间件和控制器全报错。
- 必须显式执行
composer require topthink/think-skeleton:^6.0或新建 TP6 项目再迁移逻辑,不能原地升级 - TP5 的
application/目录在 TP6 中被废弃,控制器、模型、中间件等全部按 PSR-4 规范放在app/下对应命名空间目录中 -
config/目录从 PHP 数组返回改为返回纯数组或支持.env驱动,旧版database.php中的mysql://连接字符串写法不再被识别
TP6 路由定义方式变更导致 404 或参数丢失
TP6 默认关闭了“自动解析 URL 参数”行为,且路由注册入口从 route.php 移到了 app/route/app.php,同时要求显式声明变量名和类型。
使用场景:你把 TP5 的 Route::get('user/:id', 'index/user/read') 直接复制过去,结果访问 /user/123 返回 404 或 $id 为空。
立即学习“PHP免费学习笔记(深入)”;
- TP6 必须用闭包或完整类方法路径,且推荐用资源路由或分组路由替代单条定义,例如:
Route::get('user/:id', [\app\controller\User::class, 'read'])->option(['merge_param' => true]) - 如果依赖 URL 路径参数,务必加上
->option(['merge_param' => true]),否则Request::param()拿不到路由变量 - TP6 的
Request::param()默认只取 query + post + route 合并后的值,但旧代码可能依赖input('id')的模糊匹配,此时要改用request()->param('id')并确认是否开启merge_param
数据库查询语法兼容性断裂:Query 类方法大量废弃
TP6 把原本混用的 Db::table() 和 Model 查询彻底分离,whereTime、whereLike 等快捷方法被移入查询构建器扩展包,原生 Query 类不再内置这些方法。
常见错误现象:Call to undefined method think\db\Query::whereTime()、Db::name('user')->field('id,name')->select() 返回对象而非数组。
- 必须安装扩展包:
composer require topthink/think-helper,然后在查询前调用use think\helper\Query;才能用whereTime等方法 -
Db::name('user')->select()返回的是Collection对象,不是数组,需显式调用->toArray();若仍需数组,可改用Db::name('user')->select()->toArray() - 模型查询默认启用严格模式,
UserModel::find(1)若找不到会返回null,而 TP5 是返回空模型实例,注意判空逻辑是否被绕过
中间件注册和执行顺序混乱导致权限/日志失效
TP6 中间件不再通过 app/middleware.php 全局配置,而是按层级注册:全局中间件、应用中间件、路由中间件三者作用域和执行时机完全不同,且不自动继承父级。
使用场景:你把 TP5 的全局日志中间件复制进 TP6 的 app/middleware.php,结果只有首页记录日志,后台接口完全没触发。
- TP6 的
app/middleware.php只控制「应用级中间件」,对命令行、多应用、子域名路由无效;真正全局生效得在app/provider.php中绑定think\Middleware服务 - 路由级中间件必须在定义路由时用
->middleware()显式挂载,例如:Route::group('admin', function () { /* ... */ })->middleware([\app\middleware\Auth::class]) - 中间件类构造函数不能再依赖容器自动注入(如
Log实例),必须改用app('log')或在handle()方法里通过$this->app->log获取,否则启动时报Too few arguments
最易被忽略的一点:TP6 的中间件执行是「洋葱模型」,但异常捕获逻辑变了——如果中间件里抛出异常,且没有被上层 try/catch 捕获,框架会直接终止后续中间件,连 AppEnd 事件都不会触发。调试时别只盯着日志输出位置,先看异常是否被静默吞掉。











