Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.98% covered (success)
97.98%
242 / 247
96.23% covered (success)
96.23%
51 / 53
CRAP
0.00% covered (danger)
0.00%
0 / 1
Form
97.98% covered (success)
97.98%
242 / 247
96.23% covered (success)
96.23%
51 / 53
141
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 createFromConfig
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 createFromFieldsetConfig
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 createFieldset
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
5
 setAction
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setMethod
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getAction
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMethod
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setAttribute
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
5
 setAttributes
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 addFieldset
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 removeFieldset
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 getFieldset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 addColumn
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 hasColumn
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getColumn
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 removeColumn
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 getCurrent
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setCurrent
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getLegend
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 setLegend
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 addField
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 addFields
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 addFieldFromConfig
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 addFieldsFromConfig
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
7
 addFieldsetsFromConfig
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 insertFieldBefore
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 insertFieldAfter
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 count
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 toArray
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getField
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 getFields
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 hasField
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasFields
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 removeField
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 getFieldValue
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 setFieldValue
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 setFieldValues
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 filterValue
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
8
 filter
85.71% covered (success)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
4.05
 isValid
100.00% covered (success)
100.00%
7 / 7
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
2
 getAllErrors
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 reset
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 clearTokens
50.00% covered (warning)
50.00%
4 / 8
0.00% covered (danger)
0.00%
0 / 1
8.12
 prepare
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
8
 prepareForView
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 render
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 __toString
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
1
 __isset
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 __unset
100.00% covered (success)
100.00%
3 / 3
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
16use Pop\Dom\Child;
17use Pop\Form\Element;
18use ReturnTypeWillChange;
19
20/**
21 * Form class
22 *
23 * @category   Pop
24 * @package    Pop\Form
25 * @author     Nick Sagona, III <dev@nolainteractive.com>
26 * @copyright  Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com)
27 * @license    http://www.popphp.org/license     New BSD License
28 * @version    3.6.0
29 */
30
31class Form extends Child implements FormInterface, \ArrayAccess, \Countable, \IteratorAggregate
32{
33
34    /**
35     * Trait declaration
36     */
37    use FormTrait;
38
39    /**
40     * Field fieldsets
41     * @var array
42     */
43    protected $fieldsets = [];
44
45    /**
46     * Form columns
47     * @var array
48     */
49    protected $columns = [];
50
51    /**
52     * Current field fieldset
53     * @var int
54     */
55    protected $current = 0;
56
57    /**
58     * Constructor
59     *
60     * Instantiate the form object
61     *
62     * @param  array  $fields
63     * @param  string $action
64     * @param  string $method
65     */
66    public function __construct(array $fields = null, $action = null, $method = 'post')
67    {
68        if (null === $action) {
69            $action = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : '#';
70        }
71
72        parent::__construct('form');
73        $this->setAction($action);
74        $this->setMethod($method);
75
76        if (null !== $fields) {
77            $this->addFields($fields);
78        }
79    }
80
81    /**
82     * Method to create form object and fields from config
83     *
84     * @param  array|FormConfig $config
85     * @param  string           $action
86     * @param  string           $method
87     * @return Form
88     */
89    public static function createFromConfig($config, $action = null, $method = 'post')
90    {
91        $form = new static(null, $action, $method);
92        $form->addFieldsFromConfig($config);
93        return $form;
94    }
95
96    /**
97     * Method to create form object and fields from config
98     *
99     * @param  array|FormConfig $config
100     * @param  string           $container
101     * @param  string           $action
102     * @param  string           $method
103     * @return Form
104     */
105    public static function createFromFieldsetConfig($config, $container = null, $action = null, $method = 'post')
106    {
107        $form = new static(null, $action, $method);
108        $form->addFieldsetsFromConfig($config, $container);
109        return $form;
110    }
111
112    /**
113     * Method to create a new fieldset object
114     *
115     * @param  string  $legend
116     * @param  string  $container
117     * @return Fieldset
118     */
119    public function createFieldset($legend = null, $container = null)
120    {
121        $fieldset = new Fieldset();
122        if (null !== $legend) {
123            $fieldset->setLegend($legend);
124        }
125        if (null !== $container) {
126            $fieldset->setContainer($container);
127        }
128
129        $this->addFieldset($fieldset);
130
131        $id = (null !== $this->getAttribute('id')) ?
132            $this->getAttribute('id') . '-fieldset-' . ($this->current + 1) : 'pop-form-fieldset-' . ($this->current + 1);
133
134        $class = (null !== $this->getAttribute('class')) ?
135            $this->getAttribute('id') . '-fieldset' : 'pop-form-fieldset';
136
137        $fieldset->setAttribute('id', $id);
138        $fieldset->setAttribute('class', $class);
139
140        return $fieldset;
141    }
142
143    /**
144     * Method to set action
145     *
146     * @param  string $action
147     * @return Form
148     */
149    public function setAction($action)
150    {
151        $this->setAttribute('action', str_replace(['?captcha=1', '&captcha=1'], ['', ''], $action));
152        return $this;
153    }
154
155    /**
156     * Method to set method
157     *
158     * @param  string $method
159     * @return Form
160     */
161    public function setMethod($method)
162    {
163        $this->setAttribute('method', $method);
164        return $this;
165    }
166
167    /**
168     * Method to get action
169     *
170     * @return string
171     */
172    public function getAction()
173    {
174        return $this->getAttribute('action');
175    }
176
177    /**
178     * Method to get method
179     *
180     * @return string
181     */
182    public function getMethod()
183    {
184        return $this->getAttribute('method');
185    }
186
187    /**
188     * Method to set an attribute
189     *
190     * @param  string $a
191     * @param  string $v
192     * @return Form
193     */
194    public function setAttribute($a, $v)
195    {
196        parent::setAttribute($a, $v);
197
198        if ($a == 'id') {
199            foreach ($this->fieldsets as $i => $fieldset) {
200                $id = $v . '-fieldset-' . ($i + 1);
201                $fieldset->setAttribute('id', $id);
202            }
203
204        } else if ($a == 'class') {
205            foreach ($this->fieldsets as $i => $fieldset) {
206                $class = $v . '-fieldset';
207                $fieldset->setAttribute('class', $class);
208            }
209        }
210
211        return $this;
212    }
213
214    /**
215     * Method to set attributes
216     *
217     * @param  array $a
218     * @return Form
219     */
220    public function setAttributes(array $a)
221    {
222        foreach ($a as $name => $value) {
223            $this->setAttribute($name, $value);
224        }
225        return $this;
226    }
227
228    /**
229     * Method to add fieldset
230     *
231     * @param  Fieldset $fieldset
232     * @return Form
233     */
234    public function addFieldset(Fieldset $fieldset)
235    {
236        $this->fieldsets[] = $fieldset;
237        $this->current     = count($this->fieldsets) - 1;
238        return $this;
239    }
240
241    /**
242     * Method to remove fieldset
243     *
244     * @param  int $i
245     * @return Form
246     */
247    public function removeFieldset($i)
248    {
249        if (isset($this->fieldsets[(int)$i])) {
250            unset($this->fieldsets[(int)$i]);
251        }
252        $this->fieldsets = array_values($this->fieldsets);
253        if (!isset($this->fieldsets[$this->current])) {
254            $this->current = (count($this->fieldsets) > 0) ? count($this->fieldsets) - 1 : 0;
255        }
256        return $this;
257    }
258
259    /**
260     * Method to get current fieldset
261     *
262     * @return Fieldset
263     */
264    public function getFieldset()
265    {
266        return (isset($this->fieldsets[$this->current])) ? $this->fieldsets[$this->current] : null;
267    }
268
269    /**
270     * Method to add form column
271     *
272     * @param  mixed  $fieldsets
273     * @param  string $class
274     * @return Form
275     */
276    public function addColumn($fieldsets, $class = null)
277    {
278        if (!is_array($fieldsets)) {
279            $fieldsets = [$fieldsets];
280        }
281
282        foreach ($fieldsets as $i => $num) {
283            $fieldsets[$i] = (int)$num - 1;
284        }
285
286        if (null === $class) {
287            $class = 'pop-form-column-' . (count($this->columns) + 1);
288        }
289
290        $this->columns[$class] = $fieldsets;
291        return $this;
292    }
293
294    /**
295     * Method to determine if form has a column
296     *
297     * @param  string $class
298     * @return boolean
299     */
300    public function hasColumn($class)
301    {
302        if (is_numeric($class)) {
303            $class = 'pop-form-column-' . $class;
304        }
305
306        return isset($this->columns[$class]);
307    }
308
309    /**
310     * Method to get form column
311     *
312     * @param  string $class
313     * @return array
314     */
315    public function getColumn($class)
316    {
317        if (is_numeric($class)) {
318            $class = 'pop-form-column-' . $class;
319        }
320
321        return (isset($this->columns[$class])) ? $this->columns[$class] : null;
322    }
323
324    /**
325     * Method to remove form column
326     *
327     * @param  string $class
328     * @return Form
329     */
330    public function removeColumn($class)
331    {
332        if (is_numeric($class)) {
333            $class = 'pop-form-column-' . $class;
334        }
335
336        if (isset($this->columns[$class])) {
337            unset($this->columns[$class]);
338        }
339
340        return $this;
341    }
342
343    /**
344     * Method to get current fieldset index
345     *
346     * @return int
347     */
348    public function getCurrent()
349    {
350        return $this->current;
351    }
352
353    /**
354     * Method to get current fieldset index
355     *
356     * @param  int $i
357     * @return Form
358     */
359    public function setCurrent($i)
360    {
361        $this->current = (int)$i;
362        if (!isset($this->fieldsets[$this->current])) {
363            $this->fieldsets[$this->current] = $this->createFieldset();
364        }
365        return $this;
366    }
367
368    /**
369     * Method to get the legend of the current fieldset
370     *
371     * @return string
372     */
373    public function getLegend()
374    {
375        return (isset($this->fieldsets[$this->current])) ?
376            $this->fieldsets[$this->current]->getLegend() : null;
377    }
378
379    /**
380     * Method to set the legend of the current fieldset
381     *
382     * @param  string $legend
383     * @return Form
384     */
385    public function setLegend($legend)
386    {
387        if (isset($this->fieldsets[$this->current])) {
388            $this->fieldsets[$this->current]->setLegend($legend);
389        }
390        return $this;
391    }
392
393    /**
394     * Method to add a form field
395     *
396     * @param  Element\AbstractElement $field
397     * @return Form
398     */
399    public function addField(Element\AbstractElement $field)
400    {
401        if (count($this->fieldsets) == 0) {
402            $this->createFieldset();
403        }
404        $this->fieldsets[$this->current]->addField($field);
405        return $this;
406    }
407
408    /**
409     * Method to add form fields
410     *
411     * @param  array $fields
412     * @return Form
413     */
414    public function addFields(array $fields)
415    {
416        foreach ($fields as $field) {
417            $this->addField($field);
418        }
419        return $this;
420    }
421
422    /**
423     * Method to add a form field from a config
424     *
425     * @param  string $name
426     * @param  array  $field
427     * @return Form
428     */
429    public function addFieldFromConfig($name, $field)
430    {
431        $this->addField(Fields::create($name, $field));
432        return $this;
433    }
434
435    /**
436     * Method to add form fields from config
437     *
438     * @param  array|FormConfig $config
439     * @return Form
440     */
441    public function addFieldsFromConfig($config)
442    {
443        $i = 1;
444        foreach ($config as $name => $field) {
445            if (is_numeric($name) && !isset($field[$name]['type'])) {
446                $fields = [];
447                foreach ($field as $n => $f) {
448                    $fields[$n] = Fields::create($n, $f);
449                }
450                if ($i > 1) {
451                    $this->fieldsets[$this->current]->createGroup();
452                }
453                if (!isset($this->fieldsets[$this->current])) {
454                    $this->fieldsets[$this->current] = new Fieldset();
455                }
456                $this->fieldsets[$this->current]->addFields($fields);
457                $i++;
458            } else {
459                $this->addField(Fields::create($name, $field));
460            }
461        }
462        return $this;
463    }
464
465    /**
466     * Method to add form fieldsets from config
467     *
468     * @param  array|FormConfig $fieldsets
469     * @param  string $container
470     * @return Form
471     */
472    public function addFieldsetsFromConfig($fieldsets, $container = null)
473    {
474        foreach ($fieldsets as $legend => $config) {
475            if (!is_numeric($legend)) {
476                $this->createFieldset($legend, $container);
477            } else {
478                $this->createFieldset(null, $container);
479            }
480            $this->addFieldsFromConfig($config);
481        }
482
483        return $this;
484    }
485
486    /**
487     * Method to insert a field before another one
488     *
489     * @param  string                  $name
490     * @param  Element\AbstractElement $field
491     * @return Form
492     */
493    public function insertFieldBefore($name, Element\AbstractElement $field)
494    {
495        foreach ($this->fieldsets as $fieldset) {
496            if ($fieldset->hasField($name)) {
497                $fieldset->insertFieldBefore($name, $field);
498                break;
499            }
500        }
501        return $this;
502    }
503
504    /**
505     * Method to insert a field after another one
506     *
507     * @param  string                  $name
508     * @param  Element\AbstractElement $field
509     * @return Form
510     */
511    public function insertFieldAfter($name, Element\AbstractElement $field)
512    {
513        foreach ($this->fieldsets as $fieldset) {
514            if ($fieldset->hasField($name)) {
515                $fieldset->insertFieldAfter($name, $field);
516                break;
517            }
518        }
519        return $this;
520    }
521
522    /**
523     * Method to get the count of elements in the form
524     *
525     * @return int
526     */
527    public function count(): int
528    {
529        $count = 0;
530        foreach ($this->fieldsets as $fieldset) {
531            $count += $fieldset->count();
532        }
533        return $count;
534    }
535
536    /**
537     * Method to get the field values as an array
538     *
539     * @return array
540     */
541    public function toArray(): array
542    {
543        $fieldValues = [];
544
545        foreach ($this->fieldsets as $fieldset) {
546            $fieldValues = array_merge($fieldValues, $fieldset->toArray());
547        }
548
549        return $fieldValues;
550    }
551
552    /**
553     * Method to get a field element object
554     *
555     * @param  string $name
556     * @return Element\AbstractElement
557     */
558    public function getField($name)
559    {
560        $namedField = null;
561        $fields     = $this->getFields();
562
563        foreach ($fields as $field) {
564            if ($field->getName() == $name) {
565                $namedField = $field;
566                break;
567            }
568        }
569
570        return $namedField;
571    }
572
573    /**
574     * Method to get field element objects
575     *
576     * @return array
577     */
578    public function getFields()
579    {
580        $fields = [];
581
582        foreach ($this->fieldsets as $fieldset) {
583            $fields = array_merge($fields, $fieldset->getAllFields());
584        }
585
586        return $fields;
587    }
588
589    /**
590     * Has a field element object
591     *
592     * @param  string $name
593     * @return boolean
594     */
595    public function hasField($name)
596    {
597        return (null !== $this->getField($name));
598    }
599
600    /**
601     * Has fields
602     *
603     * @return boolean
604     */
605    public function hasFields()
606    {
607        return (!empty($this->getFields()));
608    }
609
610    /**
611     * Method to remove a form field
612     *
613     * @param  string $field
614     * @return Form
615     */
616    public function removeField($field)
617    {
618        foreach ($this->fieldsets as $fieldset) {
619            if ($fieldset->hasField($field)) {
620                unset($fieldset[$field]);
621            }
622        }
623        return $this;
624    }
625
626    /**
627     * Method to get a field element value
628     *
629     * @param  string $name
630     * @return mixed
631     */
632    public function getFieldValue($name)
633    {
634        $fieldValues = $this->toArray();
635        return (isset($fieldValues[$name])) ? $fieldValues[$name] : null;
636    }
637
638    /**
639     * Method to set a field element value
640     *
641     * @param  string $name
642     * @param  mixed  $value
643     * @return Form
644     */
645    public function setFieldValue($name, $value)
646    {
647        foreach ($this->fieldsets as $fieldset) {
648            if (isset($fieldset[$name])) {
649                $fieldset[$name] = $value;
650            }
651        }
652        return $this;
653    }
654
655    /**
656     * Method to set field element values
657     *
658     * @param  array $values
659     * @return Form
660     */
661    public function setFieldValues(array $values)
662    {
663        $fields = $this->toArray();
664        foreach ($fields as $name => $value) {
665            if (isset($values[$name]) && !($this->getField($name)->isButton())) {
666                $this->setFieldValue($name, $values[$name]);
667            } else if (!($this->getField($name)->isButton())) {
668                $this->getField($name)->resetValue();
669            }
670        }
671
672        $this->filter();
673
674        return $this;
675    }
676
677    /**
678     * Filter value with the filters in the form object
679     *
680     * @param  mixed $field
681     * @return mixed
682     */
683    public function filterValue($field)
684    {
685        if ($field instanceof Element\AbstractElement) {
686            $name      = $field->getName();
687            $type      = $field->getType();
688            $realValue = $field->getValue();
689        } else {
690            $type      = null;
691            $name      = null;
692            $realValue = $field;
693        }
694
695        foreach ($this->filters as $filter) {
696            $realValue = $filter->filter($realValue, $name, $type);
697        }
698
699        if (($field instanceof Element\AbstractElement) && !($field instanceof Element\Input\Checkbox) &&
700            !($field instanceof Element\Input\Radio) && (null !== $realValue) && ($realValue != '')) {
701            $field->setValue($realValue);
702        }
703
704        return $realValue;
705    }
706
707    /**
708     * Filter values with the filters in the form object
709     *
710     * @param  mixed $values
711     * @return mixed
712     */
713    public function filter($values = null)
714    {
715        if (null === $values) {
716            $values = $this->getFields();
717        }
718
719        if (is_array($values)) {
720            foreach ($values as $key => $value) {
721                $values[$key] = $this->filterValue($value);
722            }
723        } else {
724            $values = $this->filterValue($values);
725        }
726
727        return $values;
728    }
729
730    /**
731     * Determine whether or not the form object is valid
732     *
733     * @return boolean
734     */
735    public function isValid()
736    {
737        $result = true;
738        $fields = $this->getFields();
739        $values = $this->toArray();
740
741        // Check each element for validators, validate them and return the result.
742        foreach ($fields as $field) {
743            if ($field->validate($values) == false) {
744                $result = false;
745            }
746        }
747
748        return $result;
749    }
750
751    /**
752     * Get form element errors for a field.
753     *
754     * @param  string $name
755     * @return array
756     */
757    public function getErrors($name)
758    {
759        $field  = $this->getField($name);
760        $errors = (null !== $field) ? $field->getErrors() : [];
761
762        return $errors;
763    }
764
765    /**
766     * Get all form element errors
767     *
768     * @return array
769     */
770    public function getAllErrors()
771    {
772        $errors = [];
773        $fields = $this->getFields();
774        foreach ($fields as $name => $field) {
775            if ($field->hasErrors()) {
776                $errors[str_replace('[]', '', $field->getName())] = $field->getErrors();
777            }
778        }
779
780        return $errors;
781    }
782
783    /**
784     * Method to reset and clear any form field values
785     *
786     * @return Form
787     */
788    public function reset()
789    {
790        $fields = $this->getFields();
791        foreach ($fields as $field) {
792            $field->resetValue();
793        }
794        return $this;
795    }
796
797    /**
798     * Method to clear any security tokens
799     *
800     * @return Form
801     */
802    public function clearTokens()
803    {
804        // Start a session.
805        if (session_id() == '') {
806            session_start();
807        }
808        if ($_SESSION) {
809            if (isset($_SESSION['pop_csrf'])) {
810                unset($_SESSION['pop_csrf']);
811            }
812            if (isset($_SESSION['pop_captcha'])) {
813                unset($_SESSION['pop_captcha']);
814            }
815        }
816
817        return $this;
818    }
819
820    /**
821     * Prepare form object for rendering
822     *
823     * @return Form
824     */
825    public function prepare()
826    {
827        if (null === $this->getAttribute('id')) {
828            $this->setAttribute('id', 'pop-form');
829        }
830        if (null === $this->getAttribute('class')) {
831            $this->setAttribute('class', 'pop-form');
832        }
833
834        if (count($this->columns) > 0) {
835            foreach ($this->columns as $class => $fieldsets) {
836                $column = new Child('div');
837                $column->setAttribute('class', $class);
838                foreach ($fieldsets as $i) {
839                    if (isset($this->fieldsets[$i])) {
840                        $fieldset = $this->fieldsets[$i];
841                        $fieldset->prepare();
842                        $column->addChild($fieldset);
843                    }
844                }
845                $this->addChild($column);
846            }
847        } else {
848            foreach ($this->fieldsets as $fieldset) {
849                $fieldset->prepare();
850                $this->addChild($fieldset);
851            }
852        }
853
854        return $this;
855    }
856
857    /**
858     * Prepare form object for rendering with a view
859     *
860     * @return array
861     */
862    public function prepareForView()
863    {
864        $formData = [];
865
866        foreach ($this->fieldsets as $fieldset) {
867            $formData = array_merge($formData, $fieldset->prepareForView());
868        }
869
870        return $formData;
871    }
872
873    /**
874     * Render the form object
875     *
876     * @param  int     $depth
877     * @param  string  $indent
878     * @param  boolean $inner
879     * @return mixed
880     */
881    #[\ReturnTypeWillChange]
882    public function render($depth = 0, $indent = null, $inner = false)
883    {
884        if (!($this->hasChildren())) {
885            $this->prepare();
886        }
887
888        foreach ($this->fieldsets as $fieldset) {
889            foreach ($fieldset->getAllFields() as $field) {
890                if ($field instanceof Element\Input\File) {
891                    $this->setAttribute('enctype', 'multipart/form-data');
892                    break;
893                }
894            }
895        }
896
897        return parent::render($depth, $indent, $inner);
898    }
899
900    /**
901     * Render and return the form object as a string
902     *
903     * @return string
904     */
905    public function __toString()
906    {
907        return $this->render();
908    }
909
910    /**
911     * Set method to set the property to the value of fields[$name]
912     *
913     * @param  string $name
914     * @param  mixed $value
915     * @return void
916     */
917    public function __set($name, $value)
918    {
919        $this->setFieldValue($name, $value);
920    }
921
922    /**
923     * Get method to return the value of fields[$name]
924     *
925     * @param  string $name
926     * @throws Exception
927     * @return mixed
928     */
929    public function __get($name)
930    {
931        return $this->getFieldValue($name);
932    }
933
934    /**
935     * Return the isset value of fields[$name]
936     *
937     * @param  string $name
938     * @return boolean
939     */
940    public function __isset($name)
941    {
942        $fieldValues = $this->toArray();
943        return isset($fieldValues[$name]);
944    }
945
946    /**
947     * Unset fields[$name]
948     *
949     * @param  string $name
950     * @return void
951     */
952    public function __unset($name)
953    {
954        $fieldValues = $this->toArray();
955        if (isset($fieldValues[$name])) {
956            $this->getField($name)->resetValue();
957        }
958    }
959
960}