应校验排序字段和方向白名单,使用数组语法order(['field' => 'asc']),避免字符串拼接;多字段需统一用关联数组;分页前调用order()并透传参数防丢失。

怎么用 order() 动态传入排序方向
ThinkPHP 的 order() 方法支持字符串或数组两种写法,但直接拼接用户输入的 "asc" 或 "desc" 极易引发 SQL 注入——比如用户传入 "id; DROP TABLE user;",字符串拼接会原样执行。
正确做法是先校验排序方向是否合法,再构造参数:
- 只允许
"asc"和"desc"两个值,其他一律 fallback 到默认(如"asc") - 用数组形式调用
order():例如$model->order(['create_time' => $sortDir]),TP 会自动转义值 - 字段名也不能完全信任用户输入,建议白名单过滤(如只允许
['id', 'name', 'create_time'])
示例:
$allowedFields = ['id', 'name', 'create_time'];
$allowedDirs = ['asc', 'desc'];
$field = in_array($request->param('field'), $allowedFields) ? $request->param('field') : 'id';
$dir = in_array($request->param('dir'), $allowedDirs) ? $request->param('dir') : 'asc';
$list = User::order([$field => $dir])->select();
为什么不能用字符串拼接 order()
像 $model->order($field . ' ' . $dir) 这种写法,在 TP 5.x/6.x 中不会对 $dir 做任何校验或转义,最终生成的 SQL 是 ORDER BY create_time desc ——看着没问题,但一旦 $dir 被篡改为 "desc, (SELECT password FROM user LIMIT 1)",就可能泄露敏感数据。
立即学习“PHP免费学习笔记(深入)”;
更隐蔽的问题是:TP 对字符串格式的 order() 参数几乎不拦截,也不记录警告,出问题时很难溯源。
- TP 6.0+ 的
order()数组语法才真正支持安全绑定 - 字符串语法本质是“原样塞进 SQL”,连空格都得自己控制,容易因多空格或换行导致语法错误
- 某些低版本 TP(如 5.0.24 之前)对字符串 order 存在解析漏洞,可绕过基础校验
order() 多字段排序时方向怎么分别控制
TP 支持为不同字段指定不同方向,但必须用数组语法,且键必须是字段名、值必须是方向字符串。不能混用字符串和数组,也不能在同一个 order() 调用里拆成多次。
- 正确:
order(['status' => 'asc', 'sort' => 'desc', 'id' => 'desc']) - 错误:
order('status asc, sort desc')->order('id desc')(后者会覆盖前者) - 错误:
order(['status asc', 'sort desc'])(数组值不是字符串方向,TP 会忽略)
如果用户选择多个排序字段(比如前端传 ["status:asc", "sort:desc"]),需提前解析并转成关联数组:
$raw = $request->param('orders', []);
$parsed = [];
foreach ($raw as $item) {
[$f, $d] = explode(':', $item, 2);
if (in_array($f, $allowedFields) && in_array($d, $allowedDirs)) {
$parsed[$f] = $d;
}
}
$list = User::order($parsed)->select();
分页 + 动态排序组合时的常见坑
分页方法如 paginate() 内部会重置查询条件,如果在 paginate() 之后再调用 order(),排序会被丢弃。
- 必须在
paginate()之前调用order(),顺序不能反 - TP 6.0+ 的
paginate()支持传入['query' => [...]]保留 URL 参数,但排序参数要手动加进去,否则翻页后方向丢失 - 注意缓存键是否包含排序字段和方向,否则不同排序共用同一缓存,返回错乱结果
示例(带参数透传):
$list = User::order([$field => $dir])
->paginate([
'query' => ['field' => $field, 'dir' => $dir],
'list_rows' => 15
]);
动态排序看着只是改个参数,但字段合法性、方向校验、多字段优先级、分页上下文这几个点,漏一个就可能线上出错或被利用。尤其是把用户输入直接喂给 order() 字符串形式,等于在 SQL 边界上裸奔。



















