WebGeeker-Validation: 一个强大的 PHP 参数验证器

用于对API接口的请求参数进行合法性检查。

在实现服务端的API接口时,对于每一个接口的每一个参数,都应该检测其取值是否合法,以免错误的数据输入到系统中。这个工作可以说是费时费力,但又不得不做。而且PHP本身是弱类型语言,不但要验证取值,还要验证数据的类型是否符合,这就更复杂了。

本工具就是针对这个工作而设计的,能够有效地减少编码量,代码可读性好。

看看下面这段代码,可以对用法有个大概印象,应该不难看懂:

$params = $request->query(); // 获取GET参数 // 验证(如果验证不通过,会抛出异常) Validation::validate($params, [ "offset" => "IntGe:0", // 参数"offset"必须是大于等于0的整数 "count" => "Required|IntGeLe:1,200", // 参数"count"是必需的且取值在 1 - 200 之间 ]);

支持多种数据类型的校验:整型、浮点型、bool型、字符串、数组、对象、文件、日期时间,能够验证嵌套的数据结构中的参数,还支持带条件判断的验证。

目录 1 简介 1.1 为什么要写这样一个工具? 1.2 特点 1.3 一个简单示例 2 安装 3 快速上手 3.1 一个完整的示例(不使用任何框架) 3.2 验证不通过的错误处理 3.3 在第三方框架中的用法 4 详细使用方法 4.1 验证整型参数 4.2 验证浮点型参数 4.3 验证bool型参数 4.4 验证字符串型参数 4.5 验证数组型、对象型、文件型、日期时间型参数 4.6 验证器串联(与) 4.7 Required 验证器 4.8 忽略所有 Required 验证器 4.9 嵌套参数的验证 4.10 条件判断型验证器 4.11 验证规则并联(或) 4.12 关于特殊值null, ""0false的问题 4.13 关于基本数据类型与字符串的关系 4.14 自定义错误信息输出文本 4.15 国际化 4.16 国际化(0.4版之前) 4.17 自定义验证器 A 附录 - 验证器列表 A.1 整型 A.2 浮点型 A.3 bool型 A.4 字符串型 A.5 数组型 A.6 对象型 A.7 文件型 A.8 日期和时间型 A.9 条件判断型 A.10 其它验证器

1 简介

1.1 为什么要写这样一个工具?

我在使用Laravel框架的时候,Laravel提供了一个参数验证工具,不过用起来不怎么顺畅:

每一个验证都写一个验证类(继承XXX),这样太麻烦,而且系统中会多出许多许多的类;如果这些类在多处被复用,或者为了“更加”复用(减少重复代码),再在这些类之间搞出很多的继承关系,那么这些类的维护本身就是一个大问题; 验证器有“一词多义”的问题。比如它有一个size验证器,它同时支持验证字符串、整型、文件等多种类型的参数,针对不同数据类型size的含义不一样。这就好比你去背英语单词,有那么一些英语单词,它有很多很多意思,不同的语境下有不同的含义。比如"present"这个单词,它既有“呈现”、“出席”的意思,也有“礼物”的意思。这种一词多义的单词最让人头疼了,搞不清它到底什么意思,而且记不住啊。

为了解决这些问题,所以才写了这么一个工具。

1.2 特点

每个功能特性都有单元测试(共有 44 tests, 700+ assertions) 支持无限嵌套的数据结构的验证(参考 1.3 节的例子) 支持条件验证,根据参数取值不同,应用不同的验证规则(参考 1.3 节的例子) 支持正则表达式验证 简洁,验证逻辑一目了然 轻量,不需要定义和维护各种验证classes 验证器语义明确,没有“一词多义”的问题 易学易记。比如整型验证器都是以"Int"开头,浮点型验证器都是以"Float"开头,等等。唯一不符合这一规则的是字符串型验证器,它们一部分以"Str"开头的,但也有一部分不以"Str"开头,比如Regexp, Ip, Email, Url等。 不绑定任何一个框架,无任何依赖。你可以在任何一个框架中使用这个工具,就算你不使用框架,也可以使用本工具。 支持自定义验证器,可以实现各种自定义验证功能

1.3 一个简单示例

下面这个示例展示了一个查询获取用户投诉列表的Request参数的验证(注意其中的条件验证和针对嵌套数据结构的验证):

//验证规则 $validations = [ "offset" => "IntGe:0", // 参数offset应该大于等于0 "count" => "Required|IntGeLe:1,200", // 参数count是必需的且大于等于1小于等于200 "type" => "IntIn:1,2", // 参数type可取值为: 1, 2 "state" => [ 'IfIntEq:type,1|IntEq:0', // 如果type==1(批评建议),那么参数state只能是0 'IfIntEq:type,2|IntIn:0,1,2', // 如果type==2(用户投诉),那么参数state可取值为: 1, 2, 3 ], "search.keyword" => "StrLenGeLe:1,100", // search.keyword 应该是一个长度在[1, 100]之间的字符串 "search.start_time" => "Date", // search.start_time 应该是一个包含合法日期的字符串 "search.end_time" => "Date", // search.end_time 应该是一个包含合法日期的字符串 ]; // 待验证参数 $params = [ "offset" => 0, // 从第0条记录开始 "count" => 10, // 最多返回10条记录 "type" => 2, // 1-批评建议, 2-用户投诉 "state" => 0, // 0-待处理, 1-处理中, 2-已处理 "search" => [ // 搜索条件 "keyword" => '硬件故障', // 关键字 "start_time" => "2018-01-01", // 起始日期 "end_time" => "2018-01-31", // 结束日期 ], ]; // 验证(如果验证不通过,会抛出异常) Validation::validate($params, $validations);

2 安装

通过Composer安装

composer require webgeeker/validation:^0.5.2

3 快速上手

3.1 一个完整的示例(不使用任何框架)

这个例子直接验证$_POST(POST表单)中的参数,展示了最基本的用法

<?php include "vendor/autoload.php"; use WebGeekerValidationValidation; try { Validation::validate($_POST, [ "offset" => "IntGe:0", // 参数offset应该大于等于0 "count" => "Required|IntGeLe:1,200", // 参数count是必需的且大于等于1小于等于200 ]); } catch (Exception $e) { echo $e->getMessage(); }

注意:验证不通过会抛出异常,该异常中包含有错误描述信息

3.2 验证不通过的错误处理

如果验证不通过,Validation::validate(...)方法会抛出异常,建议在框架层面统一捕获这些异常,提取错误描述信息并返回给客户端。

3.3 在第三方框架中的用法

第三方框架一般会提供Request对象,可以取到GET, POST参数(以Laravel为例)

//$params = $request->query(); // 获取GET参数 $params = $request->request->all(); // 获取POST参数 // 验证(如果验证不通过,会抛出异常) Validation::validate($params, [ // 此处省略验证规则 ]);

4 详细使用方法

4.1 验证整型参数

整型验证器全部以"Int"开头,用于验证整型数值(如123)或整型字符串(如"123")。其它数据类型均不匹配。

"size" => "IntGeLe:1,100"

这条验证要求参数"size"是整数,并且大于等于1,小于等于100。

完整的整型验证器的列表参考附录 A.1 。

4.2 验证浮点型参数

浮点型验证器全部以"Float"开头,用于验证浮点型数值(如1.0)、浮点型字符串(如"1.0")、整型数值(如123)或整型字符串(如"123")。其它数据类型均不匹配。

"height" => "FloatGeLe:0.0,100.0"

这条验证要求参数"height"是浮点数,并且大于等于0,小于等于100.0。

完整的浮点型验证器的列表参考附录 A.2 。

4.3 验证bool型参数

bool型验证器:

Bool: 合法的取值为: true, false, "true", "false"(字符串忽略大小写)。 BoolTrue: 合法的取值为: true, "true"(字符串忽略大小写)。 BoolFalse: 合法的取值为: false, "false"(字符串忽略大小写)。 BoolSmart: 合法的取值为: true, false, "true", "false", 1, 0, "1", "0", "yes", "no", "y", "n"(字符串忽略大小写) BoolSmartTrue: 合法的取值为: true, "true", 1, "1", "yes", "y"(字符串忽略大小写) BoolSmartFalse: 合法的取值为: false, "false", 0, "0", "no", "n"(字符串忽略大小写)

"accept" => "BoolSmart"

完整的bool型验证器的列表参考附录 A.3 。

4.4 验证字符串型参数

字符串型验证器不全以"Str"开头。只接收字符串型数据,其它数据类型均不匹配。

例1:

"name" => "StrLenGeLe:2,20"

这条验证要求参数"name"是字符串,长度在2-20之间(字符串长度是用mb_strlen()来计算的)。

例2:

"comment" => "ByteLenLe:1048576"

这条验证要求参数"comment"是字符串,字节长度不超过1048576(字节长度是用strlen()来计算的)。

例3:

"email" => "Email"

这条验证要求参数"email"是必须是合法的电子邮件地址。

例4(正则表达式验证):

"phone" => "Regexp:/^1(3[0-9]|4[579]|5[0-35-9]|7[0135678]|8[0-9]|66|9[89])d{8}$/"

这条验证要求参数"phone"是合法的手机号。

关于正则表达式中的哪些特殊字符需要转义的问题,只需要用 preg_match() 函数验证好,如:

preg_match('/^string$/', $string);

然后把两个'/'号及其中间的部分拷贝出来,放在Regexp:后面即可,不需要再做额外的转义,即使正则中有'|'这种特殊符号,也不需要再转义。

关于正则匹配中文的问题 用户 preq_match() 匹配中文的代码如下:

$matched = preg_match("/^[x{4e00}-x{9fa5}]+$/u", "你好");

注意正则表达式结尾需要加一个'u',意思是开启UTF8模式。

用我们的验证库代码应该这样写:

Validation::validate(["param" => "你好"], ["param" => "Regexp:/^[x{4e00}-x{9fa5}]+$/"]);

注意正则表达式结尾不需要加'u'。

完整的字符串型验证器的列表参考附录 A.4 。

4.5 验证数组型、对象型、文件型、日期时间型参数

参考附录A.5-A.8

4.6 验证器串联(与)

一条规则中可以有多个验证器前后串联,它们之间是“AND”的关系,如:

"file" => "FileMaxSize:10m|FileImage"

这个验证要求参数"file"是一个图像文件,并且文件大小不超过10m

4.7 Required 验证器

Required验证器要求参数必须存在,且其值不能为null(这个是PHP的null值,而不是字符串"null")(参数值为null等价于参数不存在)。 如果多个验证器串联,Required验证器必须在其它验证器前面。 如果还有条件验证器,Required必须串联在条件验证器后面。 如果验证规则中没有 Required,当参数存在时才进行验证,验证不通过会抛异常;如果参数不存在,那么就不验证(相当于验证通过)

例:

"size" => "Required|StrIn:small,middle,large"

该验证要求参数"size"必须是字符串的"small", "middle"或者"large"。

4.8 忽略所有 Required 验证器

比如当创建一个用户时,要求姓名、性别、年龄全部都要提供;但是当更新用户信息时,不需要提供全部信息,提供哪个信息就更新哪个信息。

$validations = [ "name" => "Required|StrLenGeLe:2,20", "sex" => "Required|IntIn:0,1", "age" => "Required|IntGeLe:1,200", ]; $userInfo = [ "name" => "tom", "sex" => "0", "age" => "10", ]; Validation::validate($userInfo, $validations); // 创建用户时的验证 unset($userInfo["age"]); // 删除age字段 Validation::validate($userInfo, $validations, true); // 更新用户信息时的验证

注意上面代码的最后一行:validate()函数的第三个参数为true表示忽略所有的 Required 验证器。

这样我们就只需要写一份验证规则,就可以同时用于创建用户和更新用户信息这两个接口。

4.9 嵌套参数的验证

下面这个例子展示了包含数组和对象的嵌套的参数的验证:

$params = [ "comments" => [ [ "title" => "title 1", "content" => "content 1", ], [ "title" => "title 1", "content" => "content 1", ], [ "title" => "title 1", "content" => "content 1", ], ] ]; $validations = [ "comments[*].title" => "Required|StrLenGeLe:2,50", "comments[*].content" => "Required|StrLenGeLe:2,500", ]; Validation::validate($params, $validations);

4.10 条件判断型验证器

条件判断型验证器都以"If"开头。

如果条件不满足,则条件验证器后面的规则都不检测,忽略当前这条验证规则。

比如你想招聘一批模特,男的要求180以上,女的要求170以上,验证可以这样写:

$validations = [ "sex" => "StrIn:male,female", "height" => [ "IfStrEq:sex,male|IntGe:180", "IfStrEq:sex,female|IntGe:170", ], ];

参数"sex"的值不同,参数"height"的验证规则也不一样。

除了IfExistIfNotExist,其它的条件验证器 IfXxx 都要求条件参数必须存在。如果希望条件参数是可选的,那么可以结合IfExistIfNotExist一起使用, 如:

"IfExist:sex|IfStrEq:sex,male|IntGe:180"

注意:
设计条件验证器的主要目的是根据一个参数的取值不同,对另外一个参数应用不同的验证规则。
"IfXxx:"的后面应该是另一个参数的名称,而不是当前参数,这一点一定要注意。
比如上面的例子中,是根据参数"sex"的取值不同,对参数"height"应用了不同的验证规则,"IfXxx:"后面跟的是"sex"。

完整的条件判断型验证器的列表参考附录 A.9 。

4.11 验证规则并联(或)

多条验证规则可以并联,它们之间是“或”的关系,如

"type" => [ "StrIn:small,middle,large", "IntIn:1,2,3", ]

上面这条验证要求参数"type"既可以是字符串"small", "middle"或"large",也可以整型的1, 2或3

验证规则并联不是简单的“或”的关系,具体验证流程如下:

按顺序验证这些规则,如果有一条验证规则通过, 则该参数验证通过。 如果全部验证规则都被忽略(If验证器条件不满足,或者没有Required验证器并且该参数不存在,或者有0条验证规则),也算参数验证通过。 上面两条都不满足, 则该参数验证失败。

这些规则如果要完全理清并不是一件容易的事,所以不建议使用验证规则并联,也尽量不要设计需要这种验证方式的参数。

4.12 关于特殊值null, ""0false的问题

这些特殊的值是不等价的,它们是不同的数据类型(需要用不同的验证器去验证):

""是字符串。 0是整型。 false是bool型。 null是PHP的空。在本工具中它有特殊的含义。

如果某个参数的值为null,则本工具会视为该参数不存在。

比如下面两个array对于本工具来说是等价的.

$params = [ "name" => "hello", ];

$params = [ "name" => "hello", "comment" => null, ];

是等价的。

4.13 关于基本数据类型与字符串的关系

对于以下url地址

http://abc.com/index.php?p1=&&p2=hello&&p3=123

我们将得到的参数数组:

$params = [ "p1" => "", "p2" => "hello", "p3" => "123", ];

注意

参数"p1"的值为空字符串"",而不是null。 参数"p3"的值为字符串"123",而不是整型123。 GET方式的HTTP请求是传递不了null值的。

本工具的所有验证器都是强类型的,"Int*"验证的是整型,"Float*"验证的是浮点型,"Str*"验证的是字符串型,数据类型不匹配,验证是通不过的。但是字符串类型是个例外。

因为常规的HTTP请求,所有的基本数据类型最终都会转换成字符串,所以:

整型123和字符串"123"均可以通过验证器"Int"的验证; 浮点型123.0和字符串"123.0"均可以通过验证器"Float"的验证; bool型true和字符串"true"均可以通过验证器"Bool"的验证; 但是null值和字符串"null"永远不等价,字符串"null"就只是普通的字符串。

4.14 自定义错误信息输出文本

如果参数验证不通过,Validation::validate()方法会抛出异常,这个异常会包含验证不通过的错误信息描述的文本。

但是这个描述文本对用户来说可能不那么友好,我们可以通过两个伪验证器来自定义这些文本:

Alias 用于自定义参数名称(这个名称会与内部的错误信息模版相结合,生成最终的错误信息描述文本) >>> 用于自定义错误描述文本(这个文本会完全取代模版生成的错误描述文本)。

看下面的例子:

$params = [ "title" => "a", ]; Validation::validate($params, [ "title" => "Required|StrLenGeLe:2,50", ]); // 抛出异常的错误描述为:“title”长度必须在 2 - 50 之间 Validation::validate($params, [ "title" => "Required|StrLenGeLe:2,50|Alias:标题", // 自定义参数名称 ]); // 抛出异常的错误描述为:“标题”长度必须在 2 - 50 之间 Validation::validate($params, [ "title" => "Required|StrLenGeLe:2,50|>>>:标题长度应在2~50之间", // 自定义错误信息描述文本 ]); // 抛出异常的错误描述为:标题长度应在2~50之间

参考附录A.10获取更详细的信息

4.15 国际化

从0.4版开始:

使用新的静态成员变量 $langCode2ErrorTemplates 来进行“错误提示信息模版”的翻译,主要目的是简化格式(感谢 @gitHusband 的建议)。 旧的翻译表 $langCodeToErrorTemplates 仍然有效,已有代码无需修改(参考下一节)。如果新旧翻译表同时提供,优先新的,新表中查不到再使用旧的。

要支持国际化,需要自定义一个类,继承WebGeekerValidationValidation,重载两个静态成员变量:

$langCode2ErrorTemplates用于提供“错误提示信息模版”的翻译对照表。完整的错误提示信息模版列表可以在WebGeekerValidationValidation::$errorTemplates成员变量中找到 $langCodeToTranslations用于提供“自定义参数名称”(由Alias指定)和“自定义错误描述文本”(由>>>指定)的翻译对照表。

下面提供一个示例类:

class MyValidation extends Validation { // “错误提示信息模版”翻译对照表 protected static $langCodeToErrorTemplates = [ "zh-tw" => [ 'Int' => '“{{param}}”必須是整數', //

版权声明:

1、该文章(资料)来源于互联网公开信息,我方只是对该内容做点评,所分享的下载地址为原作者公开地址。
2、网站不提供资料下载,如需下载请到原作者页面进行下载。
3、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考学习用!
4、如文档内容存在违规,或者侵犯商业秘密、侵犯著作权等,请点击“违规举报”。