0%

in_array的诡异行为

记录之前在使用laravel5的验证时踩到的一个坑

现象

1
2
3
4
5
Validator::make(
['name' => 'Dayle'],
['name' => 'in:Peter, Dayle']
);
// The selected name is invalid.

分析

in的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
// in的实现
protected function validateIn($attribute, $value, $parameters)
{
return in_array((string) $value, $parameters);
}

// $parameters的获取
protected function parseParameters($rule, $parameter)
{
if (strtolower($rule) == 'regex') return array($parameter);

return str_getcsv($parameter);
}

可以看到in的验证就是简单的调用了in_array方法

而参数的获取则是调用了str_getcsv

则原因在于逗号之后的空格导致参数被拆分成了 PeterDayle (注意Dayle前面有空格)

Dayle前面多了一个空格导致in_array判断失败

异象

回想一下之前的用法

1
2
3
4
Validator::make(
['id' => '1'],
['id' => 'in:1, 2, 3']
);

竟然没问题!?

看起来原因在传入的值为数字还是字符上

测试

为了空格能显示得更清楚, 这边的空格用_代替, 下同

1
2
3
4
5
6
7
8
9
var_dump(in_array('1', ['1', '2', '3'])); // bool(true)
var_dump(in_array('1', ['1_', '2', '3'])); // bool(false)
var_dump(in_array('1', ['_1', '2', '3'])); // bool(true)
var_dump(in_array(1, ['1', '2', '3'])); // bool(true)
var_dump(in_array(1, ['1_', '2', '3'])); // bool(true)
var_dump(in_array(1, ['_1', '2', '3'])); // bool(true)
var_dump(in_array('a', ['a', 'b', 'c'])); // bool(true)
var_dump(in_array('a', ['a_', 'b', 'c'])); // bool(false)
var_dump(in_array('a', ['_a', 'b', 'c'])); // bool(false)

结论

从结果上来看:

  1. string类型数字可以前置空格
  2. int类型可以前置/后置空格
  3. 非数字字符严格匹配

根据PHP的官方文档:

如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换为数值并且比较按照数值来进行。

可以解释 2 和 3 的结论

但是

1
2
var_dump('1'== '_1'); // bool(true)
var_dump('1'== '1_'); // bool(false)

喵喵喵?

说好的转换呢?

反过来对于in的验证

1
2
3
4
Validator::make(
['id' => '1'],
['id' => 'in:1 ,2 ,3']
);

将导致验证失败

所以对于使用laravel的in做验证时, 最好写成

1
2
3
4
Validator::make(
['id' => '1'],
['id' => 'in:1,2,3']
);

#少给自己挖坑#

为自己以前的写法捏把汗