Completed
Push — master ( fb30fb...b9dc46 )
by Neomerx
13:06
created

Validator::validate()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 16
c 0
b 0
f 0
ccs 8
cts 8
cp 1
rs 9.2
cc 4
eloc 8
nc 3
nop 1
crap 4
1
<?php namespace Limoncello\Flute\Validation\Form;
2
3
/**
4
 * Copyright 2015-2017 [email protected]
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use Generator;
20
use Limoncello\Container\Traits\HasContainerTrait;
21
use Limoncello\Contracts\L10n\FormatterInterface;
22
use Limoncello\Flute\Contracts\Validation\FormValidatorInterface;
23
use Limoncello\Flute\Exceptions\InvalidArgumentException;
24
use Limoncello\Flute\Validation\Form\Execution\AttributeRulesSerializer;
25
use Limoncello\Validation\Contracts\Errors\ErrorCodes;
26
use Limoncello\Validation\Contracts\Errors\ErrorInterface;
27
use Limoncello\Validation\Contracts\Execution\ContextStorageInterface;
28
use Limoncello\Validation\Errors\Error;
29
use Limoncello\Validation\Execution\BlockInterpreter;
30
use Limoncello\Validation\Execution\ContextStorage;
31
use Limoncello\Validation\Validator\BaseValidator;
32
use Psr\Container\ContainerInterface;
33
34
/**
35
 * @package Limoncello\Flute
36
 *
37
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
38
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
39
 */
40
class Validator extends BaseValidator implements FormValidatorInterface
41
{
42
    use HasContainerTrait;
43
44
    /**
45
     * @var ContextStorageInterface
46
     */
47
    private $contextStorage;
48
49
    /**
50
     * @var FormatterInterface
51
     */
52
    private $messageFormatter;
53
54
    /**
55
     * @var array
56
     */
57
    private $blocks;
58
59
    /**
60
     * @var int[]
61
     */
62
    private $attributeRules;
63
64
    /**
65
     * @var array
66
     */
67
    private $attributeRulesIdx;
68
69
    /**
70
     * @param string             $name
71
     * @param array              $data
72
     * @param ContainerInterface $container
73
     * @param FormatterInterface $messageFormatter
74
     *
75
     * @SuppressWarnings(PHPMD.StaticAccess)
76
     */
77 3
    public function __construct(
78
        string $name,
79
        array $data,
80
        ContainerInterface $container,
81
        FormatterInterface $messageFormatter
82
    ) {
83
        $this
84 3
            ->setContainer($container)
85 3
            ->setMessageFormatter($messageFormatter)
86 3
            ->setBlocks(AttributeRulesSerializer::extractBlocks($data))
87 3
            ->setAttributeRules(AttributeRulesSerializer::getAttributeRules($name, $data));
88
89 3
        parent::__construct();
90
    }
91
92
    /**
93
     * @inheritdoc
94
     *
95
     * @SuppressWarnings(PHPMD.ElseExpression)
96
     */
97 3
    public function validate($input): bool
98
    {
99 3
        if (is_array($input) === false && ($input instanceof Generator) === false) {
100 1
            throw new InvalidArgumentException();
101
        }
102
103 2
        if ($this->areAggregatorsDirty() === true) {
104 1
            $this->resetAggregators();
105
        }
106
107 2
        $this->validateAttributes($input)->markAggregatorsAsDirty();
0 ignored issues
show
Documentation introduced by
$input is of type array|object<Generator>, but the function expects a object<Limoncello\Flute\Validation\Form\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
108
109 2
        $hasNoErrors = $this->getErrorAggregator()->count() <= 0;
110
111 2
        return $hasNoErrors;
112
    }
113
114
    /**
115
     * @inheritdoc
116
     */
117 1
    public function getMessages(): iterable
118
    {
119 1
        $formatter = $this->getMessageFormatter();
120 1
        foreach ($this->getErrors() as $error) {
121
            /** @var ErrorInterface $error */
122 1
            $message = $formatter->formatMessage($error->getMessageCode(), $error->getMessageContext() ?? []);
123
124 1
            yield $error->getParameterName() => $message;
125
        }
126
    }
127
128
    /**
129
     * @return BaseValidator
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use Validator.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
130
     */
131 3
    protected function resetAggregators(): BaseValidator
132
    {
133 3
        $self = parent::resetAggregators();
134
135 3
        $this->contextStorage = $this->createContextStorage();
136
137 3
        return $self;
138
    }
139
140
    /**
141
     * @return ContextStorageInterface
142
     */
143 2
    protected function getContextStorage(): ContextStorageInterface
144
    {
145 2
        return $this->contextStorage;
146
    }
147
148
    /**
149
     * @return FormatterInterface
150
     */
151 1
    protected function getMessageFormatter(): FormatterInterface
152
    {
153 1
        return $this->messageFormatter;
154
    }
155
156
    /**
157
     * @param FormatterInterface $messageFormatter
158
     *
159
     * @return self
160
     */
161 3
    private function setMessageFormatter(FormatterInterface $messageFormatter): self
162
    {
163 3
        $this->messageFormatter = $messageFormatter;
164
165 3
        return $this;
166
    }
167
168
    /**
169
     * @return ContextStorageInterface
170
     */
171 3
    protected function createContextStorage(): ContextStorageInterface
172
    {
173 3
        return new ContextStorage($this->getBlocks(), $this->getContainer());
174
    }
175
176
    /**
177
     * @param iterable $attributes
178
     *
179
     * @return self
180
     * @SuppressWarnings(PHPMD.StaticAccess)
181
     * @SuppressWarnings(PHPMD.ElseExpression)
182
     */
183 2
    private function validateAttributes(iterable $attributes): self
184
    {
185
        // execute start(s)
186 2
        $this->executeStarts(AttributeRulesSerializer::getRulesStartIndexes($this->getAttributeRules()));
187
188 2
        foreach ($attributes as $name => $value) {
189 2
            if (($index = $this->getAttributeIndex($name)) !== null) {
190 2
                $this->executeBlock($value, $index);
191
            } else {
192 2
                $this->getErrorAggregator()->add(new Error($name, $value, ErrorCodes::INVALID_VALUE, null));
193
            }
194
        }
195
196
        // execute end(s)
197 2
        $this->executeEnds(AttributeRulesSerializer::getRulesEndIndexes($this->getAttributeRules()));
198
199 2
        return $this;
200
    }
201
202
    /**
203
     * @param mixed $input
204
     * @param int   $index
205
     *
206
     * @return void
207
     *
208
     * @SuppressWarnings(PHPMD.StaticAccess)
209
     */
210 2
    private function executeBlock($input, int $index): void
211
    {
212 2
        BlockInterpreter::executeBlock(
213 2
            $input,
214 2
            $index,
215 2
            $this->getBlocks(),
216 2
            $this->getContextStorage(),
217 2
            $this->getCaptureAggregator(),
218 2
            $this->getErrorAggregator()
219
        );
220
    }
221
222
    /**
223
     * @param array $indexes
224
     *
225
     * @return void
226
     *
227
     * @SuppressWarnings(PHPMD.StaticAccess)
228
     */
229 2
    private function executeStarts(array $indexes): void
230
    {
231 2
        BlockInterpreter::executeStarts(
232 2
            $indexes,
233 2
            $this->getBlocks(),
234 2
            $this->getContextStorage(),
235 2
            $this->getErrorAggregator()
236
        );
237
    }
238
239
    /**
240
     * @param array $indexes
241
     *
242
     * @return void
243
     *
244
     * @SuppressWarnings(PHPMD.StaticAccess)
245
     */
246 2
    private function executeEnds(array $indexes): void
247
    {
248 2
        BlockInterpreter::executeEnds(
249 2
            $indexes,
250 2
            $this->getBlocks(),
251 2
            $this->getContextStorage(),
252 2
            $this->getErrorAggregator()
253
        );
254
    }
255
256
    /**
257
     * @param array $rules
258
     *
259
     * @return self
260
     *
261
     * @SuppressWarnings(PHPMD.StaticAccess)
262
     */
263 3
    private function setAttributeRules(array $rules): self
264
    {
265 3
        assert($this->debugCheckIndexesExist($rules));
266
267 3
        $this->attributeRules    = $rules;
268 3
        $this->attributeRulesIdx = AttributeRulesSerializer::getRulesIndexes($rules);
269
270 3
        return $this;
271
    }
272
273
    /**
274
     * @return int[]
275
     */
276 2
    private function getAttributeRules(): array
277
    {
278 2
        return $this->attributeRules;
279
    }
280
281
    /**
282
     * @return array
283
     */
284 3
    private function getBlocks(): array
285
    {
286 3
        return $this->blocks;
287
    }
288
289
    /**
290
     * @param array $blocks
291
     *
292
     * @return self
293
     */
294 3
    private function setBlocks(array $blocks): self
295
    {
296 3
        $this->blocks = $blocks;
297
298 3
        return $this;
299
    }
300
301
    /**
302
     * @param string $name
303
     *
304
     * @return int|null
305
     *
306
     * @SuppressWarnings(PHPMD.StaticAccess)
307
     */
308 2
    private function getAttributeIndex(string $name): ?int
309
    {
310 2
        $index = $this->attributeRulesIdx[$name] ?? null;
311
312 2
        return $index;
313
    }
314
315
    /**
316
     * @param array $rules
317
     *
318
     * @return bool
319
     *
320
     * @SuppressWarnings(PHPMD.StaticAccess)
321
     */
322 3
    private function debugCheckIndexesExist(array $rules): bool
323
    {
324 3
        $allOk = true;
325
326 3
        $indexes = array_merge(
327 3
            AttributeRulesSerializer::getRulesIndexes($rules),
328 3
            AttributeRulesSerializer::getRulesStartIndexes($rules),
329 3
            AttributeRulesSerializer::getRulesEndIndexes($rules)
330
        );
331
332 3
        foreach ($indexes as $index) {
333 3
            $allOk = $allOk && is_int($index) && AttributeRulesSerializer::isRuleExist($index, $this->getBlocks());
334
        }
335
336 3
        return $allOk;
337
    }
338
}
339