Completed
Push — develop ( 1776d4...322507 )
by Neomerx
04:39 queued 02:40
created

Validator::getMessages()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 0
cts 5
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 0
crap 6
1
<?php namespace Limoncello\Application\FormValidation;
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 Limoncello\Application\Contracts\Validation\FormValidatorInterface;
20
use Limoncello\Application\Exceptions\InvalidArgumentException;
21
use Limoncello\Application\FormValidation\Execution\FormRuleSerializer;
22
use Limoncello\Container\Traits\HasContainerTrait;
23
use Limoncello\Contracts\L10n\FormatterInterface;
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\Execution\ContextStorage;
30
use Limoncello\Validation\Validator\BaseValidator;
31
use Psr\Container\ContainerInterface;
32
33
/**
34
 * @package Limoncello\Flute
35
 *
36
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
37
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
38
 */
39
class Validator extends BaseValidator implements FormValidatorInterface
40
{
41
    use HasContainerTrait;
42
43
    /**
44
     * Namespace for string resources.
45
     */
46
    const RESOURCES_NAMESPACE = 'Limoncello.Application.Validation';
47
48
    /** Rule description index */
49
    const RULE_INDEX = 0;
50
51
    /** Rule description index */
52
    const RULE_ATTRIBUTES = self::RULE_INDEX + 1;
53
54
    /** Rule description index */
55
    const RULE_TO_ONE = self::RULE_ATTRIBUTES + 1;
56
57
    /** Rule description index */
58
    const RULE_TO_MANY = self::RULE_TO_ONE + 1;
59
60
    /** Rule description index */
61
    const RULE_UNLISTED_ATTRIBUTE = self::RULE_TO_MANY + 1;
62
63
    /** Rule description index */
64
    const RULE_UNLISTED_RELATIONSHIP = self::RULE_UNLISTED_ATTRIBUTE + 1;
65
66
    /**
67
     * @var ContextStorageInterface
68
     */
69
    private $contextStorage;
70
71
    /**
72
     * @var FormatterInterface
73
     */
74
    private $messageFormatter;
75
76
    /**
77
     * @var array
78
     */
79
    private $blocks;
80
81
    /**
82
     * @var int[]
83
     */
84
    private $attributeRules;
85
86
    /**
87
     * @param string             $name
88
     * @param array              $data
89
     * @param ContainerInterface $container
90
     * @param FormatterInterface $messageFormatter
91
     *
92
     * @SuppressWarnings(PHPMD.StaticAccess)
93
     */
94
    public function __construct(
95
        string $name,
96
        array $data,
97
        ContainerInterface $container,
98
        FormatterInterface $messageFormatter
99
    ) {
100
        $this
101
            ->setContainer($container)
102
            ->setMessageFormatter($messageFormatter)
103
            ->setBlocks(FormRuleSerializer::extractBlocks($data))
104
            ->setAttributeRules(FormRuleSerializer::getAttributeRules($name, $data));
105
106
        parent::__construct();
107
    }
108
109
    /**
110
     * @inheritdoc
111
     *
112
     * @SuppressWarnings(PHPMD.ElseExpression)
113
     */
114
    public function validate($input): bool
115
    {
116
        if (is_array($input) === false) {
117
            throw new InvalidArgumentException();
118
        }
119
120
        if ($this->areAggregatorsDirty() === true) {
121
            $this->resetAggregators();
122
        }
123
124
        $this->validateAttributes($input)->markAggregatorsAsDirty();
125
126
        $hasNoErrors = $this->getErrorAggregator()->count() <= 0;
127
128
        return $hasNoErrors;
129
    }
130
131
    /**
132
     * @inheritdoc
133
     */
134
    public function getMessages(): iterable
135
    {
136
        $formatter = $this->getMessageFormatter();
137
        foreach ($this->getErrors() as $error) {
138
            /** @var ErrorInterface $error */
139
            $message = $formatter->formatMessage($error->getMessageCode(), $error->getMessageContext() ?? []);
140
141
            yield $error->getParameterName() => $message;
142
        }
143
    }
144
145
    /**
146
     * @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...
147
     */
148
    protected function resetAggregators(): BaseValidator
149
    {
150
        $self = parent::resetAggregators();
151
152
        $this->contextStorage = $this->createContextStorage();
153
154
        return $self;
155
    }
156
157
    /**
158
     * @return ContextStorageInterface
159
     */
160
    protected function getContextStorage(): ContextStorageInterface
161
    {
162
        return $this->contextStorage;
163
    }
164
165
    /**
166
     * @return FormatterInterface
167
     */
168
    protected function getMessageFormatter(): FormatterInterface
169
    {
170
        return $this->messageFormatter;
171
    }
172
173
    /**
174
     * @param FormatterInterface $messageFormatter
175
     *
176
     * @return self
177
     */
178
    private function setMessageFormatter(FormatterInterface $messageFormatter): self
179
    {
180
        $this->messageFormatter = $messageFormatter;
181
182
        return $this;
183
    }
184
185
    /**
186
     * @return ContextStorageInterface
187
     */
188
    protected function createContextStorage(): ContextStorageInterface
189
    {
190
        return new ContextStorage($this->getBlocks(), $this->getContainer());
0 ignored issues
show
Unused Code introduced by
The call to ContextStorage::__construct() has too many arguments starting with $this->getContainer().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
191
    }
192
193
    /**
194
     * @param array $attributes
195
     *
196
     * @return self
197
     * @SuppressWarnings(PHPMD.StaticAccess)
198
     * @SuppressWarnings(PHPMD.ElseExpression)
199
     */
200
    private function validateAttributes(array $attributes): self
201
    {
202
        // execute start(s)
203
        $this->executeStarts(FormRuleSerializer::getRulesStartIndexes($this->getAttributeRules()));
204
205
        foreach ($attributes as $name => $value) {
206
            if (($index = $this->getAttributeIndex($name)) !== null) {
207
                $this->executeBlock($value, $index);
208
            } else {
209
                $this->getErrorAggregator()->add(new Error($name, $value, ErrorCodes::INVALID_VALUE, null));
210
            }
211
        }
212
213
        // execute end(s)
214
        $this->executeEnds(FormRuleSerializer::getRulesEndIndexes($this->getAttributeRules()));
215
216
        return $this;
217
    }
218
219
    /**
220
     * @param mixed $input
221
     * @param int   $index
222
     *
223
     * @return void
224
     *
225
     * @SuppressWarnings(PHPMD.StaticAccess)
226
     */
227
    private function executeBlock($input, int $index): void
228
    {
229
        BlockInterpreter::executeBlock(
230
            $input,
231
            $index,
232
            $this->getBlocks(),
233
            $this->getContextStorage(),
234
            $this->getCaptureAggregator(),
235
            $this->getErrorAggregator()
236
        );
237
    }
238
239
    /**
240
     * @param array $indexes
241
     *
242
     * @return void
243
     *
244
     * @SuppressWarnings(PHPMD.StaticAccess)
245
     */
246
    private function executeStarts(array $indexes): void
247
    {
248
        BlockInterpreter::executeStarts(
249
            $indexes,
250
            $this->getBlocks(),
251
            $this->getContextStorage(),
252
            $this->getErrorAggregator()
253
        );
254
    }
255
256
    /**
257
     * @param array $indexes
258
     *
259
     * @return void
260
     *
261
     * @SuppressWarnings(PHPMD.StaticAccess)
262
     */
263
    private function executeEnds(array $indexes): void
264
    {
265
        BlockInterpreter::executeEnds(
266
            $indexes,
267
            $this->getBlocks(),
268
            $this->getContextStorage(),
269
            $this->getErrorAggregator()
270
        );
271
    }
272
273
    /**
274
     * @param array $rules
275
     *
276
     * @return self
277
     */
278
    private function setAttributeRules(array $rules): self
279
    {
280
        assert($this->debugCheckIndexesExist($rules));
281
282
        $this->attributeRules = $rules;
283
284
        return $this;
285
    }
286
287
    /**
288
     * @return int[]
289
     */
290
    private function getAttributeRules(): array
291
    {
292
        return $this->attributeRules;
293
    }
294
295
    /**
296
     * @return array
297
     */
298
    private function getBlocks(): array
299
    {
300
        return $this->blocks;
301
    }
302
303
    /**
304
     * @param array $blocks
305
     *
306
     * @return self
307
     */
308
    private function setBlocks(array $blocks): self
309
    {
310
        $this->blocks = $blocks;
311
312
        return $this;
313
    }
314
315
    /**
316
     * @param string $name
317
     *
318
     * @return int|null
319
     *
320
     * @SuppressWarnings(PHPMD.StaticAccess)
321
     */
322
    private function getAttributeIndex(string $name): ?int
323
    {
324
        $indexes = FormRuleSerializer::getRulesIndexes($this->getAttributeRules());
325
        $index   = $indexes[$name] ?? null;
326
327
        return $index;
328
    }
329
330
    /**
331
     * @param array $rules
332
     *
333
     * @return bool
334
     *
335
     * @SuppressWarnings(PHPMD.StaticAccess)
336
     */
337
    private function debugCheckIndexesExist(array $rules): bool
338
    {
339
        $allOk = true;
340
341
        $indexes = array_merge(
342
            FormRuleSerializer::getRulesIndexes($rules),
343
            FormRuleSerializer::getRulesStartIndexes($rules),
344
            FormRuleSerializer::getRulesEndIndexes($rules)
345
        );
346
347
        foreach ($indexes as $index) {
348
            $allOk = $allOk && is_int($index) && FormRuleSerializer::isRuleExist($index, $this->getBlocks());
349
        }
350
351
        return $allOk;
352
    }
353
}
354