Passed
Push — cirework ( 7349fb...97b46e )
by Richard
04:26
created

Element::setPattern()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 3
cts 3
cp 1
crap 1
1
<?php
2
/*
3
 You may not change or alter any portion of this comment or credits
4
 of supporting developers from this source code or any supporting source code
5
 which is considered copyrighted (c) material of the original comment or credit authors.
6
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
*/
11
12
13
namespace Xoops\Form;
14
15
use Xoops\Html\Attributes;
16
17
/**
18
 * Element - Abstract base class for form elements
19
 *
20
 * @category  Xoops\Form\Element
21
 * @package   Xoops\Form
22
 * @author    trabis <[email protected]>
23
 * @copyright 2012-2016 XOOPS Project (http://xoops.org)
24
 * @license   GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
25
 * @link      http://xoops.org
26
 */
27
abstract class Element extends Attributes
28
{
29
    /**
30
     * @var string[] list of attributes to NOT render
31
     */
32
    protected $suppressList = ['caption', 'datalist', 'description', 'option'];
33
34
    /**
35
     * Javascript performing additional validation of this element data
36
     *
37
     * This property contains a list of Javascript snippets that will be sent to
38
     * \Xoops\Form\Form::renderValidationJS().
39
     * NB: All elements are added to the output one after the other, so don't forget
40
     * to add a ";" after each to ensure no Javascript syntax error is generated.
41
     *
42
     * @var array ()
43
     */
44
    public $customValidationCode = array();
45
46
    /**
47
     * extra attributes to go in the tag
48
     *
49
     * @var array
50
     */
51
    protected $extra = array();
52
53
    /**
54
     * __construct
55
     *
56
     * @param array $attributes array of attribute name => value pairs
57
     *                           Control attributes:
58
     *                               ElementFactory::FORM_KEY optional form or tray to hold this element
59
     */
60 135
    public function __construct($attributes = array())
61
    {
62 135
        parent::__construct($attributes);
63 135
        if ($this->has(ElementFactory::FORM_KEY)
64 135
            && $this->get(ElementFactory::FORM_KEY) instanceof ContainerInterface) {
65 4
            $this->get(ElementFactory::FORM_KEY)->addElement($this);
66
        }
67 135
    }
68
69
    /**
70
     * render - Generates output for the element.
71
     *
72
     * This method is abstract and must be overwritten by the child classes.
73
     *
74
     * @return    string
75
     */
76
    abstract public function render();
77
78
    /**
79
     * render attributes as a string to include in HTML output
80
     *
81
     * @return string
82
     */
83 58
    public function renderAttributeString()
84
    {
85 58
        $this->suppressRender($this->suppressList);
86
87
        // title attribute needs to be generated if not already set
88 58
        if (!$this->has('title')) {
89 56
            $this->set('title', $this->getTitle());
90
        }
91
        // generate id from name if not already set
92 58
        if (!$this->has('id')) {
93 50
            $id = $this->get('name');
94 50
            if (substr($id, -2) === '[]') {
0 ignored issues
show
Bug introduced by
It seems like $id can also be of type false; however, parameter $string of substr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

94
            if (substr(/** @scrutinizer ignore-type */ $id, -2) === '[]') {
Loading history...
95 1
                $id = substr($id, 0, strlen($id)-2);
0 ignored issues
show
Bug introduced by
It seems like $id can also be of type false; however, parameter $string of strlen() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

95
                $id = substr($id, 0, strlen(/** @scrutinizer ignore-type */ $id)-2);
Loading history...
96
            }
97 50
            $this->set('id', $id);
98
        }
99 58
        return parent::renderAttributeString();
100
    }
101
102
    /**
103
     * getValue - Get an array of pre-selected values
104
     *
105
     * @param boolean $encode True to encode special characters
106
     *
107
     * @return mixed
108
     */
109 41
    public function getValue($encode = false)
110
    {
111 41
        $values = $this->get('value', '');
112 41
        if (is_array($values)) {
113 1
            $ret = array();
114 1
            foreach ($values as $value) {
115 1
                $ret[] = $encode ? htmlspecialchars($value, ENT_QUOTES) : $value;
116
            }
117 1
            return $ret;
118
        }
119 41
        return $encode ? htmlspecialchars($values, ENT_QUOTES) : $values;
120
    }
121
122
    /**
123
     * setValue - Set pre-selected values
124
     *
125
     * @param mixed $value value to assign to this element
126
     *
127
     * @return void
128
     */
129 18
    public function setValue($value)
130
    {
131 18
        $this->set('value', $value);
132 18
    }
133
134
    /**
135
     * setName - set the "name" attribute for the element
136
     *
137
     * @param string $name "name" attribute for the element
138
     *
139
     * @return void
140
     */
141 34
    public function setName($name)
142
    {
143 34
        $this->set('name', $name);
144 34
    }
145
146
    /**
147
     * getName - get the "name" attribute for the element
148
     *
149
     * @return string
150
     */
151 28
    public function getName()
152
    {
153 28
        return (string) $this->get('name');
154
    }
155
156
    /**
157
     * setAccessKey - set the access key attribute for the element
158
     *
159
     * @param string $key "accesskey" attribute for the element
160
     *
161
     * @return void
162
     */
163 3
    public function setAccessKey($key)
164
    {
165 3
        $this->set('accesskey', $key);
166 3
    }
167
168
    /**
169
     * getAccessKey - get the access key attribute for the element
170
     *
171
     * @return string "accesskey" attribute value
172
     */
173 2
    public function getAccessKey()
174
    {
175 2
        return (string) $this->get('accesskey');
176
    }
177
178
    /**
179
     * getAccessString - If the access key is found in the specified string, underline it
180
     *
181
     * @param string $str string to add access key highlight to
182
     *
183
     * @return string Enhanced string with the 1st occurrence of "accesskey underlined
184
     */
185 1
    public function getAccessString($str)
186
    {
187 1
        $access = $this->getAccessKey();
188 1
        if (!empty($access) && (false !== ($pos = strpos($str, $access)))) {
189 1
            return htmlspecialchars(substr($str, 0, $pos), ENT_QUOTES)
190 1
                . '<span style="text-decoration: underline;">'
191 1
                . htmlspecialchars(substr($str, $pos, 1), ENT_QUOTES) . '</span>'
192 1
                . htmlspecialchars(substr($str, $pos + 1), ENT_QUOTES);
193
        }
194 1
        return htmlspecialchars($str, ENT_QUOTES);
195
    }
196
197
    /**
198
     * setClass - set the "class" attribute for the element
199
     *
200
     * @param string $class class attribute for the element
201
     *
202
     * @return void
203
     */
204 5
    public function setClass($class)
205
    {
206 5
        $this->add('class', (string) $class);
207 5
    }
208
209
    /**
210
     * getClass - get the "class" attribute for the element
211
     *
212
     * @return string "class" attribute value
213
     */
214 3
    public function getClass()
215
    {
216 3
        $class = $this->get('class', false);
217 3
        if ($class === false) {
218 1
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type string.
Loading history...
219
        }
220 3
        return htmlspecialchars(implode(' ', $class), ENT_QUOTES);
221
    }
222
223
    /**
224
     * setPattern - set the "pattern" attribute for the element
225
     *
226
     * @param string $pattern            pattern attribute for the element
227
     * @param string $patternDescription pattern description
228
     *
229
     * @return void
230
     */
231 5
    public function setPattern($pattern, $patternDescription = '')
232
    {
233 5
        $this->set('pattern', $pattern);
234 5
        $this->set(':pattern_description', $patternDescription);
235 5
    }
236
237
    /**
238
     * getPattern - get the "pattern" attribute for the element
239
     *
240
     * @return string "pattern"
241
     */
242 1
    public function getPattern()
243
    {
244 1
        return (string) $this->get('pattern', '');
245
    }
246
247
    /**
248
     * getPatternDescription - get the "pattern_description"
249
     *
250
     * @return string "pattern_description"
251
     */
252 5
    public function getPatternDescription()
253
    {
254 5
        return (string) $this->get(':pattern_description', '');
255
    }
256
257
    /**
258
     * setDatalist - set the datalist attribute for the element
259
     *
260
     * @param string[]|string $datalist datalist attribute for the element
261
     *
262
     * @return void
263
     */
264 4
    public function setDatalist($datalist)
265
    {
266 4
        $this->add('datalist', $datalist);
267 4
    }
268
269
    /**
270
     * renderDatalist - get the datalist attribute for the element
271
     *
272
     * @return string "datalist" attribute value
273
     */
274 3
    public function renderDatalist()
275
    {
276 3
        if (!$this->isDatalist()) {
277 3
            return '';
278
        }
279 2
        $ret = "\n" . '<datalist id="list_' . $this->getName() . '">' . "\n";
280 2
        foreach ($this->get('datalist') as $datalist) {
281 2
            $ret .= '<option value="' . htmlspecialchars($datalist, ENT_QUOTES) . '">' . "\n";
282
        }
283 2
        $ret .= '</datalist>' . "\n";
284 2
        return $ret;
285
    }
286
287
    /**
288
     * isDatalist - is there a datalist for the element?
289
     *
290
     * @return boolean true if element has a non-empty datalist
291
     */
292 13
    public function isDatalist()
293
    {
294 13
        return $this->has('datalist');
295
    }
296
297
    /**
298
     * setCaption - set the caption for the element
299
     *
300
     * @param string $caption caption for element
301
     *
302
     * @return void
303
     */
304 34
    public function setCaption($caption)
305
    {
306 34
        $this->set('caption', $caption);
307 34
    }
308
309
    /**
310
     * getCaption - get the caption for the element
311
     *
312
     * @return string
313
     */
314 15
    public function getCaption()
315
    {
316 15
        return $this->get('caption', '');
317
        //return htmlspecialchars($this->caption, ENT_QUOTES);
318
    }
319
320
    /**
321
     * setTitle - set the title for the element
322
     *
323
     * @param string $title title for element
324
     *
325
     * @return void
326
     */
327 2
    public function setTitle($title)
328
    {
329 2
        $this->set('title', $title);
330 2
    }
331
332
    /**
333
     * getTitle - get the title for the element
334
     *
335
     * @return string
336
     */
337 57
    public function getTitle()
338
    {
339 57
        if ($this->has('title')) {
340 1
            return $this->get('title');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->get('title') could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
341
        } else {
342 57
            if ($this->has(':pattern_description')) {
343 4
                return htmlspecialchars(
344 4
                    strip_tags($this->get('caption') . ' - ' . $this->get(':pattern_description')),
0 ignored issues
show
Bug introduced by
Are you sure $this->get(':pattern_description') of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

344
                    strip_tags($this->get('caption') . ' - ' . /** @scrutinizer ignore-type */ $this->get(':pattern_description')),
Loading history...
Bug introduced by
Are you sure $this->get('caption') of type false|mixed can be used in concatenation? ( Ignorable by Annotation )

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

344
                    strip_tags(/** @scrutinizer ignore-type */ $this->get('caption') . ' - ' . $this->get(':pattern_description')),
Loading history...
345 4
                    ENT_QUOTES
346
                );
347
            } else {
348 55
                return htmlspecialchars(strip_tags($this->get('caption')), ENT_QUOTES);
0 ignored issues
show
Bug introduced by
It seems like $this->get('caption') can also be of type false; however, parameter $str of strip_tags() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

348
                return htmlspecialchars(strip_tags(/** @scrutinizer ignore-type */ $this->get('caption')), ENT_QUOTES);
Loading history...
349
            }
350
        }
351
    }
352
353
    /**
354
     * setDescription - set the element's description
355
     *
356
     * @param string $description description
357
     *
358
     * @return void
359
     */
360 2
    public function setDescription($description)
361
    {
362 2
        $this->set('description', $description);
363 2
    }
364
365
    /**
366
     * getDescription - get the element's description
367
     *
368
     * @param boolean $encode True to encode special characters
369
     *
370
     * @return string
371
     */
372 6
    public function getDescription($encode = false)
373
    {
374 6
        $description = $this->get('description', '');
375 6
        return $encode ? htmlspecialchars($description, ENT_QUOTES) : $description;
376
    }
377
378
    /**
379
     * setHidden - flag the element as "hidden"
380
     *
381
     * @return void
382
     */
383 18
    public function setHidden()
384
    {
385 18
        $this->set('hidden');
386 18
    }
387
388
    /**
389
     * isHidden - is this a hidden element?
390
     *
391
     * @return boolean true if hidden
392
     */
393 13
    public function isHidden()
394
    {
395 13
        return $this->has('hidden');
396
    }
397
398
    /**
399
     * setRequired - set entry required
400
     *
401
     * @param boolean $bool true to set required entry for this element
402
     *
403
     * @return void
404
     */
405 11
    public function setRequired($bool = true)
406
    {
407 11
        if ($bool) {
408 11
            $this->set('required');
409
        }
410 11
    }
411
412
    /**
413
     * isRequired - is entry required for this element?
414
     *
415
     * @return boolean true if entry is required
416
     */
417 16
    public function isRequired()
418
    {
419 16
        return $this->has('required');
420
    }
421
422
    /**
423
     * setExtra - Add extra attributes to the element.
424
     *
425
     * This string will be inserted verbatim and unvalidated in the
426
     * element's tag. Know what you are doing!
427
     *
428
     * @param string  $extra   extra raw text to insert into form
429
     * @param boolean $replace If true, passed string will replace current
430
     *                         content, otherwise it will be appended to it
431
     *
432
     * @return string[] New content of the extra string
433
     *
434
     * @deprecated please use attributes for event scripting
435
     */
436 2
    public function setExtra($extra, $replace = false)
437
    {
438 2
        if ($replace) {
439 1
            $this->extra = array(trim($extra));
440
        } else {
441 2
            $this->extra[] = trim($extra);
442
        }
443 2
        return $this->extra;
444
    }
445
446
    /**
447
     * getExtra - Get the extra attributes for the element
448
     *
449
     * @param boolean $encode True to encode special characters
450
     *
451
     * @return string
452
     *
453
     * @see setExtra() this is going to disappear
454
     */
455 56
    public function getExtra($encode = false)
456
    {
457 56
        if (!$encode) {
458 56
            return implode(' ', $this->extra);
459
        }
460 1
        $value = array();
461 1
        foreach ($this->extra as $val) {
462 1
            $value[] = str_replace('>', '&gt;', str_replace('<', '&lt;', $val));
463
        }
464 1
        return empty($value) ? '' : implode(' ', $value);
465
    }
466
467
    /**
468
     * addCustomValidationCode - Add custom validation javascript
469
     *
470
     * This string will be inserted verbatim and unvalidated in the page.
471
     * Know what you are doing!
472
     *
473
     * @param string  $code    javascript code  to insert into form
474
     * @param boolean $replace If true, passed string will replace current code,
475
     *                          otherwise it will be appended to it
476
     *
477
     * @return void
478
     */
479 1
    public function addCustomValidationCode($code, $replace = false)
480
    {
481 1
        if ($replace) {
482 1
            $this->customValidationCode = [$code];
483
        } else {
484 1
            $this->customValidationCode[] = $code;
485
        }
486 1
    }
487
488
    /**
489
     * renderValidationJS - Render custom javascript validation code
490
     *
491
     * @return string|false
492
     */
493 4
    public function renderValidationJS()
494
    {
495
        // render custom validation code if any
496 4
        if (!empty($this->customValidationCode)) {
497 1
            return implode("\n", $this->customValidationCode);
498
            // generate validation code if required
499
        } else {
500 4
            if ($this->isRequired() && $eltname = $this->getName()) {
501
                // $eltname    = $this->getName();
502 1
                $eltcaption = $this->getCaption();
503 1
                $eltmsg = empty($eltcaption)
504 1
                    ? sprintf(\XoopsLocale::F_ENTER, $eltname)
505 1
                    : sprintf(\XoopsLocale::F_ENTER, $eltcaption);
506 1
                $eltmsg = str_replace(array(':', '?', '%'), '', $eltmsg);
507 1
                $eltmsg = str_replace('"', '\"', stripslashes($eltmsg));
508 1
                $eltmsg = strip_tags($eltmsg);
509
                return "\n"
510 1
                    . "if ( myform.{$eltname}.value == \"\" ) { window.alert(\"{$eltmsg}\");"
511 1
                    . " myform.{$eltname}.focus(); return false; }\n";
512
            }
513
        }
514 4
        return false;
515
    }
516
517
    /**
518
     * Test if a class that starts with the pattern string is set
519
     *
520
     * @param string $pattern 'starts with' to match
521
     *
522
     * @return integer|false false if no match, or index of matching class
523
     */
524 39
    public function hasClassLike($pattern)
525
    {
526 39
        $class = $this->get('class');
527 39
        if ($class) {
528 7
            $length = strlen($pattern);
529 7
            foreach ((array) $class as $i => $value) {
530 7
                if (0 === strncmp($value, $pattern, $length)) {
531 7
                    return $i;
532
                }
533
            }
534
        }
535 39
        return false;
536
    }
537
538
    /**
539
     * themeDecorateElement - add theme decoration to element
540
     *
541
     * @return void
542
     *
543
     * @todo this should ask the theme
544
     */
545 38
    public function themeDecorateElement()
546
    {
547 38
        $class = 'form-control';
548 38
        if ($this instanceof Button) {
549 6
            if (false !== $this->hasClassLike('btn')) {
0 ignored issues
show
introduced by
The condition false !== $this->hasClassLike('btn') can never be true.
Loading history...
550 2
                return;
551
            }
552 5
            $class = 'btn btn-default';
553 33
        } elseif (false !== $this->hasClassLike('form-') && false !== $this->hasClassLike('col-')) {
0 ignored issues
show
introduced by
The condition false !== $this->hasClas...s->hasClassLike('col-') can never be true.
Loading history...
554
            return;
555 33
        } elseif ($this instanceof TextArea) {
556 5
            $class = 'form-control';
557
        } /**
558
        } elseif ($this instanceof OptionElement) {
559
            $class = 'col-md-3';
560
            $options = $this->get('option', []);
561
            foreach ($options as $value) {
562
                if (is_array($value)) { // optgroup
563
                    foreach ($value as $subvalue) {
564
                        if (strlen($subvalue) > 20) {
565
                            $class = 'col-md-4';
566
                            break 2;
567
                        }
568
                    }
569
                } elseif (strlen($value) > 20) {
570
                    $class = 'col-md-4';
571
                    break;
572
                }
573
            }
574
        } else {
575
            $size = $this->get('size', 0);
576
            if ($size < 20) {
577
//                $class = 'col-md-2';
578
//            } elseif ($size < 30) {
579
                $class = 'col-md-3';
580
            } else {
581
                $class = 'form-control';
582
            }
583
        }
584
     */
585 38
        $this->add('class', $class);
586 38
    }
587
588
    /**
589
     * Convenience method to assist with setting attributes when using BC Element syntax
590
     *
591
     * Set attribute $name to $value, replacing $value with $default if $value is empty, or if the
592
     * value is not one of the values specified in the (optional) $enum array
593
     *
594
     * @param string $name    attribute name
595
     * @param mixed  $value   attribute value
596
     * @param mixed  $default default value
597
     * @param array  $enum    optional list of valid values
598
     *
599
     * @return void
600
     */
601 93
    public function setWithDefaults($name, $value, $default = null, $enum = null)
602
    {
603 93
        if (empty($value)) {
604 32
            $value = $default;
605 86
        } elseif (null !== $enum && !in_array($value, $enum)) {
606 1
            $value = $default;
607
        }
608 93
        $this->set($name, $value);
609 93
    }
610
611
    /**
612
     * Convenience method to assist with setting attributes when using BC Element syntax
613
     *
614
     * Set attribute $name to $value, replacing $value with $default if $value is empty, or if the
615
     * value is not one of the values specified in the (optional) $enum array
616
     *
617
     * @param string $name  attribute name
618
     * @param mixed  $value attribute value
619
     *
620
     * @return void
621
     */
622 27
    public function setIfNotEmpty($name, $value)
623
    {
624
        // don't overwrite
625 27
        if (!$this->has($name) && !empty($value)) {
626 17
            $this->set($name, $value);
627
        }
628 27
    }
629
630
    /**
631
     * Convenience method to assist with setting attributes
632
     *
633
     * Set attribute $name to $value, replacing $value with $default if $value is empty, or if the
634
     * value is not one of the values specified in the (optional) $enum array
635
     *
636
     * @param string $name  attribute name
637
     * @param mixed  $value attribute value
638
     *
639
     * @return void
640
     */
641 38
    public function setIfNotSet($name, $value)
642
    {
643
        // don't overwrite
644 38
        if (!$this->has($name)) {
645 38
            $this->set($name, $value);
646
        }
647 38
    }
648
}
649