Skip to content

Commit e52f20e

Browse files
committed
update:
add new validator: `arrList`, `each` support wildcard value validate: `['users.*.id', 'distinct']` some logic modified.
1 parent e35024d commit e52f20e

File tree

8 files changed

+220
-31
lines changed

8 files changed

+220
-31
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
- 支持将规则按场景进行分组设置。或者部分验证
1313
- 支持在进行验证前对值使用过滤器进行净化过滤[内置过滤器](#built-in-filters)
1414
- 支持自定义每个验证的错误消息,字段翻译,消息翻译,支持默认值
15-
- 支持基本的数组检查,数组的子级值检查
16-
- 方便的获取错误信息,验证后的安全数据获取
15+
- 支持基本的数组检查,数组的子级(`'goods.apple'`)值检查, 通配符的子级检查 (`'users.*.id' 'goods.*'`)
16+
- 方便的获取错误信息,验证后的安全数据获取(只会收集有规则检查过的数据)
1717
- 已经内置了50多个常用的验证器[内置验证器](#built-in-validators)
1818
- 规则设置参考 yii. 部分规则参考自 laravel, Respect/Validation
19-
- 新增了独立的过滤器 `Inhere\Validate\Filter\Filtration` 用于数据过滤
19+
- 新增了独立的过滤器 `Inhere\Validate\Filter\Filtration`,可单独用于数据过滤
2020

2121
支持两种规则配置方式:
2222

@@ -537,10 +537,13 @@ $v = Validation::make($_POST,[
537537
`isMap` | 验证值是否是一个非自然数组 map (key - value 形式的) | `['goods', 'isMap']`
538538
`isList` | 验证值是否是一个自然数组 list (key是从0自然增长的) | `['tags', 'isList']`
539539
`isArray` | 验证是否是数组 | `['goods', 'isArray']`
540+
`each` | 对数组中的每个值都应用给的验证器(这里的绝大多数都可以使用),并且要全部通过 | `['goods.*', 'each', 'string']`, `['goods.*', 'each', 'string', 'min' => 3]`
540541
`hasKey` | 验证数组存在给定的key(s) | `['goods', 'hasKey', 'pear']` `['goods', 'hasKey', ['pear', 'banana']]`
542+
`distinct` | 数组中的值必须是唯一的 | `['goods', 'distinct']`, `['users.*.id', 'distinct']`
541543
`intList` | 验证字段值是否是一个 int list | `['tagIds', 'intList']`
542544
`numList` | 验证字段值是否是一个 number list | `['tagIds', 'numList']`
543545
`strList` | 验证字段值是否是一个 string list | `['tags', 'strList']`
546+
`arrList` | 验证字段值是否是一个 array list(多维数组) | `['tags', 'arrList']`
544547
`min` | 最小边界值验证 | `['title', 'min', 40]`
545548
`max` | 最大边界值验证 | `['title', 'max', 40]`
546549
`size/range/between` | 验证大小范围, 可以支持验证 `int`, `string`, `array` 数据类型 | `['tagId', 'size', 'min'=>4, 'max'=>567]`

src/Filter/FilterList.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public static function float($val, $decimal = null, $flags = FILTER_FLAG_ALLOW_F
107107

108108
/**
109109
* 去除标签,去除或编码特殊字符。
110-
* @param string $val
110+
* @param string|array $val
111111
* @param int $flags 标志
112112
* FILTER_FLAG_NO_ENCODE_QUOTES - 该标志不编码引号
113113
* FILTER_FLAG_STRIP_LOW - 去除 ASCII 值在 32 以下的字符
@@ -119,6 +119,10 @@ public static function float($val, $decimal = null, $flags = FILTER_FLAG_ALLOW_F
119119
*/
120120
public static function string($val, $flags = 0)
121121
{
122+
if (\is_array($val)) {
123+
return array_map(self::class . '::string', $val);
124+
}
125+
122126
$settings = [];
123127

124128
if ((int)$flags !== 0) {
@@ -438,7 +442,7 @@ public static function fullSpecialChars($val, $flags = 0)
438442
* @param string $string 字符串
439443
* @param integer $start 起始长度
440444
* @param int $end 结束位置
441-
* @return mixed
445+
* @return string
442446
*/
443447
public static function stringCute($string, $start = 0, $end = null)
444448
{
@@ -450,6 +454,17 @@ public static function stringCute($string, $start = 0, $end = null)
450454
return Helper::subStr($string, $start, $end);
451455
}
452456

457+
/**
458+
* @param string $string
459+
* @param int $start
460+
* @param null $end
461+
* @return string
462+
*/
463+
public static function cut($string, $start = 0, $end = null)
464+
{
465+
return self::stringCute($string, $start, $end);
466+
}
467+
453468
/**
454469
* url地址过滤 移除所有不符合 url 的字符
455470
* @note 该过滤器允许所有的字母、数字以及 $-_.+!*'(),{}|\^~[]`"><#%;/?:@&=

src/Utils/ErrorMessageTrait.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ trait ErrorMessageTrait
101101
'intList' => '{attr} must be an array and value is all integers',
102102
'numList' => '{attr} must be an array and value is all numbers',
103103
'strList' => '{attr} must be an array and value is all strings',
104+
'arrList' => '{attr} must be an array and value is all arrays',
105+
106+
'each' => '{attr} must be through the {value0} verify',
107+
'hasKey' => '{attr} must be contains the key {value0}',
108+
'distinct' => 'there should not be duplicate keys in the {attr}',
104109

105110
'json' => '{attr} must be an json string',
106111

src/Utils/UserAndContextValidatorsTrait.php

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -465,14 +465,55 @@ public function inFieldValidator($val, string $anotherField)
465465
}
466466

467467
/**
468-
* 验证数组时,指定的字段不能有任何重复值。
469-
* `['foo.*.id', 'distinct']`
470-
* @param mixed $val
468+
* 对数组中的每个值都应用给定的验证器,并且要全部通过
469+
* `['foo.*.id', 'each', 'number']`
470+
* `['goods.*', 'each', 'string']`
471+
* @param array $values
472+
* @param array ...$args
473+
* - string|\Closure $validator
474+
* - ... args for $validator
471475
* @return bool
472476
*/
473-
public function distinctValidator($val)
477+
public function eachValidator(array $values, ...$args)
474478
{
475-
return ValidatorList::distinct($val);
479+
if (!$validator = array_shift($args)) {
480+
throw new \InvalidArgumentException('must setting a validator for each validate.');
481+
}
482+
483+
foreach ($values as $value) {
484+
$passed = false;
485+
486+
if (\is_object($validator) && method_exists($validator, '__invoke')) {
487+
$passed = $validator($value, ...$args);
488+
} elseif (\is_string($validator)) {
489+
// special for required
490+
if ('required' === $validator) {
491+
$passed = !ValidatorList::isEmpty($value);
492+
493+
} elseif (isset(self::$_validators[$validator])) {
494+
$callback = self::$_validators[$validator];
495+
$passed = $callback($value, ...$args);
496+
497+
} elseif (method_exists($this, $method = $validator . 'Validator')) {
498+
$passed = $this->$method($value, ...$args);
499+
500+
} elseif (method_exists(ValidatorList::class, $validator)) {
501+
$passed = ValidatorList::$validator($value, ...$args);
502+
503+
// it is function name
504+
} elseif (\function_exists($validator)) {
505+
$passed = $validator($value, ...$args);
506+
} else {
507+
throw new \InvalidArgumentException("The validator [$validator] don't exists!");
508+
}
509+
}
510+
511+
if (!$passed) {
512+
return false;
513+
}
514+
}
515+
516+
return true;
476517
}
477518

478519
/**
@@ -483,7 +524,7 @@ public function distinctValidator($val)
483524
* @param int $expected
484525
* @param string $op
485526
*/
486-
public function intervalDayValidator($val, $compareField, $expected, $op = '>=')
527+
public function intervalDayValidator($val, string $compareField, int $expected, string $op = '>=')
487528
{
488529

489530
}

src/ValidationTrait.php

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -214,12 +214,8 @@ public function validate(array $onlyChecked = null, $stopOnError = null)
214214

215215
// required* 系列字段检查器 || 文件资源检查
216216
if (\is_string($validator) && (self::isCheckRequired($validator) || self::isCheckFile($validator))) {
217-
if (!$this->fieldValidate($field, $value, $validator, $args)) {
218-
$this->addError($field, $this->getMessage($validator, $field, $args, $defMsg));
219-
220-
if ($this->isStopOnError()) {
221-
break;
222-
}
217+
if (!$this->fieldValidate($field, $value, $validator, $args, $defMsg) && $this->isStopOnError()) {
218+
break;
223219
}
224220

225221
continue;
@@ -237,12 +233,8 @@ public function validate(array $onlyChecked = null, $stopOnError = null)
237233
}
238234

239235
// 字段值验证检查
240-
if (!$this->valueValidate($field, $value, $validator, $args)) {
241-
$this->addError($field, $this->getMessage($validator, $field, $args, $defMsg));
242-
243-
if ($this->isStopOnError()) {
244-
break;
245-
}
236+
if (!$this->valueValidate($field, $value, $validator, $args, $defMsg) && $this->isStopOnError()) {
237+
break;
246238
}
247239
}
248240

@@ -276,10 +268,11 @@ public function validate(array $onlyChecked = null, $stopOnError = null)
276268
* @param mixed $value 属性值
277269
* @param string $validator required* 验证器
278270
* @param array $args 验证需要的参数
271+
* @param string $defMsg
279272
* @return bool
280273
* @throws \InvalidArgumentException
281274
*/
282-
protected function fieldValidate(string $field, $value, string $validator, array $args)
275+
protected function fieldValidate(string $field, $value, string $validator, array $args, $defMsg)
283276
{
284277
// required 检查
285278
if ($validator === 'required') {
@@ -305,6 +298,8 @@ protected function fieldValidate(string $field, $value, string $validator, array
305298
return true;
306299
}
307300

301+
$this->addError($field, $this->getMessage($validator, $field, $args, $defMsg));
302+
308303
return false;
309304
}
310305

@@ -314,10 +309,10 @@ protected function fieldValidate(string $field, $value, string $validator, array
314309
* @param mixed $value 属性值
315310
* @param \Closure|string $validator 验证器
316311
* @param array $args 验证需要的参数
312+
* @param string $defMsg
317313
* @return bool
318-
* @throws \InvalidArgumentException
319314
*/
320-
protected function valueValidate(string $field, $value, $validator, array $args)
315+
protected function valueValidate(string $field, $value, $validator, array $args, $defMsg)
321316
{
322317
// if field don't exists.
323318
if (null === $value) {
@@ -340,7 +335,6 @@ protected function valueValidate(string $field, $value, $validator, array $args)
340335
} elseif (method_exists($this, $method = $validator . 'Validator')) {
341336
$passed = $this->$method($value, ...$args);
342337

343-
// $validator is a method of the class 'ValidatorList'
344338
} elseif (method_exists(ValidatorList::class, $validator)) {
345339
$passed = ValidatorList::$validator($value, ...$args);
346340

@@ -352,7 +346,6 @@ protected function valueValidate(string $field, $value, $validator, array $args)
352346
}
353347
} else {
354348
$passed = Helper::call($validator, $value, ...$args);
355-
// throw new \InvalidArgumentException('Validator type is error, must is String or Closure!');
356349
}
357350

358351
// validate success, save value to safeData
@@ -362,6 +355,8 @@ protected function valueValidate(string $field, $value, $validator, array $args)
362355
return true;
363356
}
364357

358+
$this->addError($field, $this->getMessage($validator, $field, $args, $defMsg));
359+
365360
return false;
366361
}
367362

@@ -482,13 +477,32 @@ protected function prepareRule(array &$rule)
482477
******************************************************************************/
483478

484479
/**
485-
* @param string $path 'users.*.id'
480+
* @param string $path 'users.*.id' 'goods.*' 'foo.bar.*.id'
481+
* @param null|mixed $default
486482
* @return mixed
487483
*/
488-
protected function getByWildcard(string $path)
484+
protected function getByWildcard(string $path, $default = null)
489485
{
486+
list($first, $last) = explode('.*', $path, 2);
487+
$recently = Helper::getValueOfArray($this->data, $first, $default);
488+
489+
// 'goods.*'
490+
if ('' === $last) {
491+
return $recently;
492+
}
493+
494+
if (!$recently || !\is_array($recently)) {
495+
return $default;
496+
}
497+
498+
$last = trim($last, '.');
490499
$result = [];
491500

501+
foreach ($recently as $item) {
502+
$result[] = $item[$last];
503+
}
504+
505+
return $result;
492506
}
493507

494508
/**

src/ValidatorList.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,27 @@ public static function strList($val)
753753
return true;
754754
}
755755

756+
/**
757+
* 验证字段值是否是一个 array list, 多维数组
758+
* @param $val
759+
* @return bool
760+
*/
761+
public static function arrList($val)
762+
{
763+
if (!$val || !\is_array($val)) {
764+
return false;
765+
}
766+
767+
/** @var array $val */
768+
foreach ($val as $k => $v) {
769+
if (!\is_array($v)) {
770+
return false;
771+
}
772+
}
773+
774+
return true;
775+
}
776+
756777
/**
757778
* @param array|mixed $val
758779
* @param string|int|array $key
@@ -778,7 +799,8 @@ public static function hasKey($val, $key)
778799
}
779800

780801
/**
781-
* 数组里面的值是唯一,不重复的
802+
* 验证数组时,指定的字段不能有任何重复值。
803+
* `['foo.*.id', 'distinct']`
782804
* @param mixed $val
783805
* @return bool
784806
*/

tests/RuleValidationTest.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,4 +371,67 @@ public function testArrayValidate()
371371
$this->assertTrue($v->isOk());
372372
$this->assertFalse($v->failed());
373373
}
374+
375+
/**
376+
* 验证的 字段值 必须存在于另一个字段(anotherField)的值中。
377+
*/
378+
public function testInField()
379+
{
380+
$v = RuleValidation::makeAndValidate([
381+
'status' => 3,
382+
'some' => 30,
383+
'allowed' => [3, 4, 5],
384+
], [
385+
['status', 'inField', 'allowed'],
386+
['some', 'inField', 'allowed'],
387+
]);
388+
389+
$this->assertFalse($v->isOk());
390+
$this->assertCount(1, $v->getErrors());
391+
$this->assertTrue($v->inError('some'));
392+
}
393+
394+
public function testDistinct()
395+
{
396+
$v = RuleValidation::makeAndValidate([
397+
'tags' => [3, 4, 4],
398+
'goods' => ['apple', 'pear'],
399+
'users' => [
400+
['id' => 34, 'name' => 'tom'],
401+
['id' => 89, 'name' => 'john'],
402+
],
403+
], [
404+
['tags', 'distinct'],
405+
['goods.*', 'distinct'],
406+
['users.*.id', 'distinct'],
407+
]);
408+
409+
// var_dump($v->getErrors());
410+
$this->assertFalse($v->isOk());
411+
$this->assertCount(1, $v->getErrors());
412+
$this->assertTrue($v->inError('tags'));
413+
}
414+
415+
public function testEach()
416+
{
417+
$v = RuleValidation::makeAndValidate([
418+
'tags' => [3, 4, 5],
419+
'goods' => ['apple', 'pear'],
420+
'users' => [
421+
['id' => 34, 'name' => 'tom'],
422+
['id' => 89, 'name' => 'john'],
423+
],
424+
], [
425+
['tags', 'each', 'number'],
426+
['goods.*', 'each', 'string', 'min' => 4],
427+
['users.*.id', 'each', 'required'],
428+
['users.*.id', 'each', 'number', 'min' => 34],
429+
['users.*.name', 'each', 'string', 'min' => 5],
430+
]);
431+
432+
// var_dump($v->getErrors());
433+
$this->assertFalse($v->isOk());
434+
$this->assertCount(1, $v->getErrors());
435+
$this->assertTrue($v->inError('users.*.name'));
436+
}
374437
}

0 commit comments

Comments
 (0)