Completed
Push — develop ( fc40f2...fd0e92 )
by Neomerx
08:05 queued 06:22
created

Validator::validate()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 16
c 0
b 0
f 0
ccs 8
cts 8
cp 1
rs 9.4285
cc 3
eloc 8
nc 3
nop 1
crap 3
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\Validation\Contracts\Errors\ErrorCodes;
25
use Limoncello\Validation\Contracts\Execution\ContextStorageInterface;
26
use Limoncello\Validation\Errors\Error;
27
use Limoncello\Validation\Execution\BlockInterpreter;
28
use Limoncello\Validation\Validator\BaseValidator;
29
use Psr\Container\ContainerInterface;
30
31
/**
32
 * @package Limoncello\Flute
33
 *
34
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
35
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
36
 */
37
class Validator extends BaseValidator implements FormValidatorInterface
38
{
39
    use HasContainerTrait;
40
41
    /**
42
     * Namespace for string resources.
43
     */
44
    const RESOURCES_NAMESPACE = 'Limoncello.Flute.Validation';
45
46
    /** Rule description index */
47
    const RULE_INDEX = 0;
48
49
    /** Rule description index */
50
    const RULE_ATTRIBUTES = self::RULE_INDEX + 1;
51
52
    /** Rule description index */
53
    const RULE_TO_ONE = self::RULE_ATTRIBUTES + 1;
54
55
    /** Rule description index */
56
    const RULE_TO_MANY = self::RULE_TO_ONE + 1;
57
58
    /** Rule description index */
59
    const RULE_UNLISTED_ATTRIBUTE = self::RULE_TO_MANY + 1;
60
61
    /** Rule description index */
62
    const RULE_UNLISTED_RELATIONSHIP = self::RULE_UNLISTED_ATTRIBUTE + 1;
63
64
    /**
65
     * @var ContextStorageInterface
66
     */
67
    private $contextStorage;
68
69
    /**
70
     * @var array
71
     */
72
    private $blocks;
73
74
    /**
75
     * @var int[]
76
     */
77
    private $attributeRules;
78
79
    /**
80
     * @param string             $name
81
     * @param array              $data
82
     * @param ContainerInterface $container
83
     *
84
     * @SuppressWarnings(PHPMD.StaticAccess)
85
     */
86 2
    public function __construct(string $name, array $data, ContainerInterface $container)
87
    {
88
        $this
89 2
            ->setContainer($container)
90 2
            ->setBlocks(FormRuleSerializer::extractBlocks($data))
91 2
            ->setAttributeRules(FormRuleSerializer::getAttributeRules($name, $data));
92
93 2
        parent::__construct();
94
    }
95
96
    /**
97
     * @inheritdoc
98
     *
99
     * @SuppressWarnings(PHPMD.ElseExpression)
100
     */
101 2
    public function validate($input): bool
102
    {
103 2
        if (is_array($input) === false) {
104 1
            throw new InvalidArgumentException();
105
        }
106
107 1
        if ($this->areAggregatorsDirty() === true) {
108 1
            $this->resetAggregators();
109
        }
110
111 1
        $this->validateAttributes($input)->markAggregatorsAsDirty();
112
113 1
        $hasNoErrors = $this->getErrorAggregator()->count() <= 0;
0 ignored issues
show
Bug introduced by
The method getErrorAggregator() does not seem to exist on object<Limoncello\Applic...rmValidation\Validator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
114
115 1
        return $hasNoErrors;
116
    }
117
118
    /**
119
     * @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...
120
     */
121 2
    protected function resetAggregators(): BaseValidator
122
    {
123 2
        $self = parent::resetAggregators();
124
125 2
        $this->contextStorage = $this->createContextStorage();
126
127 2
        return $self;
128
    }
129
130
    /**
131
     * @return ContextStorageInterface
132
     */
133 1
    protected function getContextStorage(): ContextStorageInterface
134
    {
135 1
        return $this->contextStorage;
136
    }
137
138
    /**
139
     * @return ContextStorageInterface
140
     */
141 2
    protected function createContextStorage(): ContextStorageInterface
142
    {
143 2
        return new ContextStorage($this->getContainer(), $this->getBlocks());
144
    }
145
146
    /**
147
     * @param array $attributes
148
     *
149
     * @return self
150
     * @SuppressWarnings(PHPMD.ElseExpression)
151
     */
152 1
    private function validateAttributes(array $attributes): self
153
    {
154
        // execute start(s)
155 1
        $this->executeStarts(FormRuleSerializer::getRulesStartIndexes($this->getAttributeRules()));
156
157 1
        foreach ($attributes as $name => $value) {
158 1
            if (($index = $this->getAttributeIndex($name)) !== null) {
159 1
                $this->executeBlock($value, $index);
160
            } else {
161 1
                $this->getErrorAggregator()->add(new Error($name, $value, ErrorCodes::INVALID_VALUE, null));
0 ignored issues
show
Bug introduced by
The method getErrorAggregator() does not seem to exist on object<Limoncello\Applic...rmValidation\Validator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
162
            }
163
        }
164
165
        // execute end(s)
166 1
        $this->executeEnds(FormRuleSerializer::getRulesEndIndexes($this->getAttributeRules()));
167
168 1
        return $this;
169
    }
170
171
    /**
172
     * @param mixed $input
173
     * @param int   $index
174
     *
175
     * @return void
176
     *
177
     * @SuppressWarnings(PHPMD.StaticAccess)
178
     */
179 1
    private function executeBlock($input, int $index): void
180
    {
181 1
        BlockInterpreter::executeBlock(
182 1
            $input,
183 1
            $index,
184 1
            $this->getBlocks(),
185 1
            $this->getContextStorage(),
186 1
            $this->getCaptureAggregator(),
0 ignored issues
show
Bug introduced by
The method getCaptureAggregator() does not seem to exist on object<Limoncello\Applic...rmValidation\Validator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
187 1
            $this->getErrorAggregator()
0 ignored issues
show
Bug introduced by
The method getErrorAggregator() does not seem to exist on object<Limoncello\Applic...rmValidation\Validator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
188
        );
189
    }
190
191
    /**
192
     * @param array $indexes
193
     *
194
     * @return void
195
     *
196
     * @SuppressWarnings(PHPMD.StaticAccess)
197
     */
198 1
    private function executeStarts(array $indexes): void
199
    {
200 1
        BlockInterpreter::executeStarts(
201 1
            $indexes,
202 1
            $this->getBlocks(),
203 1
            $this->getContextStorage(),
204 1
            $this->getErrorAggregator()
0 ignored issues
show
Bug introduced by
The method getErrorAggregator() does not seem to exist on object<Limoncello\Applic...rmValidation\Validator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
205
        );
206
    }
207
208
    /**
209
     * @param array $indexes
210
     *
211
     * @return void
212
     *
213
     * @SuppressWarnings(PHPMD.StaticAccess)
214
     */
215 1
    private function executeEnds(array $indexes): void
216
    {
217 1
        BlockInterpreter::executeEnds(
218 1
            $indexes,
219 1
            $this->getBlocks(),
220 1
            $this->getContextStorage(),
221 1
            $this->getErrorAggregator()
0 ignored issues
show
Bug introduced by
The method getErrorAggregator() does not seem to exist on object<Limoncello\Applic...rmValidation\Validator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
222
        );
223
    }
224
225
    /**
226
     * @param array $rules
227
     *
228
     * @return self
229
     */
230 2
    private function setAttributeRules(array $rules): self
231
    {
232 2
        assert($this->debugCheckIndexesExist($rules));
233
234 2
        $this->attributeRules = $rules;
235
236 2
        return $this;
237
    }
238
239
    /**
240
     * @return int[]
241
     */
242 1
    private function getAttributeRules(): array
243
    {
244 1
        return $this->attributeRules;
245
    }
246
247
    /**
248
     * @return array
249
     */
250 2
    private function getBlocks(): array
251
    {
252 2
        return $this->blocks;
253
    }
254
255
    /**
256
     * @param array $blocks
257
     *
258
     * @return self
259
     */
260 2
    private function setBlocks(array $blocks): self
261
    {
262 2
        $this->blocks = $blocks;
263
264 2
        return $this;
265
    }
266
267
    /**
268
     * @param string $name
269
     *
270
     * @return int|null
271
     *
272
     * @SuppressWarnings(PHPMD.StaticAccess)
273
     */
274 1
    private function getAttributeIndex(string $name): ?int
275
    {
276 1
        $indexes = FormRuleSerializer::getRulesIndexes($this->getAttributeRules());
277 1
        $index   = $indexes[$name] ?? null;
278
279 1
        return $index;
280
    }
281
282
    /**
283
     * @param array $rules
284
     *
285
     * @return bool
286
     *
287
     * @SuppressWarnings(PHPMD.StaticAccess)
288
     */
289 2
    private function debugCheckIndexesExist(array $rules): bool
290
    {
291 2
        $allOk = true;
292
293 2
        $indexes = array_merge(
294 2
            FormRuleSerializer::getRulesIndexes($rules),
295 2
            FormRuleSerializer::getRulesStartIndexes($rules),
296 2
            FormRuleSerializer::getRulesEndIndexes($rules)
297
        );
298
299 2
        foreach ($indexes as $index) {
300 2
            $allOk = $allOk && is_int($index) && FormRuleSerializer::isRuleExist($index, $this->getBlocks());
301
        }
302
303 2
        return $allOk;
304
    }
305
}
306