Passed
Pull Request — master (#222)
by Alexander
05:06 queued 02:29
created

Validator::validate()   C

Complexity

Conditions 10
Paths 204

Size

Total Lines 50
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 10.005

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 10
eloc 30
c 2
b 0
f 0
nc 204
nop 2
dl 0
loc 50
ccs 26
cts 27
cp 0.963
crap 10.005
rs 6.7833

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator;
6
7
use InvalidArgumentException;
8
use JetBrains\PhpStorm\Pure;
9
use Psr\Container\ContainerExceptionInterface;
10
use Psr\Container\ContainerInterface;
11
use Psr\Container\NotFoundExceptionInterface;
12
use Yiisoft\Validator\DataSet\ArrayDataSet;
13
use Yiisoft\Validator\DataSet\ScalarDataSet;
14
use Yiisoft\Validator\Rule\Callback\Callback;
15
use function is_array;
16
use function is_object;
17
18
/**
19
 * Validator validates {@link DataSetInterface} against rules set for data set attributes.
20
 */
21
final class Validator implements ValidatorInterface
22
{
23
    public const PARAMETER_PREVIOUS_RULES_ERRORED = 'previousRulesErrored';
24
25
    private ContainerInterface $container;
26
27 495
    public function __construct(ContainerInterface $container)
28
    {
29 495
        $this->container = $container;
30
    }
31
32
    /**
33
     * @param DataSetInterface|mixed|RulesProviderInterface $data
34
     * @param RuleInterface[]|RuleInterface[][] $rules
35
     * @psalm-param iterable<string, RuleInterface[]> $rules
36
     */
37 28
    public function validate($data, iterable $rules = []): Result
38
    {
39 28
        $data = $this->normalizeDataSet($data);
40 28
        if ($data instanceof RulesProviderInterface) {
41 2
            $rules = $data->getRules();
42
        }
43
44 28
        $context = new ValidationContext($data);
45 28
        $compoundResult = new Result();
46
47 28
        $results = [];
48
49 28
        foreach ($rules as $attribute => $attributeRules) {
50 28
            $attributeName = is_string($attribute) ? $attribute : null;
51 28
            $result = new Result($attributeName);
52
53 28
            $tempRule = is_array($attributeRules) ? $attributeRules : [$attributeRules];
54 28
            $attributeRules = $this->normalizeRules($tempRule);
55
56 28
            if (is_int($attribute)) {
57 16
                $validatedData = $data->getData();
58 16
                $validatedContext = $context;
59
            } else {
60 12
                $validatedData = $data->getAttributeValue($attribute);
61 12
                $validatedContext = $context->withAttribute($attribute);
62
            }
63
64 28
            $tempResult = $this->validateInternal(
65
                $validatedData,
66
                $attributeRules,
67
                $validatedContext
68
            );
69
70 28
            foreach ($tempResult->getErrors() as $error) {
71 14
                $result->merge($error);
72
            }
73 28
            $results[] = $result;
74
        }
75
76 28
        foreach ($results as $result) {
77 28
            foreach ($result->getErrors() as $error) {
78 14
                $compoundResult->merge($error);
79
            }
80
        }
81
82 28
        if ($data instanceof PostValidationHookInterface) {
83
            $data->processValidationResult($compoundResult);
84
        }
85
86 28
        return $compoundResult;
87
    }
88
89 28
    #[Pure]
90
    private function normalizeDataSet($data): DataSetInterface
91
    {
92 28
        if ($data instanceof DataSetInterface) {
93 5
            return $data;
94
        }
95
96 23
        if (is_object($data) || is_array($data)) {
97 4
            return new ArrayDataSet((array)$data);
98
        }
99
100 22
        return new ScalarDataSet($data);
101
    }
102
103
    /**
104
     * @param $value
105
     * @param RuleInterface[] $rules
106
     * @param ValidationContext $context
107
     *
108
     * @throws ContainerExceptionInterface
109
     * @throws NotFoundExceptionInterface
110
     *
111
     * @return Result
112
     */
113 28
    private function validateInternal($value, iterable $rules, ValidationContext $context): Result
114
    {
115 28
        $compoundResult = new Result();
116 28
        foreach ($rules as $rule) {
117 28
            $ruleValidator = $this->container->get($rule->getValidatorClassName());
118 28
            $ruleResult = $ruleValidator->validate($value, $rule, $this, $context);
119 28
            if ($ruleResult->isValid()) {
120 23
                continue;
121
            }
122
123 14
            $context->setParameter(self::PARAMETER_PREVIOUS_RULES_ERRORED, true);
124
125 14
            foreach ($ruleResult->getErrors() as $error) {
126 14
                $compoundResult->merge($error);
127
            }
128
        }
129 28
        return $compoundResult;
130
    }
131
132 28
    private function normalizeRules(array $rules): iterable
133
    {
134 28
        foreach ($rules as $rule) {
135 28
            yield $this->normalizeRule($rule);
136
        }
137
    }
138
139 28
    private function normalizeRule($rule): RuleInterface
140
    {
141 28
        if (is_callable($rule)) {
142 3
            return new Callback($rule);
143
        }
144
145 28
        if (!$rule instanceof RuleInterface) {
146
            throw new InvalidArgumentException(sprintf(
147
                'Rule should be either an instance of %s or a callable, %s given.',
148
                RuleInterface::class,
149
                gettype($rule)
150
            ));
151
        }
152
153 28
        return $rule;
154
    }
155
}
156