ThinkPHP3.2.3 升级 ThinkPHP8.1 实战指南
从 ThinkPHP3.2.3 跳跃升级至 ThinkPHP8.1,核心难点在于底层 API、语法规范、目录结构的大幅变更。本文覆盖控制器、模型、路由、验证、数据库操作等核心模块,通过“TP3.2.3 旧代码 vs TP8.1 新代码”的对比形式,直观呈现升级要点,助力开发者快速落地修改。
前置说明:升级前需完成 3 件事① 备份代码与数据库,创建独立升级分支;② 部署 PHP8.0+ 环境(推荐 PHP8.1),安装最新版 Composer;③ 新建 TP8.1 项目(composer create-project topthink/think tp81-project),后续将 TP3.2.3 核心代码迁移至新项目并修改。
一、目录结构与入口文件修改
TP3.2.3 与 TP8.1 目录结构差异显著,需先完成基础目录迁移与入口文件调整,确保项目可正常访问。
1. 目录迁移核心规则
| TP3.2.3 目录/文件 | TP8.1 对应目录/文件 | 关键说明 |
|---|---|---|
| Application/ | app/ | 模块(如 Home、Admin)直接迁移至 app/,目录名小写(推荐) |
| Application/Common/Conf/ | config/ | 公共配置迁移至 config/ 目录,按 TP8.1 文件名规范重命名(如 config.php、database.php) |
| index.php(根目录) | public/index.php | 替换为 TP8.1 入口文件,Web 服务器根目录指向 public/ |
2. 入口文件代码修改示例
TP3.2.3 根目录 index.php(旧代码):
<?php
// 定义应用目录
define('APP_PATH', './Application/');
// 引入ThinkPHP入口文件
require './ThinkPHP/ThinkPHP.php';TP8.1 public/index.php(新代码,直接使用框架默认入口文件,无需修改核心逻辑):
<?php // [ 应用入口文件 ] namespace think; // 加载基础文件 require __DIR__ . '/../vendor/autoload.php'; // 执行应用并响应 $http = (new App())->http; $response = $http->run(); $response->send(); $http->end($response);
二、控制器代码修改:命名空间与继承适配
TP3.2.3 控制器无强制命名空间要求,继承 Think\Controller;TP8.1 需严格遵循命名空间规范,继承think\Controller,同时支持依赖注入。
核心修改示例:基础控制器
TP3.2.3 Application/Home/Controller/IndexController.php(旧代码):
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
// 首页方法
public function index() {
// 接收参数
$id = I('get.id', 0, 'intval');
// 渲染模板
$this->assign('id', $id);
$this->display();
}
}TP8.1 app/controller/Index.php(新代码):
<?php
namespace app\controller;
use think\Controller;
use think\facade\Request; // 引入请求类
class Index extends Controller {
// 首页方法(支持依赖注入,如注入Request对象)
public function index(Request $request) {
// 接收参数(替代I()函数)
$id = $request->get('id', 0, 'intval');
// 渲染模板(模板目录调整至 view/ 下)
return $this->fetch('index', ['id' => $id]);
}
}关键修改点:① 命名空间统一为 app\controller;② 接收参数使用 think\facade\Request 替代 I() 函数;③ 渲染模板用 fetch() 替代 display(),模板目录迁移至 view/[模块名]/ 下。
三、模型代码修改:ORM 与软删除适配
TP8.1 依赖 think-orm 3.0+,模型的时间戳处理、软删除逻辑、关联查询语法与 TP3.2.3 差异极大,需重点重构。
1. 基础模型修改示例
TP3.2.3 Application/Home/Model/UserModel.php(旧代码):
<?php
namespace Home\Model;
use Think\Model;
class UserModel extends Model {
// 表名(默认无需指定,按规则匹配)
protected $tableName = 'user';
// 自动完成(时间戳)
protected $_auto = [
['create_time', 'time', self::MODEL_INSERT, 'function'],
['update_time', 'time', self::MODEL_BOTH, 'function'],
];
// 软删除配置
protected $deleteTime = 'delete_time';
}TP8.1 app/model/User.php(新代码):
<?php
namespace app\model;
use think\Model;
use think\model\concern\SoftDelete; // 引入软删除Trait
class User extends Model {
// 表名(若与模型名不一致需指定)
protected $name = 'user';
// 时间戳字段(显式声明,默认自动写入)
protected $createTime = 'create_time';
protected $updateTime = 'update_time';
// 软删除配置(替代TP3.2.3的$deleteTime)
use SoftDelete;
protected $deleteTime = 'delete_time';
protected $defaultSoftDelete = 0; // 软删除标识(0=未删除,非0=删除时间戳)
}2. 关联查询修改示例
TP3.2.3 模型关联(旧代码):
// UserModel 中定义关联
public function order() {
// 一对一关联:用户对应一个订单
return $this->hasOne('OrderModel', 'user_id', 'id');
}
// 控制器中调用
$user = D('User')->find(1);
$orderInfo = $user->order;TP8.1 模型关联(新代码):
// User 模型中定义关联
use app\model\Order; // 引入关联模型
public function order() {
// 一对一关联,语法更严格
return $this->hasOne(Order::class, 'user_id', 'id');
}
// 控制器中调用(需通过with()预加载,提升性能)
use app\model\User;
$user = User::with('order')->find(1);
$orderInfo = $user->order;四、路由配置修改:从自动匹配到显式定义
TP3.2.3 支持 URL 自动匹配模块/控制器/方法,TP8.1 强化路由功能,推荐显式定义路由,同时正则写法需调整。
核心修改示例
TP3.2.3 路由(无单独配置文件,依赖自动匹配,或在 Application/Common/Conf/config.php 中简单配置):
'URL_ROUTE_RULES' => [ // 路由规则:/user/:id 对应 Home/UserController/info 方法 'user/:id' => 'Home/User/info', // 带正则约束的路由 'article/:id\d' => 'Home/Article/detail', ]
TP8.1 路由(配置文件 config/route.php,新代码):
<?php
use think\facade\Route;
// 路由规则:/user/:id 对应 app/controller/User.php 的 info 方法
Route::get('user/:id', 'User/info')->rule([
'id' => '\d+' // 正则约束(替代TP3.2.3的\d后缀)
]);
// 带参数验证的路由(新增特性)
Route::get('article/:id', 'Article/detail')
->rule(['id' => '\d+'])
->when(['id' => '>0'], '文章ID必须大于0'); // 路由变量验证
// 路由分组(新增特性,便于管理)
Route::group('admin', function() {
Route::get('login', 'Admin/login');
Route::get('dashboard', 'Admin/dashboard');
})->middleware('Auth'); // 绑定中间件关键修改点:① 路由配置集中至 config/route.php;② 用 Route::get/post() 显式声明请求方式;③ 正则约束通过 rule() 方法定义,移除 TP3.2.3 的 URL 后缀约束;④ 新增路由分组、中间件绑定、参数验证等特性。
五、验证逻辑修改:从函数验证到验证类
TP3.2.3 常用 D() 函数或 Think\Verify 进行验证,TP8.1 推荐使用独立验证类,扩展了更多验证规则。
核心修改示例
TP3.2.3 验证逻辑(旧代码,写在控制器中):
public function add() {
if (IS_POST) {
$data = I('post.');
// 验证规则
$rules = [
['username', 'require', '用户名不能为空'],
['password', '6,20', '密码长度必须在6-20位之间'],
['email', 'email', '邮箱格式错误'],
];
$user = D('User');
if (!$user->validate($rules)->create($data)) {
$this->error($user->getError());
}
// 数据入库...
}
}TP8.1 验证逻辑(新代码,独立验证类+控制器调用):
1. 新建验证类 app/validate/User.php:
<?php
namespace app\validate;
use think\Validate;
class User extends Validate {
// 验证规则
protected $rule = [
'username' => 'require|chsAlphaNum', // 新增汉字字母数字验证
'password' => 'require|length:6,20',
'email' => 'require|email',
];
// 错误提示
protected $message = [
'username.require' => '用户名不能为空',
'username.chsAlphaNum' => '用户名只能是汉字、字母或数字',
'password.require' => '密码不能为空',
'password.length' => '密码长度必须在6-20位之间',
'email.email' => '邮箱格式错误',
];
// 场景验证(新增特性,按需验证)
protected $scene = [
'add' => ['username', 'password', 'email'], // 新增场景需验证所有字段
'edit' => ['username'], // 编辑场景仅验证用户名
];
}2. 控制器中调用验证类:
use app\validate\User as UserValidate;
use think\facade\Request;
public function add() {
if (Request::isPost()) {
$data = Request::post();
// 实例化验证类并指定场景
$validate = new UserValidate();
if (!$validate->scene('add')->check($data)) {
$this->error($validate->getError());
}
// 数据入库(可通过模型实例化入库)
$user = new \app\model\User();
$user->save($data);
$this->success('新增成功');
}
}六、数据库操作修改:从 M()/D() 到 ORM 方法
TP3.2.3 常用 M()(简单模型)、D()(自定义模型)操作数据库,TP8.1 推荐使用 ORM 方法或查询构造器,废弃 M()/D() 函数。
核心修改示例
TP3.2.3 数据库操作(旧代码):
// 1. 用M()函数查询
$userList = M('User')
->where('status', 1)
->where('create_time', '>', strtotime('-7 day'))
->order('id desc')
->limit(10)
->select();
// 2. 用D()函数新增数据
$user = D('User');
$data = [
'username' => 'test',
'password' => md5('123456'),
'create_time' => time(),
];
$user->add($data); // 新增数据
// 3. 闭包查询
$list = M('Article')
->where(function($query) {
$query->where('cat_id', 1)->orWhere('is_top', 1);
})
->select();TP8.1 数据库操作(新代码):
use app\model\User;
use app\model\Article;
use think\facade\Db; // 或使用Db门面
// 1. 模型查询(推荐)
$userList = User::where('status', 1)
->where('create_time', '>', date('Y-m-d H:i:s', strtotime('-7 day'))) // 需传递标准时间格式
->order('id', 'desc')
->limit(10)
->select();
// 2. 模型新增数据(自动处理时间戳)
$user = new User();
$user->save([
'username' => 'test',
'password' => password_hash('123456', PASSWORD_DEFAULT), // 推荐使用password_hash
]);
// 3. 闭包查询(语法更严格)
$list = Article::where(function($query) {
$query->where('cat_id', 1)->whereOr('is_top', 1); // orWhere改为whereOr
})
->select();
// 4. 用Db门面操作(适合复杂查询)
$list = Db::table('article')
->where(function($query) {
$query->where('cat_id', 1)->whereOr('is_top', 1);
})
->select();七、常见报错与代码修复对照表
| 升级后常见报错 | 报错原因 | 修复代码示例 |
|---|---|---|
| Call to undefined function I() | TP8.1 废弃 I() 函数 | 用 Request 门面替代:$id = I('get.id') → $id = Request::get('id') |
| Class 'Think\Controller' not found | 命名空间变更 | use 语句修改:use Think\Controller → use think\Controller |
| Invalid datetime format: 1292 Incorrect datetime value | 时间戳格式不匹配 | 传递标准时间格式:time() → date('Y-m-d H:i:s') |
| Route not found | 路由未显式定义或正则格式错误 | 在 config/route.php 中定义路由,正则移除斜杠:regex:/\d+/ → regex:\d+ |
八、升级收尾:测试与优化
1. 全量测试:重点测试路由访问、数据增删改查、核心业务(登录、支付、上传)、兼容性(PHP8.1~8.4);2. 性能优化:执行 php think optimize:schema 生成数据库结构缓存,开启 Redis 缓存提升性能;3. 安全优化:检查文件权限(public 目录可写,其他目录只读),替换 md5 加密为 password_hash 加密。
