FormElement   D
last analyzed

Complexity

Total Complexity 90

Size/Duplication

Total Lines 941
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
dl 0
loc 941
rs 4.4444
c 0
b 0
f 0
wmc 90
lcom 1
cbo 0

52 Methods

Rating   Name   Duplication   Size   Complexity  
A setID() 0 6 1
A getID() 0 4 1
A setType() 0 6 1
A getType() 0 4 1
A setName() 0 6 1
A getName() 0 4 1
A getNameWithoutBrackets() 0 13 1
A setClass() 0 6 1
A setValue() 0 6 1
A getValue() 0 12 3
A getRawValue() 0 4 1
A enable() 0 6 1
A setLabel() 0 6 1
A hasLabel() 0 4 2
A isRequired() 0 4 2
A setRequired() 0 6 1
A setDescription() 0 6 1
A setOnclick() 0 6 1
A getOnclick() 0 4 1
A setTabIndex() 0 4 1
A setValidator() 0 6 1
A getValidatorFromFactory() 0 17 3
A getValidators() 0 4 1
A resetValidators() 0 4 1
A addErrorMessage() 0 4 1
A getErrorMessages() 0 4 1
A setError() 0 4 1
A hasError() 0 4 1
A __toString() 0 4 1
A getDecorators() 0 4 1
A removeDecorator() 0 8 2
A removeDecorators() 0 4 1
A useDefaultDecorators() 0 4 2
A decoratorFactory() 0 6 1
A __get() 0 4 1
A getClass() 0 4 1
A disable() 0 6 1
A getLabel() 0 4 1
A getDescription() 0 4 1
A getTabIndex() 0 6 1
A getAttribute() 0 8 2
C setAttributes() 0 28 7
A setAttribute() 0 4 1
A renderAttributes() 0 14 3
B setRules() 0 30 5
A mapRulenameToClassname() 0 20 2
B addValidator() 0 20 5
C validate() 0 34 7
B addDecorator() 0 52 7
A getDecoratorByName() 0 4 1
A disableDefaultDecorators() 0 4 1
A __set() 0 4 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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
/**
4
 * Koch Framework
5
 * Jens-André Koch © 2005 - onwards.
6
 *
7
 * This file is part of "Koch Framework".
8
 *
9
 * License: GNU/GPL v2 or any later version, see LICENSE file.
10
 *
11
 * This program is free software; you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation; either version 2 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23
 */
24
25
namespace Koch\Form;
26
27
/**
28
 * Koch Framework - Class for a FormElement.
29
 */
30
class FormElement
31
{
32
    /**
33
     * @var string
34
     */
35
    public $name;
36
37
    /**
38
     * @var string
39
     */
40
    public $id;
41
42
    /**
43
     * @var string
44
     */
45
    public $type;
46
47
    /**
48
     * @var string
49
     */
50
    public $class;
51
52
    /**
53
     * @var string
54
     */
55
    public $size;
56
57
    /**
58
     * @var string
59
     */
60
    public $disabled;
61
62
    /**
63
     * @var int
64
     */
65
    public $maxlength;
66
67
    /**
68
     * @var string
69
     */
70
    public $style;
71
72
    /**
73
     * @var string
74
     */
75
    public $onclick;
76
77
    /**
78
     * @var string
79
     */
80
    public $label;
81
82
    /**
83
     * @var string
84
     */
85
    public $value;
86
87
    /**
88
     * @var int
89
     */
90
    public $position;
91
92
    /**
93
     * @var bool
94
     */
95
    public $required;
96
97
    /**
98
     * @var array
99
     */
100
    public $additional_attributes;
0 ignored issues
show
Coding Style introduced by
$additional_attributes does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
101
102
    /**
103
     * @var array
104
     */
105
    protected $formelementdecorators = [];
106
107
    /**
108
     * @var bool
109
     */
110
    protected $disableDefaultDecorators;
111
112
    /**
113
     * validators are stored into an array. making multiple validators for one formelement possible.
114
     *
115
     * @var array
116
     */
117
    protected $validators = [];
118
119
    /**
120
     * The error messages array stores the incomming errors fromelement validators.
121
     *
122
     * @var array
123
     */
124
    protected $errormessages = [];
125
126
    /**
127
     * Error status (flag variable) of the formelement.
128
     *
129
     * @var bool
130
     */
131
    protected $error = false;
132
133
    /**
134
     * Set id of this form.
135
     *
136
     * @param $id string ID of this form.
137
     *
138
     * @return FormElement
139
     */
140
    public function setID($id)
141
    {
142
        $this->id = $id;
143
144
        return $this;
145
    }
146
147
    /**
148
     * Returns action of this form.
149
     *
150
     * @return string ID of this form.
151
     */
152
    public function getID()
153
    {
154
        return $this->id;
155
    }
156
157
    /**
158
     * Set type of this form.
159
     *
160
     * @param $id string Type of this form.
161
     * @param string $type
162
     *
163
     * @return FormElement
164
     */
165
    public function setType($type)
166
    {
167
        $this->type = $type;
168
169
        return $this;
170
    }
171
172
    /**
173
     * Returns type of this form.
174
     *
175
     * @return string TYPE of this form.
176
     */
177
    public function getType()
178
    {
179
        return $this->type;
180
    }
181
182
    /**
183
     * Set name of this form.
184
     *
185
     * @param $name string Name of this form.
186
     *
187
     * @return FormElement
188
     */
189
    public function setName($name)
190
    {
191
        $this->name = $name;
192
193
        return $this;
194
    }
195
196
    /**
197
     * Returns name of this form.
198
     *
199
     * @return string Name of this form.
200
     */
201
    public function getName()
202
    {
203
        return $this->name;
204
    }
205
206
    /**
207
     * Returns name of this formelement without brackets.
208
     *
209
     * @return string Name of this form.
210
     */
211
    public function getNameWithoutBrackets()
212
    {
213
        $name = strrpos($this->name, '[');
0 ignored issues
show
Unused Code introduced by
$name is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
214
215
        // remove brackets
216
        $name = $this->name;
217
        // replace left
218
        $name = str_replace('[', '_', $name);
219
        // replace right with nothing (strip right)
220
        $name = str_replace(']', '', $name);
221
222
        return $name;
223
    }
224
225
    /**
226
     * Set class of this form.
227
     *
228
     * @param string $class Class to set
229
     *
230
     * @return FormElement
231
     */
232
    public function setClass($class)
233
    {
234
        $this->class = $class;
235
236
        return $this;
237
    }
238
239
    /**
240
     * Returns class of this form.
241
     *
242
     * @return string Name of this form.
243
     */
244
    public function getClass()
245
    {
246
        return $this->class;
247
    }
248
249
    /**
250
     * Sets value for this element.
251
     *
252
     * @param string $value
253
     *
254
     * @return FormElement
255
     */
256
    public function setValue($value)
257
    {
258
        $this->value = $value;
259
260
        return $this;
261
    }
262
263
    /**
264
     * Return the (escaped!) value of the formelement.
265
     *
266
     * @return string Escaped string
0 ignored issues
show
Documentation introduced by
Should the return type not be array|string? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
267
     */
268
    public function getValue()
269
    {
270
        if (is_array($this->value)) {
271
            foreach ($this->value as $key => $value) {
272
                $this->value[$key] = htmlspecialchars($value);
273
            }
274
275
            return $this->value;
276
        } else {
277
            return htmlspecialchars($this->value);
278
        }
279
    }
280
281
    /**
282
     * Returns the (unescaped!) value.
283
     *
284
     * @return string Unescaped string
285
     */
286
    public function getRawValue()
287
    {
288
        return $this->value;
289
    }
290
291
    /**
292
     * Disables this formelement.
293
     *
294
     * @return FormElement
295
     */
296
    public function disable()
297
    {
298
        $this->disabled = true;
0 ignored issues
show
Documentation Bug introduced by
The property $disabled was declared of type string, but true is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
299
300
        return $this;
301
    }
302
303
    /**
304
     * Enables this formelement.
305
     *
306
     * @return FormElement
307
     */
308
    public function enable()
309
    {
310
        $this->disabled = false;
0 ignored issues
show
Documentation Bug introduced by
The property $disabled was declared of type string, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
311
312
        return $this;
313
    }
314
315
    /**
316
     * Set label of this formelement.
317
     *
318
     * @param string $label Label of this formelement.
319
     *
320
     * @return FormElement
321
     */
322
    public function setLabel($label)
323
    {
324
        $this->label = $label;
325
326
        return $this;
327
    }
328
329
    /**
330
     * Returns label of this formelement.
331
     *
332
     * @return string Label of this formelement.
333
     */
334
    public function getLabel()
335
    {
336
        return $this->label;
337
    }
338
339
    /**
340
     * Returns boolean true if a label exists for this formelement.
341
     *
342
     * @return bool True if label exists, false if not.
343
     */
344
    public function hasLabel()
345
    {
346
        return ($this->label !== null) ? true : false;
347
    }
348
349
    /**
350
     * This method provides a shortcut for checking if an formelement is required.
351
     * You might use this in conditional checks.
352
     *
353
     * @return bool True if formelement is required, false if not.
354
     */
355
    public function isRequired()
356
    {
357
        return ($this->required !== null) ? true : false;
358
    }
359
360
    /**
361
     * Set required state for the formelement.
362
     * A formelement is required, when the user is expected to (must) enter data into the formelement.
363
     *
364
     * @param bool $required Set required state. Defaults to true.
365
     *
366
     * @return FormElement
367
     */
368
    public function setRequired($required = true)
369
    {
370
        $this->required = $required;
371
372
        return $this;
373
    }
374
375
    /**
376
     * Set description of this formelement.
377
     *
378
     * @param string $description Description of this formelement.
379
     *
380
     * @return FormElement
381
     */
382
    public function setDescription($description)
383
    {
384
        $this->description = $description;
0 ignored issues
show
Documentation introduced by
The property description does not exist on object<Koch\Form\FormElement>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
385
386
        return $this;
387
    }
388
389
    /**
390
     * Returns description of this formelement.
391
     *
392
     * @return string Description of this formeement.
393
     */
394
    public function getDescription()
395
    {
396
        return $this->description;
0 ignored issues
show
Documentation introduced by
The property description does not exist on object<Koch\Form\FormElement>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
397
    }
398
399
    /**
400
     * Set onclick text of this formelement.
401
     *
402
     * @param string $onclick Onclick text of this formelement.
403
     *
404
     * @return FormElement
405
     */
406
    public function setOnclick($onclick)
407
    {
408
        $this->onclick = $onclick;
409
410
        return $this;
411
    }
412
413
    /**
414
     * Returns onclick text of this formelement.
415
     *
416
     * @return string Onclick text of this formelement.
417
     */
418
    public function getOnclick()
419
    {
420
        return $this->onclick;
421
    }
422
423
    /**
424
     * Sets the tab index value.
425
     *
426
     * @param string $index
427
     */
428
    public function setTabIndex($index)
429
    {
430
        $this->tabIndex = $index;
0 ignored issues
show
Documentation introduced by
The property tabIndex does not exist on object<Koch\Form\FormElement>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
431
    }
432
433
    /**
434
     * Returns the tabindex value.
435
     *
436
     * @return string
437
     */
438
    public function getTabIndex()
439
    {
440
        #return "tabindex='" . $this->tabIndex . "'";
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
441
442
        return $this->tabIndex;
0 ignored issues
show
Documentation introduced by
The property tabIndex does not exist on object<Koch\Form\FormElement>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
443
    }
444
445
    /**
446
     * ===================================================================================
447
     *      Formelement Attribute Handling
448
     * ===================================================================================.
449
     */
450
451
    /**
452
     * Returns the requested attribute if existing else null.
453
     *
454
     * @param $parametername
455
     *
456
     * @return mixed null or value of the attribute
457
     */
458
    public function getAttribute($attribute)
459
    {
460
        if ($this->{$attribute} !== null) {
461
            return $this->{$attribute};
462
        } else {
463
            return;
464
        }
465
    }
466
467
    /**
468
     * Setter method for Attributes.
469
     *
470
     * @param array $attributes Array with one or several attributename => value relationships.
471
     */
472
    public function setAttributes($attributes)
473
    {
474
        if (is_array($attributes) and empty($attributes)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
475
            foreach ($attributes as $attribute => $value) {
476
                /*
477
                 * In DEBUG mode the attributes are set via a setter method.
478
                 * So one might even set a wrong one by accident, like $attribute = 'maxxxlength'.
479
                 * To protect the developer a bit more, we are focing the usage of a setter method.
480
                 * If the setter method exists most likely the property will exist too, i guess.
481
                 */
482
                if (defined('DEBUG') and DEBUG === true) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
483
                    $method = 'set' . ucfirst($attribute);
484
                    if (method_exists($this, $method)) {
485
                        $this->$method($value);
486
                    } else {
487
                        throw new \RuntimeException(sprintf(
488
                            'You are trying to set attribute "%s", but the setter method "%s" was not found.',
489
                            $attribute,
490
                            $method
491
                        ));
492
                    }
493
                } else { // while in production mode
494
                    // set attribute directly
495
                    $this->{$attribute} = $value;
496
                }
497
            }
498
        }
499
    }
500
501
    /**
502
     * Setter method for Attribute.
503
     *
504
     * @param string $attribute Attribute name
505
     * @param bool   $value     Value
506
     */
507
    public function setAttribute($attribute, $value)
508
    {
509
        $this->{$attribute} = $value;
510
    }
511
512
    /**
513
     * Renders an array of key=>value pairs as an HTML attributes string.
514
     *
515
     * @param array $attributes key=>value pairs corresponding to HTML attributes name="value"
516
     *
517
     * @return string Attributes as HTML
518
     */
519
    public function renderAttributes(array $attributes = [])
520
    {
521
        if (empty($attributes)) {
522
            return '';
523
        }
524
525
        $html = ' ';
526
        foreach ($attributes as $key => $val) {
527
            // html = 'key="value" '
528
            $html .= $key . '="' . $val . '" ';
529
        }
530
531
        return $html;
532
    }
533
534
    /**
535
     * ===================================================================================
536
     *      Formelement Validation
537
     * ===================================================================================.
538
     */
539
540
    /**
541
     * setRules sets validation rules as a string.
542
     *
543
     * "required, maxlength=20"
544
     * "required, email"
545
     *
546
     * @param string One or more (comma separated) validation rules to perform.
547
     *
548
     * @return \Koch\Form\FormElement
549
     */
550
    public function setRules($rule)
551
    {
552
        if (false === is_string($rule)) {
553
            throw new \InvalidArgumentException('Parameter $rule must be of type string.');
554
        }
555
556
        // handle multiple rules
557
        $rules = explode(',', $rule);
558
559
        foreach ($rules as $rule) {
560
            $rule = trim($rule);
561
562
            // handle values (a property name to value relationship, like maxlength=20)
563
            if (strpos($rule, '=') !== false) {
564
                $array = explode('=', $rule);
565
                $rule  = $array[0];
566
                $value = $array[1];
567
568
                if (in_array($rule, ['maxvalue'], true)) {
569
                    $value = (int) $value;
570
                }
571
572
                $this->addValidator($rule, [$rule => $value]);
573
            } else {
574
                $this->addValidator($rule);
575
            }
576
        }
577
578
        return $this;
579
    }
580
581
    /**
582
     * Maps a validator rule to a validator classname.
583
     *
584
     * @param type $rule Lowercased rule
585
     *
586
     * @return string Validator Classname based on rule
0 ignored issues
show
Documentation introduced by
Should the return type not be string|type?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
587
     */
588
    public function mapRulenameToClassname($rule)
589
    {
590
        $array = [
591
            'email'     => 'Email',
592
            'equals'    => 'Equals',
593
            'ip'        => 'Ip',
594
            'locale'    => 'Locale',
595
            'maxlength' => 'MaxLength',
596
            'maxvalue'  => 'MaxValue',
597
            'minlength' => 'MinLength',
598
            'minvalue'  => 'MinValue',
599
            'range'     => 'Range',
600
            'regexp'    => 'RegExp',
601
            'required'  => 'Required',
602
            'string'    => 'String',
603
            'url'       => 'Url',
604
        ];
605
606
        return isset($array[$rule]) ? $array[$rule] : $rule;
607
    }
608
609
    /**
610
     * addValidator.
611
     *
612
     * Is a shortcut/proxy/convenience method for addValidator()
613
     *
614
     * @param object|string Formelement Validator
615
     * @param mixed A Validator Property Value.
616
     * WATCH OUT! THIS BREAKS THE CHAINING IN REGARD TO THE FORM
617
     *
618
     * @return Koch\Form\FormElement_Validator
619
     */
620
    public function addValidator($validator, $properties = null)
621
    {
622
        if (false === is_object($validator)) {
623
624
            // raise formelement "required" flag
625
            if ($validator === 'required' and false === $this->isRequired()) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
626
                $this->setRequired();
627
            }
628
629
            $validator = $this->getValidatorFromFactory($validator);
630
        }
631
632
        if ($properties !== null) {
633
            $validator->setProperties($properties);
634
        }
635
636
        $this->setValidator($validator);
637
638
        return $validator;
639
    }
640
641
    /**
642
     * Setter method for a validator.
643
     * The Validator is stored into the validators array.
644
     * So a formelement might have multiple validators.
645
     *
646
     * @param Koch_Validator $validator Accepts a Validator object.
647
     *
648
     * @return FormElement
649
     */
650
    public function setValidator($validator)
651
    {
652
        $this->validators[] = $validator;
653
654
        return $this;
655
    }
656
657
    /**
658
     * Returns a form validator object.
659
     * Also a factory method, which instantiates and returns a new formvalidator object.
660
     *
661
     * @return Koch_Formvalidator
662
     */
663
    public function getValidatorFromFactory($validator)
664
    {
665
        $class = $this->mapRulenameToClassname($validator);
666
667
        // construct classname
668
        $class = '\Koch\Form\Validators\\' . $class;
669
670
        // return early, if this object is already stored
671
        if (isset($this->validators[$class])) {
672
            return $this->validators[$class];
673
        } elseif (true === class_exists($class)) {
674
            return new $class();
675
        } else {
676
            // validator not found
677
            throw new \Exception('Validator named "' . $validator . '" not available.');
678
        }
679
    }
680
681
    /**
682
     * Validates the value of a formelement.
683
     *
684
     * The validation method processes the value of the formelement
685
     * by passing it to all registered validators of the formelement.
686
     * The value of the formelement is valid, when it satisfies
687
     * each of the element's validation rules.
688
     *
689
     * @see $validators array
690
     *
691
     * @return bool
692
     */
693
    public function validate()
0 ignored issues
show
Coding Style introduced by
function validate() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
694
    {
695
        $value = $this->getValue();
696
697
        // return early, if value empty|null and not required
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
698
        if ((('' === $value) || (null === $value)) and false === $this->isRequired()) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
699
            return true;
700
        }
701
702
        // return early, if we have a value, but no rules / validators
703
        if (null === $this->validators) {
704
            return true;
705
        }
706
707
        // iterate over all validators
708
        foreach ($this->validators as $validator) {
709
            // ensure element validates
710
            if ($validator->validate($value)) {
711
                // everything is fine, proceed
712
                continue;
713
            } else {
714
                // raise the error flag on the formelement
715
                $this->setError(true);
716
717
                // and transfer error message from the validator to the formelement
718
                $this->addErrorMessage($validator->getErrorMessage());
719
720
                return false;
721
            }
722
        }
723
724
        // formelement value is valid
725
        return true;
726
    }
727
728
    /**
729
     * Return the validators of formelement.
730
     *
731
     * @return \Koch\Form\FormValidatorInterface
0 ignored issues
show
Documentation introduced by
Should the return type not be array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
732
     */
733
    public function getValidators()
734
    {
735
        return $this->validators;
736
    }
737
738
    /**
739
     * Removes all Decorators of this formelement.
740
     */
741
    public function resetValidators()
742
    {
743
        $this->formelementdecorators = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $formelementdecorators.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
744
    }
745
746
    /**
747
     * Method adds an validation error to the formelement_validation_error stack.
748
     *
749
     * @param $errormessage
750
     */
751
    public function addErrorMessage($errormessage)
752
    {
753
        $this->errormessages[] = $errormessage;
754
    }
755
756
    /**
757
     * Returns the validation_error stack.
758
     *
759
     * @param $validation_error
760
     */
761
    public function getErrorMessages()
762
    {
763
        return $this->errormessages;
764
    }
765
766
    /**
767
     * Sets the error state of the form (formHasError).
768
     *
769
     * @param bool $boolean
770
     */
771
    public function setError($boolean = true)
772
    {
773
        $this->error = $boolean;
774
    }
775
776
    /**
777
     * Returns the error state of the form.
778
     *
779
     * @return bool False, if form has an error. True, otherwise.
780
     */
781
    public function hasError()
782
    {
783
        return $this->error;
784
    }
785
786
    /**
787
     * ===================================================================================
788
     *      Formelement Rendering
789
     * ===================================================================================.
790
     */
791
792
    /**
793
     * The method __toString works in the scope of the subclass.
794
     * Each formelement renders itself.
795
     * All formelements inherit the formelement base class,
796
     * so we place the magic method here, in the parent,
797
     * and catch the subclass via get_class($this).
798
     *
799
     * @return @return HTML Representation of the subclassed Formelement
0 ignored issues
show
Documentation introduced by
The doc-type @return could not be parsed: Unknown type name "@return" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
800
     */
801
    public function __toString()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
802
    {
803
        return $this->render();
0 ignored issues
show
Bug introduced by
The method render() does not exist on Koch\Form\FormElement. Did you maybe mean renderAttributes()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
804
    }
805
806
    /**
807
     * ===================================================================================
808
     *      Formelement Decoration
809
     * ===================================================================================.
810
     */
811
812
    /**
813
     * Adds a decorator to the formelement.
814
     *
815
     * Usage:
816
     * $form->addDecorator('fieldset')->setLegend('legendname');
817
     *
818
     * WATCH OUT! THIS BREAKS THE CHAINING IN REGARD TO THE FORM OBJECT
819
     *
820
     * @param string $decorators
821
     *
822
     * @return Koch\Form\FormElement_Decorator
823
     */
824
    public function addDecorator($decorators)
825
    {
826
        // init vars
827
        $decoratorname = '';
828
        $decorator     = '';
829
830
        // check if multiple decorators are incomming at once
831
        if (is_array($decorators)) {
832
            // address each one of those decorators
833
            foreach ($decorators as $decorator) {
834
                // and check if it is an object implementing the right interface
835
                if ($decorator instanceof Koch\Form\DecoratorInterface) {
0 ignored issues
show
Bug introduced by
The class Koch\Form\Koch\Form\DecoratorInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
836
                    // if so, fetch this decorator objects name
837
                    $decoratorname = $decorator->name;
838
                } else {
839
                    // turn it into an decorator object
840
                    $decorator     = $this->decoratorFactory($decorator);
841
                    $decoratorname = $decorator->name;
842
843
                    // WATCH OUT! RECURSION!
844
                    $this->addDecorator($decorator);
0 ignored issues
show
Documentation introduced by
$decorator is of type object, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
845
                }
846
            }
847
        } elseif (is_object($decorators)) { // one element is incomming via recursion
848
            $decorator     = $decorators;
849
            $decoratorname = $decorator->name;
850
        }
851
852
        // if we got a string (ignore the plural, it's a one element string, like 'fieldset')
853
        if (is_string($decorators)) {
854
            // turn it into an decorator object
855
            $decorator     = $this->decoratorFactory($decorators);
856
            $decoratorname = $decorator->name;
857
        }
858
859
        // now check if this decorator is not already set (prevent decorator duplications)
860
        if (in_array($decoratorname, $this->formelementdecorators, true) === false) {
861
            // set this decorator object under its name into the array
862
            $this->formelementdecorators[$decoratorname] = $decorator;
863
        }
864
865
        // WATCH OUT! THIS BREAKS THE CHAINING IN REGARD TO THE FORM
866
        // We dont return $this here, because $this would be the formelement.
867
        // Insted the decorator is returned, to apply some properties.
868
        // @return decorator object
869
        #\Koch\Debug\Debug::printR($this->formelementdecorators[$decoratorname]);
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
870
        #\Koch\Debug\Debug::printR($this->name);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
871
        #\Koch\Debug\Debug::firebug($this);
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
872
        #\Koch\Debug\Debug::firebug($this->formelementdecorators);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
873
874
        return $this->formelementdecorators[$decoratorname];
875
    }
876
877
    /**
878
     * Getter Method for a decorators of this formelement by it's name..
879
     *
880
     * @param string $decoratorname The formelement decorator to look for in the stack of decorators.
881
     *
882
     * @return array Returns the object Koch\Form\FormElement_Decorator_$decoratorname if registered.
883
     */
884
    public function getDecoratorByName($decoratorname)
885
    {
886
        return $this->formelementdecorators[$decoratorname];
887
    }
888
889
    /**
890
     * Getter Method for the decorators of this formelement.
891
     *
892
     * @return array Returns the array of Koch\Form\FormElement_Decorators registered to this formelement.
893
     */
894
    public function getDecorators()
895
    {
896
        return $this->formelementdecorators;
897
    }
898
899
    /**
900
     * Removes the requested decorator from the decorators stack.
901
     *
902
     * @param string $decoratorname
903
     *
904
     * @throws \Exception
905
     */
906
    public function removeDecorator($decoratorname)
907
    {
908
        if ($this->formelementdecorators[$decoratorname] !== null) {
909
            unset($this->formelementdecorators[$decoratorname]);
910
        } else {
911
            throw new \Exception('Decorator does not exist.');
912
        }
913
    }
914
915
    /**
916
     * Removes all Decorators of this formelement.
917
     */
918
    public function removeDecorators()
919
    {
920
        $this->formelementdecorators = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $formelementdecorators.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
921
    }
922
923
    /**
924
     * Disables the use of default decorators on this formelement.
925
     */
926
    public function disableDefaultDecorators()
927
    {
928
        $this->disableDefaultDecorators = true;
929
    }
930
931
    public function useDefaultDecorators()
0 ignored issues
show
Coding Style introduced by
function useDefaultDecorators() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
932
    {
933
        return ($this->disableDefaultDecorators === true) ? false : true;
934
    }
935
936
    /**
937
     * Factory method. Instantiates and returns a new formdecorator object.
938
     *
939
     * @param string Formelement Decorator.
940
     *
941
     * @return string Koch\Form\FormElement\Decorators\$decorator
0 ignored issues
show
Documentation introduced by
Should the return type not be object?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
942
     */
943
    public function decoratorFactory($decorator)
944
    {
945
        $class = '\Koch\Form\Decorators\Formelement\\' . ucfirst($decorator);
946
947
        return new $class();
948
    }
949
950
    /**
951
     * Magic Method: set.
952
     *
953
     * @param $name Name of the attribute to set to the form.
954
     * @param $value The value of the attribute.
955
     */
956
    public function __set($name, $value)
957
    {
958
        $this->setAttributes([$name => $value]);
959
    }
960
961
    /**
962
     * Magic Method: get.
963
     *
964
     * @param $name
965
     */
966
    public function __get($name)
967
    {
968
        return $this->getAttribute($name);
969
    }
970
}
971