Completed
Push — master ( 1a67d2...dfab50 )
by Neomerx
11:35 queued 22s
created

FormValidator   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 333
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 8

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 29
lcom 2
cbo 8
dl 0
loc 333
rs 10
c 0
b 0
f 0
ccs 92
cts 92
cp 1

20 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 1
A validate() 0 16 4
A getMessages() 0 10 2
A resetAggregators() 0 8 1
A getSerializer() 0 4 1
A setSerializer() 0 8 1
A getContext() 0 4 1
A setContext() 0 6 1
A getMessageFormatter() 0 4 1
A setMessageFormatter() 0 6 1
A validateAttributes() 0 18 3
A executeBlock() 0 11 1
A executeStarts() 0 9 1
A executeEnds() 0 9 1
A setRuleIndexes() 0 9 1
A getRuleIndexes() 0 4 1
A getBlocks() 0 4 1
A setBlocks() 0 6 1
A getAttributeIndex() 0 6 1
A debugCheckIndexesExist() 0 16 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\Contracts\L10n\FormatterInterface;
21
use Limoncello\Flute\Contracts\Validation\FormRulesSerializerInterface;
22
use Limoncello\Flute\Contracts\Validation\FormValidatorInterface;
23
use Limoncello\Flute\Exceptions\InvalidArgumentException;
24
use Limoncello\Validation\Contracts\Errors\ErrorCodes;
25
use Limoncello\Validation\Contracts\Errors\ErrorInterface;
26
use Limoncello\Validation\Contracts\Execution\ContextStorageInterface;
27
use Limoncello\Validation\Errors\Error;
28
use Limoncello\Validation\Execution\BlockInterpreter;
29
use Limoncello\Validation\Validator\BaseValidator;
30
31
/**
32
 * @package Limoncello\Flute
33
 *
34
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
35
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
36
 */
37
class FormValidator extends BaseValidator implements FormValidatorInterface
38
{
39
    /**
40
     * It is string though it can be used to access static methods of the interface.
41
     *
42
     * @var FormRulesSerializerInterface|string
43
     */
44
    private $serializerClass;
45
46
    /**
47
     * @var ContextStorageInterface
48
     */
49
    private $contextStorage;
50
51
    /**
52
     * @var FormatterInterface
53
     */
54
    private $messageFormatter;
55
56
    /**
57
     * @var array
58
     */
59
    private $blocks;
60
61
    /**
62
     * @var int[]
63
     */
64
    private $ruleIndexes;
65
66
    /**
67
     * @var array
68
     */
69
    private $ruleMainIndexes;
70
71
    /**
72
     * @param string                  $rulesClass
73
     * @param string                  $serializerClass
74
     * @param array                   $serializedData
75
     * @param ContextStorageInterface $context
76
     * @param FormatterInterface      $messageFormatter
77
     */
78 3
    public function __construct(
79
        string $rulesClass,
80
        string $serializerClass,
81
        array $serializedData,
82
        ContextStorageInterface $context,
83
        FormatterInterface $messageFormatter
84
    ) {
85
        $this
86 3
            ->setSerializer($serializerClass)
87 3
            ->setContext($context)
88 3
            ->setMessageFormatter($messageFormatter);
89
90
        $this
91 3
            ->setBlocks($this->getSerializer()::readBlocks($serializedData))
92 3
            ->setRuleIndexes($this->getSerializer()::readRules($rulesClass, $serializedData));
93
94 3
        parent::__construct();
95
    }
96
97
    /**
98
     * @inheritdoc
99
     *
100
     * @SuppressWarnings(PHPMD.ElseExpression)
101
     */
102 3
    public function validate($input): bool
103
    {
104 3
        if (is_array($input) === false && ($input instanceof Generator) === false) {
105 1
            throw new InvalidArgumentException();
106
        }
107
108 2
        if ($this->areAggregatorsDirty() === true) {
109 1
            $this->resetAggregators();
110
        }
111
112 2
        $this->validateAttributes($input)->markAggregatorsAsDirty();
113
114 2
        $hasNoErrors = $this->getErrorAggregator()->count() <= 0;
115
116 2
        return $hasNoErrors;
117
    }
118
119
    /**
120
     * @inheritdoc
121
     */
122 1
    public function getMessages(): iterable
123
    {
124 1
        $formatter = $this->getMessageFormatter();
125 1
        foreach ($this->getErrors() as $error) {
126
            /** @var ErrorInterface $error */
127 1
            $message = $formatter->formatMessage($error->getMessageCode(), $error->getMessageContext() ?? []);
128
129 1
            yield $error->getParameterName() => $message;
130
        }
131
    }
132
133
    /**
134
     * @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...
135
     */
136 3
    protected function resetAggregators(): BaseValidator
137
    {
138 3
        parent::resetAggregators();
139
140 3
        $this->contextStorage->clear();
141
142 3
        return $this;
143
    }
144
145
    /**
146
     * @return FormRulesSerializerInterface|string
147
     */
148 3
    protected function getSerializer()
149
    {
150 3
        return $this->serializerClass;
151
    }
152
153
    /**
154
     * @param string $serializerClass
155
     *
156
     * @return self
157
     */
158 3
    protected function setSerializer(string $serializerClass): self
159
    {
160 3
        assert(in_array(FormRulesSerializerInterface::class, class_implements($serializerClass)));
161
162 3
        $this->serializerClass = $serializerClass;
163
164 3
        return $this;
165
    }
166
167
    /**
168
     * @return ContextStorageInterface
169
     */
170 2
    protected function getContext(): ContextStorageInterface
171
    {
172 2
        return $this->contextStorage;
173
    }
174
175
    /**
176
     * @param ContextStorageInterface $context
177
     *
178
     * @return self
179
     */
180 3
    protected function setContext(ContextStorageInterface $context): self
181
    {
182 3
        $this->contextStorage = $context;
183
184 3
        return $this;
185
    }
186
187
    /**
188
     * @return FormatterInterface
189
     */
190 1
    protected function getMessageFormatter(): FormatterInterface
191
    {
192 1
        return $this->messageFormatter;
193
    }
194
195
    /**
196
     * @param FormatterInterface $messageFormatter
197
     *
198
     * @return self
199
     */
200 3
    private function setMessageFormatter(FormatterInterface $messageFormatter): self
201
    {
202 3
        $this->messageFormatter = $messageFormatter;
203
204 3
        return $this;
205
    }
206
207
    /**
208
     * @param iterable $attributes
209
     *
210
     * @return self
211
     * @SuppressWarnings(PHPMD.StaticAccess)
212
     * @SuppressWarnings(PHPMD.ElseExpression)
213
     */
214 2
    private function validateAttributes(iterable $attributes): self
215
    {
216
        // execute start(s)
217 2
        $this->executeStarts($this->getSerializer()::readRuleStartIndexes($this->getRuleIndexes()));
218
219 2
        foreach ($attributes as $name => $value) {
220 2
            if (($index = $this->getAttributeIndex($name)) !== null) {
221 2
                $this->executeBlock($value, $index);
222
            } else {
223 2
                $this->getErrorAggregator()->add(new Error($name, $value, ErrorCodes::INVALID_VALUE, null));
224
            }
225
        }
226
227
        // execute end(s)
228 2
        $this->executeEnds($this->getSerializer()::readRuleEndIndexes($this->getRuleIndexes()));
229
230 2
        return $this;
231
    }
232
233
    /**
234
     * @param mixed $input
235
     * @param int   $index
236
     *
237
     * @return void
238
     *
239
     * @SuppressWarnings(PHPMD.StaticAccess)
240
     */
241 2
    private function executeBlock($input, int $index): void
242
    {
243 2
        BlockInterpreter::executeBlock(
244 2
            $input,
245 2
            $index,
246 2
            $this->getBlocks(),
247 2
            $this->getContext(),
248 2
            $this->getCaptureAggregator(),
249 2
            $this->getErrorAggregator()
250
        );
251
    }
252
253
    /**
254
     * @param array $indexes
255
     *
256
     * @return void
257
     *
258
     * @SuppressWarnings(PHPMD.StaticAccess)
259
     */
260 2
    private function executeStarts(array $indexes): void
261
    {
262 2
        BlockInterpreter::executeStarts(
263 2
            $indexes,
264 2
            $this->getBlocks(),
265 2
            $this->getContext(),
266 2
            $this->getErrorAggregator()
267
        );
268
    }
269
270
    /**
271
     * @param array $indexes
272
     *
273
     * @return void
274
     *
275
     * @SuppressWarnings(PHPMD.StaticAccess)
276
     */
277 2
    private function executeEnds(array $indexes): void
278
    {
279 2
        BlockInterpreter::executeEnds(
280 2
            $indexes,
281 2
            $this->getBlocks(),
282 2
            $this->getContext(),
283 2
            $this->getErrorAggregator()
284
        );
285
    }
286
287
    /**
288
     * @param array $ruleIndexes
289
     *
290
     * @return self
291
     *
292
     * @SuppressWarnings(PHPMD.StaticAccess)
293
     */
294 3
    private function setRuleIndexes(array $ruleIndexes): self
295
    {
296 3
        assert($this->debugCheckIndexesExist($ruleIndexes));
297
298 3
        $this->ruleIndexes     = $ruleIndexes;
299 3
        $this->ruleMainIndexes = $this->getSerializer()::readRuleMainIndexes($ruleIndexes);
300
301 3
        return $this;
302
    }
303
304
    /**
305
     * @return int[]
306
     */
307 2
    private function getRuleIndexes(): array
308
    {
309 2
        return $this->ruleIndexes;
310
    }
311
312
    /**
313
     * @return array
314
     */
315 3
    private function getBlocks(): array
316
    {
317 3
        return $this->blocks;
318
    }
319
320
    /**
321
     * @param array $blocks
322
     *
323
     * @return self
324
     */
325 3
    private function setBlocks(array $blocks): self
326
    {
327 3
        $this->blocks = $blocks;
328
329 3
        return $this;
330
    }
331
332
    /**
333
     * @param string $name
334
     *
335
     * @return int|null
336
     *
337
     * @SuppressWarnings(PHPMD.StaticAccess)
338
     */
339 2
    private function getAttributeIndex(string $name): ?int
340
    {
341 2
        $index = $this->ruleMainIndexes[$name] ?? null;
342
343 2
        return $index;
344
    }
345
346
    /**
347
     * @param array $rules
348
     *
349
     * @return bool
350
     *
351
     * @SuppressWarnings(PHPMD.StaticAccess)
352
     */
353 3
    private function debugCheckIndexesExist(array $rules): bool
354
    {
355 3
        $allOk = true;
356
357 3
        $indexes = array_merge(
358 3
            $this->getSerializer()::readRuleMainIndexes($rules),
359 3
            $this->getSerializer()::readRuleStartIndexes($rules),
360 3
            $this->getSerializer()::readRuleEndIndexes($rules)
361
        );
362
363 3
        foreach ($indexes as $index) {
364 3
            $allOk = $allOk && is_int($index) && array_key_exists($index, $this->getBlocks());
365
        }
366
367 3
        return $allOk;
368
    }
369
}
370