1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Yiisoft\Validator\Helper; |
6
|
|
|
|
7
|
|
|
use InvalidArgumentException; |
8
|
|
|
use IteratorAggregate; |
9
|
|
|
use Traversable; |
10
|
|
|
use Yiisoft\Validator\Rule\Callback; |
11
|
|
|
use Yiisoft\Validator\RuleInterface; |
12
|
|
|
use Yiisoft\Validator\SkipOnEmptyInterface; |
13
|
|
|
|
14
|
|
|
use function is_callable; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* An iterator for a set of rules, performs normalization for every individual rule unifying other provided types and |
18
|
|
|
* verifying that every item is a valid rule instance. It also applies some default settings if needed. |
19
|
|
|
* |
20
|
|
|
* @internal |
21
|
|
|
* |
22
|
|
|
* @template-implements IteratorAggregate<int, RuleInterface> |
23
|
|
|
*/ |
24
|
|
|
final class RulesNormalizerIterator implements IteratorAggregate |
25
|
|
|
{ |
26
|
|
|
/** |
27
|
|
|
* @var callable|null A default "skip on empty" condition ({@see SkipOnEmptyInterface}), already normalized. Used to |
28
|
|
|
* optimize setting the same value in all the rules. Defaults to `null` meaning that it's not used. |
29
|
|
|
*/ |
30
|
|
|
private $defaultSkipOnEmptyCondition; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @param iterable $rules A rules' iterable for checking and normalization. |
34
|
|
|
* @param callable|null $defaultSkipOnEmptyCondition A default "skip on empty" condition |
35
|
|
|
* ({@see SkipOnEmptyInterface}), already normalized. Used to optimize setting the same value in all the rules. |
36
|
|
|
* Defaults to `null` meaning that it's not used. |
37
|
|
|
*/ |
38
|
|
|
public function __construct( |
39
|
|
|
private iterable $rules, |
40
|
|
|
?callable $defaultSkipOnEmptyCondition = null, |
41
|
|
|
) { |
42
|
|
|
$this->defaultSkipOnEmptyCondition = $defaultSkipOnEmptyCondition; |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
public function getIterator(): Traversable |
46
|
|
|
{ |
47
|
|
|
/** @var mixed $rule */ |
48
|
|
|
foreach ($this->rules as $rule) { |
49
|
|
|
yield self::normalizeRule($rule, $this->defaultSkipOnEmptyCondition); |
50
|
|
|
} |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Normalizes a single rule: |
55
|
|
|
* |
56
|
|
|
* - A callable is wrapped with {@see Callback} rule. |
57
|
|
|
* - For any other type verifies that it's a valid rule instance. |
58
|
|
|
* - If default "skip on empty" condition is set, applies it if possible. |
59
|
|
|
* |
60
|
|
|
* @param mixed $rule A raw rule. |
61
|
|
|
* @param callable|null $defaultSkipOnEmptyCondition A "skip on empty" condition ({@see SkipOnEmptyInterface}) to |
62
|
|
|
* apply as default, already normalized. `null` means there is no condition to apply. |
63
|
|
|
* |
64
|
|
|
* @throws InvalidArgumentException When rule is neither a callable nor a {@see RuleInterface} implementation. |
65
|
|
|
* |
66
|
|
|
* @return RuleInterface Ready to use rule instance. |
67
|
|
|
*/ |
68
|
|
|
private static function normalizeRule(mixed $rule, ?callable $defaultSkipOnEmptyCondition): RuleInterface |
69
|
|
|
{ |
70
|
|
|
if (is_callable($rule)) { |
71
|
|
|
return new Callback($rule); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
if (!$rule instanceof RuleInterface) { |
75
|
|
|
throw new InvalidArgumentException( |
76
|
|
|
sprintf( |
77
|
|
|
'Rule must be either an instance of %s or a callable, %s given.', |
78
|
|
|
RuleInterface::class, |
79
|
|
|
get_debug_type($rule) |
80
|
|
|
) |
81
|
|
|
); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
if ( |
85
|
|
|
$defaultSkipOnEmptyCondition !== null |
86
|
|
|
&& $rule instanceof SkipOnEmptyInterface |
87
|
|
|
&& $rule->getSkipOnEmpty() === null |
88
|
|
|
) { |
89
|
|
|
$rule = $rule->skipOnEmpty($defaultSkipOnEmptyCondition); |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
return $rule; |
93
|
|
|
} |
94
|
|
|
} |
95
|
|
|
|