FormValidator::getMessages()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 2
cts 2
cp 1
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 2
1
<?php declare (strict_types = 1);
2
3
namespace Limoncello\Flute\Validation\Form;
4
5
/**
6
 * Copyright 2015-2019 [email protected]
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
use Generator;
22
use Limoncello\Common\Reflection\ClassIsTrait;
23
use Limoncello\Contracts\L10n\FormatterInterface;
24
use Limoncello\Flute\Contracts\Validation\FormRulesSerializerInterface;
25
use Limoncello\Flute\Contracts\Validation\FormValidatorInterface;
26
use Limoncello\Flute\Exceptions\InvalidArgumentException;
27
use Limoncello\Flute\L10n\Messages;
28
use Limoncello\Validation\Contracts\Errors\ErrorCodes;
29
use Limoncello\Validation\Contracts\Errors\ErrorInterface;
30
use Limoncello\Validation\Contracts\Execution\ContextStorageInterface;
31
use Limoncello\Validation\Errors\Error;
32
use Limoncello\Validation\Execution\BlockInterpreter;
33
use Limoncello\Validation\Validator\BaseValidator;
34
use function array_key_exists;
35
use function array_merge;
36
use function assert;
37
use function is_array;
38
use function is_int;
39
40
/**
41
 * @package Limoncello\Flute
42
 *
43
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
44
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
45
 */
46
class FormValidator extends BaseValidator implements FormValidatorInterface
47
{
48
    use ClassIsTrait;
49
50
    /**
51
     * It is string though it can be used to access static methods of the interface.
52
     *
53
     * @var FormRulesSerializerInterface|string
54
     */
55
    private $serializerClass;
56
57
    /**
58
     * @var ContextStorageInterface
59
     */
60
    private $contextStorage;
61
62
    /**
63
     * @var FormatterInterface
64
     */
65
    private $messageFormatter;
66
67
    /**
68
     * @var array
69
     */
70
    private $blocks;
71
72
    /**
73
     * @var int[]
74
     */
75
    private $ruleIndexes;
76
77
    /**
78
     * @var array
79
     */
80
    private $ruleMainIndexes;
81 4
82
    /**
83
     * @param string                  $rulesClass
84
     * @param string                  $serializerClass
85
     * @param array                   $serializedData
86
     * @param ContextStorageInterface $context
87
     * @param FormatterInterface      $messageFormatter
88
     */
89 4
    public function __construct(
90 4
        string $rulesClass,
91 4
        string $serializerClass,
92
        array $serializedData,
93
        ContextStorageInterface $context,
94 4
        FormatterInterface $messageFormatter
95 4
    ) {
96
        $this
97 4
            ->setSerializer($serializerClass)
98
            ->setContext($context)
99
            ->setMessageFormatter($messageFormatter);
100
101
        $this
102
            ->setBlocks($this->getSerializer()::readBlocks($serializedData))
103
            ->setRuleIndexes($this->getSerializer()::readRules($rulesClass, $serializedData));
104
105 4
        parent::__construct();
106
    }
107 4
108 1
    /**
109
     * @inheritdoc
110
     *
111 3
     * @SuppressWarnings(PHPMD.ElseExpression)
112 2
     */
113
    public function validate($input): bool
114
    {
115 3
        if (is_array($input) === false && ($input instanceof Generator) === false) {
116
            throw new InvalidArgumentException();
117 3
        }
118
119 3
        if ($this->areAggregatorsDirty() === true) {
120
            $this->resetAggregators();
121
        }
122
123
        $this->validateAttributes($input)->markAggregatorsAsDirty();
124
125 2
        $hasNoErrors = $this->getErrorAggregator()->count() <= 0;
126
127 2
        return $hasNoErrors;
128 2
    }
129
130 2
    /**
131
     * @inheritdoc
132 2
     */
133
    public function getMessages(): iterable
134
    {
135
        $formatter = $this->getMessageFormatter();
136
        foreach ($this->getErrors() as $error) {
137
            /** @var ErrorInterface $error */
138
            $message = $formatter->formatMessage($error->getMessageTemplate(), $error->getMessageParameters());
139 4
140
            yield $error->getParameterName() => $message;
141 4
        }
142
    }
143 4
144
    /**
145 4
     * @return BaseValidator
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use FormValidator.

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...
146
     */
147
    protected function resetAggregators(): BaseValidator
148
    {
149
        parent::resetAggregators();
150
151 4
        $this->contextStorage->clear();
152
153 4
        return $this;
154
    }
155
156
    /**
157
     * @return FormRulesSerializerInterface|string
158
     */
159
    protected function getSerializer()
160
    {
161 4
        return $this->serializerClass;
162
    }
163 4
164
    /**
165 4
     * @param string $serializerClass
166
     *
167 4
     * @return self
168
     */
169
    protected function setSerializer(string $serializerClass): self
170
    {
171
        assert(static::classImplements($serializerClass, FormRulesSerializerInterface::class));
172
173 3
        $this->serializerClass = $serializerClass;
174
175 3
        return $this;
176
    }
177
178
    /**
179
     * @return ContextStorageInterface
180
     */
181
    protected function getContext(): ContextStorageInterface
182
    {
183 4
        return $this->contextStorage;
184
    }
185 4
186
    /**
187 4
     * @param ContextStorageInterface $context
188
     *
189
     * @return self
190
     */
191
    protected function setContext(ContextStorageInterface $context): self
192
    {
193 2
        $this->contextStorage = $context;
194
195 2
        return $this;
196
    }
197
198
    /**
199
     * @return FormatterInterface
200
     */
201
    protected function getMessageFormatter(): FormatterInterface
202
    {
203 4
        return $this->messageFormatter;
204
    }
205 4
206
    /**
207 4
     * @param FormatterInterface $messageFormatter
208
     *
209
     * @return self
210
     */
211
    private function setMessageFormatter(FormatterInterface $messageFormatter): self
212
    {
213
        $this->messageFormatter = $messageFormatter;
214
215
        return $this;
216
    }
217 3
218
    /**
219
     * @param iterable $attributes
220 3
     *
221
     * @return self
222 3
     * @SuppressWarnings(PHPMD.StaticAccess)
223 3
     * @SuppressWarnings(PHPMD.ElseExpression)
224 3
     */
225
    private function validateAttributes(iterable $attributes): self
226 1
    {
227 3
        // execute start(s)
228
        $this->executeStarts($this->getSerializer()::readRuleStartIndexes($this->getRuleIndexes()));
229
230
        foreach ($attributes as $name => $value) {
231
            if (($index = $this->getAttributeIndex($name)) !== null) {
232
                $this->executeBlock($value, $index);
233 3
            } else {
234
                $this->getErrorAggregator()->add(
235 3
                    new Error($name, $value, ErrorCodes::INVALID_VALUE, Messages::INVALID_VALUE, [])
236
                );
237
            }
238
        }
239
240
        // execute end(s)
241
        $this->executeEnds($this->getSerializer()::readRuleEndIndexes($this->getRuleIndexes()));
242
243
        return $this;
244
    }
245
246 3
    /**
247
     * @param mixed $input
248 3
     * @param int   $index
249 3
     *
250 3
     * @return void
251 3
     *
252 3
     * @SuppressWarnings(PHPMD.StaticAccess)
253 3
     */
254 3
    private function executeBlock($input, int $index): void
255
    {
256
        BlockInterpreter::executeBlock(
257
            $input,
258
            $index,
259
            $this->getBlocks(),
260
            $this->getContext(),
261
            $this->getCaptureAggregator(),
262
            $this->getErrorAggregator()
263
        );
264
    }
265 3
266
    /**
267 3
     * @param array $indexes
268 3
     *
269 3
     * @return void
270 3
     *
271 3
     * @SuppressWarnings(PHPMD.StaticAccess)
272
     */
273
    private function executeStarts(array $indexes): void
274
    {
275
        BlockInterpreter::executeStarts(
276
            $indexes,
277
            $this->getBlocks(),
278
            $this->getContext(),
279
            $this->getErrorAggregator()
280
        );
281
    }
282 3
283
    /**
284 3
     * @param array $indexes
285 3
     *
286 3
     * @return void
287 3
     *
288 3
     * @SuppressWarnings(PHPMD.StaticAccess)
289
     */
290
    private function executeEnds(array $indexes): void
291
    {
292
        BlockInterpreter::executeEnds(
293
            $indexes,
294
            $this->getBlocks(),
295
            $this->getContext(),
296
            $this->getErrorAggregator()
297
        );
298
    }
299 4
300
    /**
301 4
     * @param array $ruleIndexes
302
     *
303 4
     * @return self
304 4
     *
305
     * @SuppressWarnings(PHPMD.StaticAccess)
306 4
     */
307
    private function setRuleIndexes(array $ruleIndexes): self
308
    {
309
        assert($this->debugCheckIndexesExist($ruleIndexes));
310
311
        $this->ruleIndexes     = $ruleIndexes;
312 3
        $this->ruleMainIndexes = $this->getSerializer()::readRuleMainIndexes($ruleIndexes);
313
314 3
        return $this;
315
    }
316
317
    /**
318
     * @return int[]
319
     */
320 4
    private function getRuleIndexes(): array
321
    {
322 4
        return $this->ruleIndexes;
323
    }
324
325
    /**
326
     * @return array
327
     */
328
    private function getBlocks(): array
329
    {
330 4
        return $this->blocks;
331
    }
332 4
333
    /**
334 4
     * @param array $blocks
335
     *
336
     * @return self
337
     */
338
    private function setBlocks(array $blocks): self
339
    {
340
        $this->blocks = $blocks;
341
342
        return $this;
343
    }
344 3
345
    /**
346 3
     * @param string $name
347
     *
348 3
     * @return int|null
349
     *
350
     * @SuppressWarnings(PHPMD.StaticAccess)
351
     */
352
    private function getAttributeIndex(string $name): ?int
353
    {
354
        $index = $this->ruleMainIndexes[$name] ?? null;
355
356
        return $index;
357
    }
358 4
359
    /**
360 4
     * @param array $rules
361
     *
362 4
     * @return bool
363 4
     *
364 4
     * @SuppressWarnings(PHPMD.StaticAccess)
365 4
     */
366
    private function debugCheckIndexesExist(array $rules): bool
367
    {
368 4
        $allOk = true;
369 4
370
        $indexes = array_merge(
371
            $this->getSerializer()::readRuleMainIndexes($rules),
372 4
            $this->getSerializer()::readRuleStartIndexes($rules),
373
            $this->getSerializer()::readRuleEndIndexes($rules)
374
        );
375
376
        foreach ($indexes as $index) {
377
            $allOk = $allOk && is_int($index) && array_key_exists($index, $this->getBlocks());
378
        }
379
380
        return $allOk;
381
    }
382
}
383