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