FormElement   F
last analyzed

Complexity

Total Complexity 79

Size/Duplication

Total Lines 554
Duplicated Lines 0 %

Test Coverage

Coverage 78.18%

Importance

Changes 0
Metric Value
eloc 223
dl 0
loc 554
ccs 172
cts 220
cp 0.7818
rs 2.08
c 0
b 0
f 0
wmc 79

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A offsetGet() 0 3 2
A value() 0 9 3
B validate() 0 81 9
A useNameAsDefaultLabel() 0 4 2
A offsetSet() 0 6 2
A getValue() 0 3 1
A checked() 0 3 1
A useNameAsDefaultValue() 0 4 2
A getEscapedValue() 0 3 1
A rawValue() 0 3 1
A getElementId() 0 3 2
A setValue() 0 3 1
F getHTMLDetails() 0 168 42
A offsetUnset() 0 3 1
A offsetExists() 0 3 1
A escValue() 0 3 1
A getValidationMessages() 0 11 3
A getRawValue() 0 3 1
A setDefault() 0 3 1
A isButton() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like FormElement often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FormElement, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Anax\HTMLForm;
4
5
/**
6
 * A utility class to easy creating and handling of forms
7
 */
8
abstract class FormElement implements \ArrayAccess
9
{
10
11
    /**
12
     * @var array $attributes        settings to use to create element
13
     * @var array $config            default settings to use to create element
14
     * @var array $characterEncoding setting for character encoding
15
     */
16
    public $attributes;
17
    public $config;
18
    public $characterEncoding;
19
20
21
22
    /**
23
     * Constructor creating a form element.
24
     *
25
     * @param string $name       of the element.
26
     * @param array  $attributes to set to the element. Default is an empty
27
     *                           array.
28
     */
29 26
    public function __construct($name, $attributes = [])
30
    {
31 26
        $this->attributes = $attributes;
32 26
        $this['name'] = $name;
33
        //$this['key'] = $name;
34
        //$this['name'] = isset($this['name']) ? $this['name'] : $name;
35
36 26
        $this->characterEncoding = 'UTF-8';
37 26
        $this->default["wrapper-element"] = "p";
0 ignored issues
show
Bug Best Practice introduced by
The property default does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
38 26
        $this->default["br-after-label"] = true;
39 26
        $this->default["escape-values"] = true;
40 26
    }
41
42
43
44
    /**
45
     * Set default values to use, merge incoming with existing.
46
     *
47
     * @param array  $options key value array with settings to use.
48
     *
49
     * @return void
50
     */
51 4
    public function setDefault($options)
52
    {
53 4
        $this->default = array_merge($this->default, $options);
0 ignored issues
show
Bug Best Practice introduced by
The property default does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
54 4
    }
55
56
57
58
    /**
59
     * Implementing ArrayAccess for this->attributes
60
     *
61
     * @return void
62
     */
63 26
    public function offsetSet($offset, $value)
64
    {
65 26
        if (is_null($offset)) {
66
            $this->attributes[] = $value;
67
        } else {
68 26
            $this->attributes[$offset] = $value;
69
        }
70 26
    }
71
72
73
74
    /**
75
     * Implementing ArrayAccess for this->attributes
76
     */
77 26
    public function offsetExists($offset)
78
    {
79 26
        return isset($this->attributes[$offset]);
80
    }
81
82
83
84
    /**
85
     * Implementing ArrayAccess for this->attributes
86
     */
87
    public function offsetUnset($offset)
88
    {
89
        unset($this->attributes[$offset]);
90
    }
91
92
93
94
    /**
95
     * Implementing ArrayAccess for this->attributes
96
     */
97 25
    public function offsetGet($offset)
98
    {
99 25
        return isset($this->attributes[$offset]) ? $this->attributes[$offset] : null;
100
    }
101
102
103
104
    /**
105
     * Get id of an element.
106
     *
107
     * @return HTML code for the element.
0 ignored issues
show
Bug introduced by
The type Anax\HTMLForm\HTML was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
108
     */
109 9
    public function getElementId()
110
    {
111 9
        return ($this['id'] = isset($this['id']) ? $this['id'] : 'form-element-' . $this['name']);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this['id'] = Iss...ement-' . $this['name'] also could return the type string which is incompatible with the documented return type Anax\HTMLForm\HTML.
Loading history...
112
    }
113
114
115
116
    /**
117
     * Get alll validation messages.
118
     *
119
     * @return HTML code for the element.
120
     */
121 9
    public function getValidationMessages()
122
    {
123 9
        $messages = null;
124 9
        if (isset($this['validation-messages'])) {
125
            $message = null;
126
            foreach ($this['validation-messages'] as $val) {
127
                $message .= "<li>{$val}</li>\n";
128
            }
129
            $messages = "<ul class='validation-message'>\n{$message}</ul>\n";
130
        }
131 9
        return $messages;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $messages also could return the type string which is incompatible with the documented return type Anax\HTMLForm\HTML.
Loading history...
132
    }
133
134
135
136
    /**
137
     * Get details for a HTML element, prepare for creating HTML code for it.
138
     *
139
     * @return HTML code for the element.
140
     */
141 9
    public function getHTMLDetails()
142
    {
143
        // Add disabled to be able to disable a form element
144
        // Add maxlength
145 9
        $id =  $this->getElementId();
146
147 9
        $class = isset($this['class'])
148
            ? "{$this['class']}"
149 9
            : null;
150
151 9
        $validates = (isset($this['validation-pass']) && $this['validation-pass'] === false)
152
            ? ' validation-failed'
153 9
            : null;
154
155 9
        $class = (isset($class) || isset($validates))
156
            ? " class='{$class}{$validates}'"
157 9
            : null;
158
159 9
        $wrapperElement = isset($this['wrapper-element'])
160
            ? $this['wrapper-element']
161 9
            : $this->default["wrapper-element"];
162
163 9
        $wrapperClass = isset($this['wrapper-class'])
164
            ? " class=\"{$this['wrapper-class']}\""
165 9
            : null;
166
167 9
        $brAfterLabel = isset($this['br-after-label'])
168
            ? $this['br-after-label']
169 9
            : $this->default["br-after-label"];
170
171 9
        $brAfterLabel = $brAfterLabel
172 9
            ? "<br>"
173 9
            : null;
174
175 9
        $name = " name='{$this['name']}'";
176
177 9
        $label = isset($this['label'])
178 5
            ? ($this['label'] . (isset($this['required']) && $this['required']
179
                ? "<span class='form-element-required'>*</span>"
180 5
                : null))
181 9
            : null;
182
183 9
        $autofocus = isset($this['autofocus']) && $this['autofocus']
184
            ? " autofocus='autofocus'"
185 9
            : null;
186
187 9
        $required = isset($this['required']) && $this['required']
188
            ? " required='required'"
189 9
            : null;
190
191 9
        $readonly = isset($this['readonly']) && $this['readonly']
192
            ? " readonly='readonly'"
193 9
            : null;
194
195 9
        $placeholder = isset($this['placeholder']) && $this['placeholder']
196
            ? " placeholder='{$this['placeholder']}'"
197 9
            : null;
198
199 9
        $multiple = isset($this['multiple']) && $this['multiple']
200
            ? " multiple"
201 9
            : null;
202
203 9
        $max = isset($this['max'])
204
            ? " max='{$this['max']}'"
205 9
            : null;
206
207 9
        $min = isset($this['min'])
208
            ? " min='{$this['min']}'"
209 9
            : null;
210
211 9
        $low = isset($this['low'])
212
            ? " low='{$this['low']}'"
213 9
            : null;
214
215 9
        $high = isset($this['high'])
216
            ? " high='{$this['high']}'"
217 9
            : null;
218
219 9
        $optimum = isset($this['optimum'])
220
            ? " optimum='{$this['optimum']}'"
221 9
            : null;
222
223 9
        $step = isset($this['step'])
224
            ? " step='{$this['step']}'"
225 9
            : null;
226
227 9
        $size = isset($this['size'])
228
            ? " size='{$this['size']}'"
229 9
            : null;
230
231 9
        $text = isset($this['text'])
232
            ? htmlentities($this['text'], ENT_QUOTES, $this->characterEncoding)
233 9
            : null;
234
235 9
        $checked = isset($this['checked']) && $this['checked']
236
            ? " checked='checked'"
237 9
            : null;
238
239 9
        $type = isset($this['type'])
240 9
            ? " type='{$this['type']}'"
241 9
            : null;
242
243 9
        $title = isset($this['title'])
244
            ? " title='{$this['title']}'"
245 9
            : null;
246
247 9
        $pattern = isset($this['pattern'])
248
            ? " pattern='{$this['pattern']}'"
249 9
            : null;
250
251 9
        $description = isset($this['description'])
252
            ? $this['description']
253 9
            : null;
254
255 9
        $novalidate = isset($this['formnovalidate'])
256 1
            ? " formnovalidate='formnovalidate'"
257 9
            : null;
258
259 9
        $onlyValue = isset($this['value'])
260 2
            ? htmlentities($this['value'], ENT_QUOTES, $this->characterEncoding)
261 9
            : null;
262
263 9
        $value = isset($this['value'])
264 2
            ? " value='{$onlyValue}'"
265 9
            : null;
266
267 9
        $onclick = isset($this['onclick'])
268
            ? " onclick=\"{$this['onclick']}\""
269 9
            : null;
270
271 9
        $maxlength = isset($this['maxlength'])
272
            ? " maxlength='{$this['maxlength']}'"
273 9
            : null;
274
275 9
        $messages = $this->getValidationMessages();
276
277
        return [
278 9
            'id'             => $id,
279 9
            'class'          => $class,
280 9
            'wrapperElement' => $wrapperElement,
281 9
            'wrapperClass'   => $wrapperClass,
282 9
            'brAfterLabel'   => $brAfterLabel,
283 9
            'name'           => $name,
284 9
            'label'          => $label,
285 9
            'autofocus'      => $autofocus,
286 9
            'required'       => $required,
287 9
            'readonly'       => $readonly,
288 9
            'placeholder'    => $placeholder,
289 9
            'multiple'       => $multiple,
290 9
            'min'            => $min,
291 9
            'max'            => $max,
292 9
            'maxlength'      => $maxlength,
293 9
            'low'            => $low,
294 9
            'high'           => $high,
295 9
            'step'           => $step,
296 9
            'optimum'        => $optimum,
297 9
            'size'           => $size,
298 9
            'text'           => $text,
299 9
            'checked'        => $checked,
300 9
            'type'           => $type,
301 9
            'title'          => $title,
302 9
            'pattern'        => $pattern,
303 9
            'description'    => $description,
304 9
            'novalidate'     => $novalidate,
305 9
            'onlyValue'      => $onlyValue,
306 9
            'value'          => $value,
307 9
            'onclick'        => $onclick,
308 9
            'messages'       => $messages,
309
        ];
310
    }
311
312
313
314
    /**
315
     * Get HTML code for a element, must be implemented by each subclass.
316
     *
317
     * @return HTML code for the element.
318
     */
319
    abstract public function getHTML();
320
321
322
323
    /**
324
     * Validate the form element value according a ruleset.
325
     *
326
     * @param array     $rules validation rules.
327
     * @param Form|null $form  the parent form.
328
     *
329
     * @return boolean true if all rules pass, else false.
330
     */
331 2
    public function validate($rules, $form = null)
0 ignored issues
show
Unused Code introduced by
The parameter $form is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

331
    public function validate($rules, /** @scrutinizer ignore-unused */ $form = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
332
    {
333 2
        $regExpEmailAddress = '/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b/i';
0 ignored issues
show
Unused Code introduced by
The assignment to $regExpEmailAddress is dead and can be removed.
Loading history...
334
        $tests = [
335 2
            'fail' => [
336
                'message' => 'Will always fail.',
337
                'test' => 'return false;'
338
            ],
339
340
            'pass' => [
341
                'message' => 'Will always pass.',
342
                'test' => 'return true;'
343
            ],
344
345
            'not_empty' => [
346
                'message' => 'Can not be empty.',
347
                'test' => 'return $value != "";'
348
            ],
349
350
            'not_equal' => [
351
                'message' => 'Value not valid.',
352
                'test' => 'return $value != $arg;'
353
            ],
354
355
            'number' => [
356
                'message' => 'Must be a number.',
357
                'test' => 'return is_numeric($value);'
358
            ],
359
360
            'email' => [
361 2
                'message' => 'Must be an email adress.',
362
                'test' => function ($value) {
363 1
                    return preg_match('/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b/i', $value) === 1;
364 2
                }
365
            ],
366
367
            'match' => [
368
                'message' => 'The field does not match.',
369
                'test' => 'return $value == $form[$arg]["value"] ;'
370
            ],
371
372
            'must_accept' => [
373
                'message' => 'You must accept this.',
374
                'test' => 'return $checked;'
375
            ],
376
377
            'custom_test' => true,
378
        ];
379
380
        // max tecken, min tecken, datum, tid, datetime, mysql datetime
381
382 2
        $pass = true;
383 2
        $messages = array();
384 2
        $value = $this['value'];
385 2
        $checked = $this['checked'];
0 ignored issues
show
Unused Code introduced by
The assignment to $checked is dead and can be removed.
Loading history...
386
387 2
        foreach ($rules as $key => $val) {
388 2
            $rule = is_numeric($key) ? $val : $key;
389 2
            if (!isset($tests[$rule])) {
390 1
                throw new Exception("Validation of form element failed, no such validation rule exists: $rule");
391
            }
392 1
            $arg = is_numeric($key) ? null : $val;
393
394 1
            $test = ($rule == 'custom_test') ? $arg : $tests[$rule];
395 1
            $status = null;
396 1
            if (is_callable($test['test'])) {
397 1
                $status = $test['test']($value);
398
            } else {
399
                $status = eval($test['test']);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
400
            }
401
402 1
            if ($status === false) {
403
                $messages[] = $test['message'];
404 1
                $pass = false;
405
            }
406
        }
407
408 1
        if (!empty($messages)) {
409
            $this['validation-messages'] = $messages;
410
        }
411 1
        return $pass;
412
    }
413
414
415
416
    /**
417
     * Use the element name as label if label is not set.
418
     *
419
     * @param string $append a colon as default to the end of the label.
420
     *
421
     * @return void
422
     */
423 19
    public function useNameAsDefaultLabel($append = ':')
424
    {
425 19
        if (!isset($this['label'])) {
426 19
            $this['label'] = ucfirst(strtolower(str_replace(array('-','_'), ' ', $this['name']))).$append;
427
        }
428 19
    }
429
430
431
432
    /**
433
     * Use the element name as value if value is not set.
434
     *
435
     * @return void
436
     */
437 3
    public function useNameAsDefaultValue()
438
    {
439 3
        if (!isset($this['value'])) {
440 3
            $this['value'] = ucfirst(strtolower(str_replace(array('-','_'), ' ', $this['name'])));
441
        }
442 3
    }
443
444
445
446
    /**
447
     * Get the value of the form element.
448
     *
449
     * @deprecated
450
     *
451
     * @return mixed the value of the form element.
452
     */
453 6
    public function getValue()
454
    {
455 6
        return $this['value'];
456
    }
457
458
459
460
    /**
461
     * Get the escaped value of the form element.
462
     *
463
     * @return mixed the value of the form element.
464
     */
465 5
    public function getEscapedValue()
466
    {
467 5
        return htmlentities($this['value']);
468
    }
469
470
471
472
    /**
473
     * Get the unescaped value of the form element.
474
     *
475
     * @return mixed the value of the form element.
476
     */
477 5
    public function getRawValue()
478
    {
479 5
        return $this['value'];
480
    }
481
482
483
484
    /**
485
     * Get the value of the form element and respect configuration
486
     * details whether it should be raw or escaped.
487
     *
488
     * @return mixed the value of the form element.
489
     */
490
    public function value()
491
    {
492
        $escape = isset($this->default["escape-values"])
493
            ? $this->default["escape-values"]
494
            : true;
495
496
        return $escape
497
            ? $this->getEscapedValue()
498
            : $this->getRawValue();
499
    }
500
501
502
503
    /**
504
     * Get the escaped value of the form element and respect configuration
505
     * details whether it should be raw or escaped.
506
     *
507
     * @return mixed the value of the form element.
508
     */
509 1
    public function escValue()
510
    {
511 1
        return $this->getEscapedValue();
512
    }
513
514
515
516
    /**
517
     * Get the raw value of the form element.
518
     *
519
     * @return mixed the value of the form element.
520
     */
521 1
    public function rawValue()
522
    {
523 1
        return $this->getRawValue();
524
    }
525
526
527
528
    /**
529
     * Set the value for the element.
530
     *
531
     * @param mixed $value set this to be the value of the formelement.
532
     *
533
     * @return mixed the value of the form element.
534
     */
535 1
    public function setValue($value)
536
    {
537 1
        return $this['value'] = $value;
538
    }
539
540
541
542
    /**
543
     * Get the status of the form element if it is checked or not.
544
     *
545
     * @return mixed the value of the form element.
546
     */
547
    public function checked()
548
    {
549
        return $this['checked'];
550
    }
551
552
553
554
    /**
555
     * Check if the element is a button.
556
     *
557
     * @return boolean true or false.
558
     */
559
    public function isButton()
560
    {
561
        return in_array($this["type"], ["submit", "reset", "button"]);
562
    }
563
}
564