Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
98.52% |
133 / 135 |
|
92.86% |
26 / 28 |
CRAP | |
0.00% |
0 / 1 |
FormValidator | |
98.52% |
133 / 135 |
|
92.86% |
26 / 28 |
95 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
6 | |||
createFromConfig | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
6 | |||
addValidators | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
addValidator | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
5 | |||
hasValidators | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
4 | |||
getValidators | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
4 | |||
hasValidator | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
2 | |||
getValidator | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
3 | |||
removeValidators | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
4 | |||
removeValidator | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
setRequired | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
4 | |||
isRequired | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
removeRequired | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
setValues | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getValues | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
filterValue | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
filter | |
85.71% |
6 / 7 |
|
0.00% |
0 / 1 |
4.05 | |||
validate | |
97.22% |
35 / 36 |
|
0.00% |
0 / 1 |
21 | |||
hasErrors | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
getErrors | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
getError | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
3 | |||
addError | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
count | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
toArray | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__set | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__get | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__isset | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__unset | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (http://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@nolainteractive.com> |
7 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Form; |
15 | |
16 | /** |
17 | * Form validator class |
18 | * |
19 | * @category Pop |
20 | * @package Pop\Form |
21 | * @author Nick Sagona, III <dev@nolainteractive.com> |
22 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
23 | * @license http://www.popphp.org/license New BSD License |
24 | * @version 4.0.0 |
25 | */ |
26 | |
27 | class FormValidator implements FormInterface, \ArrayAccess, \Countable, \IteratorAggregate |
28 | { |
29 | |
30 | /** |
31 | * Trait declaration |
32 | */ |
33 | use FormTrait; |
34 | |
35 | /** |
36 | * Form validators |
37 | * @var array |
38 | */ |
39 | protected array $validators = []; |
40 | |
41 | /** |
42 | * Required fields |
43 | * @var array |
44 | */ |
45 | protected array $required = []; |
46 | |
47 | /** |
48 | * Form values |
49 | * @var array |
50 | */ |
51 | protected array $values = []; |
52 | |
53 | /** |
54 | * Form validation errors |
55 | * @var array |
56 | */ |
57 | protected array $errors = []; |
58 | |
59 | /** |
60 | * Constructor |
61 | * |
62 | * Instantiate the form validator object |
63 | * |
64 | * @param ?array $validators |
65 | * @param mixed $required |
66 | * @param ?array $values |
67 | * @param mixed $filters |
68 | */ |
69 | public function __construct(array $validators = null, mixed $required = null, ?array $values = null, mixed $filters = null) |
70 | { |
71 | if (!empty($validators)) { |
72 | $this->addValidators($validators); |
73 | } |
74 | if ($required !== null) { |
75 | $this->setRequired($required); |
76 | } |
77 | if ($values !== null) { |
78 | $this->setValues($values); |
79 | } |
80 | if ($filters !== null) { |
81 | if (is_array($filters)) { |
82 | $this->addFilters($filters); |
83 | } else { |
84 | $this->addFilter($filters); |
85 | } |
86 | } |
87 | } |
88 | |
89 | /** |
90 | * Create form validator from config |
91 | * |
92 | * @param array|FormConfig $formConfig |
93 | * @param mixed $required |
94 | * @param ?array $values |
95 | * @param mixed $filters |
96 | * @return FormValidator |
97 | */ |
98 | public static function createFromConfig( |
99 | array|FormConfig $formConfig, mixed $required = null, ?array $values = null, mixed $filters = null |
100 | ): FormValidator |
101 | { |
102 | $validators = []; |
103 | $required = []; |
104 | |
105 | foreach ($formConfig as $key => $value) { |
106 | if (!empty($value['validator'])) { |
107 | $validators[$key] = $value['validator']; |
108 | } else if (!empty($value['validators'])) { |
109 | $validators[$key] = $value['validators']; |
110 | } |
111 | if (isset($value['required']) && ($value['required'] == true)) { |
112 | $required[] = $key; |
113 | } |
114 | } |
115 | |
116 | return new self($validators, $required, $values, $filters); |
117 | } |
118 | |
119 | /** |
120 | * Add validators |
121 | * |
122 | * @param array $validators |
123 | * @return FormValidator |
124 | */ |
125 | public function addValidators(array $validators): FormValidator |
126 | { |
127 | foreach ($validators as $field => $validator) { |
128 | $this->addValidator($field, $validator); |
129 | } |
130 | return $this; |
131 | } |
132 | |
133 | /** |
134 | * Add validator |
135 | * |
136 | * @param string $field |
137 | * @param mixed $validator |
138 | * @return FormValidator |
139 | */ |
140 | public function addValidator(string $field, mixed $validator): FormValidator |
141 | { |
142 | if (!isset($this->validators[$field])) { |
143 | $this->validators[$field] = []; |
144 | } |
145 | |
146 | if (!is_array($validator)) { |
147 | $validator = [$validator]; |
148 | } |
149 | |
150 | foreach ($validator as $valid) { |
151 | if (!in_array($valid, $this->validators[$field], true)) { |
152 | $this->validators[$field][] = $valid; |
153 | } |
154 | } |
155 | |
156 | return $this; |
157 | } |
158 | |
159 | /** |
160 | * Has validators |
161 | * |
162 | * @param ?string $field |
163 | * @return bool |
164 | */ |
165 | public function hasValidators(?string $field = null) |
166 | { |
167 | if ($field === null) { |
168 | return (count($this->validators) > 0); |
169 | } else if (($field !== null) && isset($this->validators[$field])) { |
170 | return (count($this->validators[$field]) > 0); |
171 | } else { |
172 | return false; |
173 | } |
174 | } |
175 | |
176 | /** |
177 | * Get validators |
178 | * |
179 | * @param string $field |
180 | * @return mixed |
181 | */ |
182 | public function getValidators(?string $field = null) |
183 | { |
184 | if ($field === null) { |
185 | return $this->validators; |
186 | } else if (($field !== null) && isset($this->validators[$field])) { |
187 | return $this->validators[$field]; |
188 | } else { |
189 | return null; |
190 | } |
191 | } |
192 | |
193 | /** |
194 | * Has validator |
195 | * |
196 | * @param string $field |
197 | * @param int $index |
198 | * @return bool |
199 | */ |
200 | public function hasValidator(string $field, int $index): bool |
201 | { |
202 | return (isset($this->validators[$field]) && isset($this->validators[$field][$index])); |
203 | } |
204 | |
205 | /** |
206 | * Get validator |
207 | * |
208 | * @param string $field |
209 | * @param int $index |
210 | * @return mixed |
211 | */ |
212 | public function getValidator(string $field, int $index) |
213 | { |
214 | return (isset($this->validators[$field]) && isset($this->validators[$field][$index])) ? |
215 | $this->validators[$field][$index] : null; |
216 | } |
217 | |
218 | /** |
219 | * Remove validators |
220 | * |
221 | * @param ?string $field |
222 | * @return FormValidator |
223 | */ |
224 | public function removeValidators(?string $field = null): FormValidator |
225 | { |
226 | if (($field !== null) && isset($this->validators[$field])) { |
227 | unset($this->validators[$field]); |
228 | } else if ($field === null) { |
229 | $this->validators = []; |
230 | } |
231 | return $this; |
232 | } |
233 | |
234 | /** |
235 | * Remove validator |
236 | * |
237 | * @param string $field |
238 | * @param int $index |
239 | * @return FormValidator |
240 | */ |
241 | public function removeValidator(string $field, int $index): FormValidator |
242 | { |
243 | if (isset($this->validators[$field]) && isset($this->validators[$field][$index])) { |
244 | unset($this->validators[$field][$index]); |
245 | } |
246 | return $this; |
247 | } |
248 | |
249 | /** |
250 | * Set required |
251 | * |
252 | * @param mixed $required |
253 | * @return FormValidator |
254 | */ |
255 | public function setRequired(mixed $required): FormValidator |
256 | { |
257 | if (!is_array($required)) { |
258 | $required = [$required]; |
259 | } |
260 | |
261 | foreach ($required as $req) { |
262 | if (!in_array($req, $this->required)) { |
263 | $this->required[] = $req; |
264 | } |
265 | } |
266 | |
267 | return $this; |
268 | } |
269 | |
270 | /** |
271 | * Is required |
272 | * |
273 | * @param string $field |
274 | * @return bool |
275 | */ |
276 | public function isRequired(string $field): bool |
277 | { |
278 | return (in_array($field, $this->required)); |
279 | } |
280 | |
281 | /** |
282 | * Remove required |
283 | * |
284 | * @param string $field |
285 | * @return FormValidator |
286 | */ |
287 | public function removeRequired(string $field): FormValidator |
288 | { |
289 | if (in_array($field, $this->required)) { |
290 | unset($this->required[array_search($field, $this->required)]); |
291 | } |
292 | |
293 | return $this; |
294 | } |
295 | |
296 | /** |
297 | * Set values |
298 | * |
299 | * @param array $values |
300 | * @return FormValidator |
301 | */ |
302 | public function setValues(array $values): FormValidator |
303 | { |
304 | $this->values = $values; |
305 | return $this; |
306 | } |
307 | |
308 | /** |
309 | * Get values |
310 | * |
311 | * @return array |
312 | */ |
313 | public function getValues(): array |
314 | { |
315 | return $this->values; |
316 | } |
317 | |
318 | /** |
319 | * Filter value with the filters |
320 | * |
321 | * @param mixed $field |
322 | * @throws Exception |
323 | * @return mixed |
324 | */ |
325 | public function filterValue(mixed $field): mixed |
326 | { |
327 | if (!isset($this->values[$field])) { |
328 | throw new Exception("Error: A value for '" . $field . "' has not been set."); |
329 | } |
330 | |
331 | $value = $this->values[$field]; |
332 | |
333 | foreach ($this->filters as $filter) { |
334 | $value = $filter->filter($value, $field); |
335 | } |
336 | |
337 | $this->values[$field] = $value; |
338 | |
339 | return $value; |
340 | } |
341 | |
342 | /** |
343 | * Filter values with the filters |
344 | * |
345 | * @param mixed $values |
346 | * @return mixed |
347 | */ |
348 | public function filter(mixed $values = null): mixed |
349 | { |
350 | if ($values !== null) { |
351 | $this->values = $values; |
352 | } |
353 | |
354 | if (is_array($this->values)) { |
355 | foreach ($this->values as $name => $value) { |
356 | $this->values[$name] = $this->filterValue($name); |
357 | } |
358 | } else { |
359 | $this->values = $this->filterValue($this->values); |
360 | } |
361 | |
362 | return $this->values; |
363 | } |
364 | |
365 | /** |
366 | * Validate values |
367 | * |
368 | * @param mixed $fields |
369 | * @return bool |
370 | */ |
371 | public function validate(mixed $fields = null): bool |
372 | { |
373 | $this->filter(); |
374 | |
375 | if ($fields !== null) { |
376 | $fields = (!is_array($fields)) ? [$fields] : $fields; |
377 | $formFields = array_filter( |
378 | $this->values, |
379 | function ($key) use ($fields) { |
380 | return in_array($key, $fields); |
381 | }, |
382 | ARRAY_FILTER_USE_KEY |
383 | ); |
384 | |
385 | foreach ($formFields as $field) { |
386 | if (in_array($field, $this->required) && !isset($formFields[$field])) { |
387 | $this->addError($field, 'This field is required.'); |
388 | } |
389 | } |
390 | } else { |
391 | $formFields = $this->values; |
392 | // Check for required fields |
393 | foreach ($this->required as $required) { |
394 | if (!isset($formFields[$required])) { |
395 | $this->addError($required, 'This field is required.'); |
396 | } |
397 | } |
398 | } |
399 | |
400 | // Check for required fields and execute any field validators |
401 | foreach ($formFields as $field => $value) { |
402 | if ($this->hasValidators($field)) { |
403 | foreach ($this->validators[$field] as $validator) { |
404 | if ($validator instanceof \Pop\Validator\ValidatorInterface) { |
405 | if (!$validator->evaluate($value)) { |
406 | $this->addError($field, $validator->getMessage()); |
407 | } |
408 | } else if (is_callable($validator)) { |
409 | $result = call_user_func_array($validator, [$value, $formFields]); |
410 | if ($result instanceof \Pop\Validator\ValidatorInterface) { |
411 | if (!$result->evaluate($value)) { |
412 | $this->addError($field, $result->getMessage()); |
413 | } |
414 | } else if (is_array($result)) { |
415 | foreach ($result as $val) { |
416 | if ($val instanceof \Pop\Validator\ValidatorInterface) { |
417 | if (!$val->evaluate($value)) { |
418 | $this->addError($field, $val->getMessage()); |
419 | } |
420 | } |
421 | } |
422 | } else if ($result !== null) { |
423 | $this->addError($field, $result); |
424 | } |
425 | } |
426 | } |
427 | } |
428 | } |
429 | |
430 | return !$this->hasErrors(); |
431 | } |
432 | |
433 | /** |
434 | * Has errors |
435 | * |
436 | * @param ?string $field |
437 | * @return bool |
438 | */ |
439 | public function hasErrors(?string $field = null): bool |
440 | { |
441 | if ($field !== null) { |
442 | return (isset($this->errors[$field]) && (count($this->errors[$field]) > 0)); |
443 | } else { |
444 | return (count($this->errors) > 0); |
445 | } |
446 | } |
447 | |
448 | /** |
449 | * Get errors |
450 | * |
451 | * @param ?string $field |
452 | * @return array |
453 | */ |
454 | public function getErrors(?string $field = null): array |
455 | { |
456 | if (($field !== null) && isset($this->errors[$field])) { |
457 | return $this->errors[$field]; |
458 | } else { |
459 | return $this->errors; |
460 | } |
461 | } |
462 | |
463 | /** |
464 | * Get error |
465 | * |
466 | * @param string $field |
467 | * @param int $index |
468 | * @return mixed |
469 | */ |
470 | public function getError(string $field, int $index): mixed |
471 | { |
472 | return (isset($this->errors[$field]) && isset($this->errors[$field][$index])) ? |
473 | $this->errors[$field][$index] : null; |
474 | } |
475 | |
476 | /** |
477 | * Add error |
478 | * |
479 | * @param string $field |
480 | * @param string $error |
481 | * @return FormValidator |
482 | */ |
483 | protected function addError(string $field, string $error): FormValidator |
484 | { |
485 | if (!isset($this->errors[$field])) { |
486 | $this->errors[$field] = []; |
487 | } |
488 | |
489 | if (!in_array($error, $this->errors[$field])) { |
490 | $this->errors[$field][] = $error; |
491 | } |
492 | |
493 | return $this; |
494 | } |
495 | |
496 | /** |
497 | * Count of values |
498 | * |
499 | * @return int |
500 | */ |
501 | public function count(): int |
502 | { |
503 | return count($this->values); |
504 | } |
505 | |
506 | /** |
507 | * Get values |
508 | * |
509 | * @return array |
510 | */ |
511 | public function toArray(): array |
512 | { |
513 | return $this->values; |
514 | } |
515 | |
516 | /** |
517 | * Set method to set the property to the value of values[$name] |
518 | * |
519 | * @param string $name |
520 | * @param mixed $value |
521 | * @return void |
522 | */ |
523 | public function __set(string $name, mixed $value): void |
524 | { |
525 | $this->values[$name] = $value; |
526 | } |
527 | |
528 | /** |
529 | * Get method to return the value of values[$name] |
530 | * |
531 | * @param string $name |
532 | * @return mixed |
533 | */ |
534 | public function __get(string $name): mixed |
535 | { |
536 | return $this->values[$name] ?? null; |
537 | } |
538 | |
539 | /** |
540 | * Return the isset value of values[$name] |
541 | * |
542 | * @param string $name |
543 | * @return bool |
544 | */ |
545 | public function __isset(string $name): bool |
546 | { |
547 | return isset($this->values[$name]); |
548 | } |
549 | |
550 | /** |
551 | * Unset values[$name] |
552 | * |
553 | * @param string $name |
554 | * @return void |
555 | */ |
556 | public function __unset(string $name): void |
557 | { |
558 | if (isset($this->values[$name])) { |
559 | unset($this->values[$name]); |
560 | } |
561 | } |
562 | |
563 | } |