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
, ""
,0
,false
的问题
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"的验证规则也不一样。
除了IfExist
和IfNotExist
,其它的条件验证器 IfXxx 都要求条件参数必须存在。如果希望条件参数是可选的,那么可以结合IfExist
或IfNotExist
一起使用, 如:
"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
, ""
,0
,false
的问题
这些特殊的值是不等价的,它们是不同的数据类型(需要用不同的验证器去验证):
""
是字符串。
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、如文档内容存在违规,或者侵犯商业秘密、侵犯著作权等,请点击“违规举报”。