Completed
Push — master ( fd0e92...ea7c8b )
by Neomerx
04:49
created

Validator::getMessages()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
ccs 5
cts 5
cp 1
cc 2
eloc 5
nc 2
nop 0
crap 2
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\ContextStorage;
22
use Limoncello\Application\FormValidation\Execution\FormRuleSerializer;
23
use Limoncello\Container\Traits\HasContainerTrait;
24
use Limoncello\Contracts\L10n\FormatterInterface;
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\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 2
    public function __construct(
95
        string $name,
96
        array $data,
97
        ContainerInterface $container,
98
        FormatterInterface $messageFormatter
99
    ) {
100
        $this
101 2
            ->setContainer($container)
102 2
            ->setMessageFormatter($messageFormatter)
103 2
            ->setBlocks(FormRuleSerializer::extractBlocks($data))
104 2
            ->setAttributeRules(FormRuleSerializer::getAttributeRules($name, $data));
105
106 2
        parent::__construct();
107
    }
108
109
    /**
110
     * @inheritdoc
111
     *
112
     * @SuppressWarnings(PHPMD.ElseExpression)
113
     */
114 2
    public function validate($input): bool
115
    {
116 2
        if (is_array($input) === false) {
117 1
            throw new InvalidArgumentException();
118
        }
119
120 1
        if ($this->areAggregatorsDirty() === true) {
121 1
            $this->resetAggregators();
122
        }
123
124 1
        $this->validateAttributes($input)->markAggregatorsAsDirty();
125
126 1
        $hasNoErrors = $this->getErrorAggregator()->count() <= 0;
127
128 1
        return $hasNoErrors;
129
    }
130
131
    /**
132
     * @inheritdoc
133
     */
134 1
    public function getMessages(): iterable
135
    {
136 1
        $formatter = $this->getMessageFormatter();
137 1
        foreach ($this->getErrors() as $error) {
138
            /** @var ErrorInterface $error */
139 1
            $message = $formatter->formatMessage($error->getMessageCode(), $error->getMessageContext() ?? []);
140
141 1
            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 2
    protected function resetAggregators(): BaseValidator
149
    {
150 2
        $self = parent::resetAggregators();
151
152 2
        $this->contextStorage = $this->createContextStorage();
153
154 2
        return $self;
155
    }
156
157
    /**
158
     * @return ContextStorageInterface
159
     */
160 1
    protected function getContextStorage(): ContextStorageInterface
161
    {
162 1
        return $this->contextStorage;
163
    }
164
165
    /**
166
     * @return FormatterInterface
167
     */
168 1
    protected function getMessageFormatter(): FormatterInterface
169
    {
170 1
        return $this->messageFormatter;
171
    }
172
173
    /**
174
     * @param FormatterInterface $messageFormatter
175
     *
176
     * @return self
177
     */
178 2
    private function setMessageFormatter(FormatterInterface $messageFormatter): self
179
    {
180 2
        $this->messageFormatter = $messageFormatter;
181
182 2
        return $this;
183
    }
184
185
    /**
186
     * @return ContextStorageInterface
187
     */
188 2
    protected function createContextStorage(): ContextStorageInterface
189
    {
190 2
        return new ContextStorage($this->getContainer(), $this->getBlocks());
191
    }
192
193
    /**
194
     * @param array $attributes
195
     *
196
     * @return self
197
     * @SuppressWarnings(PHPMD.ElseExpression)
198
     */
199 1
    private function validateAttributes(array $attributes): self
200
    {
201
        // execute start(s)
202 1
        $this->executeStarts(FormRuleSerializer::getRulesStartIndexes($this->getAttributeRules()));
203
204 1
        foreach ($attributes as $name => $value) {
205 1
            if (($index = $this->getAttributeIndex($name)) !== null) {
206 1
                $this->executeBlock($value, $index);
207
            } else {
208 1
                $this->getErrorAggregator()->add(new Error($name, $value, ErrorCodes::INVALID_VALUE, null));
209
            }
210
        }
211
212
        // execute end(s)
213 1
        $this->executeEnds(FormRuleSerializer::getRulesEndIndexes($this->getAttributeRules()));
214
215 1
        return $this;
216
    }
217
218
    /**
219
     * @param mixed $input
220
     * @param int   $index
221
     *
222
     * @return void
223
     *
224
     * @SuppressWarnings(PHPMD.StaticAccess)
225
     */
226 1
    private function executeBlock($input, int $index): void
227
    {
228 1
        BlockInterpreter::executeBlock(
229 1
            $input,
230 1
            $index,
231 1
            $this->getBlocks(),
232 1
            $this->getContextStorage(),
233 1
            $this->getCaptureAggregator(),
234 1
            $this->getErrorAggregator()
235
        );
236
    }
237
238
    /**
239
     * @param array $indexes
240
     *
241
     * @return void
242
     *
243
     * @SuppressWarnings(PHPMD.StaticAccess)
244
     */
245 1
    private function executeStarts(array $indexes): void
246
    {
247 1
        BlockInterpreter::executeStarts(
248 1
            $indexes,
249 1
            $this->getBlocks(),
250 1
            $this->getContextStorage(),
251 1
            $this->getErrorAggregator()
252
        );
253
    }
254
255
    /**
256
     * @param array $indexes
257
     *
258
     * @return void
259
     *
260
     * @SuppressWarnings(PHPMD.StaticAccess)
261
     */
262 1
    private function executeEnds(array $indexes): void
263
    {
264 1
        BlockInterpreter::executeEnds(
265 1
            $indexes,
266 1
            $this->getBlocks(),
267 1
            $this->getContextStorage(),
268 1
            $this->getErrorAggregator()
269
        );
270
    }
271
272
    /**
273
     * @param array $rules
274
     *
275
     * @return self
276
     */
277 2
    private function setAttributeRules(array $rules): self
278
    {
279 2
        assert($this->debugCheckIndexesExist($rules));
280
281 2
        $this->attributeRules = $rules;
282
283 2
        return $this;
284
    }
285
286
    /**
287
     * @return int[]
288
     */
289 1
    private function getAttributeRules(): array
290
    {
291 1
        return $this->attributeRules;
292
    }
293
294
    /**
295
     * @return array
296
     */
297 2
    private function getBlocks(): array
298
    {
299 2
        return $this->blocks;
300
    }
301
302
    /**
303
     * @param array $blocks
304
     *
305
     * @return self
306
     */
307 2
    private function setBlocks(array $blocks): self
308
    {
309 2
        $this->blocks = $blocks;
310
311 2
        return $this;
312
    }
313
314
    /**
315
     * @param string $name
316
     *
317
     * @return int|null
318
     *
319
     * @SuppressWarnings(PHPMD.StaticAccess)
320
     */
321 1
    private function getAttributeIndex(string $name): ?int
322
    {
323 1
        $indexes = FormRuleSerializer::getRulesIndexes($this->getAttributeRules());
324 1
        $index   = $indexes[$name] ?? null;
325
326 1
        return $index;
327
    }
328
329
    /**
330
     * @param array $rules
331
     *
332
     * @return bool
333
     *
334
     * @SuppressWarnings(PHPMD.StaticAccess)
335
     */
336 2
    private function debugCheckIndexesExist(array $rules): bool
337
    {
338 2
        $allOk = true;
339
340 2
        $indexes = array_merge(
341 2
            FormRuleSerializer::getRulesIndexes($rules),
342 2
            FormRuleSerializer::getRulesStartIndexes($rules),
343 2
            FormRuleSerializer::getRulesEndIndexes($rules)
344
        );
345
346 2
        foreach ($indexes as $index) {
347 2
            $allOk = $allOk && is_int($index) && FormRuleSerializer::isRuleExist($index, $this->getBlocks());
348
        }
349
350 2
        return $allOk;
351
    }
352
}
353