Laravel一次单元测试发现的'BUG',分析并解决问题

下面这段代码是Laravel自带的表单验证的语法,不太了解的可以先查看文档

'group_num' => 'min:1|max:21'  // 也可以使用between替换min和max

我们期望的结果是能校验group_num字段最小值是1,最大值是21

but !!!

...

当我单元测试的时候发现,竟然校验通过了!

// 单元测试代码

$warehouseId = 1;   
$prods = [      
    [         
        'prod_id' => 1,      
        'group_num' => 111,      
        'location' => 1,   
    ]
];   
$warehouseLogic = new WarehouseLogic();   
$result = $warehouseLogic->addProds($warehouseId, $prods);    
$this->assertEquals(ErrSvc::ERR_OK, $result['code']);

文档分析

我们看一下文档

min:value

验证中的字段必须具有最小值。字符串、数字、数组或是文件大小的计算方式都用 size 方法进行评估。

文章提到数字使用size方法进行评估,我们看一下size方法的文档

size:value

验证的字段必须具有与给定值匹配的大小。对于字符串来说,value 对应于字符数。对于数字来说,value 对应于给定的整数值。对于数组来说,
size 对应的是数组的 count 值。对文件来说,size 对应的是文件大小(单位 kb )。

代码分析

文档一切正常,我们翻一翻代码试着分析下原因

我们找到验证类的文件并打开:\vendor\laravel\framework\src\Illuminate\Validation\Validator.php

大约在1180行,我们看到validateMin()方法

validateMin()

第一行代码,是对参数个数进行验证的,可以pass掉

第二行代码,调用了getSize()方法,并对getSize()返回结果直接进行大小比较,问题很有可能就出现在getSize()方法身上

我们看一下getSize()的代码

getSize()

我们可以看到if里面判断了如果值是数字类型并且$hasNumeric就直接返回原始值,如果返回原始值的话,validateMin()方法应该会正确校验,所以if条件应该是不成立

原因很可能就在$hasNumeric这个变量上

$hasNumeric调用了$this->hasRule( $attribute, $this->numericRules)方法,并传了两个参数过去

$attribute:当前属性名

$this->numericRules:['Numeric', 'Integer']

然后我们看一下$this->hasRule()方法究竟做了什么?

hasRule方法直接调用了$this->getRule()方法,并且将参数原封不动传递过去

我们看一下getRule()方法干了什么?

getRule()

我们已知$attribute是当前字段名,比如文章举例用的字段group_num

$this->rules其实就是字段+校验规则拼装的数组,格式如下:

既然第一个if语句的两个变量都知道了,我们就能判断出第一个条件是不成立的,我们继续看接下来的代码

代码是对当前需要校验字段的规则进行遍历,并且格式化

list($rule, $parameters) = $this->parseRule($rule);

假设是上图中的group_num字段,他有3个校验规则,分别是:required、min、max

第一次循环$rule就是Required,$parameters为空

第二次循环$rule就是Min,$parameters就是[1]

我们会发现parseRule($rule)后,会对规则进行in_array的判断,$rules是参数(['Numeric', 'Integer'])上文有写

假设我们此时的字段是group_num:

第一个规则是required,条件不成立;

第二个规则是Min,条件依然不成立;

第三个规则是Max,条件还是不成立!

getRule()返回值:

条件都不成立,方法走完,没有任何返回值,返回值为null

hasRule()返回值:

回到hasRule()方法,会对getRule()方法值进行is_null(),并进行逻辑非处理(!),所以返回值为false

我们接着回到getSize()方法,此时我们就知道$hasNumeric的值是false

所以下面的if条件都不成立,最后Laravel使用mb_strlen()对我们数字类型的值进行了长度计算!!!

解决问题

既然我们知道原因在于Laravel对当前字段所有规则进行了in_array($rule, ['Numeric', 'Integer'])

所以解决思路就是,如果我们要对字段进行大小进行范围校验,我们需要把规则修改成:

'group_num' => 'integer|min:1|max:21'

所以文章开头的校验,对于数值类型的字段,是错误的!

其实这不是一个BUG,单纯的是Laravel的校验机制,不过Laravel文档写的很模糊!

所以大家在开发的时候记得一定要认真测试

原文在自己的博客:Laravel一次单元测试发现的’BUG’,分析并解决问题 - 木鱼博客

你可能感兴趣的:(bug,laravel,php)