PHP中如何基于预定义JSON表单结构校验提交的JSON数据合法性
PHP实现动态表单details字段非法字段拦截方案
你原来写的校验逻辑存在逻辑错误:双层遍历的判断规则会导致合法字段只要和其他任意表单项key不匹配就被误判为非法,完全无法正常工作。
正确实现的核心思路是提前提取表单配置里的所有合法key作为白名单,所有提交的字段只需要和白名单做比对即可,不需要双层循环遍历配置。同时可以附加格式、类型校验,避免非法结构写入JSON字段。
完整实现代码
/** * 校验employee表details提交数据合法性 * @param array $data 前端提交的details数组(json_decode后的数组格式) * @param array $formDefinition 后台预定义的表单结构配置 * @return bool 校验通过返回true * @throws InvalidArgumentException 校验不通过抛出对应错误 */ public function dataValidator(array $data, array $formDefinition): bool { // 预提取合法字段白名单、字段-类型映射表,只需要遍历一次表单配置 $allowedKeys = array_column($formDefinition, 'key'); $keyTypeMap = array_column($formDefinition, 'datatype', 'key'); foreach ($data as $index => $item) { // 先校验基础结构:每个元素必须是单键值对结构,和约定的存储格式一致 if (!is_array($item)) { throw new InvalidArgumentException(sprintf('第%d个数据项格式非法,必须为键值对对象', $index + 1)); } if (count($item) !== 1) { throw new InvalidArgumentException(sprintf('第%d个数据项仅允许传入1个表单字段', $index + 1)); } $currentKey = array_key_first($item); $currentValue = $item[$currentKey]; // 核心白名单校验:不在预定义key列表里的字段直接拦截 if (!in_array($currentKey, $allowedKeys, true)) { throw new InvalidArgumentException(sprintf('检测到未定义的非法字段:%s,禁止写入', $currentKey)); } // 可选附加:按表单定义的datatype校验值类型,不需要可以删掉这段 $expectedType = $keyTypeMap[$currentKey]; $isTypeMatch = match ($expectedType) { 'string' => is_string($currentValue), 'boolean' => is_bool($currentValue), 'choice' => is_scalar($currentValue), // 选项值一般是字符串/数字,可根据自身业务调整校验规则 default => true }; if (!$isTypeMatch) { throw new InvalidArgumentException(sprintf('字段%s类型错误,期望类型为%s', $currentKey, $expectedType)); } } return true; }
调用示例
// 你的预定义表单配置(注意原配置里isRetired的value为空属于语法错误,这里补为null) $formDefinition = [ ['key' => 'name', 'value' => '', 'datatype' => 'string'], ['key' => 'sex', 'value' => '', 'datatype' => 'choice'], ['key' => 'occupation', 'value' => '', 'datatype' => 'string'], ['key' => 'isRetired', 'value' => null, 'datatype' => 'boolean'] ]; // 前端提交后,先把JSON字符串转成数组再传入校验 $submitDetails = json_decode($request->input('details'), true); try { $this->dataValidator($submitDetails, $formDefinition); // 校验通过再写入数据库 $employee = Employee::find($id); $employee->details = json_encode($submitDetails); $employee->save(); } catch (InvalidArgumentException $e) { // 校验失败直接返回错误提示 return back()->with('error', $e->getMessage()); }
校验效果说明
- 所有未在
formDefinition中定义的字段(比如额外传入age、address等字段)会被直接拦截,从根源杜绝非法字段写入 - 不符合约定存储结构的数据(比如单个数组元素塞多个键值对、传入非键值对格式内容)会被提前拦截
- 附加的类型校验可以过滤和字段预期类型不匹配的内容,减少后续数据读取、展示时的异常
内容的提问来源于stack exchange,提问作者Curtis Lanz
相关产品推荐
相关产品推荐

