Completed
Pull Request — master (#1)
by Alexis
01:32
created

Validator   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 412
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 50
lcom 1
cbo 3
dl 0
loc 412
rs 8.6206
c 0
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A isValid() 0 4 1
D validate() 0 34 10
A addError() 0 6 1
A addErrors() 0 8 2
A getData() 0 4 1
A getDefaultMessages() 0 4 1
A getErrors() 0 4 1
A getFirstError() 0 10 2
A getParamErrors() 0 4 2
A getParamRuleError() 0 4 2
A getStoreErrorsWithRules() 0 4 1
A getValue() 0 4 2
A getValues() 0 4 1
A setData() 0 6 1
A setDefaultMessage() 0 6 1
A setDefaultMessages() 0 6 1
A setErrors() 0 6 1
A setParamErrors() 0 6 1
A setStoreErrorsWithRules() 0 6 1
A setValues() 0 6 1
B getRequestParam() 0 16 6
D setMessages() 0 37 9

How to fix   Complexity   

Complex Class

Complex classes like Validator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Validator, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Awurth\SlimValidation;
4
5
use InvalidArgumentException;
6
use Psr\Http\Message\ServerRequestInterface as Request;
7
use ReflectionClass;
8
use Respect\Validation\Exceptions\NestedValidationException;
9
use Respect\Validation\Rules\AbstractComposite;
10
use Respect\Validation\Validator as RespectValidator;
11
12
/**
13
 * Validator.
14
 *
15
 * @author Alexis Wurth <[email protected]>
16
 */
17
class Validator
18
{
19
    /**
20
     * The validated data.
21
     *
22
     * @var array
23
     */
24
    protected $data;
25
26
    /**
27
     * The default error messages for the given rules.
28
     *
29
     * @var array
30
     */
31
    protected $defaultMessages;
32
33
    /**
34
     * The list of validation errors.
35
     *
36
     * @var array
37
     */
38
    protected $errors;
39
40
    /**
41
     * Tells if errors should be stored in an associative array
42
     * where the key is the name of the validation rule.
43
     *
44
     * @var bool
45
     */
46
    protected $storeErrorsWithRules;
47
48
    /**
49
     * Constructor.
50
     *
51
     * @param bool $storeErrorsWithRules
52
     * @param array $defaultMessages
53
     */
54
    public function __construct($storeErrorsWithRules = true, array $defaultMessages = [])
55
    {
56
        $this->storeErrorsWithRules = $storeErrorsWithRules;
57
        $this->defaultMessages = $defaultMessages;
58
        $this->errors = [];
59
        $this->data = [];
60
    }
61
62
    /**
63
     * Tells if there is no error.
64
     *
65
     * @return bool
66
     */
67
    public function isValid()
68
    {
69
        return empty($this->errors);
70
    }
71
72
    /**
73
     * Validates request parameters with the given rules.
74
     *
75
     * @param Request $request
76
     * @param array $rules
77
     * @param array $messages
78
     *
79
     * @return $this
80
     */
81
    public function validate(Request $request, array $rules, array $messages = [])
82
    {
83
        foreach ($rules as $param => $options) {
84
            $value = $this->getRequestParam($request, $param);
85
86
            try {
87
                if ($options instanceof RespectValidator) {
88
                    $options->assert($value);
89
                } else {
90
                    if (!is_array($options) || !isset($options['rules']) || !($options['rules'] instanceof RespectValidator)) {
91
                        throw new InvalidArgumentException('Validation rules are missing');
92
                    }
93
94
                    $options['rules']->assert($value);
95
                }
96
            } catch (NestedValidationException $e) {
97
                // If the 'message' key exists, set it as only message for this param
98
                if (is_array($options) && isset($options['message'])) {
99
                    if (!is_string($options['message'])) {
100
                        throw new InvalidArgumentException(sprintf('Expected custom message to be of type string, %s given', gettype($options['message'])));
101
                    }
102
103
                    $this->errors[$param] = [$options['message']];
104
                } else {
105
                    // If the 'messages' key exists, override global messages
106
                    $this->setMessages($e, $param, $options, $messages);
107
                }
108
            }
109
110
            $this->data[$param] = $value;
111
        }
112
113
        return $this;
114
    }
115
116
    /**
117
     * Adds an error for a parameter.
118
     *
119
     * @param string $param
120
     * @param string $message
121
     *
122
     * @return $this
123
     */
124
    public function addError($param, $message)
125
    {
126
        $this->errors[$param][] = $message;
127
128
        return $this;
129
    }
130
131
    /**
132
     * Adds errors for a parameter.
133
     *
134
     * @deprecated since version 2.1, will be removed in 3.0.
135
     *
136
     * @param string $param
137
     * @param string[] $messages
138
     *
139
     * @return $this
140
     */
141
    public function addErrors($param, array $messages)
142
    {
143
        foreach ($messages as $message) {
144
            $this->errors[$param][] = $message;
145
        }
146
147
        return $this;
148
    }
149
150
    /**
151
     * Gets the validated data.
152
     *
153
     * @deprecated since version 2.1, will be removed in 3.0. Use getValues() instead.
154
     *
155
     * @return array
156
     */
157
    public function getData()
158
    {
159
        return $this->data;
160
    }
161
162
    /**
163
     * Gets all default messages.
164
     *
165
     * @return array
166
     */
167
    public function getDefaultMessages()
168
    {
169
        return $this->defaultMessages;
170
    }
171
172
    /**
173
     * Gets all errors.
174
     *
175
     * @return array
176
     */
177
    public function getErrors()
178
    {
179
        return $this->errors;
180
    }
181
182
    /**
183
     * Gets the first error of a parameter.
184
     *
185
     * @param string $param
186
     *
187
     * @return string
188
     */
189
    public function getFirstError($param)
190
    {
191
        if (isset($this->errors[$param])) {
192
            $first = array_slice($this->errors[$param], 0, 1);
193
194
            return array_shift($first);
195
        }
196
197
        return '';
198
    }
199
200
    /**
201
     * Gets errors of a parameter.
202
     *
203
     * @param string $param
204
     *
205
     * @return array
206
     */
207
    public function getParamErrors($param)
208
    {
209
        return isset($this->errors[$param]) ? $this->errors[$param] : [];
210
    }
211
212
    /**
213
     * Gets the error of a validation rule for a parameter.
214
     *
215
     * @param string $param
216
     * @param string $rule
217
     *
218
     * @return string
219
     */
220
    public function getParamRuleError($param, $rule)
221
    {
222
        return isset($this->errors[$param][$rule]) ? $this->errors[$param][$rule] : '';
223
    }
224
225
    /**
226
     * Tells whether errors should be stored in an associative array or an indexed array.
227
     *
228
     * @return bool
229
     */
230
    public function getStoreErrorsWithRules()
231
    {
232
        return $this->storeErrorsWithRules;
233
    }
234
235
    /**
236
     * Gets the value of a parameter in validated data.
237
     *
238
     * @param string $param
239
     *
240
     * @return string
241
     */
242
    public function getValue($param)
243
    {
244
        return isset($this->data[$param]) ? $this->data[$param] : '';
245
    }
246
247
    /**
248
     * Gets the validated data.
249
     *
250
     * @return array
251
     */
252
    public function getValues()
253
    {
254
        return $this->data;
255
    }
256
257
    /**
258
     * Sets the validator data.
259
     *
260
     * @param array $data
261
     *
262
     * @return $this
263
     */
264
    public function setData(array $data)
265
    {
266
        $this->data = $data;
267
268
        return $this;
269
    }
270
271
    /**
272
     * Sets the default error message for a validation rule.
273
     *
274
     * @param string $rule
275
     * @param string $message
276
     *
277
     * @return $this
278
     */
279
    public function setDefaultMessage($rule, $message)
280
    {
281
        $this->defaultMessages[$rule] = $message;
282
283
        return $this;
284
    }
285
286
    /**
287
     * Sets default error messages.
288
     *
289
     * @param array $messages
290
     *
291
     * @return $this
292
     */
293
    public function setDefaultMessages(array $messages)
294
    {
295
        $this->defaultMessages = $messages;
296
297
        return $this;
298
    }
299
300
    /**
301
     * Sets all errors.
302
     *
303
     * @param array $errors
304
     *
305
     * @return $this
306
     */
307
    public function setErrors(array $errors)
308
    {
309
        $this->errors = $errors;
310
311
        return $this;
312
    }
313
314
    /**
315
     * Sets the errors of a parameter.
316
     *
317
     * @param string $param
318
     * @param array $errors
319
     *
320
     * @return $this
321
     */
322
    public function setParamErrors($param, array $errors)
323
    {
324
        $this->errors[$param] = $errors;
325
326
        return $this;
327
    }
328
329
    /**
330
     * Sets errors storage mode.
331
     *
332
     * @param bool $bool
333
     *
334
     * @return $this
335
     */
336
    public function setStoreErrorsWithRules($bool)
337
    {
338
        $this->storeErrorsWithRules = (bool) $bool;
339
340
        return $this;
341
    }
342
343
    /**
344
     * Sets the value of parameters.
345
     *
346
     * @param array $data
347
     *
348
     * @return $this
349
     */
350
    public function setValues(array $data)
351
    {
352
        $this->data = array_merge($this->data, $data);
353
354
        return $this;
355
    }
356
357
    /**
358
     * Fetch request parameter value from body or query string (in that order).
359
     *
360
     * @param  Request $request
361
     * @param  string  $key The parameter key.
362
     * @param  string  $default The default value.
363
     *
364
     * @return mixed The parameter value.
365
     */
366
    protected function getRequestParam(Request $request, $key, $default = null)
367
    {
368
        $postParams = $request->getParsedBody();
369
        $getParams = $request->getQueryParams();
370
371
        $result = $default;
372
        if (is_array($postParams) && isset($postParams[$key])) {
373
            $result = $postParams[$key];
374
        } elseif (is_object($postParams) && property_exists($postParams, $key)) {
375
            $result = $postParams->$key;
376
        } elseif (isset($getParams[$key])) {
377
            $result = $getParams[$key];
378
        }
379
380
        return $result;
381
    }
382
383
    /**
384
     * Sets error messages after validation.
385
     *
386
     * @param NestedValidationException $e
387
     * @param string $param
388
     * @param AbstractComposite|array $options
389
     * @param array $messages
390
     */
391
    protected function setMessages(NestedValidationException $e, $param, $options, array $messages)
392
    {
393
        $paramRules = $options instanceof RespectValidator ? $options->getRules() : $options['rules']->getRules();
394
395
        // Get the names of all rules used for this param
396
        $rulesNames = [];
397
        foreach ($paramRules as $rule) {
398
            $rulesNames[] = lcfirst((new ReflectionClass($rule))->getShortName());
399
        }
400
401
        $params = [
402
            $e->findMessages($rulesNames)
403
        ];
404
405
        // If default messages are defined
406
        if (!empty($this->defaultMessages)) {
407
            $params[] = $e->findMessages($this->defaultMessages);
408
        }
409
410
        // If global messages are defined
411
        if (!empty($messages)) {
412
            $params[] = $e->findMessages($messages);
413
        }
414
415
        // If individual messages are defined
416
        if (is_array($options) && isset($options['messages'])) {
417
            if (!is_array($options['messages'])) {
418
                throw new InvalidArgumentException(sprintf('Expected custom individual messages to be of type array, %s given', gettype($options['messages'])));
419
            }
420
421
            $params[] = $e->findMessages($options['messages']);
422
        }
423
424
        $errors = array_filter(call_user_func_array('array_merge', $params));
425
426
        $this->errors[$param] = $this->storeErrorsWithRules ? $errors : array_values($errors);
427
    }
428
}
429