Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
89 / 89
100.00% covered (success)
100.00%
46 / 46
CRAP
100.00% covered (success)
100.00%
1 / 1
AbstractElement
100.00% covered (success)
100.00%
89 / 89
100.00% covered (success)
100.00%
46 / 46
68
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setName
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setValue
n/a
0 / 0
n/a
0 / 0
0
 resetValue
n/a
0 / 0
n/a
0 / 0
0
 setLabel
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setHint
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setPrepend
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setAppend
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setLabelAttribute
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setLabelAttributes
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setHintAttribute
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setHintAttributes
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 setRequired
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 setRequiredMessage
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setDisabled
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setReadonly
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setErrorPre
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 isErrorPre
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setValidators
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 clearErrors
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getType
n/a
0 / 0
n/a
0 / 0
0
 getValue
n/a
0 / 0
n/a
0 / 0
0
 getLabel
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasLabel
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getHint
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasHint
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPrepend
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasPrepend
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAppend
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasAppend
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getLabelAttributes
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasLabelAttributes
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getHintAttributes
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasHintAttributes
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getValidators
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasValidators
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isRequired
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasRequiredMessage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRequiredMessage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isDisabled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isReadonly
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isButton
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
4
 getErrors
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasErrors
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addValidator
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 addValidators
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 validateValue
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
7
 validateCallable
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
8
 validate
n/a
0 / 0
n/a
0 / 0
0
 __toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
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 */
14namespace Pop\Form\Element;
15
16use Pop\Dom\Child;
17use Pop\Validator;
18
19/**
20 * Abstract form element class
21 *
22 * @category   Pop
23 * @package    Pop\Form
24 * @author     Nick Sagona, III <dev@nolainteractive.com>
25 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
26 * @license    http://www.popphp.org/license     New BSD License
27 * @version    4.0.0
28 */
29abstract class AbstractElement extends Child implements ElementInterface
30{
31
32    /**
33     * Element name
34     * @var ?string
35     */
36    protected ?string $name = null;
37
38    /**
39     * Form element label
40     * @var ?string
41     */
42    protected ?string $label = null;
43
44    /**
45     * Form element hint
46     * @var ?string
47     */
48    protected ?string $hint = null;
49
50    /**
51     * Form element label attributes
52     * @var array
53     */
54    protected array $labelAttributes = [];
55
56    /**
57     * Form element hint attributes
58     * @var array
59     */
60    protected array $hintAttributes = [];
61
62    /**
63     * Form element prepend contents
64     * @var ?string
65     */
66    protected ?string $prepend = null;
67
68    /**
69     * Form element append contents
70     * @var ?string
71     */
72    protected ?string $append = null;
73
74    /**
75     * Form element required property
76     * @var bool
77     */
78    protected bool $required = false;
79
80    /**
81     * Form element required message
82     * @var ?string
83     */
84    protected ?string $requiredMessage = null;
85
86    /**
87     * Form element disabled property
88     * @var bool
89     */
90    protected bool $disabled = false;
91
92    /**
93     * Form element readonly property
94     * @var bool
95     */
96    protected bool $readonly = false;
97
98    /**
99     * Form element validators
100     * @var array
101     */
102    protected array $validators = [];
103
104    /**
105     * Form element error display position
106     * @var bool
107     */
108    protected bool $errorPre = false;
109
110    /**
111     * Form element errors
112     * @var array
113     */
114    protected array $errors = [];
115
116    /**
117     * Constructor
118     *
119     * Instantiate the form element object
120     *
121     * @param  string  $name
122     * @param  ?string $value
123     * @param  array   $options
124     */
125    public function __construct(string $name, ?string $value = null, array $options = [])
126    {
127        parent::__construct($name, $value, $options);
128    }
129
130    /**
131     * Set the name of the form element object
132     *
133     * @param  string $name
134     * @return AbstractElement
135     */
136    public function setName(string $name): AbstractElement
137    {
138        $this->name = $name;
139        return $this;
140    }
141
142    /**
143     * Set the value of the form element
144     *
145     * @param  mixed $value
146     * @return AbstractElement
147     */
148    abstract public function setValue(mixed $value): AbstractElement;
149
150    /**
151     * Reset the value of the form element
152     *
153     * @return AbstractElement
154     */
155    abstract public function resetValue(): AbstractElement;
156
157    /**
158     * Set the label of the form element object
159     *
160     * @param  string $label
161     * @return AbstractElement
162     */
163    public function setLabel(string $label): AbstractElement
164    {
165        $this->label = $label;
166        return $this;
167    }
168
169    /**
170     * Set the hint of the form element object
171     *
172     * @param  string $hint
173     * @return AbstractElement
174     */
175    public function setHint(string $hint): AbstractElement
176    {
177        $this->hint = $hint;
178        return $this;
179    }
180
181    /**
182     * Set the prepend contents of the form element object
183     *
184     * @param  string $prepend
185     * @return AbstractElement
186     */
187    public function setPrepend(string $prepend): AbstractElement
188    {
189        $this->prepend = $prepend;
190        return $this;
191    }
192
193    /**
194     * Set the append contents of the form element object
195     *
196     * @param  string $append
197     * @return AbstractElement
198     */
199    public function setAppend(string $append): AbstractElement
200    {
201        $this->append = $append;
202        return $this;
203    }
204
205    /**
206     * Set an attribute of the label of the form element object
207     *
208     * @param  string $a
209     * @param  string $v
210     * @return AbstractElement
211     */
212    public function setLabelAttribute(string $a, string $v): AbstractElement
213    {
214        $this->labelAttributes[$a] = $v;
215        return $this;
216    }
217
218    /**
219     * Set the attributes of the label of the form element object
220     *
221     * @param  array $attribs
222     * @return AbstractElement
223     */
224    public function setLabelAttributes(array $attribs): AbstractElement
225    {
226        foreach ($attribs as $a => $v) {
227            $this->setLabelAttribute($a, $v);
228        }
229        return $this;
230    }
231
232    /**
233     * Set an attribute of the hint of the form element object
234     *
235     * @param  string $a
236     * @param  string $v
237     * @return AbstractElement
238     */
239    public function setHintAttribute(string $a, string $v): AbstractElement
240    {
241        $this->hintAttributes[$a] = $v;
242        return $this;
243    }
244
245    /**
246     * Set the attributes of the hint of the form element object
247     *
248     * @param  array $attribs
249     * @return AbstractElement
250     */
251    public function setHintAttributes(array $attribs): AbstractElement
252    {
253        foreach ($attribs as $a => $v) {
254            $this->setHintAttribute($a, $v);
255        }
256        return $this;
257    }
258
259    /**
260     * Set whether the form element is required
261     *
262     * @param  bool    $required
263     * @param  ?string $requiredMessage
264     * @return AbstractElement
265     */
266    public function setRequired(bool $required, ?string $requiredMessage = 'This field is required.'): AbstractElement
267    {
268        $this->required = $required;
269
270        if (!empty($requiredMessage)) {
271            $this->setRequiredMessage($requiredMessage);
272        }
273
274        return $this;
275    }
276
277    /**
278     * Set the form element is required message
279     *
280     * @param  string $requiredMessage
281     * @return AbstractElement
282     */
283    public function setRequiredMessage(string $requiredMessage): AbstractElement
284    {
285        $this->requiredMessage = $requiredMessage;
286        return $this;
287    }
288
289    /**
290     * Set whether the form element is disabled
291     *
292     * @param  bool $disabled
293     * @return AbstractElement
294     */
295    public function setDisabled(bool $disabled): AbstractElement
296    {
297        $this->disabled = $disabled;
298        return $this;
299    }
300
301    /**
302     * Set whether the form element is readonly
303     *
304     * @param  bool $readonly
305     * @return AbstractElement
306     */
307    public function setReadonly(bool $readonly): AbstractElement
308    {
309        $this->readonly = $readonly;
310        return $this;
311    }
312
313    /**
314     * Set error pre-display
315     *
316     * @param  bool $pre
317     * @return AbstractElement
318     */
319    public function setErrorPre(bool $pre): AbstractElement
320    {
321        $this->errorPre = $pre;
322        return $this;
323    }
324
325    /**
326     * Determine if error to display before the element
327     *
328     * @return bool
329     */
330    public function isErrorPre(): bool
331    {
332        return $this->errorPre;
333    }
334
335    /**
336     * Set validators
337     *
338     * @param  array $validators
339     * @return AbstractElement
340     */
341    public function setValidators(array $validators = []): AbstractElement
342    {
343        $this->validators = $validators;
344        return $this;
345    }
346
347    /**
348     * Clear errors
349     *
350     * @return AbstractElement
351     */
352    public function clearErrors(): AbstractElement
353    {
354        $this->errors = [];
355        return $this;
356    }
357
358    /**
359     * Get form element object name
360     *
361     * @return ?string
362     */
363    public function getName(): ?string
364    {
365        return $this->name;
366    }
367
368    /**
369     * Get form element object type
370     *
371     * @return ?string
372     */
373    abstract public function getType() : ?string;
374
375    /**
376     * Get form element value
377     *
378     * @return mixed
379     */
380    abstract public function getValue(): mixed;
381
382    /**
383     * Get form element object label
384     *
385     * @return ?string
386     */
387    public function getLabel(): ?string
388    {
389        return $this->label;
390    }
391
392    /**
393     * Determine if form element has a label
394     *
395     * @return bool
396     */
397    public function hasLabel(): bool
398    {
399        return !empty($this->label);
400    }
401
402    /**
403     * Get form element object hint
404     *
405     * @return ?string
406     */
407    public function getHint(): ?string
408    {
409        return $this->hint;
410    }
411
412    /**
413     * Determine if form element has a hint
414     *
415     * @return bool
416     */
417    public function hasHint(): bool
418    {
419        return !empty($this->hint);
420    }
421
422    /**
423     * Get form element object prepend contents
424     *
425     * @return ?string
426     */
427    public function getPrepend(): ?string
428    {
429        return $this->prepend;
430    }
431
432    /**
433     * Determine if form element has prepend content
434     *
435     * @return bool
436     */
437    public function hasPrepend(): bool
438    {
439        return !empty($this->prepend);
440    }
441
442    /**
443     * Get form element object append contents
444     *
445     * @return ?string
446     */
447    public function getAppend(): ?string
448    {
449        return $this->append;
450    }
451
452    /**
453     * Determine if form element has append content
454     *
455     * @return bool
456     */
457    public function hasAppend(): bool
458    {
459        return !empty($this->append);
460    }
461
462    /**
463     * Get the attributes of the form element object label
464     *
465     * @return array
466     */
467    public function getLabelAttributes(): array
468    {
469        return $this->labelAttributes;
470    }
471
472    /**
473     * Determine if form element has label attributes
474     *
475     * @return bool
476     */
477    public function hasLabelAttributes(): bool
478    {
479        return !empty($this->labelAttributes);
480    }
481
482    /**
483     * Get the attributes of the form element object hint
484     *
485     * @return array
486     */
487    public function getHintAttributes(): array
488    {
489        return $this->hintAttributes;
490    }
491
492    /**
493     * Determine if form element has hint attributes
494     *
495     * @return bool
496     */
497    public function hasHintAttributes(): bool
498    {
499        return !empty($this->hintAttributes);
500    }
501
502    /**
503     * Get validators
504     *
505     * @return array
506     */
507    public function getValidators(): array
508    {
509        return $this->validators;
510    }
511
512    /**
513     * Determine if form element has validators
514     *
515     * @return bool
516     */
517    public function hasValidators(): bool
518    {
519        return !empty($this->validators);
520    }
521
522    /**
523     * Get whether the form element object is required
524     *
525     * @return bool
526     */
527    public function isRequired(): bool
528    {
529        return $this->required;
530    }
531
532    /**
533     * Does the form element object have a required message
534     *
535     * @return bool
536     */
537    public function hasRequiredMessage(): bool
538    {
539        return !empty($this->requiredMessage);
540    }
541
542    /**
543     * Get the form element object required message
544     *
545     * @return ?string
546     */
547    public function getRequiredMessage(): ?string
548    {
549        return $this->requiredMessage;
550    }
551
552    /**
553     * Get whether the form element object is disabled
554     *
555     * @return bool
556     */
557    public function isDisabled(): bool
558    {
559        return $this->disabled;
560    }
561
562    /**
563     * Get whether the form element object is readonly
564     *
565     * @return bool
566     */
567    public function isReadonly(): bool
568    {
569        return $this->readonly;
570    }
571
572    /**
573     * Get whether the form element object is a button
574     *
575     * @return bool
576     */
577    public function isButton(): bool
578    {
579        return (($this instanceof Button) || ($this instanceof Input\Button) ||
580            ($this instanceof Input\Submit) || ($this instanceof Input\Reset));
581    }
582
583    /**
584     * Get form element object errors
585     *
586     * @return array
587     */
588    public function getErrors(): array
589    {
590        return $this->errors;
591    }
592
593    /**
594     * Get if form element object has errors
595     *
596     * @return bool
597     */
598    public function hasErrors(): bool
599    {
600        return (count($this->errors) > 0);
601    }
602
603    /**
604     * Add a validator the form element
605     *
606     * @param  mixed $validator
607     * @throws Exception
608     * @return AbstractElement
609     */
610    public function addValidator(mixed $validator): AbstractElement
611    {
612        if (!($validator instanceof \Pop\Validator\AbstractValidator) && !is_callable($validator)) {
613            throw new Exception('Error: The validator must be an instance of Pop\Validator\AbstractValidator or a callable object.');
614        }
615        $this->validators[] = $validator;
616        return $this;
617    }
618
619    /**
620     * Add multiple validators the form element
621     *
622     * @param  array $validators
623     * @throws Exception
624     * @return AbstractElement
625     */
626    public function addValidators(array $validators): AbstractElement
627    {
628        foreach ($validators as $validator) {
629            $this->addValidator($validator);
630        }
631        return $this;
632    }
633
634    /**
635     * Validate the value
636     *
637     * @param  mixed $value
638     * @param  array $formValues
639     * @return void
640     */
641    public function validateValue(mixed $value, array $formValues = []): void
642    {
643        // Check field validators
644        if (count($this->validators) > 0) {
645            foreach ($this->validators as $validator) {
646                if ($validator instanceof \Pop\Validator\ValidatorInterface) {
647                    if (!$validator->evaluate($value)) {
648                        if (!in_array($validator->getMessage(), $this->errors)) {
649                            $this->errors[] = $validator->getMessage();
650                        }
651                    }
652                } else if (is_callable($validator)) {
653                    $this->validateCallable($validator, $value, $formValues);
654                }
655            }
656        }
657    }
658
659    /**
660     * Validate the value by callable
661     *
662     * @param  callable $validator
663     * @param  mixed    $value
664     * @param  array    $formValues
665     * @return void
666     */
667    public function validateCallable(callable $validator, mixed $value, array $formValues = []): void
668    {
669        $result = call_user_func_array($validator, [$value, $formValues]);
670        if ($result instanceof \Pop\Validator\ValidatorInterface) {
671            if (!$result->evaluate($value)) {
672                $this->errors[] = $result->getMessage();
673            }
674        } else if (is_array($result)) {
675            foreach ($result as $val) {
676                if ($val instanceof \Pop\Validator\ValidatorInterface) {
677                    if (!$val->evaluate($value)) {
678                        $this->errors[] = $val->getMessage();
679                    }
680                }
681            }
682        } else if ($result !== null) {
683            $this->errors[] = $result;
684        }
685    }
686
687    /**
688     * Validate the form element object
689     *
690     * @param  array $formValues
691     * @return bool
692     */
693    abstract public function validate(array $formValues = []): bool;
694
695    /**
696     * Print form element
697     *
698     * @return string
699     */
700    public function __toString(): string
701    {
702        return $this->render();
703    }
704
705}