Validator::getRules()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 7
rs 10
1
<?php
2
3
/**
4
 * Platine Validator
5
 *
6
 * Platine Validator is a simple, extensible validation library with support for filtering
7
 *
8
 * This content is released under the MIT License (MIT)
9
 *
10
 * Copyright (c) 2020 Platine Validator
11
 *
12
 * Permission is hereby granted, free of charge, to any person obtaining a copy
13
 * of this software and associated documentation files (the "Software"), to deal
14
 * in the Software without restriction, including without limitation the rights
15
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
 * copies of the Software, and to permit persons to whom the Software is
17
 * furnished to do so, subject to the following conditions:
18
 *
19
 * The above copyright notice and this permission notice shall be included in all
20
 * copies or substantial portions of the Software.
21
 *
22
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
 * SOFTWARE.
29
 */
30
31
/**
32
 *  @file Validator.php
33
 *
34
 *  The Validator class used to validate the input based on defined rules
35
 *
36
 *  @package    Platine\Validator
37
 *  @author Platine Developers Team
38
 *  @copyright  Copyright (c) 2020
39
 *  @license    http://opensource.org/licenses/MIT  MIT License
40
 *  @link   https://www.platine-php.com
41
 *  @version 1.0.0
42
 *  @filesource
43
 */
44
45
declare(strict_types=1);
46
47
namespace Platine\Validator;
48
49
use Platine\Lang\Lang;
50
51
/**
52
 * @class Validator
53
 * @package Platine\Validator
54
 *
55
 * A Validator contains a set of validation rules and
56
 * associated metadata for ensuring that a given data set
57
 * is valid and returned correctly.
58
 */
59
class Validator
60
{
61
    /**
62
     * The data to validate
63
     * @var array<string, mixed>
64
     */
65
    protected array $data = [];
66
67
    /**
68
     * The field labels
69
     * @var array<string, string>
70
     */
71
    protected array $labels = [];
72
73
    /**
74
     * The filters to use to filter validation data
75
     * @var array<string, callable[]>
76
     */
77
    protected array $filters = [];
78
79
    /**
80
     * The validate rules
81
     * @var array<string, RuleInterface[]>
82
     */
83
    protected array $rules = [];
84
85
    /**
86
     * The validate errors
87
     * @var array<string, string>
88
     */
89
    protected array $errors = [];
90
91
    /**
92
     * The status of the validation
93
     * @var bool
94
     */
95
    protected bool $valid = false;
96
97
98
    /**
99
     * Create new instance
100
     * @param Lang $lang The language to use
101
     * @param string $langDomain The validation language domain to use
102
     */
103
    public function __construct(
104
        protected Lang $lang,
105
        protected string $langDomain = 'validators'
106
    ) {
107
        $this->lang = $lang;
108
        $this->langDomain = $langDomain;
109
110
        //Add the domain for the validator
111
        $this->lang->addDomain($langDomain);
112
113
        $this->reset();
114
    }
115
116
    /**
117
     * Return the language instance
118
     * @return Lang
119
     */
120
    public function getLang(): Lang
121
    {
122
        return $this->lang;
123
    }
124
125
    /**
126
     * Translation a single message
127
     * @param string $message
128
     * @param mixed $args
129
     * @return string
130
     */
131
    public function translate(string $message, mixed $args = []): string
132
    {
133
        if (is_array($args) === false) {
134
            $args = array_slice(func_get_args(), 1);
135
        }
136
137
        return $this->lang->trd($message, $this->langDomain, $args);
138
    }
139
140
    /**
141
     * Reset the validation instance
142
     *
143
     * @return $this
144
     */
145
    public function reset(): self
146
    {
147
        $this->rules = [];
148
        $this->labels = [];
149
        $this->errors = [];
150
        $this->filters = [];
151
        $this->valid = false;
152
        $this->data = [];
153
154
        return $this;
155
    }
156
157
    /**
158
     * Set the validation data
159
     * @param array<string, mixed> $data the data to be validated
160
     *
161
     * @return $this
162
     */
163
    public function setData(array $data): self
164
    {
165
        $this->data = $data;
166
        return $this;
167
    }
168
169
    /**
170
     * Return the validation data
171
     *
172
     * @param string $field if is set will return only the data for this filed
173
     * @param mixed $default the default value to return if can not find field value
174
     * @return mixed
175
     */
176
    public function getData(?string $field = null, mixed $default = null): mixed
177
    {
178
        if ($field === null) {
179
            return $this->data;
180
        }
181
182
        return $this->data[$field] ?? $default;
183
    }
184
185
    /**
186
     * Return the validation status
187
     *
188
     * @return bool
189
     */
190
    public function isValid(): bool
191
    {
192
        return $this->valid;
193
    }
194
195
    /**
196
     * Set the Label for a given Field
197
     * @param string $field
198
     * @param string $label
199
     *
200
     * @return $this
201
     */
202
    public function setLabel(string $field, string $label): self
203
    {
204
        $this->labels[$field] = $label;
205
206
        return $this;
207
    }
208
209
    /**
210
     * Return the label for a given Field
211
     * @param string $field
212
     *
213
     * @return string the label if none is set will use the humanize value
214
     * of field
215
     */
216
    public function getLabel(string $field): string
217
    {
218
        return $this->labels[$field] ?? $this->humanizeFieldName($field);
219
    }
220
221
    /**
222
     * Add a filter for the given field
223
     * @param string $field
224
     * @param callable $filter
225
     *
226
     * @return $this
227
     */
228
    public function addFilter(string $field, callable $filter): self
229
    {
230
        if (!isset($this->filters[$field])) {
231
            $this->filters[$field] = [];
232
        }
233
234
        $this->filters[$field][] = $filter;
235
236
        return $this;
237
    }
238
239
    /**
240
     * Add a list of filter for the given field
241
     * @param string $field
242
     * @param callable[] $filters
243
     *
244
     * @return $this
245
     */
246
    public function addFilters(string $field, array $filters): self
247
    {
248
        foreach ($filters as $filter) {
249
            $this->addFilter($field, $filter);
250
        }
251
252
        return $this;
253
    }
254
255
    /**
256
     * Add a rule for the given field
257
     * @param string $field
258
     * @param RuleInterface $rule
259
     *
260
     * @return $this
261
     */
262
    public function addRule(string $field, RuleInterface $rule): self
263
    {
264
        if (!isset($this->rules[$field])) {
265
            $this->rules[$field] = [];
266
        }
267
268
        if (!isset($this->labels[$field])) {
269
            $this->labels[$field] = $this->humanizeFieldName($field);
270
        }
271
272
        $this->rules[$field][] = $rule;
273
274
        return $this;
275
    }
276
277
    /**
278
     * Add a list of rules for the given field
279
     * @param string $field
280
     * @param RuleInterface[] $rules the array of RuleInterface
281
     *
282
     * @return $this
283
     */
284
    public function addRules(string $field, array $rules): self
285
    {
286
        foreach ($rules as $rule) {
287
            $this->addRule($field, $rule);
288
        }
289
290
        return $this;
291
    }
292
293
    /**
294
     * Return all currently defined rules
295
     * @param string $field if set will return only for this field
296
     *
297
     * @return RuleInterface[]|array<string, RuleInterface[]>
298
     */
299
    public function getRules(?string $field = null): array
300
    {
301
        if ($field === null) {
302
            return $this->rules;
303
        }
304
305
        return $this->rules[$field] ?? [];
306
    }
307
308
    /**
309
     * Validate the data
310
     * @param  array<string, mixed>  $data
311
     * @return bool the validation status
312
     */
313
    public function validate(array $data = []): bool
314
    {
315
        if (count($data) > 0) {
316
            $this->data = $data;
317
        }
318
319
        $this->applyFilters();
320
321
        $this->errors = $this->validateRules();
322
323
        return $this->valid = count($this->errors) === 0;
324
    }
325
326
    /**
327
     * Return the validation errors
328
     * @return array<string, string> the validation errors
329
     *
330
     * @example array(
331
     *   'field1' => 'message 1',
332
     *   'field2' => 'message 2',
333
     * )
334
     */
335
    public function getErrors(): array
336
    {
337
        return $this->errors;
338
    }
339
340
    /**
341
     * Process to validation of fields rules
342
     * @return array<string, string> the validation errors
343
     */
344
    protected function validateRules(): array
345
    {
346
        if (count($this->rules) === 0) {
347
            return [];
348
        }
349
        $errors = [];
350
351
        foreach ($this->rules as $field => $rules) {
352
            list($result, $error) = $this->validateFieldRules($field, $rules);
353
            if ($result === false) {
354
                $errors[$field] = $error;
355
            }
356
        }
357
358
        return $errors;
359
    }
360
361
    /**
362
     * Validate the rules for the given field
363
     * @param  string $field
364
     * @param  RuleInterface[]  $rules the array of rules
365
     * @return array<mixed>     array(Status, error)
366
     */
367
    protected function validateFieldRules(string $field, array $rules): array
368
    {
369
        $value = $this->data[$field] ?? null;
370
        foreach ($rules as $rule) {
371
            list($result, $error) = $this->validateRule($field, $value, $rule);
372
            if ($result === false) {
373
                return [false, $error];
374
            }
375
        }
376
377
        return [true, null];
378
    }
379
380
    /**
381
     * Validate single rule for the given field
382
     * @param  string $field
383
     * @param  mixed $value
384
     * @param  RuleInterface  $rule the rule instance to validate
385
     * @return array<mixed>  array(Status, error)
386
     */
387
    protected function validateRule(string $field, mixed $value, RuleInterface $rule): array
388
    {
389
        $result = $rule->validate($field, $value, $this);
390
        if ($result) {
391
            return [true, null];
392
        }
393
394
        return [false, $rule->getErrorMessage($field, $value, $this)];
395
    }
396
397
    /**
398
     * Apply any defined filters to the validation data
399
     * @return array<string, mixed> the filtered data
400
     */
401
    protected function applyFilters(): array
402
    {
403
        if (count($this->filters) === 0) {
404
            return $this->data;
405
        }
406
407
        $data = $this->data;
408
        foreach ($this->filters as $field => $filters) {
409
            if (!isset($data[$field])) {
410
                continue;
411
            }
412
413
            $value = $data[$field];
414
            foreach ($filters as $filter) {
415
                $value = call_user_func($filter, $value);
416
            }
417
418
            $data[$field] = $value;
419
        }
420
        $this->data = $data;
421
422
        return $this->data;
423
    }
424
425
    /**
426
     * Humanize the given field
427
     * @param  string $field
428
     *
429
     * @return string
430
     */
431
    protected function humanizeFieldName(string $field): string
432
    {
433
        return str_replace(['-', '_'], ' ', $field);
434
    }
435
}
436