x Sorry, these patches are not available anymore due to data migration. Please run a fresh inspection.
Passed
Push — main ( 4e0970...2bf451 )
by Stefan
09:05
created

FormElement   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 382
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 99
c 2
b 0
f 0
dl 0
loc 382
rs 6.96
wmc 53

24 Methods

Rating   Name   Duplication   Size   Complexity  
A setID() 0 3 1
A setTabindex() 0 3 1
A __construct() 0 3 1
A setTitle() 0 4 2
A readAdditionalXML() 0 12 4
A addFlags() 0 3 1
A setCol() 0 3 1
A setParent() 0 6 1
A getFG() 0 3 1
A buildTabindex() 0 7 2
A buildID() 0 7 2
A buildAttributes() 0 12 4
A buildStyle() 0 11 3
A setClass() 0 3 1
A parseStyle() 0 12 4
A getAttribute() 0 3 3
A buildContainerDiv() 0 13 4
A addClass() 0 6 2
A onParentSet() 0 2 1
A getStyle() 0 3 1
A addStyle() 0 7 2
A buildValue() 0 12 5
A buildClass() 0 7 2
A addAttribute() 0 14 4

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
declare(strict_types=1);
3
4
namespace SKien\Formgenerator;
5
6
/**
7
 * Base-class for all elements of a form.
8
 *
9
 * @package Formgenerator
10
 * @author Stefanius <[email protected]>
11
 * @copyright MIT License - see the LICENSE file for details
12
 */
13
abstract class FormElement implements FormElementInterface
14
{
15
    use XMLHelper;
16
17
    /** @var FormGenerator the FormGenerator this element belongs to     */
18
    protected FormGenerator $oFG;
19
    /** @var FormCollection the parent element - only FormGenerator must has no parent     */
20
    protected ?FormCollection $oParent = null;
21
    /** @var int tab index of the element if it can get focus     */
22
    protected int $iTabindex = -1;
23
    /** @var int col inside current line     */
24
    protected int $iCol = 0;
25
    /** @var string element name     */
26
    protected string $strName = '';
27
    /** @var string element id     */
28
    protected string $strID = '';
29
    /** @var string CSS class of the element     */
30
    protected string $strClass = '';
31
    /** @var FormFlags flags that specify the appearance and behaviour     */
32
    protected FormFlags $oFlags;
33
    /** @var array attributes of the element     */
34
    protected ?array $aAttrib = null;
35
    /** @var array (CSS) styles of the element     */
36
    protected ?array $aStyle = null;
37
38
    /**
39
     * Create any kind of form element.
40
     * @see FormFlags
41
     * @param int $wFlags   any combination of FormFlag constants
42
     */
43
    public function __construct(int $wFlags)
44
    {
45
        $this->oFlags = new FormFlags($wFlags);
46
    }
47
48
    /**
49
     * Create the form element from the given XML-element.
50
     * Because several subclasses of FormElement has different constructors, we
51
     * need one uniform static function to be able to create all elements
52
     * independent from the subclass within a loop.
53
     * In the derived classes this function must return an instance of the
54
     * requested class.
55
     * @param \DOMElement $oXMLElement  the XML-element containing the information
56
     * @param FormCollection $oFormParent   the form parent of the element to create
57
     * @return FormElement|NULL the created element
58
     * @internal
59
     */
60
    abstract static public function fromXML(\DOMElement $oXMLElement, FormCollection $oFormParent) : ?FormElement;
61
62
    /**
63
     * In addition to the parameters needed by the constructor of each separate class,
64
     * some further attribs can be loaded after creation.
65
     * In contrast to the static fromXML() method, this method can be defined in parent
66
     * classes for general attributes.
67
     * In the derived classes this method should called it's parent down through all
68
     * parent-classes!
69
     * @param \DOMElement $oXMLElement
70
     * @internal
71
     */
72
    public function readAdditionalXML(\DOMElement $oXMLElement) : void
73
    {
74
        if (($strID = self::getAttribString($oXMLElement, 'id')) !== null) {
75
            $this->setID($strID);
76
        }
77
        if (($strStyle = self::getAttribString($oXMLElement, 'style')) !== null) {
78
            $this->parseStyle($strStyle);
79
        }
80
        if (($strCSSClass = self::getAttribString($oXMLElement, 'class')) !== null) {
81
            $this->addClass($strCSSClass);
82
        }
83
        $this->aAttrib = $this->readElementAttributes($oXMLElement, $this->aAttrib);
84
    }
85
86
    /**
87
     * Return the FormGenerator this element belongs to.
88
     * @return FormGenerator
89
     * @internal
90
     */
91
    public function getFG() : ?FormGenerator
92
    {
93
        return $this->oFG;
94
    }
95
96
    /**
97
     * Set the parent of this element.
98
     * The Formgenerator of the parent is adopted for this element.
99
     * If there are global flags set for the FormGenerator, this flags are added.
100
     * @param FormCollection $oParent
101
     * @internal
102
     */
103
    public function setParent(FormCollection $oParent) : void
104
    {
105
        $this->oParent = $oParent;
106
        $this->oFG = $oParent->oFG;
107
        $this->addFlags($this->oFG->getGlobalFlags());
108
        $this->onParentSet();
109
    }
110
111
    /**
112
     * Set the current col.
113
     * If this element is added as a child of a FormLine, the column is set in
114
     * order to be able to calculate the correct width of the element.
115
     * @param int $iCol
116
     * @internal
117
     */
118
    public function setCol(int $iCol) : void
119
    {
120
        $this->iCol = $iCol;
121
    }
122
123
    /**
124
     * Set ID for the element.
125
     * @param string $strID
126
     */
127
    public function setID(string $strID) : void
128
    {
129
        $this->strID = $strID;
130
    }
131
132
    /**
133
     * Set text for the elements title attribute.
134
     * @param string $strTitle
135
     */
136
    public function setTitle(string $strTitle) : void
137
    {
138
        if (strlen($strTitle) > 0) {
139
            $this->addAttribute('title', $strTitle);
140
        }
141
    }
142
143
    /**
144
     * Set the tab index of the element.
145
     * Method is called from the PageGenerator after an element is added to the form.
146
     * @param int $iTabindex
147
     * @return int the number of indexes, the element needs
148
     * @internal
149
     */
150
    public function setTabindex(/** @scrutinizer ignore-unused */ int $iTabindex) : int
151
    {
152
        return 0;
153
    }
154
155
    /**
156
     * Add flags to element.
157
     * @param int $wFlags
158
     */
159
    public function addFlags(int $wFlags) : void
160
    {
161
        $this->oFlags->add($wFlags);
162
    }
163
164
    /**
165
     * Add any attribute.
166
     * Attributes with null value are ignored!
167
     * If the attribute allready exist, the value will be overwritten.
168
     * @param string $strName attribute name
169
     * @param string $strValue attribute value
170
     */
171
    public function addAttribute(string $strName, ?string $strValue = '') : void
172
    {
173
        if ($strValue === null) {
174
            return;
175
        }
176
        $strName = strtolower($strName);
177
        if ($this->aAttrib == null) {
178
            $this->aAttrib = array();
179
        }
180
        if ($strName == 'style') {
181
            // style should no longer be set through AddAttribute()
182
            trigger_error('use AddStyle() to define additional styles for element!', E_USER_ERROR);
183
        }
184
        $this->aAttrib[$strName] = $strValue;
185
    }
186
187
    /**
188
     * Add any style.
189
     * If the style allready exist, the value will be overwritten.
190
     * @param string $strName   style
191
     * @param string $strValue  value
192
     */
193
    public function addStyle(string $strName, string $strValue) : void
194
    {
195
        $strName = strtolower($strName);
196
        if ($this->aStyle == null) {
197
            $this->aStyle = array();
198
        }
199
        $this->aStyle[$strName] = $strValue;
200
    }
201
202
    /**
203
     * Set the CSS class of the element.
204
     * Any previously setting will be overwritten.
205
     * @param string $strClass
206
     */
207
    public function setClass(string $strClass) : void
208
    {
209
        $this->strClass = $strClass;
210
    }
211
212
    /**
213
     * Add additional class to element.
214
     * Class is added to the existing classname separated with a blank
215
     * @param string $strClass
216
     */
217
    public function addClass(string $strClass) : void
218
    {
219
        if (strlen($this->strClass) > 0) {
220
            $this->strClass .= ' ';
221
        }
222
        $this->strClass .= $strClass;
223
    }
224
225
    /**
226
     * Get CSS styles related to this element.
227
     * This method gives each element the chance to add special styles to the
228
     * current page. <br/>
229
     * <b>This method is only called for elements having member bCreateStyle set to true!</b>
230
     * @return string
231
     * @internal
232
     */
233
    public function getStyle() : string
234
    {
235
        return '';
236
    }
237
238
    /**
239
     * @return string
240
     * @internal
241
     */
242
    abstract public function getHTML() : string;
243
244
    /**
245
     * Method called, after parent amd formgenerator is set properly.
246
     * Enhancing classes can use this method, to initialize properties that
247
     * nneds the parent or formgenerator (... configuration, global settings)
248
     */
249
    protected function onParentSet() : void
250
    {
251
    }
252
253
    /**
254
     * Build the 'container' div arround the current element.
255
     * Additional styles (alignment, ...) can be passed.
256
     * @param string $strStyle
257
     * @return string
258
     */
259
    protected function buildContainerDiv(string $strStyle = '') : string
260
    {
261
        if (strpos($strStyle, 'float') === false) {
262
            $strStyle = 'float: left; ' . $strStyle;
263
        }
264
        $strWidth = ($this->oParent ? $this->oParent->getColWidth($this->iCol) : '');
265
        if (!empty($strWidth)) {
266
            $strStyle = rtrim($strStyle, '; ');
267
            $strStyle .= '; width: ' . $strWidth . ';';
268
        }
269
        $strHTML = '<div style="' . $strStyle . '">';
270
271
        return $strHTML;
272
    }
273
274
    /**
275
     * Build the style attribute for the element.
276
     * @return string
277
     */
278
    protected function buildStyle() : string
279
    {
280
        $strStyle = '';
281
        if ($this->aStyle != null) {
282
            $strStyle = ' style="';
283
            foreach ($this->aStyle as $strName => $strValue) {
284
                $strStyle .= ' ' . $strName . ': ' . $strValue . ';';
285
            }
286
            $strStyle .= '"';
287
        }
288
        return $strStyle;
289
    }
290
291
    /**
292
     * Build all defined attributes for the element.
293
     * @return string
294
     */
295
    protected function buildAttributes() : string
296
    {
297
        $strAttrib = '';
298
        if ($this->aAttrib != null) {
299
            foreach ($this->aAttrib as $strName => $strValue) {
300
                $strAttrib .= ' ' . $strName;
301
                if (strlen($strValue) > 0) {
302
                    $strAttrib .= '="' . $strValue . '"';
303
                }
304
            }
305
        }
306
        return $strAttrib;
307
    }
308
309
    /**
310
     * Build the markup for the value attribute.
311
     * Retrieve value for the element from the FormData.
312
     * @return string Empty string if no value set, complete attribute if set
313
     */
314
    protected function buildValue() : string
315
    {
316
        $strHTML = '';
317
        $strValue = $this->oFG->getData()->getValue($this->strName);
318
319
        if ($this->oFlags->isSet(FormFlags::TRIM)) {
320
            $strValue = trim($strValue);
321
        }
322
        if (!$this->oFlags->isSet(FormFlags::NO_ZERO) || ($strValue != 0 && $strValue != '0')) {
323
            $strHTML = ' value="' . str_replace('"', '&quot;', $strValue) . '"';
324
        }
325
        return $strHTML;
326
    }
327
328
    /**
329
     * Build the markup for the class attribute.
330
     * @return string Empty string if no class set, complete attribute if set
331
     */
332
    protected function buildClass() : string
333
    {
334
        $strClass = '';
335
        if (!empty($this->strClass)) {
336
            $strClass .= ' class="' . $this->strClass . '"';
337
        }
338
        return $strClass;
339
    }
340
341
    /**
342
     * Build the markup for the ID attribute.
343
     * @return string Empty string if no id set, complete attribute if set
344
     */
345
    protected function buildID() : string
346
    {
347
        $strID = '';
348
        if (!empty($this->strID)) {
349
            $strID .= ' id="' . $this->strID . '"';
350
        }
351
        return $strID;
352
    }
353
354
    /**
355
     * Build the markup for the tabindex attribute.
356
     * @return string Empty string if $iTabindex = 0, complete attribute if set
357
     */
358
    protected function buildTabindex() : string
359
    {
360
        $strTabindex = '';
361
        if ($this->iTabindex > 0) {
362
            $strTabindex = ' tabindex="' . $this->iTabindex . '"';
363
        }
364
        return $strTabindex;
365
    }
366
367
    /**
368
     * Parse a given style attribute into the components it contains.
369
     * @param string $strStyle
370
     */
371
    protected function parseStyle($strStyle) : void
372
    {
373
        if ($this->aStyle === null) {
374
            $this->aStyle = array();
375
        }
376
        $aStyles = explode(';', trim($strStyle));
377
        foreach ($aStyles as $strStyleDef) {
378
            $aStyleDef = explode(':', trim($strStyleDef));
379
            if (count($aStyleDef) == 2) {
380
                $strName = trim($aStyleDef[0]);
381
                $strValue = str_replace(';', '', $aStyleDef[1]);
382
                $this->aStyle[$strName] = trim($strValue);
383
            }
384
        }
385
    }
386
387
    /**
388
     * Get the requested attribute.
389
     * @param string $strName
390
     * @return string|NULL      return null, if attribute not set
391
     */
392
    protected function getAttribute(string $strName) : ?string
393
    {
394
        return ($this->aAttrib && isset($this->aAttrib[$strName])) ? $this->aAttrib[$strName] : null;
395
    }
396
}
397
398