当前位置:首页>php>PHP 数据校验极简方案,防非法请求

PHP 数据校验极简方案,防非法请求

  • 2026-09-02 19:49:05
PHP 数据校验极简方案,防非法请求

大家好,我是专注 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个避坑点一定要记牢!

五、核心价值与落地步骤

核心价值

  1. 少写80%冗余代码:无需重复写if判断,一行代码实现多规则校验;

  2. 杜绝非法请求:从源头拦截非法参数,减少数据库异常和业务bug;

  3. 前后端对接高效:错误提示统一、可自定义,无需反复确认校验规则,提升用户体验;

  4. 灵活扩展:可根据业务需求,新增校验类型(如银行卡、手机号归属地),自定义消息支持多场景适配。

落地步骤(1分钟搞定)

  1. 新建 Validator.php 文件,复制上面的校验类代码;

  2. 在需要校验的接口中,引入 require_once 'Validator.php';;

  3. 根据接口参数,定义校验规则,调用 batchCheck 执行校验;

  4. 判断校验结果,校验失败返回错误信息,校验通过执行业务逻辑。

落地步骤(1分钟搞定)

  1. 新建 Validator.php 文件,复制上面的校验类代码;

  2. 在需要校验的接口中,引入 require_once 'Validator.php';;

  3. 根据接口参数,定义校验规则,调用 batchCheck 执行校验;

  4. 判断校验结果,校验失败返回错误信息,校验通过执行业务逻辑。

总结

PHP数据校验,核心不是“写得多复杂”,而是“简单、高效、不遗漏”。这个极简校验类,无需第三方扩展,直接复制可用,覆盖中小项目90%的校验场景,既能少写冗余代码,又能杜绝非法请求,新手也能快速上手。

关注我,后续继续分享PHP实战干货,每天3分钟,提升开发效率。

欢迎在留言区告诉我:你项目中遇到过哪些参数校验的坑?比如非法参数导致的bug、前端绕过校验的问题,我们一起交流解决方案!

最新文章

随机文章