Completed
Branch 2.x (0b1fa1)
by Alexis
03:21
created

Validator::setMessages()   D

Complexity

Conditions 9
Paths 80

Size

Total Lines 37
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 37
rs 4.909
c 0
b 0
f 0
cc 9
eloc 17
nc 80
nop 4
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 errors.
164
     *
165
     * @return array
166
     */
167
    public function getErrors()
168
    {
169
        return $this->errors;
170
    }
171
172
    /**
173
     * Gets the first error of a parameter.
174
     *
175
     * @param string $param
176
     *
177
     * @return string
178
     */
179
    public function getFirstError($param)
180
    {
181
        if (isset($this->errors[$param])) {
182
            $first = array_slice($this->errors[$param], 0, 1);
183
184
            return array_shift($first);
185
        }
186
187
        return '';
188
    }
189
190
    /**
191
     * Gets errors of a parameter.
192
     *
193
     * @param string $param
194
     *
195
     * @return array
196
     */
197
    public function getParamErrors($param)
198
    {
199
        return isset($this->errors[$param]) ? $this->errors[$param] : [];
200
    }
201
202
    /**
203
     * Gets the error of a validation rule for a parameter.
204
     *
205
     * @param string $param
206
     * @param string $rule
207
     *
208
     * @return string
209
     */
210
    public function getParamRuleError($param, $rule)
211
    {
212
        return isset($this->errors[$param][$rule]) ? $this->errors[$param][$rule] : '';
213
    }
214
215
    /**
216
     * Tells whether errors should be stored in an associative array or an indexed array.
217
     *
218
     * @return bool
219
     */
220
    public function getStoreErrorsWithRules()
221
    {
222
        return $this->storeErrorsWithRules;
223
    }
224
225
    /**
226
     * Gets the value of a parameter in validated data.
227
     *
228
     * @param string $param
229
     *
230
     * @return string
231
     */
232
    public function getValue($param)
233
    {
234
        return isset($this->data[$param]) ? $this->data[$param] : '';
235
    }
236
237
    /**
238
     * Gets the validated data.
239
     *
240
     * @return array
241
     */
242
    public function getValues()
243
    {
244
        return $this->data;
245
    }
246
247
    /**
248
     * Sets the validator data.
249
     *
250
     * @param array $data
251
     *
252
     * @return $this
253
     */
254
    public function setData(array $data)
255
    {
256
        $this->data = $data;
257
258
        return $this;
259
    }
260
261
    /**
262
     * Sets the default error message for a validation rule.
263
     *
264
     * @param string $rule
265
     * @param string $message
266
     *
267
     * @return $this
268
     */
269
    public function setDefaultMessage($rule, $message)
270
    {
271
        $this->defaultMessages[$rule] = $message;
272
273
        return $this;
274
    }
275
276
    /**
277
     * Sets default error messages.
278
     *
279
     * @param array $messages
280
     *
281
     * @return $this
282
     */
283
    public function setDefaultMessages(array $messages)
284
    {
285
        $this->defaultMessages = $messages;
286
287
        return $this;
288
    }
289
290
    /**
291
     * Sets all errors.
292
     *
293
     * @param array $errors
294
     *
295
     * @return $this
296
     */
297
    public function setErrors(array $errors)
298
    {
299
        $this->errors = $errors;
300
301
        return $this;
302
    }
303
304
    /**
305
     * Sets the errors of a parameter.
306
     *
307
     * @param string $param
308
     * @param array $errors
309
     *
310
     * @return $this
311
     */
312
    public function setParamErrors($param, array $errors)
313
    {
314
        $this->errors[$param] = $errors;
315
316
        return $this;
317
    }
318
319
    /**
320
     * Sets errors storage mode.
321
     *
322
     * @param bool $bool
323
     *
324
     * @return $this
325
     */
326
    public function setStoreErrorsWithRules($bool)
327
    {
328
        $this->storeErrorsWithRules = (bool) $bool;
329
330
        return $this;
331
    }
332
333
    /**
334
     * Sets the value of parameters.
335
     *
336
     * @param array $data
337
     *
338
     * @return $this
339
     */
340
    public function setValues(array $data)
341
    {
342
        $this->data = array_merge($this->data, $data);
343
344
        return $this;
345
    }
346
347
    /**
348
     * Fetch request parameter value from body or query string (in that order).
349
     *
350
     * @param  Request $request
351
     * @param  string  $key The parameter key.
352
     * @param  string  $default The default value.
353
     *
354
     * @return mixed The parameter value.
355
     */
356
    protected function getRequestParam(Request $request, $key, $default = null)
357
    {
358
        $postParams = $request->getParsedBody();
359
        $getParams = $request->getQueryParams();
360
361
        $result = $default;
362
        if (is_array($postParams) && isset($postParams[$key])) {
363
            $result = $postParams[$key];
364
        } elseif (is_object($postParams) && property_exists($postParams, $key)) {
365
            $result = $postParams->$key;
366
        } elseif (isset($getParams[$key])) {
367
            $result = $getParams[$key];
368
        }
369
370
        return $result;
371
    }
372
373
    /**
374
     * Sets error messages after validation.
375
     *
376
     * @param NestedValidationException $e
377
     * @param string $param
378
     * @param AbstractComposite|array $options
379
     * @param array $messages
380
     */
381
    protected function setMessages(NestedValidationException $e, $param, $options, array $messages)
382
    {
383
        $paramRules = $options instanceof RespectValidator ? $options->getRules() : $options['rules']->getRules();
384
385
        // Get the names of all rules used for this param
386
        $rulesNames = [];
387
        foreach ($paramRules as $rule) {
388
            $rulesNames[] = lcfirst((new ReflectionClass($rule))->getShortName());
389
        }
390
391
        $params = [
392
            $e->findMessages($rulesNames)
393
        ];
394
395
        // If default messages are defined
396
        if (!empty($this->defaultMessages)) {
397
            $params[] = $e->findMessages($this->defaultMessages);
398
        }
399
400
        // If global messages are defined
401
        if (!empty($messages)) {
402
            $params[] = $e->findMessages($messages);
403
        }
404
405
        // If individual messages are defined
406
        if (is_array($options) && isset($options['messages'])) {
407
            if (!is_array($options['messages'])) {
408
                throw new InvalidArgumentException(sprintf('Expected custom individual messages to be of type array, %s given', gettype($options['messages'])));
409
            }
410
411
            $params[] = $e->findMessages($options['messages']);
412
        }
413
414
        $errors = array_filter(call_user_func_array('array_merge', $params));
415
416
        $this->errors[$param] = $this->storeErrorsWithRules ? $errors : array_values($errors);
417
    }
418
}
419