ThinkPHP中where数组空值过滤需按字段语义判断:字符串用isset($v) && strlen((string)$v)>0,数字用is_numeric($v)或显式允许0,时间范围整体判断;array_filter需加ARRAY_FILTER_USE_BOTH保留键名;CLI下需手动解析$_SERVER['argv'];allowField()仅在模型实例中有效且字段名须严格匹配数据库列名。

ThinkPHP的where()传数组时怎么安全过滤空值
直接用 where($array) 会把 ['name' => ''] 当成真实条件执行,查不到数据还难排查。关键不是“要不要过滤”,而是“用什么逻辑判断才算‘该过滤’”。
-
empty()会把'0'、0、false全当空,但 status=0 是合法查询条件 - 推荐用
isset($v) && strlen((string)$v) > 0判断字符串类字段(兼容'0') - 数字字段优先用
is_numeric($v)或显式允许零值:in_array($v, [0, 1, 2], true) - 时间范围这类组合条件,必须整体判断:
!empty($params['start_time']) && !empty($params['end_time']),不能单字段过滤
array_filter() 过滤 where 数组时为什么丢了键名
默认调用 array_filter($where) 后键名会被重排成数字索引,['title|content' => ['like', '%x%']] 变成 [0 => ['like', '%x%']],导致多字段 OR 查询失效。
- 必须加第三个参数
ARRAY_FILTER_USE_BOTH,保留原始键名 - 过滤回调里不能只写
return $v;——0、false会被误删,得明确排除:return $v !== null && $v !== [] && strlen((string)$v) > 0; - 如果要保留
0或'0',单独加判断:is_numeric($v) || $v === '0'
命令行请求里 input() 和 param() 为啥拿不到参数
因为 ThinkPHP 的 input()、$this->request->param() 全部依赖 HTTP 生命周期,而 CLI 模式下 $_GET、$_POST 为空,Request 实例根本没解析 $_SERVER['argv']。
- 不要试图在
handle()里调input('name'),它永远返回空或默认值 - 手动解析:用
array_slice($_SERVER['argv'], 2)拿到参数段,再array_map('trim', ...)清空格 - 对形如
--user=john的参数,用explode('=', $arg, 2)拆分,再用Validate::filter()做类型校验 - 别漏掉短参数支持,比如
-u john需额外遍历匹配
allowField() 为什么没拦住非法字段
allowField() 是唯一真正起作用的白名单机制,但很多人调用方式错了,导致它完全不生效。
立即学习“PHP免费学习笔记(深入)”;
- 静态调用
User::save($data)绕过模型实例,allowField()彻底失效 - 字段名必须和数据库列名**完全一致**:配置里写
userName,但库中是user_name→ 直接被丢弃且不报错 - 用
Db::name('user')->update($data)也绕过模型,只能手写array_intersect_key($data, array_flip($whitelist)) - 动态白名单必须清洗:去重(
array_unique())、过滤非字符串项(array_filter($fields, 'is_string'))、校验字段是否存在((new User())->getSchema()->getFields())
array_filter($arr, 'strlen') 就完事——status=0、score=0、is_hot='0' 全是有效值,硬过滤等于埋雷。



















