Passed
Push — master ( d37094...f4e4de )
by
unknown
05:26 queued 02:26
created

RulesNormalizer::normalize()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 30
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 12
c 1
b 0
f 0
dl 0
loc 30
ccs 11
cts 11
cp 1
rs 9.5555
cc 5
nc 3
nop 3
crap 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Helper;
6
7
use InvalidArgumentException;
8
use Yiisoft\Validator\Rule\Callback;
9
use Yiisoft\Validator\RuleInterface;
10
use Yiisoft\Validator\RulesProviderInterface;
11
use Yiisoft\Validator\SkipOnEmptyInterface;
12
use Yiisoft\Validator\ValidatorInterface;
13
14
use function is_callable;
15
use function is_int;
16
use function is_string;
17
18
/**
19
 * @psalm-import-type RulesType from ValidatorInterface
20
 */
21
final class RulesNormalizer
22
{
23
    /**
24
     * @psalm-param RulesType $rules
25
     *
26
     * @throws InvalidArgumentException
27
     *
28
     * @return iterable<int|string, iterable<int|string, RuleInterface>>
29
     */
30 824
    public static function normalize(
31
        iterable|object|callable|null $rules,
32
        mixed $data = null,
33
        ?callable $defaultSkipOnEmptyCriteria = null,
34
    ): iterable {
35 824
        $rules = self::prepareRulesArray($rules, $data);
36
37 823
        $normalizedRules = [];
38
39
        /**
40
         * @var mixed $attribute
41
         * @var mixed $attributeRules
42
         */
43 823
        foreach ($rules as $attribute => $attributeRules) {
44 811
            if (!is_int($attribute) && !is_string($attribute)) {
45 1
                throw new InvalidArgumentException(
46 1
                    sprintf(
47
                        'An attribute can only have an integer or a string type. %s given.',
48 1
                        get_debug_type($attribute),
49
                    )
50
                );
51
            }
52
53 810
            $normalizedRules[$attribute] = self::normalizeAttributeRules(
54 810
                is_iterable($attributeRules) ? $attributeRules : [$attributeRules],
55
                $defaultSkipOnEmptyCriteria
56
            );
57
        }
58
59 822
        return $normalizedRules;
60
    }
61
62
    /**
63
     * @psalm-param RulesType $rules
64
     *
65
     * @throws InvalidArgumentException
66
     */
67 824
    private static function prepareRulesArray(
68
        iterable|object|callable|null $rules,
69
        mixed $data,
70
    ): iterable {
71 824
        if ($rules === null) {
72 34
            return $data instanceof RulesProviderInterface
73 30
                ? $data->getRules()
74 34
                : [];
75
        }
76
77 794
        if ($rules instanceof RulesProviderInterface) {
78 2
            return $rules->getRules();
79
        }
80
81 792
        if ($rules instanceof RuleInterface || is_callable($rules)) {
82 1
            return [$rules];
83
        }
84
85
        /** @psalm-suppress RedundantConditionGivenDocblockType */
86 791
        if (is_iterable($rules)) {
87 790
            return $rules;
88
        }
89
90 1
        throw new InvalidArgumentException('A rules object must implement RulesProviderInterface or RuleInterface.');
91
    }
92
93
    /**
94
     * @throws InvalidArgumentException
95
     *
96
     * @return iterable<int|string, RuleInterface>
97
     */
98 810
    private static function normalizeAttributeRules(iterable $rules, ?callable $defaultSkipOnEmptyCriteria): iterable
99
    {
100
        /** @var mixed $rule */
101 810
        foreach ($rules as $rule) {
102 810
            yield self::normalizeRule($rule, $defaultSkipOnEmptyCriteria);
103
        }
104
    }
105
106
    /**
107
     * @throws InvalidArgumentException
108
     */
109 810
    private static function normalizeRule(mixed $rule, ?callable $defaultSkipOnEmptyCriteria): RuleInterface
110
    {
111 810
        if (is_callable($rule)) {
112 4
            return new Callback($rule);
113
        }
114
115 809
        if (!$rule instanceof RuleInterface) {
116 1
            throw new InvalidArgumentException(
117 1
                sprintf(
118
                    'Rule should be either an instance of %s or a callable, %s given.',
119
                    RuleInterface::class,
120 1
                    get_debug_type($rule)
121
                )
122
            );
123
        }
124
125 809
        if ($rule instanceof SkipOnEmptyInterface && $rule->getSkipOnEmpty() === null) {
126 722
            $rule = $rule->skipOnEmpty($defaultSkipOnEmptyCriteria);
127
        }
128
129 809
        return $rule;
130
    }
131
}
132