ThinkPHP 中需在中间件统一归一化空值并区分请求类型处理:JSON 请求须在 AppInit 阶段解析原始 input,multipart 表单用 param() + merge();TP5.1 无 input('user.*') 语法,须手动提取子数组;$request->post() 是只读缓存,TP6.0.13+ 才支持 setPost() 同步更新。

ThinkPHP 6 的 validate 怎么接住前端传来的空字符串和 null?
默认校验会把 null 当缺失字段跳过,'' 又可能被当成合法值,结果业务层还得反复判空。关键不是“要不要校验”,而是“校验前先归一化”。
-
validate前手动调用$request->only()+array_filter($data, function($v) { return $v !== null && $v !== ''; })不行 —— 会误删 0、'0'、false 等有效值 - 正确做法是在中间件里统一处理:用
$request->param()拿原始数据,对每个字段做trim()和is_null() ? '' : $v转换,再塞回请求实例($request->merge()) - 注意
application/multipart-formdata和application/json的解析时机不同,json请求需在AppInit阶段就处理input('')原始体,否则param()已解析完,改不动了
怎么让 ThinkPHP 5.1 的 input() 返回数组结构而不是扁平键名?
前端传 { "user": { "name": "a", "age": 18 } },input('user.name') 能取到,但想直接拿到整个 user 子数组就得写 input('user/a.*') —— 这个语法只在 TP6 支持,TP5.1 默认不认。
- TP5.1 必须用
input('')拿全部,再用array_key_exists('user', $data) ? $data['user'] : []手动提取 - 如果用了
think-validate,可在验证规则里加user.require+user.array,但不会自动嵌套,只是保证字段存在且是数组类型 - 别依赖
input('user.')(带点结尾),TP5.1 会返回空,不是子数组 —— 这是常见误读,文档没写清,实际源码里input的点号只支持两级精确匹配
TP6 中间件里统一过滤参数,为什么 $request->post() 还是旧值?
因为 $request->post() 是只读缓存,一旦首次调用就固化,后续 merge() 或 bind() 不会影响它。你改了底层 param,但 post() 不同步。
- 解决方案只有两个:要么全程弃用
post(),只用param();要么在中间件末尾调用$request->setPost($newPostData)(TP6.0.13+ 才有该方法,低版本无效) - 检查 TP 版本:
thinkphp/library/think/Request.php里有没有setPost方法,没有就只能重写app/middleware/ValidateInput.php并确保它在LoadLangPack之前执行 - 别在控制器里再调
$this->request->post()后又去改param,顺序错乱会导致部分逻辑读到新值、部分读到旧值
JSON 请求体里传了 {"status":"on"},怎么让 status 自动转成布尔?
TP 默认不做类型推断,input('status') 拿到的就是字符串 "on"。硬编码 in_array($v, ['on','true','1']) 散落在各处,维护成本高。
立即学习“PHP免费学习笔记(深入)”;
- 推荐在验证器里加自定义规则:
['status' => 'boolean|in:on,off,true,false,1,0'],然后用filter:boolval或闭包function($v) { return in_array($v, ['on','true','1'], true); } - 更彻底的做法是写一个
StatusCast类,继承think\contract\CastInterface,在模型的$cast里配'status' => StatusCast::class,但注意这仅对Db查询和model->save()生效,不影响请求层 - 别用
filter:bool直接套 —— PHP 的(bool)"on"是true,但(bool)"off"也是true,语义全毁
post() 的缓存行为,这两点一错,后面所有标准化都白做。


















