优先用 Db::table('user')->where($condition)->select(),因 query() 不绑定参数易致 SQL 注入,where() 底层走 PDO 预处理自动绑定;when() 必须 return $query 否则条件丢失;in 查询需校验类型后交由框架处理;动态字段名须白名单校验。

ThinkPHP 执行 SQL 时做动态条件,不能直接拼接用户输入进字符串 SQL,必须用参数绑定或查询构建器;原生 query() 不支持自动绑定,要用 Db::table()->where()->select() 链式调用或手写预处理 SQL。
动态条件该用 query() 还是 where()?
优先用 Db::table('user')->where($condition)->select(),而不是 Db::query("SELECT * FROM user WHERE status = $status")。原因很实在:
-
query()是纯原生执行,不解析参数、不绑定、不转义,$status为"1 OR 1=1"就直接注入 -
where()链式调用底层走 PDO 预处理,值自动绑定,字段名由框架校验(除非你手动拼进字符串) - 只有当 SQL 极其复杂(比如多层子查询 + 窗口函数 + UNION)才退回到
query(),且必须自己用str_replace或白名单控制字段/表名
when() 动态加条件为什么没生效?
最常见原因是闭包里漏了 return $query,导致链式中断,条件被静默丢弃。现象是:参数传进来了,SQL 里却完全没那段 WHERE。
- 错误写法:
->when($name, function($query) use($name) { $query->where('name', $name); }) - 正确写法:
->when($name, function($query) use($name) { return $query->where('name', $name); }) - 注意
$name为0、""、null时会被判 false,若业务要区分0和空,改用->when(!is_null($name), ...) - 多个
when()按顺序执行,但无法控制括号层级;想实现(a=1 AND b=2) OR c=3,得用where()闭包分组
字符串条件中怎么安全拼 in 查询?
别写 "id IN (" . implode(',', $ids) . ")",数字数组还好,一旦含字符串或用户可控内容就炸。正确做法分两步:
立即学习“PHP免费学习笔记(深入)”;
- 确认
$ids全是整型:用array_map('intval', $ids)或循环is_numeric()校验 - 用
where('id', 'in', $ids),框架会自动展开成IN (1,2,3)并绑定参数 - 如果非要用
query(),就得手动生成占位符:$placeholders = str_repeat('?,', count($ids) - 1) . '?';,再execute($ids) - 字段名动态时(如按用户选的排序字段),必须白名单校验:
in_array($field, ['id', 'name', 'create_time']),否则反引号也防不住语法错
真正容易被忽略的是:当条件来自 URL 参数(如 ?sort=name_desc&status=1),status=1 看似简单,但若没做类型强制(如 (int)$status),配合 when() 可能因松散比较变成 false 而跳过——这种 bug 在测试环境常不暴露,上线后查半天才发现是 "1" 字符串没转整型。



















