FormInput   F
last analyzed

Complexity

Total Complexity 63

Size/Duplication

Total Lines 417
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 1
Metric Value
wmc 63
eloc 170
dl 0
loc 417
rs 3.36
c 3
b 0
f 1

20 Methods

Rating   Name   Duplication   Size   Complexity  
A onParentSet() 0 4 2
A setExpandFolder() 0 3 1
A readAdditionalXML() 0 11 4
A setTypeFromFlags() 0 12 5
A setPlaceholder() 0 4 2
A processFlags() 0 11 4
C buildSelectButton() 0 52 12
A setTabindex() 0 7 2
A buildSuffix() 0 11 3
A buildDatalist() 0 14 3
A buildListLink() 0 8 2
A getHTML() 0 27 2
A setSelectImg() 0 4 1
A buildSelectImage() 0 23 6
A setSize() 0 10 5
A __construct() 0 13 2
A buildClass() 0 10 4
A setSuffix() 0 3 1
A setMaxLength() 0 3 1
A fromXML() 0 14 1

How to fix   Complexity   

Complex Class

Complex classes like FormInput 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 FormInput, 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 intended to get user input.
8
 * If direrctly used, a simple `input` element is created.
9
 *
10
 * @package Formgenerator
11
 * @author Stefanius <[email protected]>
12
 * @copyright MIT License - see the LICENSE file for details
13
 */
14
class FormInput extends FormElement
15
{
16
    /** @var string value input type    */
17
    protected string $strType = '';
18
    /** @var int|string size as number or as string including dimension ('%', 'px', 'em') */
19
    protected $size = 0;
20
    /** @var string image displayed, if selectbutton is enabled     */
21
    protected string $strSelectImg = '';
22
    /** @var string tooltip for selectbutton     */
23
    protected string $strSelectImgTitle = '';
24
    /** @var string folder to expand when call the filemanager     */
25
    protected string $strExpandFolder = '';
26
    /** @var string suffix directly after the element     */
27
    protected string $strSuffix = '';
28
29
    /**
30
     * Create a simple input element.
31
     * @see FormFlags
32
     * @param string $strName   Name (if no ID specified, name is used also as ID)
33
     * @param int|string $size  integer value set the size-attribute, a string is used for the width attribute
34
     * @param int $wFlags       any combination of FormFlag constants
35
     * @param int $iMaxLength   max. input character count
36
     */
37
    public function __construct(string $strName, $size, int $wFlags = 0, int $iMaxLength = 0)
38
    {
39
        parent::__construct($wFlags);
40
        $this->strName = $strName;
41
        $this->size = $size;
42
        $this->strType = 'text';
43
        $this->strSelectImg = '';
44
        $this->strSelectImgTitle = '';
45
        $this->strSuffix = '';
46
47
        $this->addFlags($wFlags);
48
        if ($iMaxLength > 0) {
49
            $this->addAttribute('maxlength', (string)$iMaxLength);
50
        }
51
    }
52
53
    /**
54
     * {@inheritDoc}
55
     * @see \SKien\Formgenerator\FormElement::fromXML()
56
     * @internal
57
     */
58
    static public function fromXML(\DOMElement $oXMLElement, FormCollection $oFormParent) : ?FormElement
59
    {
60
        /* only while development
61
        if ($oXMLElement->nodeName !== 'Input') {
62
            trigger_error('Try to create Form' . $oXMLElement->nodeName . ' - Element from XML without defined method!', E_USER_ERROR);
63
        }
64
        */
65
        $strName = self::getAttribString($oXMLElement, 'name');
66
        $strSize = self::getAttribString($oXMLElement, 'size');
67
        $wFlags = self::getAttribFlags($oXMLElement);
68
        $oFormElement = new self($strName, $strSize, $wFlags);
69
        $oFormParent->add($oFormElement);
70
        $oFormElement->readAdditionalXML($oXMLElement);
71
        return $oFormElement;
72
    }
73
74
    /**
75
     * {@inheritDoc}
76
     * @see \SKien\Formgenerator\FormElement::readAdditionalXML()
77
     * @internal
78
     */
79
    public function readAdditionalXML(\DOMElement $oXMLElement) : void
80
    {
81
        parent::readAdditionalXML($oXMLElement);
82
        if (self::hasAttrib($oXMLElement, 'suffix')) {
83
            $this->setSuffix(self::getAttribString($oXMLElement, 'suffix'));
84
        }
85
        if (self::hasAttrib($oXMLElement, 'selectimg')) {
86
            $this->setSelectImg(self::getAttribString($oXMLElement, 'selectimg'), self::getAttribString($oXMLElement, 'selectimgtitle'));
87
        }
88
        if (self::hasAttrib($oXMLElement, 'expandfolder')) {
89
            $this->setExpandFolder(self::getAttribString($oXMLElement, 'expandfolder'));
90
        }
91
    }
92
93
    /**
94
     * Set the maxlength attribute of the element.
95
     * @param int $iMaxLength
96
     */
97
    public function setMaxLength(int $iMaxLength) : void
98
    {
99
        $this->addAttribute('maxlength', (string)$iMaxLength);
100
    }
101
102
    /**
103
     * Set placeholder to display on empty input element.
104
     * @param string $strPlaceholder
105
     */
106
    public function setPlaceholder(string $strPlaceholder) : void
107
    {
108
        if (strlen($strPlaceholder) > 0) {
109
            $this->addAttribute('placeholder', $strPlaceholder);
110
        }
111
    }
112
113
    /**
114
     * Set image and title for select-button.
115
     * The `FormFlag::ADD_SELBTN` flag have to be set with the constructor or with the
116
     * `addFlags()` method.
117
     * To handle the select button click, define  JS function `OnSelect(strField)`. The
118
     * handler is called with the name of the element the select-button belongs to.
119
     * @see FormFlags::ADD_SELBTN
120
     * @param string $strImg image to display (leave blank for default 'search' - button)
121
     * @param string $strTitle (default = '')
122
     */
123
    public function setSelectImg(string $strImg, string $strTitle = '') : void
124
    {
125
        $this->strSelectImg = $strImg;
126
        $this->strSelectImgTitle = $strTitle;
127
    }
128
129
    /**
130
     * Set the folder to expand when call the filemanager.
131
     * The `FormFlag::BROWSE_SERVER` flag have to be set with the constructor or with the
132
     * `addFlags()` method.
133
     * Only available, if the richfilemanager is configured.
134
     * @see FormFlags::BROWSE_SERVER
135
     * @param string $strExpandFolder
136
     */
137
    public function setExpandFolder(string $strExpandFolder) : void
138
    {
139
        $this->strExpandFolder = $strExpandFolder;
140
    }
141
142
    /**
143
     * Set a suffix that is outputed after the element.
144
     * @param string $strSuffix
145
     */
146
    public function setSuffix(string $strSuffix) : void
147
    {
148
        $this->strSuffix = $strSuffix;
149
    }
150
151
    /**
152
     * {@inheritDoc}
153
     * @see \SKien\Formgenerator\FormElement::onParentSet()
154
     */
155
    protected function onParentSet() : void
156
    {
157
        if ($this->oFlags->isSet(FormFlags::BROWSE_SERVER)) {
158
            $this->oFG->addConfigForJS('RichFilemanager', $this->oFG->getConfig()->getArray('RichFilemanager'));
159
        }
160
    }
161
162
    /**
163
     * Build the HTML-notation for the input element.
164
     * {@inheritDoc}
165
     * @see \SKien\Formgenerator\FormElement::getHTML()
166
     * @internal
167
     */
168
    public function getHTML() : string
169
    {
170
        $this->processFlags();
171
        $this->setSize();
172
        $strHTML = $this->buildContainerDiv();
173
174
        $this->strID = $this->strID ?: $this->strName;
175
176
        $strHTML .= '<input';
177
        $strHTML .= ' type="' . $this->strType . '"';
178
        $strHTML .= ' name="' . $this->strName . '"';
179
        $strHTML .= $this->buildClass();
180
        $strHTML .= $this->buildID();
181
        $strHTML .= $this->buildStyle();
182
        $strHTML .= $this->buildTabindex();
183
        $strHTML .= $this->buildValue();
184
        $strHTML .= $this->buildAttributes();
185
        $strHTML .= $this->buildListLink();
186
        $strHTML .= '>';
187
188
        $strHTML .= $this->buildSelectButton();
189
        $strHTML .= $this->buildSuffix();
190
        $strHTML .= $this->buildDatalist();
191
192
        $strHTML .= '</div>' . PHP_EOL;
193
194
        return $strHTML;
195
    }
196
197
    /**
198
     * Set the tab index of the element.
199
     * Method is called from the PageGenerator after an element is added to the form.
200
     * @param int $iTabindex
201
     * @return int the number of indexes, the element needs
202
     * @internal
203
     */
204
    public function setTabindex(int $iTabindex) : int
205
    {
206
        if ($this->oFlags->isSet(FormFlags::HIDDEN | FormFlags::READ_ONLY | FormFlags::DISABLED)) {
207
            return 0;
208
        }
209
        $this->iTabindex = $iTabindex;
210
        return 1;
211
    }
212
213
    /**
214
     * Process the current flags before the HTML is generated.
215
     */
216
    protected function processFlags() : void
217
    {
218
        $this->setTypeFromFlags();
219
220
        if ($this->oFlags->isSet(FormFlags::MANDATORY)) {
221
            $this->addAttribute('required');
222
        }
223
        if ($this->oFlags->isSet(FormFlags::READ_ONLY)) {
224
            $this->addAttribute('readonly');
225
        } else if ($this->oFlags->isSet(FormFlags::DISABLED)) {
226
            $this->addAttribute('disabled');
227
        }
228
    }
229
230
    /**
231
     * Set the type depending on some flags
232
     */
233
    protected function setTypeFromFlags() : void
234
    {
235
        if ($this->oFlags->isSet(FormFlags::HIDDEN)) {
236
            $this->strType = 'hidden';
237
        }
238
        if ($this->oFlags->isSet(FormFlags::PASSWORD)) {
239
            $this->strType = 'password';
240
        }
241
        if ($this->oFlags->isSet(FormFlags::FILE)) {
242
            $this->strType = 'file';
243
            if ($this->oFlags->isSet(FormFlags::HIDDEN)) {
244
                $this->addStyle('visibility', 'hidden');
245
            }
246
        }
247
    }
248
249
    /**
250
     * Set the size of the element.
251
     * If property $size contains numeric value, the HTML attrib 'size' is set, in case of a
252
     * string a width information including dimension (px, em, ...) is assumed.
253
     */
254
    protected function setSize() : void
255
    {
256
        if ($this->oFlags->isSet(FormFlags::HIDDEN)) {
257
            $this->size = '';
258
        }
259
        if ((is_numeric($this->size)) && ($this->size > 0)) {
260
            $this->addAttribute('size', (string)$this->size);
261
        } else if (!empty($this->size)) {
262
            // size given as string including dimension
263
            $this->addStyle('width', (string)$this->size);
264
        }
265
    }
266
267
    /**
268
     * {@inheritDoc}
269
     * @see \SKien\Formgenerator\FormElement::buildClass()
270
     */
271
    protected function buildClass() : string
272
    {
273
        if (!empty($this->strClass)) {
274
            $this->strClass .= ' ';
275
        }
276
        $this->strClass .= ($this->oFlags->isSet(FormFlags::MANDATORY)) ? ' inputMand' : ' inputOK';
277
        if ($this->oFlags->isSet(FormFlags::ALIGN_RIGHT)) {
278
            $this->strClass .= '_R';
279
        }
280
        return parent::buildClass();
281
    }
282
283
    /**
284
     * Build the markup for a suffix succeeding the input element.
285
     * @return string
286
     */
287
    protected function buildSuffix() : string
288
    {
289
        $strHTML = '';
290
        if (!empty($this->strSuffix)) {
291
            if ($this->oFlags->isSet(FormFlags::READ_ONLY)) {
292
                $strHTML .= '&nbsp;<span class="readonly">' . $this->strSuffix . '</span>';
293
            } else {
294
                $strHTML .= '&nbsp;' . $this->strSuffix;
295
            }
296
        }
297
        return $strHTML;
298
    }
299
300
    /**
301
     * Build attrib for associated datalist.
302
     * If the dataprovider contains a datalist in the selectoptions with the same name
303
     * as the element, we add the attrib to conect to this list.
304
     * @return string
305
     */
306
    protected function buildListLink() : string
307
    {
308
        $strLink = '';
309
        $aOptions = $this->oFG->getData()->getSelectOptions($this->strName);
310
        if (count($aOptions) > 0) {
311
            $strLink = ' list="list' . $this->strName . '"';
312
        }
313
        return $strLink;
314
    }
315
316
    /**
317
     * Build the markup for associated datalist.
318
     * If the dataprovider contains a datalist in the selectoptions with the same name
319
     * as the element, we build this datalist.
320
     * @return string
321
     */
322
    protected function buildDatalist() : string
323
    {
324
        $strHTML = '';
325
        $aOptions = $this->oFG->getData()->getSelectOptions($this->strName);
326
        if (count($aOptions) > 0) {
327
            $strHTML .= '<datalist id="list' . $this->strName . '">' . PHP_EOL;
328
            foreach ($aOptions as $strValue) {
329
                $strHTML .= '    ';
330
                $strHTML .= '<option ';
331
                $strHTML .= 'value="' . $strValue . '">' . PHP_EOL;
332
            }
333
            $strHTML .= '</datalist>' . PHP_EOL;
334
        }
335
        return $strHTML;
336
    }
337
338
    /**
339
     * Build the markup for the select button(s).
340
     * If input  is set to readonly, an additional 'delete' button is appended.
341
     * @param string $strCssClass
342
     * @return string
343
     */
344
    protected function buildSelectButton(string $strCssClass = 'picker') : string
345
    {
346
        $wButtonFlags = $this->oFlags->getButtonFlags();
347
        if ($wButtonFlags === 0 || $this->oFlags->isSet(FormFlags::HIDDEN)) {
348
            return '';
349
        }
350
351
        $strImg = '';
352
        $strTitle = '';
353
        $strOnClick = '';
354
        $strID = '';
355
        $strFor = '';
356
357
        // only one of the button flags is allowed - so we can use switch-case!
358
        switch ($wButtonFlags) {
359
        case FormFlags::ADD_DTU:
360
            $strUsername = $this->oFG->getData()->getValue('username');
361
            [$strImg, $strTitle] = $this->oFG->getStdImage(FormImage::IMG_DTU);
362
            $strOnClick = "onInsertDateTimeUser('" . $this->strName . "', '" . $strUsername . "')";
363
            $strID = $this->strName . 'DTU';
364
            break;
365
        case FormFlags::ADD_DATE_PICKER:
366
            [$strImg, $strTitle] = $this->oFG->getStdImage(FormImage::IMG_DATE_PICKER);
367
            $strFor = $this->strName;
368
            break;
369
        case FormFlags::ADD_TIME_PICKER:
370
            [$strImg, $strTitle] = $this->oFG->getStdImage(FormImage::IMG_TIME_PICKER);
371
            $strFor = $this->strName;
372
            break;
373
        case FormFlags::BROWSE_SERVER:
374
            [$strImg, $strTitle] = $this->oFG->getStdImage(FormImage::IMG_BROWSE);
375
            $strOnClick = "browseServer('" . $this->strName . "','','" . $this->strExpandFolder . "')";
376
            $strID = $this->strName . 'BS';
377
            break;
378
        case FormFlags::ADD_SELBTN:
379
            [$strImg, $strTitle] = $this->oFG->getStdImage(FormImage::IMG_SEARCH);
380
            $strOnClick = "onSelect('" . $this->strName . "')";
381
            $strID = $this->strName . 'SB';
382
            $strImg = $this->strSelectImg ?: $strImg;
383
            $strTitle = $this->strSelectImgTitle ?: $strTitle;
384
            break;
385
        default:
386
            trigger_error('Only one of the button-flags can be set!', E_USER_ERROR);
387
        }
388
        $strHTML = $this->buildSelectImage($strImg, $strTitle, $strOnClick, $strID, $strCssClass, $strFor);
389
        if (!empty($strHTML) && $this->oFlags->isSet(FormFlags::READ_ONLY)) {
390
            [$strImg, $strTitle] = $this->oFG->getStdImage(FormImage::IMG_DELETE);
391
            $strOnClick = "resetElement('" . $this->strName . "')";
392
            $strID = $this->strName . 'Del';
393
            $strHTML .= $this->buildSelectImage($strImg, $strTitle, $strOnClick, $strID, $strCssClass);
394
        }
395
        return $strHTML;
396
    }
397
398
    /**
399
     * Build the markup for a selectimage.
400
     * @param string $strImg
401
     * @param string $strTitle
402
     * @param string $strOnClick
403
     * @param string $strID
404
     * @param string $strCssClass
405
     * @param string $strFor
406
     * @return string
407
     */
408
    protected function buildSelectImage(string $strImg, string $strTitle, string $strOnClick, string $strID, string $strCssClass, string $strFor = '') : string
409
    {
410
        $strHTML = '';
411
        if (!empty($strImg)) {
412
            $strHTML = '<img class="' . $strCssClass . '" src="' . $strImg . '" alt="[?]"';
413
            if (!empty($strID)) {
414
                $strHTML .= ' id="' . $strID . '"';
415
            }
416
            if (!empty($strTitle)) {
417
                $strHTML .= ' title="' . $strTitle . '"';
418
            }
419
            if (!empty($strOnClick)) {
420
                $strHTML .= ' onclick="' . $strOnClick . '"';
421
            }
422
            if (!empty($strFor)) {
423
                // since the <img> element don't support the 'for' attribute, we set an user defined
424
                // attribute. In the initialization of the form we add an eventlistener to the click
425
                // event of all images with this attribute set and move the focus to the input element
426
                $strHTML .= ' data-for="' . $strFor . '"';
427
            }
428
            $strHTML .= '>';
429
        }
430
        return $strHTML;
431
    }
432
}
433