Completed
Push — master ( b47c78...ba6be0 )
by Alexis
02:12
created

Validator   B

Complexity

Total Complexity 42

Size/Duplication

Total Lines 325
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 42
lcom 1
cbo 3
dl 0
loc 325
rs 8.295
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A isValid() 0 4 1
D validate() 0 31 9
A addError() 0 6 1
A addErrors() 0 8 2
A getData() 0 4 1
A getErrors() 0 4 1
A getFirstError() 0 10 2
A getParamErrors() 0 4 2
A getParamRuleError() 0 4 2
A getValue() 0 4 2
A setData() 0 6 1
A setErrors() 0 5 1
A setParamErrors() 0 6 1
A setValues() 0 6 1
B getRequestParam() 0 16 6
C setMessages() 0 33 8

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 V;
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
    }
59
60
    /**
61
     * Tells if there is no error.
62
     *
63
     * @return bool
64
     */
65
    public function isValid()
66
    {
67
        return empty($this->errors);
68
    }
69
70
    /**
71
     * Validates request parameters with the given rules.
72
     *
73
     * @param Request $request
74
     * @param array $rules
75
     * @param array $messages
76
     *
77
     * @return $this
78
     */
79
    public function validate(Request $request, array $rules, array $messages = [])
0 ignored issues
show
Unused Code introduced by
The parameter $messages is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
80
    {
81
        foreach ($rules as $param => $options) {
82
            $value = $this->getRequestParam($request, $param);
83
            $this->data[$param] = $value;
84
            $isRule = $options instanceof V;
85
86
            try {
87
                if ($isRule) {
88
                    $options->assert($value);
89
                } else {
90
                    if (!isset($options['rules']) || !($options['rules'] instanceof V)) {
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 (!$isRule && isset($options['message']) && is_string($options['message'])) {
99
                    $this->errors[$param] = [$options['message']];
100
                    return $this;
101
                } else {
102
                    // If the 'messages' key exists, override global messages
103
                    $this->setMessages($e, $param, $options, $isRule);
104
                }
105
            }
106
        }
107
108
        return $this;
109
    }
110
111
    /**
112
     * Adds an error for a parameter.
113
     *
114
     * @param string $param
115
     * @param string $message
116
     *
117
     * @return $this
118
     */
119
    public function addError($param, $message)
120
    {
121
        $this->errors[$param][] = $message;
122
123
        return $this;
124
    }
125
126
    /**
127
     * Adds errors for a parameter.
128
     *
129
     * @param string $param
130
     * @param string[] $messages
131
     *
132
     * @return $this
133
     */
134
    public function addErrors($param, array $messages)
135
    {
136
        foreach ($messages as $message) {
137
            $this->errors[$param][] = $message;
138
        }
139
140
        return $this;
141
    }
142
143
    /**
144
     * Gets the validated data.
145
     *
146
     * @return array
147
     */
148
    public function getData()
149
    {
150
        return $this->data;
151
    }
152
153
    /**
154
     * Gets all errors.
155
     *
156
     * @return array
157
     */
158
    public function getErrors()
159
    {
160
        return $this->errors;
161
    }
162
163
    /**
164
     * Gets the first error of a parameter.
165
     *
166
     * @param string $param
167
     *
168
     * @return string
169
     */
170
    public function getFirstError($param)
171
    {
172
        if (isset($this->errors[$param])) {
173
            $first = array_slice($this->errors[$param], 0, 1);
174
175
            return array_shift($first);
176
        }
177
178
        return '';
179
    }
180
181
    /**
182
     * Gets errors of a parameter.
183
     *
184
     * @param string $param
185
     *
186
     * @return array
187
     */
188
    public function getParamErrors($param)
189
    {
190
        return isset($this->errors[$param]) ? $this->errors[$param] : [];
191
    }
192
193
    /**
194
     * Gets the error of a validation rule for a parameter.
195
     *
196
     * @param string $param
197
     * @param string $rule
198
     *
199
     * @return string
200
     */
201
    public function getParamRuleError($param, $rule)
202
    {
203
        return isset($this->errors[$param][$rule]) ? $this->errors[$param][$rule] : '';
204
    }
205
206
    /**
207
     * Gets the value of a parameter in validated data.
208
     *
209
     * @param string $param
210
     *
211
     * @return string
212
     */
213
    public function getValue($param)
214
    {
215
        return isset($this->data[$param]) ? $this->data[$param] : '';
216
    }
217
218
    /**
219
     * Sets the validator data.
220
     *
221
     * @param array $data
222
     *
223
     * @return $this
224
     */
225
    public function setData(array $data)
226
    {
227
        $this->data = $data;
228
229
        return $this;
230
    }
231
232
    /**
233
     * Sets all errors.
234
     *
235
     * @param array $errors
236
     *
237
     * @return $this
238
     */
239
    public function setErrors(array $errors)
240
    {
241
        $this->errors = $errors;
242
        return $this;
243
    }
244
245
    /**
246
     * Sets the errors of a parameter.
247
     *
248
     * @param string $param
249
     * @param array $errors
250
     *
251
     * @return $this
252
     */
253
    public function setParamErrors($param, array $errors)
254
    {
255
        $this->errors[$param] = $errors;
256
257
        return $this;
258
    }
259
260
    /**
261
     * Sets the value of parameters.
262
     *
263
     * @param array $data
264
     *
265
     * @return $this
266
     */
267
    public function setValues(array $data)
268
    {
269
        $this->data = array_merge($this->data, $data);
270
271
        return $this;
272
    }
273
274
    /**
275
     * Fetch request parameter value from body or query string (in that order).
276
     *
277
     * @param  Request $request
278
     * @param  string  $key The parameter key.
279
     * @param  string  $default The default value.
280
     *
281
     * @return mixed The parameter value.
282
     */
283
    protected function getRequestParam(Request $request, $key, $default = null)
284
    {
285
        $postParams = $request->getParsedBody();
286
        $getParams = $request->getQueryParams();
287
288
        $result = $default;
289
        if (is_array($postParams) && isset($postParams[$key])) {
290
            $result = $postParams[$key];
291
        } elseif (is_object($postParams) && property_exists($postParams, $key)) {
292
            $result = $postParams->$key;
293
        } elseif (isset($getParams[$key])) {
294
            $result = $getParams[$key];
295
        }
296
297
        return $result;
298
    }
299
300
    /**
301
     * Sets error messages after validation
302
     *
303
     * @param NestedValidationException $e
304
     * @param string $param
305
     * @param AbstractComposite|array $options
306
     * @param bool $isRule
307
     */
308
    protected function setMessages(NestedValidationException $e, $param, $options, $isRule)
309
    {
310
        $paramRules = $isRule ? $options->getRules() : $options['rules']->getRules();
0 ignored issues
show
Bug introduced by
It seems like $options is not always an object, but can also be of type array. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
311
312
        // Get the names of all rules used for this param
313
        $rulesNames = [];
314
        foreach ($paramRules as $rule) {
315
            $rulesNames[] = lcfirst((new ReflectionClass($rule))->getShortName());
316
        }
317
318
        $params = [
319
            $e->findMessages($rulesNames)
320
        ];
321
322
        // If default messages are defined
323
        if (!empty($this->defaultMessages)) {
324
            $params[] = $e->findMessages($this->defaultMessages);
325
        }
326
327
        // If global messages are defined
328
        if (!empty($messages)) {
0 ignored issues
show
Bug introduced by
The variable $messages seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
329
            $params[] = $e->findMessages($messages);
330
        }
331
332
        // If individual messages are defined
333
        if (!$isRule && isset($options['messages'])) {
334
            $params[] = $e->findMessages($options['messages']);
335
        }
336
337
        $errors = array_filter(call_user_func_array('array_merge', $params));
338
339
        $this->errors[$param] = $this->storeErrorsWithRules ? $errors : array_values($errors);
340
    }
341
}
342