大家好,我是专注 PHP 实战干货的博主。
做PHP接口开发,最繁琐、最容易出错的环节,莫过于数据校验。新手常犯的错:每个接口都重复写一堆if判断,校验手机号格式、密码长度、参数是否必填;忽略边界值校验,比如金额传负数、身份证号少一位;过度信任前端传参,直接将参数代入数据库操作,导致SQL异常、业务bug,甚至被恶意攻击。
更头疼的是,冗余的校验代码占了接口代码的一半,后期修改时,要逐个接口调整,效率极低。今天分享一个 PHP 数据校验极简方案,无需任何第三方扩展,1个极简校验类,一行代码实现参数校验,自动收集错误提示,少写80%冗余代码,彻底杜绝非法请求和业务bug。
一、核心痛点(每个PHP开发者都踩过)
参数校验繁琐,每个接口重复写if判断,代码冗余、可读性差;
校验规则不统一,有的接口校验手机号,有的不校验,易遗漏;
忽略边界值校验(如金额≤0、密码长度不足6位),导致业务异常;
过度信任前端传参,未校验的参数代入数据库,引发SQL错误;
错误提示零散,前后端对接时,需反复确认校验规则和错误原因。
二、核心方案:极简校验类(直接复制,无需扩展)
单文件 Validator.php,兼容PHP7+,无需Composer、无需第三方扩展,支持「必填、格式、长度、范围」四大核心校验,自动收集错误提示,直接引入项目即可使用,可根据自己的业务需求灵活扩展。
<?php/** * PHP 数据校验极简类(实战版,新增自定义错误消息) * 特性:无需第三方扩展、支持多规则校验、自动收集错误、高频场景内置、自定义错误消息 */classValidator{ // 错误信息集合 private $errors = []; // 自定义错误消息(可全局配置,也可单字段配置,优先级:单字段 > 全局 > 默认) private $customMsg = []; /** * 构造函数:初始化自定义错误消息(可选) * @param array $customMsg 全局自定义错误消息 * 格式:['字段名.规则' => '自定义消息',如 'mobile.required' => '请填写手机号'] */ public function __construct(array $customMsg = []) { $this->customMsg = $customMsg; } /** * 校验单个参数 * @param mixed $value 待校验值 * @param array $rules 校验规则(可新增msg字段,配置单字段自定义消息) * 例:['required' => true, 'type' => 'mobile', 'msg' => ['required' => '手机号不能为空']] * @param string $field 字段名(用于错误提示) * @return $this */ public function check($value, array $rules, string $field): self { // 1. 必填校验 if(isset($rules['required']) && $rules['required']) { if(empty($value) && $value !== 0 && $value !== '0') { // 优先级:单字段msg > 全局customMsg > 默认消息 $msg = $rules['msg']['required'] ?? $this->customMsg["{$field}.required"] ?? "{$field}不能为空"; $this->errors[] = $msg; return $this; } } // 空值且非必填,跳过后续校验 if(empty($value) && $value !== 0 && $value !== '0') { return $this; } // 2. 类型/格式校验 if(isset($rules['type'])) { $type = $rules['type']; $isValid = true; switch($type) { case 'mobile': // 手机号 if(!preg_match('/^1[3-9]\d{9}$/', $value)) { $isValid = false; } break; case 'email': // 邮箱 if(!filter_var($value, FILTER_VALIDATE_EMAIL)) { $isValid = false; } break; case 'idcard': // 身份证(简单校验,可根据需求优化) if(!preg_match('/^\d{17}[\dXx]$/', $value)) { $isValid = false; } break; case 'number': // 数字(整数/小数) if(!is_numeric($value)) { $isValid = false; } break; case 'integer': // 整数 if(!is_int($value) && !preg_match('/^\d+$/', $value)) { $isValid = false; } break; } // 类型校验失败,获取自定义消息 if(!$isValid) { $defaultMsg = [ 'mobile' => "{$field}格式不正确(需为11位有效手机号)", 'email' => "{$field}格式不正确", 'idcard' => "{$field}格式不正确", 'number' => "{$field}必须为数字", 'integer' => "{$field}必须为整数" ]; $msg = $rules['msg'][$type] ?? $this->customMsg["{$field}.{$type}"] ?? $defaultMsg[$type]; $this->errors[] = $msg; } } // 3. 长度校验(字符串/数组) if(isset($rules['length'])) { $len = is_array($value) ? count($value) : mb_strlen($value); $min = $rules['length']['min'] ?? 0; $max = $rules['length']['max'] ?? PHP_INT_MAX; if($len < $min || $len > $max) { $msg = $rules['msg']['length'] ?? $this->customMsg["{$field}.length"] ?? "{$field}长度必须在{$min}-{$max}之间"; $this->errors[] = $msg; } } // 4. 范围校验(数字) if(isset($rules['range']) && is_numeric($value)) { $min = $rules['range']['min'] ?? 0; $max = $rules['range']['max'] ?? PHP_INT_MAX; if($value < $min || $value > $max) { $msg = $rules['msg']['range'] ?? $this->customMsg["{$field}.range"] ?? "{$field}必须在{$min}-{$max}之间"; $this->errors[] = $msg; } } // 5. 自定义正则校验(保留原有自定义消息逻辑,兼容新增配置) if(isset($rules['regex']) && !preg_match($rules['regex'], $value)) { $msg = $rules['regex_msg'] ?? $this->customMsg["{$field}.regex"] ?? "{$field}格式不正确"; $this->errors[] = $msg; } return $this; } /** * 批量校验参数 * @param array $data 待校验数据(键为字段名,值为待校验值) * @param array $rules 批量校验规则(键为字段名,值为校验规则) * @return $this */ public function batchCheck(array $data, array $rules): self { foreach($rules as $field => $rule) { $value = $data[$field] ?? ''; $this->check($value, $rule, $field); } return $this; } /** * 判断校验是否通过 * @return bool */ public function isValid(): bool { return empty($this->errors); } /** * 获取错误信息(单个/全部) * @param bool $first 是否只返回第一个错误 * @return array|string */ public function getError(bool $first = false) { if($first) { return $this->errors[0] ?? ''; } return $this->errors; } /** * 重置错误信息 * @return $this */ public function reset(): self { $this->errors = []; return $this; } /** * 动态设置自定义错误消息(支持中途修改) * @param array $customMsg 格式同构造函数 * @return $this */ public function setCustomMsg(array $customMsg): self { $this->customMsg = array_merge($this->customMsg, $customMsg); return $this; }}
三、高频场景实战示例(直接套用)
以下是5个中小项目最常用的校验场景,重点新增「自定义错误消息」实战,支持「单字段自定义」「全局自定义」两种方式,复制代码就能用,无需修改核心逻辑,只需根据自己的字段名和提示需求调整即可。
示例1:用户注册接口校验(手机号+密码+邮箱)
<?phprequire_once 'Validator.php';// 模拟前端传参(实际项目用$_POST/$_GET获取)$params = [ 'mobile' => '13800138000', // 待校验手机号 'password' => '123456', // 待校验密码 'email' => 'test@163.com', // 待校验邮箱 'nickname' => 'PHP干货' // 待校验昵称];// 方式1:全局自定义错误消息(适合全项目统一提示风格)$globalCustomMsg = [ 'mobile.required' => '请填写您的手机号', 'mobile.mobile' => '请输入11位有效手机号', 'password.required' => '请设置登录密码', 'password.length' => '密码长度需6-16位,包含字母和数字更佳',#xA; 'email.required' => '请填写您的邮箱地址', 'email.email' => '邮箱格式不正确,请重新输入', 'nickname.required' => '请设置您的昵称', 'nickname.length' => '昵称长度需2-10位,请勿包含特殊字符'];// 初始化校验器,传入全局自定义消息$validator = new Validator($globalCustomMsg);// 方式2:单字段自定义错误消息(优先级高于全局,适合特殊字段提示)$rules = [ 'mobile' => [ 'required' => true, 'type' => 'mobile', // 单字段自定义消息(覆盖全局消息) 'msg' => [ 'required' => '手机号不能为空,用于接收登录验证码', 'mobile' => '手机号格式错误,请输入正确的11位手机号' ] ], 'password' => [ 'required' => true, 'length' => ['min' => 6, 'max' => 16], // 单字段自定义消息 'msg' => ['length' => '密码需6-16位,建议包含字母、数字和符号,提升安全性'] ], 'email' => [ 'required' => true, 'type' => 'email' // 使用全局自定义消息 ], 'nickname' => [ 'required' => true, 'length' => ['min' => 2, 'max' => 10] // 使用全局自定义消息 ]];// 方式3:中途动态设置自定义消息(适合临时调整)// $validator->setCustomMsg([// 'nickname.length' => '昵称长度调整为2-8位,请重新设置'// ]);// 3. 执行校验$validator->batchCheck($params, $rules);// 4. 校验结果判断if(!$validator->isValid()) { // 校验失败,返回自定义错误信息(前后端统一格式) echo json_encode([ 'code' => 400, 'msg' => $validator->getError(true), // 只返回第一个错误,更友好 'data' => [] ], JSON_UNESCAPED_UNICODE); exit;}// 校验通过,执行注册逻辑(省略数据库操作)echo json_encode([ 'code' => 200, 'msg' => '注册成功', 'data' => []], JSON_UNESCAPED_UNICODE);
示例2:商品添加接口校验(金额+库存)
示例3:身份证校验(用户实名认证)
<?phprequire_once 'Validator.php';$params = [ 'goods_name' => '测试商品', 'price' => 99.9, // 商品金额 'stock' => 100 // 商品库存];// 全局自定义错误消息(适配商品管理场景)$customMsg = [ 'goods_name.required' => '请填写商品名称', 'goods_name.length' => '商品名称长度需1-50字,简洁明了', 'price.required' => '请设置商品售价', 'price.number' => '商品售价必须为数字', 'price.range' => '商品售价需在0.01-99999.99元之间', 'stock.required' => '请设置商品库存', 'stock.integer' => '商品库存必须为整数', 'stock.range' => '商品库存需在0-10000件之间'];$validator = new Validator($customMsg);$rules = [ 'goods_name' => [ 'required' => true, 'length' => ['min' => 1, 'max' => 50] ], 'price' => [ 'required' => true, 'type' => 'number', 'range' => ['min' => 0.01, 'max' => 99999.99] ], 'stock' => [ 'required' => true, 'type' => 'integer', 'range' => ['min' => 0, 'max' => 10000] ]];$validator->batchCheck($params, $rules);if(!$validator->isValid()) { echo json_encode([ 'code' => 400, 'msg' => $validator->getError(true), 'data' => [] ], JSON_UNESCAPED_UNICODE); exit;}// 校验通过,执行商品添加逻辑echo json_encode([ 'code' => 200, 'msg' => '商品添加成功', 'data' => []], JSON_UNESCAPED_UNICODE);
示例4:自定义正则校验(验证码)
<?phprequire_once 'Validator.php';$params = [ 'idcard' => '110101199003071234' // 待校验身份证号];// 单字段自定义错误消息(实名认证场景,提示更精准)$validator = new Validator();$rules = [ 'idcard' => [ 'required' => true, 'type' => 'idcard', 'msg' => [ 'required' => '请填写您的身份证号,用于实名认证', 'idcard' => '身份证号格式不正确,请核对后重新输入' // 不暴露具体错误,符合安全要求 ] ]];$validator->batchCheck($params, $rules);if(!$validator->isValid()) { echo json_encode([ 'code' => 400, 'msg' => $validator->getError(true), 'data' => [] ], JSON_UNESCAPED_UNICODE); exit;}// 校验通过,执行实名认证逻辑echo json_encode([ 'code' => 200, 'msg' => '实名认证成功', 'data' => []], JSON_UNESCAPED_UNICODE);
<?phprequire_once 'Validator.php';$params = [ 'code' => '123456' // 6位数字验证码];// 结合自定义消息,优化验证码提示$validator = new Validator();$rules = [ 'code' => [ 'required' => true, 'regex' => '/^\d{6}$/', 'regex_msg' => '验证码必须为6位数字,请重新输入', // 自定义正则提示 'msg' => ['required' => '请填写收到的6位验证码'] // 自定义必填提示 ]];$validator->batchCheck($params, $rules);if(!$validator->isValid()) { echo json_encode([ 'code' => 400, 'msg' => $validator->getError(true), 'data' => [] ], JSON_UNESCAPED_UNICODE); exit;}// 校验通过,执行验证码验证逻辑echo json_encode([ 'code' => 200, 'msg' => '验证码正确', 'data' => []], JSON_UNESCAPED_UNICODE);
四、必看避坑点(生产环境防踩雷)
校验类再好用,忽略以下几点,依然会出现bug和安全隐患,这5个避坑点一定要记牢!
- 避坑1:不要忽略边界值校验比如金额校验,只校验是否为数字,不校验是否≥0.01,会导致用户提交0元订单;库存校验不限制最大值,可能导致库存填错(如1000000),引发业务异常。
- 避坑2:不要信任前端传参前端的校验只是“友好提示”,恶意用户可以绕过前端校验,直接向接口提交非法参数(如手机号填123456)。后端必须做二次校验,这是最后一道防线。
- 避坑3:敏感参数必须二次校验涉及金钱、用户隐私的参数(如金额、身份证、手机号),除了格式校验,还要做业务校验。比如手机号校验通过后,还要校验是否已被注册;金额校验通过后,还要校验是否符合商品定价规则。
- 避坑4:错误提示不要暴露过多信息比如校验身份证时,不要提示“身份证号第18位错误”,只需提示“身份证格式不正确”;避免泄露校验规则,防止恶意用户针对性绕过,自定义消息也需遵循此原则。
- 避坑5:自定义消息优先级要理清单字段配置的msg(如$rules[‘mobile’][‘msg’])> 全局自定义消息(构造函数传入)> 类内默认消息,避免配置冲突导致提示异常;建议全局消息统一风格,特殊字段用单字段消息微调。
校验类再好用,忽略以下几点,依然会出现bug和安全隐患,这4个避坑点一定要记牢!
五、核心价值与落地步骤
核心价值
少写80%冗余代码:无需重复写if判断,一行代码实现多规则校验;
杜绝非法请求:从源头拦截非法参数,减少数据库异常和业务bug;
前后端对接高效:错误提示统一、可自定义,无需反复确认校验规则,提升用户体验;
灵活扩展:可根据业务需求,新增校验类型(如银行卡、手机号归属地),自定义消息支持多场景适配。
落地步骤(1分钟搞定)
新建 Validator.php 文件,复制上面的校验类代码;
在需要校验的接口中,引入 require_once 'Validator.php';;
根据接口参数,定义校验规则,调用 batchCheck 执行校验;
判断校验结果,校验失败返回错误信息,校验通过执行业务逻辑。
落地步骤(1分钟搞定)
新建 Validator.php 文件,复制上面的校验类代码;
在需要校验的接口中,引入 require_once 'Validator.php';;
根据接口参数,定义校验规则,调用 batchCheck 执行校验;
判断校验结果,校验失败返回错误信息,校验通过执行业务逻辑。
总结
PHP数据校验,核心不是“写得多复杂”,而是“简单、高效、不遗漏”。这个极简校验类,无需第三方扩展,直接复制可用,覆盖中小项目90%的校验场景,既能少写冗余代码,又能杜绝非法请求,新手也能快速上手。
关注我,后续继续分享PHP实战干货,每天3分钟,提升开发效率。
欢迎在留言区告诉我:你项目中遇到过哪些参数校验的坑?比如非法参数导致的bug、前端绕过校验的问题,我们一起交流解决方案!