RulesNormalizerIterator::normalizeRule()   A
last analyzed

Complexity

Conditions 6
Paths 4

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 6
eloc 14
c 1
b 0
f 1
nc 4
nop 2
dl 0
loc 25
rs 9.2222
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