Passed
Pull Request — master (#403)
by Sergei
04:14 queued 01:28
created

RulesNormalizer   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 113
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 19
eloc 37
c 1
b 0
f 0
dl 0
loc 113
ccs 38
cts 38
cp 1
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A normalize() 0 30 5
A normalizeRule() 0 21 5
B prepareRulesArray() 0 28 7
A normalizeAttributeRules() 0 5 2
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 825
    public static function normalize(
31
        iterable|object|callable|null $rules,
32
        mixed $data = null,
33
        ?callable $defaultSkipOnEmptyCriteria = null,
34
    ): iterable {
35 825
        $rules = self::prepareRulesArray($rules, $data);
36
37 824
        $normalizedRules = [];
38
39
        /**
40
         * @var mixed $attribute
41
         * @var mixed $attributeRules
42
         */
43 824
        foreach ($rules as $attribute => $attributeRules) {
44 812
            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 811
            $normalizedRules[$attribute] = self::normalizeAttributeRules(
54 811
                is_iterable($attributeRules) ? $attributeRules : [$attributeRules],
55
                $defaultSkipOnEmptyCriteria
56
            );
57
        }
58
59 823
        return $normalizedRules;
60
    }
61
62
    /**
63
     * @psalm-param RulesType $rules
64
     *
65
     * @throws InvalidArgumentException
66
     */
67 825
    private static function prepareRulesArray(
68
        iterable|object|callable|null $rules,
69
        mixed $data,
70
    ): iterable {
71 825
        if ($rules === null) {
72 34
            return $data instanceof RulesProviderInterface
73 30
                ? $data->getRules()
74 34
                : [];
75
        }
76
77 795
        if ($rules instanceof RulesProviderInterface) {
78 2
            return $rules->getRules();
79
        }
80
81 793
        if ($rules instanceof RuleInterface) {
82 1
            return [$rules];
83
        }
84
85 792
        if (is_callable($rules)) {
86 1
            return [new Callback($rules)];
87
        }
88
89
        /** @psalm-suppress RedundantConditionGivenDocblockType */
90 791
        if (is_iterable($rules)) {
91 790
            return $rules;
92
        }
93
94 1
        throw new InvalidArgumentException('A rules object must implement RulesProviderInterface or RuleInterface.');
95
    }
96
97
    /**
98
     * @throws InvalidArgumentException
99
     *
100
     * @return iterable<int|string, RuleInterface>
101
     */
102 811
    private static function normalizeAttributeRules(iterable $rules, ?callable $defaultSkipOnEmptyCriteria): iterable
103
    {
104
        /** @var mixed $rule */
105 811
        foreach ($rules as $rule) {
106 811
            yield self::normalizeRule($rule, $defaultSkipOnEmptyCriteria);
107
        }
108
    }
109
110
    /**
111
     * @throws InvalidArgumentException
112
     */
113 811
    private static function normalizeRule(mixed $rule, ?callable $defaultSkipOnEmptyCriteria): RuleInterface
114
    {
115 811
        if (is_callable($rule)) {
116 4
            return new Callback($rule);
117
        }
118
119 810
        if (!$rule instanceof RuleInterface) {
120 1
            throw new InvalidArgumentException(
121 1
                sprintf(
122
                    'Rule should be either an instance of %s or a callable, %s given.',
123
                    RuleInterface::class,
124 1
                    get_debug_type($rule)
125
                )
126
            );
127
        }
128
129 810
        if ($rule instanceof SkipOnEmptyInterface && $rule->getSkipOnEmpty() === null) {
130 723
            $rule = $rule->skipOnEmpty($defaultSkipOnEmptyCriteria);
131
        }
132
133 810
        return $rule;
134
    }
135
}
136