Completed
Pull Request — master (#14)
by Martin
02:45
created

FormElement   F

Complexity

Total Complexity 81

Size/Duplication

Total Lines 558
Duplicated Lines 0 %

Test Coverage

Coverage 77.68%

Importance

Changes 0
Metric Value
eloc 227
dl 0
loc 558
ccs 174
cts 224
cp 0.7768
rs 2
c 0
b 0
f 0
wmc 81

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A offsetGet() 0 3 2
A offsetSet() 0 6 2
A getElementId() 0 3 2
A offsetUnset() 0 3 1
A offsetExists() 0 3 1
A getValidationMessages() 0 11 3
A setDefault() 0 3 1
A value() 0 9 3
B validate() 0 81 9
A useNameAsDefaultLabel() 0 4 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 setValue() 0 3 1
F getHTMLDetails() 0 172 44
A escValue() 0 3 1
A getRawValue() 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 27
    public function __construct($name, $attributes = [])
30
    {
31 27
        $this->attributes = $attributes;
32 27
        $this['name'] = $name;
33
        //$this['key'] = $name;
34
        //$this['name'] = isset($this['name']) ? $this['name'] : $name;
35
36 27
        $this->characterEncoding = 'UTF-8';
37 27
        $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 27
        $this->default["br-after-label"] = true;
39 27
        $this->default["escape-values"] = true;
40 27
    }
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 5
    public function setDefault($options)
52
    {
53 5
        $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 5
    }
55
56
57
58
    /**
59
     * Implementing ArrayAccess for this->attributes
60
     *
61
     * @return void
62
     */
63 27
    public function offsetSet($offset, $value)
64
    {
65 27
        if (is_null($offset)) {
66
            $this->attributes[] = $value;
67
        } else {
68 27
            $this->attributes[$offset] = $value;
69
        }
70 27
    }
71
72
73
74
    /**
75
     * Implementing ArrayAccess for this->attributes
76
     */
77 27
    public function offsetExists($offset)
78
    {
79 27
        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 26
    public function offsetGet($offset)
98
    {
99 26
        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 10
    public function getElementId()
110
    {
111 10
        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 10
    public function getValidationMessages()
122
    {
123 10
        $messages = null;
124 10
        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 10
        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 10
    public function getHTMLDetails()
142
    {
143
        // Add disabled to be able to disable a form element
144
        // Add maxlength
145 10
        $id =  $this->getElementId();
146
147 10
        $class = isset($this['class'])
148
            ? "{$this['class']}"
149 10
            : null;
150
151 10
        $validates = (isset($this['validation-pass']) && $this['validation-pass'] === false)
152
            ? ' validation-failed'
153 10
            : null;
154
155 10
        $class = (isset($class) || isset($validates))
156
            ? " class='{$class}{$validates}'"
157 10
            : null;
158
159 10
        $wrapperElement = isset($this['wrapper-element'])
160
            ? $this['wrapper-element']
161 10
            : $this->default["wrapper-element"];
162
163 10
        $wrapperClass = isset($this['wrapper-class'])
164
            ? " class=\"{$this['wrapper-class']}\""
165 10
            : null;
166
167 10
        $brAfterLabel = isset($this['br-after-label'])
168
            ? $this['br-after-label']
169 10
            : $this->default["br-after-label"];
170
171 10
        $brAfterLabel = $brAfterLabel
172 10
            ? "<br>"
173 10
            : null;
174
175 10
        $name = " name='{$this['name']}'";
176
177 10
        $label = isset($this['label'])
178 6
            ? ($this['label'] . (isset($this['required']) && $this['required']
179
                ? "<span class='form-element-required'>*</span>"
180 6
                : null))
181 10
            : null;
182
183 10
        $autofocus = isset($this['autofocus']) && $this['autofocus']
184
            ? " autofocus='autofocus'"
185 10
            : null;
186
187 10
        $required = isset($this['required']) && $this['required']
188
            ? " required='required'"
189 10
            : null;
190
191 10
        $readonly = isset($this['readonly']) && $this['readonly']
192
            ? " readonly='readonly'"
193 10
            : null;
194
195 10
        $placeholder = isset($this['placeholder']) && $this['placeholder']
196
            ? " placeholder='{$this['placeholder']}'"
197 10
            : null;
198
199 10
        $multiple = isset($this['multiple']) && $this['multiple']
200
            ? " multiple"
201 10
            : null;
202
203 10
        $max = isset($this['max'])
204
            ? " max='{$this['max']}'"
205 10
            : null;
206
207 10
        $min = isset($this['min'])
208
            ? " min='{$this['min']}'"
209 10
            : null;
210
211 10
        $low = isset($this['low'])
212
            ? " low='{$this['low']}'"
213 10
            : null;
214
215 10
        $high = isset($this['high'])
216
            ? " high='{$this['high']}'"
217 10
            : null;
218
219 10
        $optimum = isset($this['optimum'])
220
            ? " optimum='{$this['optimum']}'"
221 10
            : null;
222
223 10
        $step = isset($this['step'])
224
            ? " step='{$this['step']}'"
225 10
            : null;
226
227 10
        $size = isset($this['size'])
228
            ? " size='{$this['size']}'"
229 10
            : null;
230
231 10
        $text = isset($this['text'])
232
            ? $this->default["escape-values"]
233
                ? htmlentities($this['text'], ENT_QUOTES, $this->characterEncoding)
234
                : $this['text']
235 10
            : null;
236
237 10
        $checked = isset($this['checked']) && $this['checked']
238
            ? " checked='checked'"
239 10
            : null;
240
241 10
        $type = isset($this['type'])
242 10
            ? " type='{$this['type']}'"
243 10
            : null;
244
245 10
        $title = isset($this['title'])
246
            ? " title='{$this['title']}'"
247 10
            : null;
248
249 10
        $pattern = isset($this['pattern'])
250
            ? " pattern='{$this['pattern']}'"
251 10
            : null;
252
253 10
        $description = isset($this['description'])
254
            ? $this['description']
255 10
            : null;
256
257 10
        $novalidate = isset($this['formnovalidate'])
258 1
            ? " formnovalidate='formnovalidate'"
259 10
            : null;
260
261 10
        $onlyValue = isset($this['value'])
262 3
            ? $this->default["escape-values"]
263 3
                ? htmlentities($this['value'], ENT_QUOTES, $this->characterEncoding)
264 3
                : $this['value']
265 10
            : null;
266
267 10
        $value = isset($this['value'])
268 3
            ? " value='{$onlyValue}'"
269 10
            : null;
270
271 10
        $onclick = isset($this['onclick'])
272
            ? " onclick=\"{$this['onclick']}\""
273 10
            : null;
274
275 10
        $maxlength = isset($this['maxlength'])
276
            ? " maxlength='{$this['maxlength']}'"
277 10
            : null;
278
279 10
        $messages = $this->getValidationMessages();
280
281
        return [
282 10
            'id'             => $id,
283 10
            'class'          => $class,
284 10
            'wrapperElement' => $wrapperElement,
285 10
            'wrapperClass'   => $wrapperClass,
286 10
            'brAfterLabel'   => $brAfterLabel,
287 10
            'name'           => $name,
288 10
            'label'          => $label,
289 10
            'autofocus'      => $autofocus,
290 10
            'required'       => $required,
291 10
            'readonly'       => $readonly,
292 10
            'placeholder'    => $placeholder,
293 10
            'multiple'       => $multiple,
294 10
            'min'            => $min,
295 10
            'max'            => $max,
296 10
            'maxlength'      => $maxlength,
297 10
            'low'            => $low,
298 10
            'high'           => $high,
299 10
            'step'           => $step,
300 10
            'optimum'        => $optimum,
301 10
            'size'           => $size,
302 10
            'text'           => $text,
303 10
            'checked'        => $checked,
304 10
            'type'           => $type,
305 10
            'title'          => $title,
306 10
            'pattern'        => $pattern,
307 10
            'description'    => $description,
308 10
            'novalidate'     => $novalidate,
309 10
            'onlyValue'      => $onlyValue,
310 10
            'value'          => $value,
311 10
            'onclick'        => $onclick,
312 10
            'messages'       => $messages,
313
        ];
314
    }
315
316
317
318
    /**
319
     * Get HTML code for a element, must be implemented by each subclass.
320
     *
321
     * @return HTML code for the element.
322
     */
323
    abstract public function getHTML();
324
325
326
327
    /**
328
     * Validate the form element value according a ruleset.
329
     *
330
     * @param array     $rules validation rules.
331
     * @param Form|null $form  the parent form.
332
     *
333
     * @return boolean true if all rules pass, else false.
334
     */
335 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

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