Passed
Pull Request — master (#19447)
by Fedonyuk
09:24
created

BaseHtml   F

Complexity

Total Complexity 279

Size/Duplication

Total Lines 2356
Duplicated Lines 0 %

Test Coverage

Coverage 99.17%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 592
c 3
b 0
f 0
dl 0
loc 2356
ccs 595
cts 600
cp 0.9917
rs 2
wmc 279

74 Methods

Rating   Name   Duplication   Size   Complexity  
A activeRadioList() 0 3 1
A setActivePlaceholder() 0 5 3
A activeHiddenInput() 0 3 1
A activeTextInput() 0 3 1
A tag() 0 7 4
B activeBooleanInput() 0 26 8
A encode() 0 3 2
A mailto() 0 4 2
A textarea() 0 5 1
A activeDropDownList() 0 7 2
C radioList() 0 48 12
A dropDownList() 0 9 2
A checkbox() 0 3 1
A activeCheckboxList() 0 3 1
A textInput() 0 3 1
A cssFile() 0 17 5
A resetButton() 0 4 1
F checkboxList() 0 54 14
A fileInput() 0 3 1
A wrapIntoCondition() 0 7 2
C beginForm() 0 44 12
B listBox() 0 26 9
A activePasswordInput() 0 3 1
A endTag() 0 7 3
A endForm() 0 3 1
A jsFile() 0 10 2
A script() 0 3 1
A activeFileInput() 0 19 3
A radio() 0 3 1
A collectErrors() 0 22 5
A activeHint() 0 10 3
A a() 0 7 2
A activeInput() 0 12 4
A decode() 0 3 1
A activeTextarea() 0 15 4
A passwordInput() 0 3 1
A activeCheckbox() 0 3 1
A resetInput() 0 5 1
A ol() 0 4 1
A submitInput() 0 5 1
A hiddenInput() 0 3 1
A beginTag() 0 7 3
A submitButton() 0 4 1
A button() 0 7 2
A errorSummary() 0 17 4
A csrfMetaTags() 0 9 3
A buttonInput() 0 5 1
A activeLabel() 0 6 1
A style() 0 3 1
B normalizeMaxLength() 0 9 7
A ul() 0 25 5
A error() 0 12 3
A activeRadio() 0 3 1
A img() 0 17 5
A input() 0 8 3
A label() 0 4 1
A activeListBox() 0 3 1
B booleanInput() 0 31 8
B addCssStyle() 0 15 8
A activeListInput() 0 24 6
A getInputId() 0 4 1
A escapeJsRegularExpression() 0 16 3
F renderSelectOptions() 0 57 21
C getAttributeValue() 0 32 13
A mergeCssClasses() 0 11 6
A getAttributeName() 0 7 2
A cssStyleToArray() 0 11 3
A removeCssStyle() 0 8 4
A cssStyleFromArray() 0 8 3
A addCssClass() 0 11 3
A getInputName() 0 16 5
A getInputIdByName() 0 5 2
D renderTagAttributes() 0 55 21
A removeCssClass() 0 17 5

How to fix   Complexity   

Complex Class

Complex classes like BaseHtml 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 BaseHtml, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @link https://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license https://www.yiiframework.com/license/
6
 */
7
8
namespace yii\helpers;
9
10
use Yii;
11
use yii\base\InvalidArgumentException;
12
use yii\base\Model;
13
use yii\db\ActiveRecordInterface;
14
use yii\validators\StringValidator;
15
use yii\web\Request;
16
17
/**
18
 * BaseHtml provides concrete implementation for [[Html]].
19
 *
20
 * Do not use BaseHtml. Use [[Html]] instead.
21
 *
22
 * @author Qiang Xue <[email protected]>
23
 * @since 2.0
24
 */
25
class BaseHtml
26
{
27
    /**
28
     * @var string Regular expression used for attribute name validation.
29
     * @since 2.0.12
30
     */
31
    public static $attributeRegex = '/(^|.*\])([\w\.\+]+)(\[.*|$)/u';
32
    /**
33
     * @var array list of void elements (element name => 1)
34
     * @see https://html.spec.whatwg.org/multipage/syntax.html#void-element
35
     */
36
    public static $voidElements = [
37
        'area' => 1,
38
        'base' => 1,
39
        'br' => 1,
40
        'col' => 1,
41
        'command' => 1,
42
        'embed' => 1,
43
        'hr' => 1,
44
        'img' => 1,
45
        'input' => 1,
46
        'keygen' => 1,
47
        'link' => 1,
48
        'meta' => 1,
49
        'param' => 1,
50
        'source' => 1,
51
        'track' => 1,
52
        'wbr' => 1,
53
    ];
54
    /**
55
     * @var array the preferred order of attributes in a tag. This mainly affects the order of the attributes
56
     * that are rendered by [[renderTagAttributes()]].
57
     */
58
    public static $attributeOrder = [
59
        'type',
60
        'id',
61
        'class',
62
        'name',
63
        'value',
64
65
        'href',
66
        'src',
67
        'srcset',
68
        'form',
69
        'action',
70
        'method',
71
72
        'selected',
73
        'checked',
74
        'readonly',
75
        'disabled',
76
        'multiple',
77
78
        'size',
79
        'maxlength',
80
        'width',
81
        'height',
82
        'rows',
83
        'cols',
84
85
        'alt',
86
        'title',
87
        'rel',
88
        'media',
89
    ];
90
    /**
91
     * @var array list of tag attributes that should be specially handled when their values are of array type.
92
     * In particular, if the value of the `data` attribute is `['name' => 'xyz', 'age' => 13]`, two attributes
93
     * will be generated instead of one: `data-name="xyz" data-age="13"`.
94
     * @since 2.0.3
95
     */
96
    public static $dataAttributes = ['aria', 'data', 'data-ng', 'ng'];
97
    /**
98
     * @var bool whether to removes duplicate class names in tag attribute `class`
99
     * @see mergeCssClasses()
100
     * @see renderTagAttributes()
101
     * @since 2.0.44
102
     */
103
    public static $normalizeClassAttribute = false;
104
105
106
    /**
107
     * Encodes special characters into HTML entities.
108
     * The [[\yii\base\Application::charset|application charset]] will be used for encoding.
109
     * @param string $content the content to be encoded
110
     * @param bool $doubleEncode whether to encode HTML entities in `$content`. If false,
111
     * HTML entities in `$content` will not be further encoded.
112
     * @return string the encoded content
113
     * @see decode()
114
     * @see https://www.php.net/manual/en/function.htmlspecialchars.php
115
     */
116 250
    public static function encode($content, $doubleEncode = true)
117
    {
118 250
        return htmlspecialchars((string)$content, ENT_QUOTES | ENT_SUBSTITUTE, Yii::$app ? Yii::$app->charset : 'UTF-8', $doubleEncode);
119
    }
120
121
    /**
122
     * Decodes special HTML entities back to the corresponding characters.
123
     * This is the opposite of [[encode()]].
124
     * @param string $content the content to be decoded
125
     * @return string the decoded content
126
     * @see encode()
127
     * @see https://www.php.net/manual/en/function.htmlspecialchars-decode.php
128
     */
129 1
    public static function decode($content)
130
    {
131 1
        return htmlspecialchars_decode($content, ENT_QUOTES);
132
    }
133
134
    /**
135
     * Generates a complete HTML tag.
136
     * @param string|bool|null $name the tag name. If $name is `null` or `false`, the corresponding content will be rendered without any tag.
137
     * @param string $content the content to be enclosed between the start and end tags. It will not be HTML-encoded.
138
     * If this is coming from end users, you should consider [[encode()]] it to prevent XSS attacks.
139
     * @param array $options the HTML tag attributes (HTML options) in terms of name-value pairs.
140
     * These will be rendered as the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
141
     * If a value is null, the corresponding attribute will not be rendered.
142
     *
143
     * For example when using `['class' => 'my-class', 'target' => '_blank', 'value' => null]` it will result in the
144
     * html attributes rendered like this: `class="my-class" target="_blank"`.
145
     *
146
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
147
     *
148
     * @return string the generated HTML tag
149
     * @see beginTag()
150
     * @see endTag()
151
     */
152 236
    public static function tag($name, $content = '', $options = [])
153
    {
154 236
        if ($name === null || $name === false) {
155 3
            return $content;
156
        }
157 235
        $html = "<$name" . static::renderTagAttributes($options) . '>';
158 235
        return isset(static::$voidElements[strtolower($name)]) ? $html : "$html$content</$name>";
0 ignored issues
show
Bug introduced by
It seems like $name can also be of type true; however, parameter $string of strtolower() 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

158
        return isset(static::$voidElements[strtolower(/** @scrutinizer ignore-type */ $name)]) ? $html : "$html$content</$name>";
Loading history...
159
    }
160
161
    /**
162
     * Generates a start tag.
163
     * @param string|bool|null $name the tag name. If $name is `null` or `false`, the corresponding content will be rendered without any tag.
164
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
165
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
166
     * If a value is null, the corresponding attribute will not be rendered.
167
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
168
     * @return string the generated start tag
169
     * @see endTag()
170
     * @see tag()
171
     */
172 53
    public static function beginTag($name, $options = [])
173
    {
174 53
        if ($name === null || $name === false) {
175 3
            return '';
176
        }
177
178 53
        return "<$name" . static::renderTagAttributes($options) . '>';
179
    }
180
181
    /**
182
     * Generates an end tag.
183
     * @param string|bool|null $name the tag name. If $name is `null` or `false`, the corresponding content will be rendered without any tag.
184
     * @return string the generated end tag
185
     * @see beginTag()
186
     * @see tag()
187
     */
188 19
    public static function endTag($name)
189
    {
190 19
        if ($name === null || $name === false) {
191 3
            return '';
192
        }
193
194 18
        return "</$name>";
195
    }
196
197
    /**
198
     * Generates a style tag.
199
     * @param string $content the style content
200
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
201
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
202
     * If a value is null, the corresponding attribute will not be rendered.
203
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
204
     * @return string the generated style tag
205
     */
206 1
    public static function style($content, $options = [])
207
    {
208 1
        return static::tag('style', $content, $options);
209
    }
210
211
    /**
212
     * Generates a script tag.
213
     * @param string $content the script content
214
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
215
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
216
     * If a value is null, the corresponding attribute will not be rendered.
217
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
218
     * @return string the generated script tag
219
     */
220 2
    public static function script($content, $options = [])
221
    {
222 2
        return static::tag('script', $content, $options);
223
    }
224
225
    /**
226
     * Generates a link tag that refers to an external CSS file.
227
     * @param array|string $url the URL of the external CSS file. This parameter will be processed by [[Url::to()]].
228
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
229
     *
230
     * - condition: specifies the conditional comments for IE, e.g., `lt IE 9`. When this is specified,
231
     *   the generated `link` tag will be enclosed within the conditional comments. This is mainly useful
232
     *   for supporting old versions of IE browsers.
233
     * - noscript: if set to true, `link` tag will be wrapped into `<noscript>` tags.
234
     *
235
     * The rest of the options will be rendered as the attributes of the resulting link tag. The values will
236
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
237
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
238
     * @return string the generated link tag
239
     * @see Url::to()
240
     */
241 21
    public static function cssFile($url, $options = [])
242
    {
243 21
        if (!isset($options['rel'])) {
244 21
            $options['rel'] = 'stylesheet';
245
        }
246 21
        $options['href'] = Url::to($url);
247
248 21
        if (isset($options['condition'])) {
249 1
            $condition = $options['condition'];
250 1
            unset($options['condition']);
251 1
            return self::wrapIntoCondition(static::tag('link', '', $options), $condition);
252 21
        } elseif (isset($options['noscript']) && $options['noscript'] === true) {
253 1
            unset($options['noscript']);
254 1
            return '<noscript>' . static::tag('link', '', $options) . '</noscript>';
255
        }
256
257 21
        return static::tag('link', '', $options);
258
    }
259
260
    /**
261
     * Generates a script tag that refers to an external JavaScript file.
262
     * @param string $url the URL of the external JavaScript file. This parameter will be processed by [[Url::to()]].
263
     * @param array $options the tag options in terms of name-value pairs. The following option is specially handled:
264
     *
265
     * - condition: specifies the conditional comments for IE, e.g., `lt IE 9`. When this is specified,
266
     *   the generated `script` tag will be enclosed within the conditional comments. This is mainly useful
267
     *   for supporting old versions of IE browsers.
268
     *
269
     * The rest of the options will be rendered as the attributes of the resulting script tag. The values will
270
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
271
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
272
     * @return string the generated script tag
273
     * @see Url::to()
274
     */
275 25
    public static function jsFile($url, $options = [])
276
    {
277 25
        $options['src'] = Url::to($url);
278 25
        if (isset($options['condition'])) {
279 1
            $condition = $options['condition'];
280 1
            unset($options['condition']);
281 1
            return self::wrapIntoCondition(static::tag('script', '', $options), $condition);
282
        }
283
284 25
        return static::tag('script', '', $options);
285
    }
286
287
    /**
288
     * Wraps given content into conditional comments for IE, e.g., `lt IE 9`.
289
     * @param string $content raw HTML content.
290
     * @param string $condition condition string.
291
     * @return string generated HTML.
292
     */
293 2
    private static function wrapIntoCondition($content, $condition)
294
    {
295 2
        if (strpos($condition, '!IE') !== false) {
296 2
            return "<!--[if $condition]><!-->\n" . $content . "\n<!--<![endif]-->";
297
        }
298
299 2
        return "<!--[if $condition]>\n" . $content . "\n<![endif]-->";
300
    }
301
302
    /**
303
     * Generates the meta tags containing CSRF token information.
304
     * @return string the generated meta tags
305
     * @see Request::enableCsrfValidation
306
     */
307 4
    public static function csrfMetaTags()
308
    {
309 4
        $request = Yii::$app->getRequest();
310 4
        if ($request instanceof Request && $request->enableCsrfValidation) {
311 3
            return static::tag('meta', '', ['name' => 'csrf-param', 'content' => $request->csrfParam]) . "\n"
312 3
                . static::tag('meta', '', ['name' => 'csrf-token', 'content' => $request->getCsrfToken()]) . "\n";
313
        }
314
315 1
        return '';
316
    }
317
318
    /**
319
     * Generates a form start tag.
320
     * @param array|string $action the form action URL. This parameter will be processed by [[Url::to()]].
321
     * @param string $method the form submission method, such as "post", "get", "put", "delete" (case-insensitive).
322
     * Since most browsers only support "post" and "get", if other methods are given, they will
323
     * be simulated using "post", and a hidden input will be added which contains the actual method type.
324
     * See [[\yii\web\Request::methodParam]] for more details.
325
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
326
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
327
     * If a value is null, the corresponding attribute will not be rendered.
328
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
329
     *
330
     * Special options:
331
     *
332
     *  - `csrf`: whether to generate the CSRF hidden input. Defaults to true.
333
     *
334
     * @return string the generated form start tag.
335
     * @see endForm()
336
     */
337 50
    public static function beginForm($action = '', $method = 'post', $options = [])
338
    {
339 50
        $action = Url::to($action);
340
341 50
        $hiddenInputs = [];
342
343 50
        $request = Yii::$app->getRequest();
344 50
        if ($request instanceof Request) {
345 45
            if (strcasecmp($method, 'get') && strcasecmp($method, 'post')) {
346
                // simulate PUT, DELETE, etc. via POST
347 4
                $hiddenInputs[] = static::hiddenInput($request->methodParam, $method);
348 4
                $method = 'post';
349
            }
350 45
            $csrf = ArrayHelper::remove($options, 'csrf', true);
351
352 45
            if ($csrf && $request->enableCsrfValidation && strcasecmp($method, 'post') === 0) {
353 38
                $hiddenInputs[] = static::hiddenInput($request->csrfParam, $request->getCsrfToken());
354
            }
355
        }
356
357 50
        if (!strcasecmp($method, 'get') && ($pos = strpos($action, '?')) !== false) {
358
            // query parameters in the action are ignored for GET method
359
            // we use hidden fields to add them back
360 1
            foreach (explode('&', substr($action, $pos + 1)) as $pair) {
361 1
                if (($pos1 = strpos($pair, '=')) !== false) {
362 1
                    $hiddenInputs[] = static::hiddenInput(
363 1
                        urldecode(substr($pair, 0, $pos1)),
364 1
                        urldecode(substr($pair, $pos1 + 1))
365
                    );
366
                } else {
367 1
                    $hiddenInputs[] = static::hiddenInput(urldecode($pair), '');
368
                }
369
            }
370 1
            $action = substr($action, 0, $pos);
371
        }
372
373 50
        $options['action'] = $action;
374 50
        $options['method'] = $method;
375 50
        $form = static::beginTag('form', $options);
376 50
        if (!empty($hiddenInputs)) {
377 43
            $form .= "\n" . implode("\n", $hiddenInputs);
378
        }
379
380 50
        return $form;
381
    }
382
383
    /**
384
     * Generates a form end tag.
385
     * @return string the generated tag
386
     * @see beginForm()
387
     */
388 43
    public static function endForm()
389
    {
390 43
        return '</form>';
391
    }
392
393
    /**
394
     * Generates a hyperlink tag.
395
     * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code
396
     * such as an image tag. If this is coming from end users, you should consider [[encode()]]
397
     * it to prevent XSS attacks.
398
     * @param array|string|null $url the URL for the hyperlink tag. This parameter will be processed by [[Url::to()]]
399
     * and will be used for the "href" attribute of the tag. If this parameter is null, the "href" attribute
400
     * will not be generated.
401
     *
402
     * If you want to use an absolute url you can call [[Url::to()]] yourself, before passing the URL to this method,
403
     * like this:
404
     *
405
     * ```php
406
     * Html::a('link text', Url::to($url, true))
407
     * ```
408
     *
409
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
410
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
411
     * If a value is null, the corresponding attribute will not be rendered.
412
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
413
     * @return string the generated hyperlink
414
     * @see \yii\helpers\Url::to()
415
     */
416 18
    public static function a($text, $url = null, $options = [])
417
    {
418 18
        if ($url !== null) {
419 18
            $options['href'] = Url::to($url);
420
        }
421
422 18
        return static::tag('a', $text, $options);
423
    }
424
425
    /**
426
     * Generates a mailto hyperlink.
427
     * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code
428
     * such as an image tag. If this is coming from end users, you should consider [[encode()]]
429
     * it to prevent XSS attacks.
430
     * @param string|null $email email address. If this is null, the first parameter (link body) will be treated
431
     * as the email address and used.
432
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
433
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
434
     * If a value is null, the corresponding attribute will not be rendered.
435
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
436
     * @return string the generated mailto link
437
     */
438 2
    public static function mailto($text, $email = null, $options = [])
439
    {
440 2
        $options['href'] = 'mailto:' . ($email === null ? $text : $email);
441 2
        return static::tag('a', $text, $options);
442
    }
443
444
    /**
445
     * Generates an image tag.
446
     * @param array|string $src the image URL. This parameter will be processed by [[Url::to()]].
447
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
448
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
449
     * If a value is null, the corresponding attribute will not be rendered.
450
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
451
     *
452
     * Since version 2.0.12 It is possible to pass the `srcset` option as an array which keys are
453
     * descriptors and values are URLs. All URLs will be processed by [[Url::to()]].
454
     * @return string the generated image tag.
455
     */
456 10
    public static function img($src, $options = [])
457
    {
458 10
        $options['src'] = Url::to($src);
459
460 10
        if (isset($options['srcset']) && is_array($options['srcset'])) {
461 5
            $srcset = [];
462 5
            foreach ($options['srcset'] as $descriptor => $url) {
463 4
                $srcset[] = Url::to($url) . ' ' . $descriptor;
464
            }
465 5
            $options['srcset'] = implode(',', $srcset);
466
        }
467
468 10
        if (!isset($options['alt'])) {
469 9
            $options['alt'] = '';
470
        }
471
472 10
        return static::tag('img', '', $options);
473
    }
474
475
    /**
476
     * Generates a label tag.
477
     * @param string $content label text. It will NOT be HTML-encoded. Therefore you can pass in HTML code
478
     * such as an image tag. If this is is coming from end users, you should [[encode()]]
479
     * it to prevent XSS attacks.
480
     * @param string|null $for the ID of the HTML element that this label is associated with.
481
     * If this is null, the "for" attribute will not be generated.
482
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
483
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
484
     * If a value is null, the corresponding attribute will not be rendered.
485
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
486
     * @return string the generated label tag
487
     */
488 32
    public static function label($content, $for = null, $options = [])
489
    {
490 32
        $options['for'] = $for;
491 32
        return static::tag('label', $content, $options);
492
    }
493
494
    /**
495
     * Generates a button tag.
496
     * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
497
     * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
498
     * you should consider [[encode()]] it to prevent XSS attacks.
499
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
500
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
501
     * If a value is null, the corresponding attribute will not be rendered.
502
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
503
     * @return string the generated button tag
504
     */
505 3
    public static function button($content = 'Button', $options = [])
506
    {
507 3
        if (!isset($options['type'])) {
508 1
            $options['type'] = 'button';
509
        }
510
511 3
        return static::tag('button', $content, $options);
512
    }
513
514
    /**
515
     * Generates a submit button tag.
516
     *
517
     * Be careful when naming form elements such as submit buttons. According to the [jQuery documentation](https://api.jquery.com/submit/) there
518
     * are some reserved names that can cause conflicts, e.g. `submit`, `length`, or `method`.
519
     *
520
     * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
521
     * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
522
     * you should consider [[encode()]] it to prevent XSS attacks.
523
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
524
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
525
     * If a value is null, the corresponding attribute will not be rendered.
526
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
527
     * @return string the generated submit button tag
528
     */
529 1
    public static function submitButton($content = 'Submit', $options = [])
530
    {
531 1
        $options['type'] = 'submit';
532 1
        return static::button($content, $options);
533
    }
534
535
    /**
536
     * Generates a reset button tag.
537
     * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
538
     * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
539
     * you should consider [[encode()]] it to prevent XSS attacks.
540
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
541
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
542
     * If a value is null, the corresponding attribute will not be rendered.
543
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
544
     * @return string the generated reset button tag
545
     */
546 1
    public static function resetButton($content = 'Reset', $options = [])
547
    {
548 1
        $options['type'] = 'reset';
549 1
        return static::button($content, $options);
550
    }
551
552
    /**
553
     * Generates an input type of the given type.
554
     * @param string $type the type attribute.
555
     * @param string|null $name the name attribute. If it is null, the name attribute will not be generated.
556
     * @param string|null $value the value attribute. If it is null, the value attribute will not be generated.
557
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
558
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
559
     * If a value is null, the corresponding attribute will not be rendered.
560
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
561
     * @return string the generated input tag
562
     */
563 99
    public static function input($type, $name = null, $value = null, $options = [])
564
    {
565 99
        if (!isset($options['type'])) {
566 99
            $options['type'] = $type;
567
        }
568 99
        $options['name'] = $name;
569 99
        $options['value'] = $value === null ? null : (string) $value;
570 99
        return static::tag('input', '', $options);
571
    }
572
573
    /**
574
     * Generates an input button.
575
     * @param string|null $label the value attribute. If it is null, the value attribute will not be generated.
576
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
577
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
578
     * If a value is null, the corresponding attribute will not be rendered.
579
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
580
     * @return string the generated button tag
581
     */
582 1
    public static function buttonInput($label = 'Button', $options = [])
583
    {
584 1
        $options['type'] = 'button';
585 1
        $options['value'] = $label;
586 1
        return static::tag('input', '', $options);
587
    }
588
589
    /**
590
     * Generates a submit input button.
591
     *
592
     * Be careful when naming form elements such as submit buttons. According to the [jQuery documentation](https://api.jquery.com/submit/) there
593
     * are some reserved names that can cause conflicts, e.g. `submit`, `length`, or `method`.
594
     *
595
     * @param string|null $label the value attribute. If it is null, the value attribute will not be generated.
596
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
597
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
598
     * If a value is null, the corresponding attribute will not be rendered.
599
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
600
     * @return string the generated button tag
601
     */
602 1
    public static function submitInput($label = 'Submit', $options = [])
603
    {
604 1
        $options['type'] = 'submit';
605 1
        $options['value'] = $label;
606 1
        return static::tag('input', '', $options);
607
    }
608
609
    /**
610
     * Generates a reset input button.
611
     * @param string|null $label the value attribute. If it is null, the value attribute will not be generated.
612
     * @param array $options the attributes of the button tag. The values will be HTML-encoded using [[encode()]].
613
     * Attributes whose value is null will be ignored and not put in the tag returned.
614
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
615
     * @return string the generated button tag
616
     */
617 1
    public static function resetInput($label = 'Reset', $options = [])
618
    {
619 1
        $options['type'] = 'reset';
620 1
        $options['value'] = $label;
621 1
        return static::tag('input', '', $options);
622
    }
623
624
    /**
625
     * Generates a text input field.
626
     * @param string $name the name attribute.
627
     * @param string|null $value the value attribute. If it is null, the value attribute will not be generated.
628
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
629
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
630
     * If a value is null, the corresponding attribute will not be rendered.
631
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
632
     * @return string the generated text input tag
633
     */
634 1
    public static function textInput($name, $value = null, $options = [])
635
    {
636 1
        return static::input('text', $name, $value, $options);
637
    }
638
639
    /**
640
     * Generates a hidden input field.
641
     * @param string $name the name attribute.
642
     * @param string|null $value the value attribute. If it is null, the value attribute will not be generated.
643
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
644
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
645
     * If a value is null, the corresponding attribute will not be rendered.
646
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
647
     * @return string the generated hidden input tag
648
     */
649 59
    public static function hiddenInput($name, $value = null, $options = [])
650
    {
651 59
        return static::input('hidden', $name, $value, $options);
652
    }
653
654
    /**
655
     * Generates a password input field.
656
     * @param string $name the name attribute.
657
     * @param string|null $value the value attribute. If it is null, the value attribute will not be generated.
658
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
659
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
660
     * If a value is null, the corresponding attribute will not be rendered.
661
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
662
     * @return string the generated password input tag
663
     */
664 1
    public static function passwordInput($name, $value = null, $options = [])
665
    {
666 1
        return static::input('password', $name, $value, $options);
667
    }
668
669
    /**
670
     * Generates a file input field.
671
     * To use a file input field, you should set the enclosing form's "enctype" attribute to
672
     * be "multipart/form-data". After the form is submitted, the uploaded file information
673
     * can be obtained via $_FILES[$name] (see PHP documentation).
674
     * @param string $name the name attribute.
675
     * @param string|null $value the value attribute. If it is null, the value attribute will not be generated.
676
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
677
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
678
     * If a value is null, the corresponding attribute will not be rendered.
679
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
680
     * @return string the generated file input tag
681
     */
682 1
    public static function fileInput($name, $value = null, $options = [])
683
    {
684 1
        return static::input('file', $name, $value, $options);
685
    }
686
687
    /**
688
     * Generates a text area input.
689
     * @param string $name the input name
690
     * @param string $value the input value. Note that it will be encoded using [[encode()]].
691
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
692
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
693
     * If a value is null, the corresponding attribute will not be rendered.
694
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
695
     * The following special options are recognized:
696
     *
697
     * - `doubleEncode`: whether to double encode HTML entities in `$value`. If `false`, HTML entities in `$value` will not
698
     *   be further encoded. This option is available since version 2.0.11.
699
     *
700
     * @return string the generated text area tag
701
     */
702 8
    public static function textarea($name, $value = '', $options = [])
703
    {
704 8
        $options['name'] = $name;
705 8
        $doubleEncode = ArrayHelper::remove($options, 'doubleEncode', true);
706 8
        return static::tag('textarea', static::encode($value, $doubleEncode), $options);
707
    }
708
709
    /**
710
     * Generates a radio button input.
711
     * @param string $name the name attribute.
712
     * @param bool $checked whether the radio button should be checked.
713
     * @param array $options the tag options in terms of name-value pairs.
714
     * See [[booleanInput()]] for details about accepted attributes.
715
     *
716
     * @return string the generated radio button tag
717
     */
718 14
    public static function radio($name, $checked = false, $options = [])
719
    {
720 14
        return static::booleanInput('radio', $name, $checked, $options);
721
    }
722
723
    /**
724
     * Generates a checkbox input.
725
     * @param string $name the name attribute.
726
     * @param bool $checked whether the checkbox should be checked.
727
     * @param array $options the tag options in terms of name-value pairs.
728
     * See [[booleanInput()]] for details about accepted attributes.
729
     *
730
     * @return string the generated checkbox tag
731
     */
732 14
    public static function checkbox($name, $checked = false, $options = [])
733
    {
734 14
        return static::booleanInput('checkbox', $name, $checked, $options);
735
    }
736
737
    /**
738
     * Generates a boolean input.
739
     * @param string $type the input type. This can be either `radio` or `checkbox`.
740
     * @param string $name the name attribute.
741
     * @param bool $checked whether the checkbox should be checked.
742
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
743
     *
744
     * - uncheck: string, the value associated with the uncheck state of the checkbox. When this attribute
745
     *   is present, a hidden input will be generated so that if the checkbox is not checked and is submitted,
746
     *   the value of this attribute will still be submitted to the server via the hidden input.
747
     * - label: string, a label displayed next to the checkbox.  It will NOT be HTML-encoded. Therefore you can pass
748
     *   in HTML code such as an image tag. If this is is coming from end users, you should [[encode()]] it to prevent XSS attacks.
749
     *   When this option is specified, the checkbox will be enclosed by a label tag.
750
     * - labelOptions: array, the HTML attributes for the label tag. Do not set this option unless you set the "label" option.
751
     *
752
     * The rest of the options will be rendered as the attributes of the resulting checkbox tag. The values will
753
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
754
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
755
     *
756
     * @return string the generated checkbox tag
757
     * @since 2.0.9
758
     */
759 27
    protected static function booleanInput($type, $name, $checked = false, $options = [])
760
    {
761
        // 'checked' option has priority over $checked argument
762 27
        if (!isset($options['checked'])) {
763 26
            $options['checked'] = (bool) $checked;
764
        }
765 27
        $value = array_key_exists('value', $options) ? $options['value'] : '1';
766 27
        if (isset($options['uncheck'])) {
767
            // add a hidden field so that if the checkbox is not selected, it still submits a value
768 7
            $hiddenOptions = [];
769 7
            if (isset($options['form'])) {
770 1
                $hiddenOptions['form'] = $options['form'];
771
            }
772
            // make sure disabled input is not sending any value
773 7
            if (!empty($options['disabled'])) {
774 2
                $hiddenOptions['disabled'] = $options['disabled'];
775
            }
776 7
            $hidden = static::hiddenInput($name, $options['uncheck'], $hiddenOptions);
777 7
            unset($options['uncheck']);
778
        } else {
779 22
            $hidden = '';
780
        }
781 27
        if (isset($options['label'])) {
782 16
            $label = $options['label'];
783 16
            $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : [];
784 16
            unset($options['label'], $options['labelOptions']);
785 16
            $content = static::label(static::input($type, $name, $value, $options) . ' ' . $label, null, $labelOptions);
786 16
            return $hidden . $content;
787
        }
788
789 15
        return $hidden . static::input($type, $name, $value, $options);
790
    }
791
792
    /**
793
     * Generates a drop-down list.
794
     * @param string $name the input name
795
     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).
796
     * @param array $items the option data items. The array keys are option values, and the array values
797
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
798
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
799
     * If you have a list of data models, you may convert them into the format described above using
800
     * [[\yii\helpers\ArrayHelper::map()]].
801
     *
802
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
803
     * the labels will also be HTML-encoded.
804
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
805
     *
806
     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array
807
     *   to override the value and to set other tag attributes:
808
     *
809
     *   ```php
810
     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],
811
     *   ```
812
     *
813
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
814
     *   and the array values are the extra attributes for the corresponding option tags. For example,
815
     *
816
     *   ```php
817
     *   [
818
     *       'value1' => ['disabled' => true],
819
     *       'value2' => ['label' => 'value 2'],
820
     *   ];
821
     *   ```
822
     *
823
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
824
     *   except that the array keys represent the optgroup labels specified in $items.
825
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
826
     *   Defaults to false.
827
     * - encode: bool, whether to encode option prompt and option value characters.
828
     *   Defaults to `true`. This option is available since 2.0.3.
829
     * - strict: boolean, if `$selection` is an array and this value is true a strict comparison will be performed on `$items` keys. Defaults to false.
830
     *   This option is available since 2.0.37.
831
     *
832
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
833
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
834
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
835
     *
836
     * @return string the generated drop-down list tag
837
     */
838 3
    public static function dropDownList($name, $selection = null, $items = [], $options = [])
839
    {
840 3
        if (!empty($options['multiple'])) {
841 1
            return static::listBox($name, $selection, $items, $options);
842
        }
843 3
        $options['name'] = $name;
844 3
        unset($options['unselect']);
845 3
        $selectOptions = static::renderSelectOptions($selection, $items, $options);
846 3
        return static::tag('select', "\n" . $selectOptions . "\n", $options);
847
    }
848
849
    /**
850
     * Generates a list box.
851
     * @param string $name the input name
852
     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).
853
     * @param array $items the option data items. The array keys are option values, and the array values
854
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
855
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
856
     * If you have a list of data models, you may convert them into the format described above using
857
     * [[\yii\helpers\ArrayHelper::map()]].
858
     *
859
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
860
     * the labels will also be HTML-encoded.
861
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
862
     *
863
     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array
864
     *   to override the value and to set other tag attributes:
865
     *
866
     *   ```php
867
     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],
868
     *   ```
869
     *
870
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
871
     *   and the array values are the extra attributes for the corresponding option tags. For example,
872
     *
873
     *   ```php
874
     *   [
875
     *       'value1' => ['disabled' => true],
876
     *       'value2' => ['label' => 'value 2'],
877
     *   ];
878
     *   ```
879
     *
880
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
881
     *   except that the array keys represent the optgroup labels specified in $items.
882
     * - unselect: string, the value that will be submitted when no option is selected.
883
     *   When this attribute is set, a hidden field will be generated so that if no option is selected in multiple
884
     *   mode, we can still obtain the posted unselect value.
885
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
886
     *   Defaults to false.
887
     * - encode: bool, whether to encode option prompt and option value characters.
888
     *   Defaults to `true`. This option is available since 2.0.3.
889
     * - strict: boolean, if `$selection` is an array and this value is true a strict comparison will be performed on `$items` keys. Defaults to false.
890
     *   This option is available since 2.0.37.
891
     *
892
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
893
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
894
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
895
     *
896
     * @return string the generated list box tag
897
     */
898 5
    public static function listBox($name, $selection = null, $items = [], $options = [])
899
    {
900 5
        if (!array_key_exists('size', $options)) {
901 5
            $options['size'] = 4;
902
        }
903 5
        if (!empty($options['multiple']) && !empty($name) && substr_compare($name, '[]', -2, 2)) {
904 4
            $name .= '[]';
905
        }
906 5
        $options['name'] = $name;
907 5
        if (isset($options['unselect'])) {
908
            // add a hidden field so that if the list box has no option being selected, it still submits a value
909 4
            if (!empty($name) && substr_compare($name, '[]', -2, 2) === 0) {
910 2
                $name = substr($name, 0, -2);
911
            }
912 4
            $hiddenOptions = [];
913
            // make sure disabled input is not sending any value
914 4
            if (!empty($options['disabled'])) {
915 1
                $hiddenOptions['disabled'] = $options['disabled'];
916
            }
917 4
            $hidden = static::hiddenInput($name, $options['unselect'], $hiddenOptions);
918 4
            unset($options['unselect']);
919
        } else {
920 2
            $hidden = '';
921
        }
922 5
        $selectOptions = static::renderSelectOptions($selection, $items, $options);
923 5
        return $hidden . static::tag('select', "\n" . $selectOptions . "\n", $options);
924
    }
925
926
    /**
927
     * Generates a list of checkboxes.
928
     * A checkbox list allows multiple selection, like [[listBox()]].
929
     * As a result, the corresponding submitted value is an array.
930
     * @param string $name the name attribute of each checkbox.
931
     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).
932
     * @param array $items the data item used to generate the checkboxes.
933
     * The array keys are the checkbox values, while the array values are the corresponding labels.
934
     * @param array $options options (name => config) for the checkbox list container tag.
935
     * The following options are specially handled:
936
     *
937
     * - tag: string|false, the tag name of the container element. False to render checkbox without container.
938
     *   See also [[tag()]].
939
     * - unselect: string, the value that should be submitted when none of the checkboxes is selected.
940
     *   By setting this option, a hidden input will be generated.
941
     * - disabled: boolean, whether the generated by unselect option hidden input should be disabled. Defaults to false.
942
     *   This option is available since version 2.0.16.
943
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
944
     *   This option is ignored if `item` option is set.
945
     * - strict: boolean, if `$selection` is an array and this value is true a strict comparison will be performed on `$items` keys. Defaults to false.
946
     *   This option is available since 2.0.37.
947
     * - separator: string, the HTML code that separates items.
948
     * - itemOptions: array, the options for generating the checkbox tag using [[checkbox()]].
949
     * - item: callable, a callback that can be used to customize the generation of the HTML code
950
     *   corresponding to a single item in $items. The signature of this callback must be:
951
     *
952
     *   ```php
953
     *   function ($index, $label, $name, $checked, $value)
954
     *   ```
955
     *
956
     *   where $index is the zero-based index of the checkbox in the whole list; $label
957
     *   is the label for the checkbox; and $name, $value and $checked represent the name,
958
     *   value and the checked status of the checkbox input, respectively.
959
     *
960
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
961
     *
962
     * @return string the generated checkbox list
963
     */
964 5
    public static function checkboxList($name, $selection = null, $items = [], $options = [])
965
    {
966 5
        if (substr($name, -2) !== '[]') {
967 5
            $name .= '[]';
968
        }
969 5
        if (ArrayHelper::isTraversable($selection)) {
970 3
            $selection = array_map('strval', ArrayHelper::toArray($selection));
971
        }
972
973 5
        $formatter = ArrayHelper::remove($options, 'item');
974 5
        $itemOptions = ArrayHelper::remove($options, 'itemOptions', []);
975 5
        $encode = ArrayHelper::remove($options, 'encode', true);
976 5
        $separator = ArrayHelper::remove($options, 'separator', "\n");
977 5
        $tag = ArrayHelper::remove($options, 'tag', 'div');
978 5
        $strict = ArrayHelper::remove($options, 'strict', false);
979
980 5
        $lines = [];
981 5
        $index = 0;
982 5
        foreach ($items as $value => $label) {
983 5
            $checked = $selection !== null &&
984 4
                (!ArrayHelper::isTraversable($selection) && !strcmp($value, $selection)
0 ignored issues
show
Bug introduced by
It seems like $selection can also be of type array; however, parameter $string2 of strcmp() 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

984
                (!ArrayHelper::isTraversable($selection) && !strcmp($value, /** @scrutinizer ignore-type */ $selection)
Loading history...
985 5
                    || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn((string)$value, $selection, $strict));
0 ignored issues
show
Bug introduced by
It seems like $selection can also be of type string; however, parameter $haystack of yii\helpers\BaseArrayHelper::isIn() does only seem to accept iterable, 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

985
                    || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn((string)$value, /** @scrutinizer ignore-type */ $selection, $strict));
Loading history...
986 5
            if ($formatter !== null) {
987 1
                $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
988
            } else {
989 5
                $lines[] = static::checkbox($name, $checked, array_merge([
990 5
                    'value' => $value,
991 5
                    'label' => $encode ? static::encode($label) : $label,
992
                ], $itemOptions));
0 ignored issues
show
Bug introduced by
It seems like $itemOptions can also be of type null; however, parameter $arrays of array_merge() does only seem to accept array, 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

992
                ], /** @scrutinizer ignore-type */ $itemOptions));
Loading history...
993
            }
994 5
            $index++;
995
        }
996
997 5
        if (isset($options['unselect'])) {
998
            // add a hidden field so that if the list box has no option being selected, it still submits a value
999 3
            $name2 = substr($name, -2) === '[]' ? substr($name, 0, -2) : $name;
1000 3
            $hiddenOptions = [];
1001
            // make sure disabled input is not sending any value
1002 3
            if (!empty($options['disabled'])) {
1003 1
                $hiddenOptions['disabled'] = $options['disabled'];
1004
            }
1005 3
            $hidden = static::hiddenInput($name2, $options['unselect'], $hiddenOptions);
1006 3
            unset($options['unselect'], $options['disabled']);
1007
        } else {
1008 3
            $hidden = '';
1009
        }
1010
1011 5
        $visibleContent = implode($separator, $lines);
0 ignored issues
show
Bug introduced by
It seems like $separator can also be of type null; however, parameter $glue of implode() 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

1011
        $visibleContent = implode(/** @scrutinizer ignore-type */ $separator, $lines);
Loading history...
1012
1013 5
        if ($tag === false) {
1014 1
            return $hidden . $visibleContent;
1015
        }
1016
1017 5
        return $hidden . static::tag($tag, $visibleContent, $options);
1018
    }
1019
1020
    /**
1021
     * Generates a list of radio buttons.
1022
     * A radio button list is like a checkbox list, except that it only allows single selection.
1023
     * @param string $name the name attribute of each radio button.
1024
     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).
1025
     * @param array $items the data item used to generate the radio buttons.
1026
     * The array keys are the radio button values, while the array values are the corresponding labels.
1027
     * @param array $options options (name => config) for the radio button list container tag.
1028
     * The following options are specially handled:
1029
     *
1030
     * - tag: string|false, the tag name of the container element. False to render radio buttons without container.
1031
     *   See also [[tag()]].
1032
     * - unselect: string, the value that should be submitted when none of the radio buttons is selected.
1033
     *   By setting this option, a hidden input will be generated.
1034
     * - disabled: boolean, whether the generated by unselect option hidden input should be disabled. Defaults to false.
1035
     *   This option is available since version 2.0.16.
1036
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
1037
     *   This option is ignored if `item` option is set.
1038
     * - strict: boolean, if `$selection` is an array and this value is true a strict comparison will be performed on `$items` keys. Defaults to false.
1039
     *   This option is available since 2.0.37.
1040
     * - separator: string, the HTML code that separates items.
1041
     * - itemOptions: array, the options for generating the radio button tag using [[radio()]].
1042
     * - item: callable, a callback that can be used to customize the generation of the HTML code
1043
     *   corresponding to a single item in $items. The signature of this callback must be:
1044
     *
1045
     *   ```php
1046
     *   function ($index, $label, $name, $checked, $value)
1047
     *   ```
1048
     *
1049
     *   where $index is the zero-based index of the radio button in the whole list; $label
1050
     *   is the label for the radio button; and $name, $value and $checked represent the name,
1051
     *   value and the checked status of the radio button input, respectively.
1052
     *
1053
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1054
     *
1055
     * @return string the generated radio button list
1056
     */
1057 4
    public static function radioList($name, $selection = null, $items = [], $options = [])
1058
    {
1059 4
        if (ArrayHelper::isTraversable($selection)) {
1060 2
            $selection = array_map('strval', ArrayHelper::toArray($selection));
1061
        }
1062
1063 4
        $formatter = ArrayHelper::remove($options, 'item');
1064 4
        $itemOptions = ArrayHelper::remove($options, 'itemOptions', []);
1065 4
        $encode = ArrayHelper::remove($options, 'encode', true);
1066 4
        $separator = ArrayHelper::remove($options, 'separator', "\n");
1067 4
        $tag = ArrayHelper::remove($options, 'tag', 'div');
1068 4
        $strict = ArrayHelper::remove($options, 'strict', false);
1069
1070 4
        $hidden = '';
1071 4
        if (isset($options['unselect'])) {
1072
            // add a hidden field so that if the list box has no option being selected, it still submits a value
1073 3
            $hiddenOptions = [];
1074
            // make sure disabled input is not sending any value
1075 3
            if (!empty($options['disabled'])) {
1076 1
                $hiddenOptions['disabled'] = $options['disabled'];
1077
            }
1078 3
            $hidden =  static::hiddenInput($name, $options['unselect'], $hiddenOptions);
1079 3
            unset($options['unselect'], $options['disabled']);
1080
        }
1081
1082 4
        $lines = [];
1083 4
        $index = 0;
1084 4
        foreach ($items as $value => $label) {
1085 4
            $checked = $selection !== null &&
1086 2
                (!ArrayHelper::isTraversable($selection) && !strcmp($value, $selection)
0 ignored issues
show
Bug introduced by
It seems like $selection can also be of type array; however, parameter $string2 of strcmp() 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

1086
                (!ArrayHelper::isTraversable($selection) && !strcmp($value, /** @scrutinizer ignore-type */ $selection)
Loading history...
1087 4
                    || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn((string)$value, $selection, $strict));
0 ignored issues
show
Bug introduced by
It seems like $selection can also be of type string; however, parameter $haystack of yii\helpers\BaseArrayHelper::isIn() does only seem to accept iterable, 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

1087
                    || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn((string)$value, /** @scrutinizer ignore-type */ $selection, $strict));
Loading history...
1088 4
            if ($formatter !== null) {
1089 1
                $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
1090
            } else {
1091 4
                $lines[] = static::radio($name, $checked, array_merge([
1092 4
                    'value' => $value,
1093 4
                    'label' => $encode ? static::encode($label) : $label,
1094
                ], $itemOptions));
0 ignored issues
show
Bug introduced by
It seems like $itemOptions can also be of type null; however, parameter $arrays of array_merge() does only seem to accept array, 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

1094
                ], /** @scrutinizer ignore-type */ $itemOptions));
Loading history...
1095
            }
1096 4
            $index++;
1097
        }
1098 4
        $visibleContent = implode($separator, $lines);
0 ignored issues
show
Bug introduced by
It seems like $separator can also be of type null; however, parameter $glue of implode() 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

1098
        $visibleContent = implode(/** @scrutinizer ignore-type */ $separator, $lines);
Loading history...
1099
1100 4
        if ($tag === false) {
1101 1
            return $hidden . $visibleContent;
1102
        }
1103
1104 4
        return $hidden . static::tag($tag, $visibleContent, $options);
1105
    }
1106
1107
    /**
1108
     * Generates an unordered list.
1109
     * @param array|\Traversable $items the items for generating the list. Each item generates a single list item.
1110
     * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true.
1111
     * @param array $options options (name => config) for the radio button list. The following options are supported:
1112
     *
1113
     * - encode: boolean, whether to HTML-encode the items. Defaults to true.
1114
     *   This option is ignored if the `item` option is specified.
1115
     * - separator: string, the HTML code that separates items. Defaults to a simple newline (`"\n"`).
1116
     *   This option is available since version 2.0.7.
1117
     * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified.
1118
     * - item: callable, a callback that is used to generate each individual list item.
1119
     *   The signature of this callback must be:
1120
     *
1121
     *   ```php
1122
     *   function ($item, $index)
1123
     *   ```
1124
     *
1125
     *   where $index is the array key corresponding to `$item` in `$items`. The callback should return
1126
     *   the whole list item tag.
1127
     *
1128
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1129
     *
1130
     * @return string the generated unordered list. An empty list tag will be returned if `$items` is empty.
1131
     */
1132 5
    public static function ul($items, $options = [])
1133
    {
1134 5
        $tag = ArrayHelper::remove($options, 'tag', 'ul');
1135 5
        $encode = ArrayHelper::remove($options, 'encode', true);
1136 5
        $formatter = ArrayHelper::remove($options, 'item');
1137 5
        $separator = ArrayHelper::remove($options, 'separator', "\n");
1138 5
        $itemOptions = ArrayHelper::remove($options, 'itemOptions', []);
1139
1140 5
        if (empty($items)) {
1141 2
            return static::tag($tag, '', $options);
1142
        }
1143
1144 5
        $results = [];
1145 5
        foreach ($items as $index => $item) {
1146 5
            if ($formatter !== null) {
1147 2
                $results[] = call_user_func($formatter, $item, $index);
1148
            } else {
1149 5
                $results[] = static::tag('li', $encode ? static::encode($item) : $item, $itemOptions);
1150
            }
1151
        }
1152
1153 5
        return static::tag(
1154 5
            $tag,
1155 5
            $separator . implode($separator, $results) . $separator,
0 ignored issues
show
Bug introduced by
It seems like $separator can also be of type null; however, parameter $glue of implode() 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

1155
            $separator . implode(/** @scrutinizer ignore-type */ $separator, $results) . $separator,
Loading history...
1156
            $options
1157
        );
1158
    }
1159
1160
    /**
1161
     * Generates an ordered list.
1162
     * @param array|\Traversable $items the items for generating the list. Each item generates a single list item.
1163
     * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true.
1164
     * @param array $options options (name => config) for the radio button list. The following options are supported:
1165
     *
1166
     * - encode: boolean, whether to HTML-encode the items. Defaults to true.
1167
     *   This option is ignored if the `item` option is specified.
1168
     * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified.
1169
     * - item: callable, a callback that is used to generate each individual list item.
1170
     *   The signature of this callback must be:
1171
     *
1172
     *   ```php
1173
     *   function ($item, $index)
1174
     *   ```
1175
     *
1176
     *   where $index is the array key corresponding to `$item` in `$items`. The callback should return
1177
     *   the whole list item tag.
1178
     *
1179
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1180
     *
1181
     * @return string the generated ordered list. An empty string is returned if `$items` is empty.
1182
     */
1183 1
    public static function ol($items, $options = [])
1184
    {
1185 1
        $options['tag'] = 'ol';
1186 1
        return static::ul($items, $options);
1187
    }
1188
1189
    /**
1190
     * Generates a label tag for the given model attribute.
1191
     * The label text is the label associated with the attribute, obtained via [[Model::getAttributeLabel()]].
1192
     * @param Model $model the model object
1193
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1194
     * about attribute expression.
1195
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1196
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1197
     * If a value is null, the corresponding attribute will not be rendered.
1198
     * The following options are specially handled:
1199
     *
1200
     * - label: this specifies the label to be displayed. Note that this will NOT be [[encode()|encoded]].
1201
     *   If this is not set, [[Model::getAttributeLabel()]] will be called to get the label for display
1202
     *   (after encoding).
1203
     *
1204
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1205
     *
1206
     * @return string the generated label tag
1207
     */
1208 17
    public static function activeLabel($model, $attribute, $options = [])
1209
    {
1210 17
        $for = ArrayHelper::remove($options, 'for', static::getInputId($model, $attribute));
1211 17
        $attribute = static::getAttributeName($attribute);
1212 17
        $label = ArrayHelper::remove($options, 'label', static::encode($model->getAttributeLabel($attribute)));
1213 17
        return static::label($label, $for, $options);
1214
    }
1215
1216
    /**
1217
     * Generates a hint tag for the given model attribute.
1218
     * The hint text is the hint associated with the attribute, obtained via [[Model::getAttributeHint()]].
1219
     * If no hint content can be obtained, method will return an empty string.
1220
     * @param Model $model the model object
1221
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1222
     * about attribute expression.
1223
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1224
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1225
     * If a value is null, the corresponding attribute will not be rendered.
1226
     * The following options are specially handled:
1227
     *
1228
     * - hint: this specifies the hint to be displayed. Note that this will NOT be [[encode()|encoded]].
1229
     *   If this is not set, [[Model::getAttributeHint()]] will be called to get the hint for display
1230
     *   (without encoding).
1231
     *
1232
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1233
     *
1234
     * @return string the generated hint tag
1235
     * @since 2.0.4
1236
     */
1237 17
    public static function activeHint($model, $attribute, $options = [])
1238
    {
1239 17
        $attribute = static::getAttributeName($attribute);
1240 17
        $hint = isset($options['hint']) ? $options['hint'] : $model->getAttributeHint($attribute);
1241 17
        if (empty($hint)) {
1242 4
            return '';
1243
        }
1244 13
        $tag = ArrayHelper::remove($options, 'tag', 'div');
1245 13
        unset($options['hint']);
1246 13
        return static::tag($tag, $hint, $options);
1247
    }
1248
1249
    /**
1250
     * Generates a summary of the validation errors.
1251
     * If there is no validation error, an empty error summary markup will still be generated, but it will be hidden.
1252
     * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.
1253
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
1254
     *
1255
     * - header: string, the header HTML for the error summary. If not set, a default prompt string will be used.
1256
     * - footer: string, the footer HTML for the error summary. Defaults to empty string.
1257
     * - encode: boolean, if set to false then the error messages won't be encoded. Defaults to `true`.
1258
     * - showAllErrors: boolean, if set to true every error message for each attribute will be shown otherwise
1259
     *   only the first error message for each attribute will be shown. Defaults to `false`.
1260
     *   Option is available since 2.0.10.
1261
     *
1262
     * The rest of the options will be rendered as the attributes of the container tag.
1263
     *
1264
     * @return string the generated error summary
1265
     */
1266 8
    public static function errorSummary($models, $options = [])
1267
    {
1268 8
        $header = isset($options['header']) ? $options['header'] : '<p>' . Yii::t('yii', 'Please fix the following errors:') . '</p>';
1269 8
        $footer = ArrayHelper::remove($options, 'footer', '');
1270 8
        $encode = ArrayHelper::remove($options, 'encode', true);
1271 8
        $showAllErrors = ArrayHelper::remove($options, 'showAllErrors', false);
1272 8
        unset($options['header']);
1273 8
        $lines = self::collectErrors($models, $encode, $showAllErrors);
1274 8
        if (empty($lines)) {
1275
            // still render the placeholder for client-side validation use
1276 2
            $content = '<ul></ul>';
1277 2
            $options['style'] = isset($options['style']) ? rtrim($options['style'], ';') . '; display:none' : 'display:none';
1278
        } else {
1279 6
            $content = '<ul><li>' . implode("</li>\n<li>", $lines) . '</li></ul>';
1280
        }
1281
1282 8
        return Html::tag('div', $header . $content . $footer, $options);
1283
    }
1284
1285
    /**
1286
     * Return array of the validation errors
1287
     * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.
1288
     * @param $encode boolean, if set to false then the error messages won't be encoded.
1289
     * @param $showAllErrors boolean, if set to true every error message for each attribute will be shown otherwise
1290
     * only the first error message for each attribute will be shown.
1291
     * @return array of the validation errors
1292
     * @since 2.0.14
1293
     */
1294 8
    private static function collectErrors($models, $encode, $showAllErrors)
1295
    {
1296 8
        $lines = [];
1297 8
        if (!is_array($models)) {
1298 8
            $models = [$models];
1299
        }
1300
1301 8
        foreach ($models as $model) {
1302 8
            $lines = array_unique(array_merge($lines, $model->getErrorSummary($showAllErrors)));
1303
        }
1304
1305
        // If there are the same error messages for different attributes, array_unique will leave gaps
1306
        // between sequential keys. Applying array_values to reorder array keys.
1307 8
        $lines = array_values($lines);
1308
1309 8
        if ($encode) {
1310 7
            foreach ($lines as &$line) {
1311 5
                $line = Html::encode($line);
1312
            }
1313
        }
1314
1315 8
        return $lines;
1316
    }
1317
1318
    /**
1319
     * Generates a tag that contains the first validation error of the specified model attribute.
1320
     * Note that even if there is no validation error, this method will still return an empty error tag.
1321
     * @param Model $model the model object
1322
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1323
     * about attribute expression.
1324
     * @param array $options the tag options in terms of name-value pairs. The values will be HTML-encoded
1325
     * using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1326
     *
1327
     * The following options are specially handled:
1328
     *
1329
     * - tag: this specifies the tag name. If not set, "div" will be used.
1330
     *   See also [[tag()]].
1331
     * - encode: boolean, if set to false then the error message won't be encoded.
1332
     * - errorSource (since 2.0.14): \Closure|callable, callback that will be called to obtain an error message.
1333
     *   The signature of the callback must be: `function ($model, $attribute)` and return a string.
1334
     *   When not set, the `$model->getFirstError()` method will be called.
1335
     *
1336
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1337
     *
1338
     * @return string the generated label tag
1339
     */
1340 16
    public static function error($model, $attribute, $options = [])
1341
    {
1342 16
        $attribute = static::getAttributeName($attribute);
1343 16
        $errorSource = ArrayHelper::remove($options, 'errorSource');
1344 16
        if ($errorSource !== null) {
1345 1
            $error = call_user_func($errorSource, $model, $attribute);
1346
        } else {
1347 16
            $error = $model->getFirstError($attribute);
1348
        }
1349 16
        $tag = ArrayHelper::remove($options, 'tag', 'div');
1350 16
        $encode = ArrayHelper::remove($options, 'encode', true);
1351 16
        return Html::tag($tag, $encode ? Html::encode($error) : $error, $options);
1352
    }
1353
1354
    /**
1355
     * Generates an input tag for the given model attribute.
1356
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1357
     * unless they are explicitly specified in `$options`.
1358
     * @param string $type the input type (e.g. 'text', 'password')
1359
     * @param Model $model the model object
1360
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1361
     * about attribute expression.
1362
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1363
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1364
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1365
     * @return string the generated input tag
1366
     */
1367 38
    public static function activeInput($type, $model, $attribute, $options = [])
1368
    {
1369 38
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1370 38
        $value = isset($options['value']) ? $options['value'] : static::getAttributeValue($model, $attribute);
1371 38
        if (!array_key_exists('id', $options)) {
1372 34
            $options['id'] = static::getInputId($model, $attribute);
1373
        }
1374
1375 38
        static::setActivePlaceholder($model, $attribute, $options);
1376 38
        self::normalizeMaxLength($model, $attribute, $options);
1377
1378 38
        return static::input($type, $name, $value, $options);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type array; however, parameter $value of yii\helpers\BaseHtml::input() does only seem to accept null|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

1378
        return static::input($type, $name, /** @scrutinizer ignore-type */ $value, $options);
Loading history...
1379
    }
1380
1381
    /**
1382
     * If `maxlength` option is set true and the model attribute is validated by a string validator,
1383
     * the `maxlength` option will take the max value of [[\yii\validators\StringValidator::max]] and
1384
     * [[\yii\validators\StringValidator::length]].
1385
     * @param Model $model the model object
1386
     * @param string $attribute the attribute name or expression.
1387
     * @param array $options the tag options in terms of name-value pairs.
1388
     */
1389 42
    private static function normalizeMaxLength($model, $attribute, &$options)
1390
    {
1391 42
        if (isset($options['maxlength']) && $options['maxlength'] === true) {
1392 7
            unset($options['maxlength']);
1393 7
            $attrName = static::getAttributeName($attribute);
1394 7
            foreach ($model->getActiveValidators($attrName) as $validator) {
1395 7
                if ($validator instanceof StringValidator && ($validator->max !== null || $validator->length !== null)) {
1396 6
                    $options['maxlength'] = max($validator->max, $validator->length);
1397 6
                    break;
1398
                }
1399
            }
1400
        }
1401 42
    }
1402
1403
    /**
1404
     * Generates a text input tag for the given model attribute.
1405
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1406
     * unless they are explicitly specified in `$options`.
1407
     * @param Model $model the model object
1408
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1409
     * about attribute expression.
1410
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1411
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1412
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1413
     * The following special options are recognized:
1414
     *
1415
     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
1416
     *   by a string validator, the `maxlength` option will take the max value of [[\yii\validators\StringValidator::max]]
1417
     *   and [[\yii\validators\StringValidator::length].
1418
     *   This is available since version 2.0.3 and improved taking `length` into account since version 2.0.42.
1419
     * - placeholder: string|boolean, when `placeholder` equals `true`, the attribute label from the $model will be used
1420
     *   as a placeholder (this behavior is available since version 2.0.14).
1421
     *
1422
     * @return string the generated input tag
1423
     */
1424 22
    public static function activeTextInput($model, $attribute, $options = [])
1425
    {
1426 22
        return static::activeInput('text', $model, $attribute, $options);
1427
    }
1428
1429
    /**
1430
     * Generate placeholder from model attribute label.
1431
     *
1432
     * @param Model $model the model object
1433
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1434
     * about attribute expression.
1435
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1436
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1437
     * @since 2.0.14
1438
     */
1439 41
    protected static function setActivePlaceholder($model, $attribute, &$options = [])
1440
    {
1441 41
        if (isset($options['placeholder']) && $options['placeholder'] === true) {
1442 2
            $attribute = static::getAttributeName($attribute);
1443 2
            $options['placeholder'] = $model->getAttributeLabel($attribute);
1444
        }
1445 41
    }
1446
1447
    /**
1448
     * Generates a hidden input tag for the given model attribute.
1449
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1450
     * unless they are explicitly specified in `$options`.
1451
     * @param Model $model the model object
1452
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1453
     * about attribute expression.
1454
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1455
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1456
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1457
     * @return string the generated input tag
1458
     */
1459 5
    public static function activeHiddenInput($model, $attribute, $options = [])
1460
    {
1461 5
        return static::activeInput('hidden', $model, $attribute, $options);
1462
    }
1463
1464
    /**
1465
     * Generates a password input tag for the given model attribute.
1466
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1467
     * unless they are explicitly specified in `$options`.
1468
     * @param Model $model the model object
1469
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1470
     * about attribute expression.
1471
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1472
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1473
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1474
     * The following special options are recognized:
1475
     *
1476
     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
1477
     *   by a string validator, the `maxlength` option will take the max value of [[\yii\validators\StringValidator::max]]
1478
     *   and [[\yii\validators\StringValidator::length].
1479
     *   This is available since version 2.0.6 and improved taking `length` into account since version 2.0.42.
1480
     * - placeholder: string|boolean, when `placeholder` equals `true`, the attribute label from the $model will be used
1481
     *   as a placeholder (this behavior is available since version 2.0.14).
1482
     *
1483
     * @return string the generated input tag
1484
     */
1485 3
    public static function activePasswordInput($model, $attribute, $options = [])
1486
    {
1487 3
        return static::activeInput('password', $model, $attribute, $options);
1488
    }
1489
1490
    /**
1491
     * Generates a file input tag for the given model attribute.
1492
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1493
     * unless they are explicitly specified in `$options`.
1494
     * Additionally, if a separate set of HTML options array is defined inside `$options` with a key named `hiddenOptions`,
1495
     * it will be passed to the `activeHiddenInput` field as its own `$options` parameter.
1496
     * @param Model $model the model object
1497
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1498
     * about attribute expression.
1499
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1500
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1501
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1502
     * If `hiddenOptions` parameter which is another set of HTML options array is defined, it will be extracted
1503
     * from `$options` to be used for the hidden input.
1504
     * @return string the generated input tag
1505
     */
1506 2
    public static function activeFileInput($model, $attribute, $options = [])
1507
    {
1508 2
        $hiddenOptions = ['id' => null, 'value' => ''];
1509 2
        if (isset($options['name'])) {
1510 1
            $hiddenOptions['name'] = $options['name'];
1511
        }
1512
        // make sure disabled input is not sending any value
1513 2
        if (!empty($options['disabled'])) {
1514 1
            $hiddenOptions['disabled'] = $options['disabled'];
1515
        }
1516 2
        $hiddenOptions = ArrayHelper::merge($hiddenOptions, ArrayHelper::remove($options, 'hiddenOptions', []));
1517
        // Add a hidden field so that if a model only has a file field, we can
1518
        // still use isset($_POST[$modelClass]) to detect if the input is submitted.
1519
        // The hidden input will be assigned its own set of html options via `$hiddenOptions`.
1520
        // This provides the possibility to interact with the hidden field via client script.
1521
        // Note: For file-field-only model with `disabled` option set to `true` input submitting detection won't work.
1522
1523 2
        return static::activeHiddenInput($model, $attribute, $hiddenOptions)
1524 2
            . static::activeInput('file', $model, $attribute, $options);
1525
    }
1526
1527
    /**
1528
     * Generates a textarea tag for the given model attribute.
1529
     * The model attribute value will be used as the content in the textarea.
1530
     * @param Model $model the model object
1531
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1532
     * about attribute expression.
1533
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1534
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1535
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1536
     * The following special options are recognized:
1537
     *
1538
     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
1539
     *   by a string validator, the `maxlength` option will take the max value of [[\yii\validators\StringValidator::max]]
1540
     *   and [[\yii\validators\StringValidator::length].
1541
     *   This is available since version 2.0.6 and improved taking `length` into account since version 2.0.42.
1542
     * - placeholder: string|boolean, when `placeholder` equals `true`, the attribute label from the $model will be used
1543
     *   as a placeholder (this behavior is available since version 2.0.14).
1544
     *
1545
     * @return string the generated textarea tag
1546
     */
1547 4
    public static function activeTextarea($model, $attribute, $options = [])
1548
    {
1549 4
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1550 4
        if (isset($options['value'])) {
1551 1
            $value = $options['value'];
1552 1
            unset($options['value']);
1553
        } else {
1554 3
            $value = static::getAttributeValue($model, $attribute);
1555
        }
1556 4
        if (!array_key_exists('id', $options)) {
1557 4
            $options['id'] = static::getInputId($model, $attribute);
1558
        }
1559 4
        self::normalizeMaxLength($model, $attribute, $options);
1560 4
        static::setActivePlaceholder($model, $attribute, $options);
1561 4
        return static::textarea($name, $value, $options);
1562
    }
1563
1564
    /**
1565
     * Generates a radio button tag together with a label for the given model attribute.
1566
     * This method will generate the "checked" tag attribute according to the model attribute value.
1567
     * @param Model $model the model object
1568
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1569
     * about attribute expression.
1570
     * @param array $options the tag options in terms of name-value pairs.
1571
     * See [[booleanInput()]] for details about accepted attributes.
1572
     *
1573
     * @return string the generated radio button tag
1574
     */
1575 5
    public static function activeRadio($model, $attribute, $options = [])
1576
    {
1577 5
        return static::activeBooleanInput('radio', $model, $attribute, $options);
1578
    }
1579
1580
    /**
1581
     * Generates a checkbox tag together with a label for the given model attribute.
1582
     * This method will generate the "checked" tag attribute according to the model attribute value.
1583
     * @param Model $model the model object
1584
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1585
     * about attribute expression.
1586
     * @param array $options the tag options in terms of name-value pairs.
1587
     * See [[booleanInput()]] for details about accepted attributes.
1588
     *
1589
     * @return string the generated checkbox tag
1590
     */
1591 5
    public static function activeCheckbox($model, $attribute, $options = [])
1592
    {
1593 5
        return static::activeBooleanInput('checkbox', $model, $attribute, $options);
1594
    }
1595
1596
    /**
1597
     * Generates a boolean input
1598
     * This method is mainly called by [[activeCheckbox()]] and [[activeRadio()]].
1599
     * @param string $type the input type. This can be either `radio` or `checkbox`.
1600
     * @param Model $model the model object
1601
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1602
     * about attribute expression.
1603
     * @param array $options the tag options in terms of name-value pairs.
1604
     * See [[booleanInput()]] for details about accepted attributes.
1605
     * @return string the generated input element
1606
     * @since 2.0.9
1607
     */
1608 9
    protected static function activeBooleanInput($type, $model, $attribute, $options = [])
1609
    {
1610 9
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1611 9
        $value = static::getAttributeValue($model, $attribute);
1612
1613 9
        if (!array_key_exists('value', $options)) {
1614 9
            $options['value'] = '1';
1615
        }
1616 9
        if (!array_key_exists('uncheck', $options)) {
1617 5
            $options['uncheck'] = '0';
1618 4
        } elseif ($options['uncheck'] === false) {
1619 4
            unset($options['uncheck']);
1620
        }
1621 9
        if (!array_key_exists('label', $options)) {
1622 5
            $options['label'] = static::encode($model->getAttributeLabel(static::getAttributeName($attribute)));
1623 4
        } elseif ($options['label'] === false) {
1624 4
            unset($options['label']);
1625
        }
1626
1627 9
        $checked = "$value" === "{$options['value']}";
1628
1629 9
        if (!array_key_exists('id', $options)) {
1630 9
            $options['id'] = static::getInputId($model, $attribute);
1631
        }
1632
1633 9
        return static::$type($name, $checked, $options);
1634
    }
1635
1636
    /**
1637
     * Generates a drop-down list for the given model attribute.
1638
     * The selection of the drop-down list is taken from the value of the model attribute.
1639
     * @param Model $model the model object
1640
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1641
     * about attribute expression.
1642
     * @param array $items the option data items. The array keys are option values, and the array values
1643
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
1644
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
1645
     * If you have a list of data models, you may convert them into the format described above using
1646
     * [[\yii\helpers\ArrayHelper::map()]].
1647
     *
1648
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
1649
     * the labels will also be HTML-encoded.
1650
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
1651
     *
1652
     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array
1653
     *   to override the value and to set other tag attributes:
1654
     *
1655
     *   ```php
1656
     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],
1657
     *   ```
1658
     *
1659
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
1660
     *   and the array values are the extra attributes for the corresponding option tags. For example,
1661
     *
1662
     *   ```php
1663
     *   [
1664
     *       'value1' => ['disabled' => true],
1665
     *       'value2' => ['label' => 'value 2'],
1666
     *   ];
1667
     *   ```
1668
     *
1669
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
1670
     *   except that the array keys represent the optgroup labels specified in $items.
1671
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
1672
     *   Defaults to false.
1673
     * - encode: bool, whether to encode option prompt and option value characters.
1674
     *   Defaults to `true`. This option is available since 2.0.3.
1675
     *
1676
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
1677
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1678
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1679
     *
1680
     * @return string the generated drop-down list tag
1681
     */
1682 3
    public static function activeDropDownList($model, $attribute, $items, $options = [])
1683
    {
1684 3
        if (empty($options['multiple'])) {
1685 2
            return static::activeListInput('dropDownList', $model, $attribute, $items, $options);
1686
        }
1687
1688 1
        return static::activeListBox($model, $attribute, $items, $options);
1689
    }
1690
1691
    /**
1692
     * Generates a list box.
1693
     * The selection of the list box is taken from the value of the model attribute.
1694
     * @param Model $model the model object
1695
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1696
     * about attribute expression.
1697
     * @param array $items the option data items. The array keys are option values, and the array values
1698
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
1699
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
1700
     * If you have a list of data models, you may convert them into the format described above using
1701
     * [[\yii\helpers\ArrayHelper::map()]].
1702
     *
1703
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
1704
     * the labels will also be HTML-encoded.
1705
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
1706
     *
1707
     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array
1708
     *   to override the value and to set other tag attributes:
1709
     *
1710
     *   ```php
1711
     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],
1712
     *   ```
1713
     *
1714
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
1715
     *   and the array values are the extra attributes for the corresponding option tags. For example,
1716
     *
1717
     *   ```php
1718
     *   [
1719
     *       'value1' => ['disabled' => true],
1720
     *       'value2' => ['label' => 'value 2'],
1721
     *   ];
1722
     *   ```
1723
     *
1724
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
1725
     *   except that the array keys represent the optgroup labels specified in $items.
1726
     * - unselect: string, the value that will be submitted when no option is selected.
1727
     *   When this attribute is set, a hidden field will be generated so that if no option is selected in multiple
1728
     *   mode, we can still obtain the posted unselect value.
1729
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
1730
     *   Defaults to false.
1731
     * - encode: bool, whether to encode option prompt and option value characters.
1732
     *   Defaults to `true`. This option is available since 2.0.3.
1733
     *
1734
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
1735
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1736
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1737
     *
1738
     * @return string the generated list box tag
1739
     */
1740 3
    public static function activeListBox($model, $attribute, $items, $options = [])
1741
    {
1742 3
        return static::activeListInput('listBox', $model, $attribute, $items, $options);
1743
    }
1744
1745
    /**
1746
     * Generates a list of checkboxes.
1747
     * A checkbox list allows multiple selection, like [[listBox()]].
1748
     * As a result, the corresponding submitted value is an array.
1749
     * The selection of the checkbox list is taken from the value of the model attribute.
1750
     * @param Model $model the model object
1751
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1752
     * about attribute expression.
1753
     * @param array $items the data item used to generate the checkboxes.
1754
     * The array keys are the checkbox values, and the array values are the corresponding labels.
1755
     * @param array $options options (name => config) for the checkbox list container tag.
1756
     * The following options are specially handled:
1757
     *
1758
     * - tag: string|false, the tag name of the container element. False to render checkbox without container.
1759
     *   See also [[tag()]].
1760
     * - unselect: string, the value that should be submitted when none of the checkboxes is selected.
1761
     *   You may set this option to be null to prevent default value submission.
1762
     *   If this option is not set, an empty string will be submitted.
1763
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
1764
     *   This option is ignored if `item` option is set.
1765
     * - separator: string, the HTML code that separates items.
1766
     * - itemOptions: array, the options for generating the checkbox tag using [[checkbox()]].
1767
     * - item: callable, a callback that can be used to customize the generation of the HTML code
1768
     *   corresponding to a single item in $items. The signature of this callback must be:
1769
     *
1770
     *   ```php
1771
     *   function ($index, $label, $name, $checked, $value)
1772
     *   ```
1773
     *
1774
     *   where $index is the zero-based index of the checkbox in the whole list; $label
1775
     *   is the label for the checkbox; and $name, $value and $checked represent the name,
1776
     *   value and the checked status of the checkbox input.
1777
     *
1778
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1779
     *
1780
     * @return string the generated checkbox list
1781
     */
1782 3
    public static function activeCheckboxList($model, $attribute, $items, $options = [])
1783
    {
1784 3
        return static::activeListInput('checkboxList', $model, $attribute, $items, $options);
1785
    }
1786
1787
    /**
1788
     * Generates a list of radio buttons.
1789
     * A radio button list is like a checkbox list, except that it only allows single selection.
1790
     * The selection of the radio buttons is taken from the value of the model attribute.
1791
     * @param Model $model the model object
1792
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1793
     * about attribute expression.
1794
     * @param array $items the data item used to generate the radio buttons.
1795
     * The array keys are the radio values, and the array values are the corresponding labels.
1796
     * @param array $options options (name => config) for the radio button list container tag.
1797
     * The following options are specially handled:
1798
     *
1799
     * - tag: string|false, the tag name of the container element. False to render radio button without container.
1800
     *   See also [[tag()]].
1801
     * - unselect: string, the value that should be submitted when none of the radio buttons is selected.
1802
     *   You may set this option to be null to prevent default value submission.
1803
     *   If this option is not set, an empty string will be submitted.
1804
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
1805
     *   This option is ignored if `item` option is set.
1806
     * - separator: string, the HTML code that separates items.
1807
     * - itemOptions: array, the options for generating the radio button tag using [[radio()]].
1808
     * - item: callable, a callback that can be used to customize the generation of the HTML code
1809
     *   corresponding to a single item in $items. The signature of this callback must be:
1810
     *
1811
     *   ```php
1812
     *   function ($index, $label, $name, $checked, $value)
1813
     *   ```
1814
     *
1815
     *   where $index is the zero-based index of the radio button in the whole list; $label
1816
     *   is the label for the radio button; and $name, $value and $checked represent the name,
1817
     *   value and the checked status of the radio button input.
1818
     *
1819
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1820
     *
1821
     * @return string the generated radio button list
1822
     */
1823 2
    public static function activeRadioList($model, $attribute, $items, $options = [])
1824
    {
1825 2
        return static::activeListInput('radioList', $model, $attribute, $items, $options);
1826
    }
1827
1828
    /**
1829
     * Generates a list of input fields.
1830
     * This method is mainly called by [[activeListBox()]], [[activeRadioList()]] and [[activeCheckboxList()]].
1831
     * @param string $type the input type. This can be 'listBox', 'radioList', or 'checkBoxList'.
1832
     * @param Model $model the model object
1833
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1834
     * about attribute expression.
1835
     * @param array $items the data item used to generate the input fields.
1836
     * The array keys are the input values, and the array values are the corresponding labels.
1837
     * @param array $options options (name => config) for the input list. The supported special options
1838
     * depend on the input type specified by `$type`.
1839
     * @return string the generated input list
1840
     */
1841 10
    protected static function activeListInput($type, $model, $attribute, $items, $options = [])
1842
    {
1843 10
        $name = ArrayHelper::remove($options, 'name', static::getInputName($model, $attribute));
1844 10
        $selection = ArrayHelper::remove($options, 'value', static::getAttributeValue($model, $attribute));
1845 10
        if (!array_key_exists('unselect', $options)) {
1846 10
            $options['unselect'] = '';
1847 10
            if ($model->hasProperty($attribute)) {
1848 10
                $value = $model->$attribute;
1849
                try {
1850 10
                    $model->$attribute = '';
1851 1
                } catch (\Exception $e) {
0 ignored issues
show
Unused Code introduced by
catch (\Exception $e) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1852
                    unset($options['unselect']);
1853
                // PHP 7+
1854 1
                } catch (\Throwable $e) {
1855 1
                    unset($options['unselect']);
1856
                }
1857 10
                $model->$attribute = $value;
1858
            }
1859
        }
1860 10
        if (!array_key_exists('id', $options)) {
1861 8
            $options['id'] = static::getInputId($model, $attribute);
1862
        }
1863
1864 10
        return static::$type($name, $selection, $items, $options);
1865
    }
1866
1867
    /**
1868
     * Renders the option tags that can be used by [[dropDownList()]] and [[listBox()]].
1869
     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).
1870
     * @param array $items the option data items. The array keys are option values, and the array values
1871
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
1872
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
1873
     * If you have a list of data models, you may convert them into the format described above using
1874
     * [[\yii\helpers\ArrayHelper::map()]].
1875
     *
1876
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
1877
     * the labels will also be HTML-encoded.
1878
     * @param array $tagOptions the $options parameter that is passed to the [[dropDownList()]] or [[listBox()]] call.
1879
     * This method will take out these elements, if any: "prompt", "options" and "groups". See more details
1880
     * in [[dropDownList()]] for the explanation of these elements.
1881
     *
1882
     * @return string the generated list options
1883
     */
1884 9
    public static function renderSelectOptions($selection, $items, &$tagOptions = [])
1885
    {
1886 9
        if (ArrayHelper::isTraversable($selection)) {
1887 4
            $selection = array_map('strval', ArrayHelper::toArray($selection));
1888
        }
1889
1890 9
        $lines = [];
1891 9
        $encodeSpaces = ArrayHelper::remove($tagOptions, 'encodeSpaces', false);
1892 9
        $encode = ArrayHelper::remove($tagOptions, 'encode', true);
1893 9
        $strict = ArrayHelper::remove($tagOptions, 'strict', false);
1894 9
        if (isset($tagOptions['prompt'])) {
1895 3
            $promptOptions = ['value' => ''];
1896 3
            if (is_string($tagOptions['prompt'])) {
1897 3
                $promptText = $tagOptions['prompt'];
1898
            } else {
1899 1
                $promptText = $tagOptions['prompt']['text'];
1900 1
                $promptOptions = array_merge($promptOptions, $tagOptions['prompt']['options']);
1901
            }
1902 3
            $promptText = $encode ? static::encode($promptText) : $promptText;
1903 3
            if ($encodeSpaces) {
1904 1
                $promptText = str_replace(' ', '&nbsp;', $promptText);
1905
            }
1906 3
            $lines[] = static::tag('option', $promptText, $promptOptions);
1907
        }
1908
1909 9
        $options = isset($tagOptions['options']) ? $tagOptions['options'] : [];
1910 9
        $groups = isset($tagOptions['groups']) ? $tagOptions['groups'] : [];
1911 9
        unset($tagOptions['prompt'], $tagOptions['options'], $tagOptions['groups']);
1912 9
        $options['encodeSpaces'] = ArrayHelper::getValue($options, 'encodeSpaces', $encodeSpaces);
1913 9
        $options['encode'] = ArrayHelper::getValue($options, 'encode', $encode);
1914
1915 9
        foreach ($items as $key => $value) {
1916 8
            if (is_array($value)) {
1917 1
                $groupAttrs = isset($groups[$key]) ? $groups[$key] : [];
1918 1
                if (!isset($groupAttrs['label'])) {
1919 1
                    $groupAttrs['label'] = $key;
1920
                }
1921 1
                $attrs = ['options' => $options, 'groups' => $groups, 'encodeSpaces' => $encodeSpaces, 'encode' => $encode, 'strict' => $strict];
1922 1
                $content = static::renderSelectOptions($selection, $value, $attrs);
1923 1
                $lines[] = static::tag('optgroup', "\n" . $content . "\n", $groupAttrs);
1924
            } else {
1925 8
                $attrs = isset($options[$key]) ? $options[$key] : [];
1926 8
                $attrs['value'] = (string) $key;
1927 8
                if (!array_key_exists('selected', $attrs)) {
1928 8
                    $attrs['selected'] = $selection !== null &&
1929 6
                        (!ArrayHelper::isTraversable($selection) && ($strict ? !strcmp($key, $selection) : $selection == $key)
0 ignored issues
show
Bug introduced by
It seems like $selection can also be of type array; however, parameter $string2 of strcmp() 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

1929
                        (!ArrayHelper::isTraversable($selection) && ($strict ? !strcmp($key, /** @scrutinizer ignore-type */ $selection) : $selection == $key)
Loading history...
1930 6
                        || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn((string)$key, $selection, $strict));
0 ignored issues
show
Bug introduced by
It seems like $selection can also be of type string; however, parameter $haystack of yii\helpers\BaseArrayHelper::isIn() does only seem to accept iterable, 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

1930
                        || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn((string)$key, /** @scrutinizer ignore-type */ $selection, $strict));
Loading history...
1931
                }
1932 8
                $text = $encode ? static::encode($value) : $value;
1933 8
                if ($encodeSpaces) {
1934 2
                    $text = str_replace(' ', '&nbsp;', $text);
1935
                }
1936 8
                $lines[] = static::tag('option', $text, $attrs);
1937
            }
1938
        }
1939
1940 9
        return implode("\n", $lines);
1941
    }
1942
1943
    /**
1944
     * Renders the HTML tag attributes.
1945
     *
1946
     * Attributes whose values are of boolean type will be treated as
1947
     * [boolean attributes](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes).
1948
     *
1949
     * Attributes whose values are null will not be rendered.
1950
     *
1951
     * The values of attributes will be HTML-encoded using [[encode()]].
1952
     *
1953
     * `aria` and `data` attributes get special handling when they are set to an array value. In these cases,
1954
     * the array will be "expanded" and a list of ARIA/data attributes will be rendered. For example,
1955
     * `'aria' => ['role' => 'checkbox', 'value' => 'true']` would be rendered as
1956
     * `aria-role="checkbox" aria-value="true"`.
1957
     *
1958
     * If a nested `data` value is set to an array, it will be JSON-encoded. For example,
1959
     * `'data' => ['params' => ['id' => 1, 'name' => 'yii']]` would be rendered as
1960
     * `data-params='{"id":1,"name":"yii"}'`.
1961
     *
1962
     * @param array $attributes attributes to be rendered. The attribute values will be HTML-encoded using [[encode()]].
1963
     * @return string the rendering result. If the attributes are not empty, they will be rendered
1964
     * into a string with a leading white space (so that it can be directly appended to the tag name
1965
     * in a tag). If there is no attribute, an empty string will be returned.
1966
     * @see addCssClass()
1967
     */
1968 246
    public static function renderTagAttributes($attributes)
1969
    {
1970 246
        if (count($attributes) > 1) {
1971 190
            $sorted = [];
1972 190
            foreach (static::$attributeOrder as $name) {
1973 190
                if (isset($attributes[$name])) {
1974 190
                    $sorted[$name] = $attributes[$name];
1975
                }
1976
            }
1977 190
            $attributes = array_merge($sorted, $attributes);
1978
        }
1979
1980 246
        $html = '';
1981 246
        foreach ($attributes as $name => $value) {
1982 235
            if (is_bool($value)) {
1983 43
                if ($value) {
1984 43
                    $html .= " $name";
1985
                }
1986 235
            } elseif (is_array($value)) {
1987 11
                if (in_array($name, static::$dataAttributes)) {
1988 3
                    foreach ($value as $n => $v) {
1989 3
                        if (is_array($v)) {
1990 1
                            $html .= " $name-$n='" . Json::htmlEncode($v) . "'";
1991 3
                        } elseif (is_bool($v)) {
1992 1
                            if ($v) {
1993 1
                                $html .= " $name-$n";
1994
                            }
1995 3
                        } elseif ($v !== null) {
1996 2
                            $html .= " $name-$n=\"" . static::encode($v) . '"';
1997
                        }
1998
                    }
1999 10
                } elseif ($name === 'class') {
2000 9
                    if (empty($value)) {
2001 8
                        continue;
2002
                    }
2003 4
                    if (static::$normalizeClassAttribute === true && count($value) > 1) {
2004
                        // removes duplicate classes
2005 2
                        $value = explode(' ', implode(' ', $value));
2006 2
                        $value = array_unique($value);
2007
                    }
2008 4
                    $html .= " $name=\"" . static::encode(implode(' ', $value)) . '"';
2009 2
                } elseif ($name === 'style') {
2010 1
                    if (empty($value)) {
2011 1
                        continue;
2012
                    }
2013 1
                    $html .= " $name=\"" . static::encode(static::cssStyleFromArray($value)) . '"';
2014
                } else {
2015 6
                    $html .= " $name='" . Json::htmlEncode($value) . "'";
2016
                }
2017 229
            } elseif ($value !== null) {
2018 229
                $html .= " $name=\"" . static::encode($value) . '"';
2019
            }
2020
        }
2021
2022 246
        return $html;
2023
    }
2024
2025
    /**
2026
     * Adds a CSS class (or several classes) to the specified options.
2027
     *
2028
     * If the CSS class is already in the options, it will not be added again.
2029
     * If class specification at given options is an array, and some class placed there with the named (string) key,
2030
     * overriding of such key will have no effect. For example:
2031
     *
2032
     * ```php
2033
     * $options = ['class' => ['persistent' => 'initial']];
2034
     * Html::addCssClass($options, ['persistent' => 'override']);
2035
     * var_dump($options['class']); // outputs: array('persistent' => 'initial');
2036
     * ```
2037
     *
2038
     * @param array $options the options to be modified.
2039
     * @param string|array $class the CSS class(es) to be added
2040
     * @see removeCssClass()
2041
     */
2042 23
    public static function addCssClass(&$options, $class)
2043
    {
2044 23
        if (isset($options['class'])) {
2045 15
            if (is_array($options['class'])) {
2046 3
                $options['class'] = self::mergeCssClasses($options['class'], (array) $class);
2047
            } else {
2048 13
                $classes = preg_split('/\s+/', $options['class'], -1, PREG_SPLIT_NO_EMPTY);
2049 15
                $options['class'] = implode(' ', self::mergeCssClasses($classes, (array) $class));
2050
            }
2051
        } else {
2052 16
            $options['class'] = $class;
2053
        }
2054 23
    }
2055
2056
    /**
2057
     * Merges already existing CSS classes with new one.
2058
     * This method provides the priority for named existing classes over additional.
2059
     * @param array $existingClasses already existing CSS classes.
2060
     * @param array $additionalClasses CSS classes to be added.
2061
     * @return array merge result.
2062
     * @see addCssClass()
2063
     */
2064 15
    private static function mergeCssClasses(array $existingClasses, array $additionalClasses)
2065
    {
2066 15
        foreach ($additionalClasses as $key => $class) {
2067 15
            if (is_int($key) && !in_array($class, $existingClasses)) {
2068 14
                $existingClasses[] = $class;
2069 2
            } elseif (!isset($existingClasses[$key])) {
2070 1
                $existingClasses[$key] = $class;
2071
            }
2072
        }
2073
2074 15
        return static::$normalizeClassAttribute ? array_unique($existingClasses) : $existingClasses;
2075
    }
2076
2077
    /**
2078
     * Removes a CSS class from the specified options.
2079
     * @param array $options the options to be modified.
2080
     * @param string|array $class the CSS class(es) to be removed
2081
     * @see addCssClass()
2082
     */
2083 1
    public static function removeCssClass(&$options, $class)
2084
    {
2085 1
        if (isset($options['class'])) {
2086 1
            if (is_array($options['class'])) {
2087 1
                $classes = array_diff($options['class'], (array) $class);
2088 1
                if (empty($classes)) {
2089 1
                    unset($options['class']);
2090
                } else {
2091 1
                    $options['class'] = $classes;
2092
                }
2093
            } else {
2094 1
                $classes = preg_split('/\s+/', $options['class'], -1, PREG_SPLIT_NO_EMPTY);
2095 1
                $classes = array_diff($classes, (array) $class);
2096 1
                if (empty($classes)) {
2097 1
                    unset($options['class']);
2098
                } else {
2099 1
                    $options['class'] = implode(' ', $classes);
2100
                }
2101
            }
2102
        }
2103 1
    }
2104
2105
    /**
2106
     * Adds the specified CSS style to the HTML options.
2107
     *
2108
     * If the options already contain a `style` element, the new style will be merged
2109
     * with the existing one. If a CSS property exists in both the new and the old styles,
2110
     * the old one may be overwritten if `$overwrite` is true.
2111
     *
2112
     * For example,
2113
     *
2114
     * ```php
2115
     * Html::addCssStyle($options, 'width: 100px; height: 200px');
2116
     * ```
2117
     *
2118
     * @param array $options the HTML options to be modified.
2119
     * @param string|array $style the new style string (e.g. `'width: 100px; height: 200px'`) or
2120
     * array (e.g. `['width' => '100px', 'height' => '200px']`).
2121
     * @param bool $overwrite whether to overwrite existing CSS properties if the new style
2122
     * contain them too.
2123
     * @see removeCssStyle()
2124
     * @see cssStyleFromArray()
2125
     * @see cssStyleToArray()
2126
     */
2127 1
    public static function addCssStyle(&$options, $style, $overwrite = true)
2128
    {
2129 1
        if (!empty($options['style'])) {
2130 1
            $oldStyle = is_array($options['style']) ? $options['style'] : static::cssStyleToArray($options['style']);
2131 1
            $newStyle = is_array($style) ? $style : static::cssStyleToArray($style);
2132 1
            if (!$overwrite) {
2133 1
                foreach ($newStyle as $property => $value) {
2134 1
                    if (isset($oldStyle[$property])) {
2135 1
                        unset($newStyle[$property]);
2136
                    }
2137
                }
2138
            }
2139 1
            $style = array_merge($oldStyle, $newStyle);
2140
        }
2141 1
        $options['style'] = is_array($style) ? static::cssStyleFromArray($style) : $style;
2142 1
    }
2143
2144
    /**
2145
     * Removes the specified CSS style from the HTML options.
2146
     *
2147
     * For example,
2148
     *
2149
     * ```php
2150
     * Html::removeCssStyle($options, ['width', 'height']);
2151
     * ```
2152
     *
2153
     * @param array $options the HTML options to be modified.
2154
     * @param string|array $properties the CSS properties to be removed. You may use a string
2155
     * if you are removing a single property.
2156
     * @see addCssStyle()
2157
     */
2158 1
    public static function removeCssStyle(&$options, $properties)
2159
    {
2160 1
        if (!empty($options['style'])) {
2161 1
            $style = is_array($options['style']) ? $options['style'] : static::cssStyleToArray($options['style']);
2162 1
            foreach ((array) $properties as $property) {
2163 1
                unset($style[$property]);
2164
            }
2165 1
            $options['style'] = static::cssStyleFromArray($style);
2166
        }
2167 1
    }
2168
2169
    /**
2170
     * Converts a CSS style array into a string representation.
2171
     *
2172
     * For example,
2173
     *
2174
     * ```php
2175
     * print_r(Html::cssStyleFromArray(['width' => '100px', 'height' => '200px']));
2176
     * // will display: 'width: 100px; height: 200px;'
2177
     * ```
2178
     *
2179
     * @param array $style the CSS style array. The array keys are the CSS property names,
2180
     * and the array values are the corresponding CSS property values.
2181
     * @return string the CSS style string. If the CSS style is empty, a null will be returned.
2182
     */
2183 4
    public static function cssStyleFromArray(array $style)
2184
    {
2185 4
        $result = '';
2186 4
        foreach ($style as $name => $value) {
2187 4
            $result .= "$name: $value; ";
2188
        }
2189
        // return null if empty to avoid rendering the "style" attribute
2190 4
        return $result === '' ? null : rtrim($result);
2191
    }
2192
2193
    /**
2194
     * Converts a CSS style string into an array representation.
2195
     *
2196
     * The array keys are the CSS property names, and the array values
2197
     * are the corresponding CSS property values.
2198
     *
2199
     * For example,
2200
     *
2201
     * ```php
2202
     * print_r(Html::cssStyleToArray('width: 100px; height: 200px;'));
2203
     * // will display: ['width' => '100px', 'height' => '200px']
2204
     * ```
2205
     *
2206
     * @param string $style the CSS style string
2207
     * @return array the array representation of the CSS style
2208
     */
2209 3
    public static function cssStyleToArray($style)
2210
    {
2211 3
        $result = [];
2212 3
        foreach (explode(';', $style) as $property) {
2213 3
            $property = explode(':', $property);
2214 3
            if (count($property) > 1) {
2215 3
                $result[trim($property[0])] = trim($property[1]);
2216
            }
2217
        }
2218
2219 3
        return $result;
2220
    }
2221
2222
    /**
2223
     * Returns the real attribute name from the given attribute expression.
2224
     *
2225
     * An attribute expression is an attribute name prefixed and/or suffixed with array indexes.
2226
     * It is mainly used in tabular data input and/or input of array type. Below are some examples:
2227
     *
2228
     * - `[0]content` is used in tabular data input to represent the "content" attribute
2229
     *   for the first model in tabular input;
2230
     * - `dates[0]` represents the first array element of the "dates" attribute;
2231
     * - `[0]dates[0]` represents the first array element of the "dates" attribute
2232
     *   for the first model in tabular input.
2233
     *
2234
     * If `$attribute` has neither prefix nor suffix, it will be returned back without change.
2235
     * @param string $attribute the attribute name or expression
2236
     * @return string the attribute name without prefix and suffix.
2237
     * @throws InvalidArgumentException if the attribute name contains non-word characters.
2238
     */
2239 69
    public static function getAttributeName($attribute)
2240
    {
2241 69
        if (preg_match(static::$attributeRegex, $attribute, $matches)) {
2242 66
            return $matches[2];
2243
        }
2244
2245 3
        throw new InvalidArgumentException('Attribute name must contain word characters only.');
2246
    }
2247
2248
    /**
2249
     * Returns the value of the specified attribute name or expression.
2250
     *
2251
     * For an attribute expression like `[0]dates[0]`, this method will return the value of `$model->dates[0]`.
2252
     * See [[getAttributeName()]] for more details about attribute expression.
2253
     *
2254
     * If an attribute value is an instance of [[ActiveRecordInterface]] or an array of such instances,
2255
     * the primary value(s) of the AR instance(s) will be returned instead.
2256
     *
2257
     * @param Model $model the model object
2258
     * @param string $attribute the attribute name or expression
2259
     * @return string|array the corresponding attribute value
2260
     * @throws InvalidArgumentException if the attribute name contains non-word characters.
2261
     */
2262 61
    public static function getAttributeValue($model, $attribute)
2263
    {
2264 61
        if (!preg_match(static::$attributeRegex, $attribute, $matches)) {
2265 1
            throw new InvalidArgumentException('Attribute name must contain word characters only.');
2266
        }
2267 60
        $attribute = $matches[2];
2268 60
        $value = $model->$attribute;
2269 60
        if ($matches[3] !== '') {
2270
            foreach (explode('][', trim($matches[3], '[]')) as $id) {
2271
                if ((is_array($value) || $value instanceof \ArrayAccess) && isset($value[$id])) {
2272
                    $value = $value[$id];
2273
                } else {
2274
                    return null;
2275
                }
2276
            }
2277
        }
2278
2279
        // https://github.com/yiisoft/yii2/issues/1457
2280 60
        if (is_array($value)) {
2281 2
            foreach ($value as $i => $v) {
2282 1
                if ($v instanceof ActiveRecordInterface) {
2283 1
                    $v = $v->getPrimaryKey(false);
2284 1
                    $value[$i] = is_array($v) ? json_encode($v) : $v;
2285
                }
2286
            }
2287 59
        } elseif ($value instanceof ActiveRecordInterface) {
2288 1
            $value = $value->getPrimaryKey(false);
2289
2290 1
            return is_array($value) ? json_encode($value) : $value;
2291
        }
2292
2293 60
        return $value;
2294
    }
2295
2296
    /**
2297
     * Generates an appropriate input name for the specified attribute name or expression.
2298
     *
2299
     * This method generates a name that can be used as the input name to collect user input
2300
     * for the specified attribute. The name is generated according to the [[Model::formName|form name]]
2301
     * of the model and the given attribute name. For example, if the form name of the `Post` model
2302
     * is `Post`, then the input name generated for the `content` attribute would be `Post[content]`.
2303
     *
2304
     * See [[getAttributeName()]] for explanation of attribute expression.
2305
     *
2306
     * @param Model $model the model object
2307
     * @param string $attribute the attribute name or expression
2308
     * @return string the generated input name
2309
     * @throws InvalidArgumentException if the attribute name contains non-word characters.
2310
     */
2311 94
    public static function getInputName($model, $attribute)
2312
    {
2313 94
        $formName = $model->formName();
2314 94
        if (!preg_match(static::$attributeRegex, $attribute, $matches)) {
2315 1
            throw new InvalidArgumentException('Attribute name must contain word characters only.');
2316
        }
2317 93
        $prefix = $matches[1];
2318 93
        $attribute = $matches[2];
2319 93
        $suffix = $matches[3];
2320 93
        if ($formName === '' && $prefix === '') {
2321 1
            return $attribute . $suffix;
2322 92
        } elseif ($formName !== '') {
2323 91
            return $formName . $prefix . "[$attribute]" . $suffix;
2324
        }
2325
2326 1
        throw new InvalidArgumentException(get_class($model) . '::formName() cannot be empty for tabular inputs.');
2327
    }
2328
2329
    /**
2330
     * Converts input name to ID.
2331
     *
2332
     * For example, if `$name` is `Post[content]`, this method will return `post-content`.
2333
     *
2334
     * @param string $name the input name
2335
     * @return string the generated input ID
2336
     * @since 2.0.43
2337
     */
2338 83
    public static function getInputIdByName($name)
2339
    {
2340 83
        $charset = Yii::$app ? Yii::$app->charset : 'UTF-8';
2341 83
        $name = mb_strtolower($name, $charset);
2342 83
        return str_replace(['[]', '][', '[', ']', ' ', '.', '--'], ['', '-', '-', '', '-', '-', '-'], $name);
2343
    }
2344
2345
    /**
2346
     * Generates an appropriate input ID for the specified attribute name or expression.
2347
     *
2348
     * @param Model $model the model object
2349
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for explanation of attribute expression.
2350
     * @return string the generated input ID.
2351
     * @throws InvalidArgumentException if the attribute name contains non-word characters.
2352
     */
2353 75
    public static function getInputId($model, $attribute)
2354
    {
2355 75
        $name = static::getInputName($model, $attribute);
2356 75
        return static::getInputIdByName($name);
2357
    }
2358
2359
    /**
2360
     * Escapes regular expression to use in JavaScript.
2361
     * @param string $regexp the regular expression to be escaped.
2362
     * @return string the escaped result.
2363
     * @since 2.0.6
2364
     */
2365 1
    public static function escapeJsRegularExpression($regexp)
2366
    {
2367 1
        $pattern = preg_replace('/\\\\x\{?([0-9a-fA-F]+)\}?/', '\u$1', $regexp);
2368 1
        $deliminator = substr($pattern, 0, 1);
2369 1
        $pos = strrpos($pattern, $deliminator, 1);
2370 1
        $flag = substr($pattern, $pos + 1);
2371 1
        if ($deliminator !== '/') {
2372 1
            $pattern = '/' . str_replace('/', '\\/', substr($pattern, 1, $pos - 1)) . '/';
2373
        } else {
2374 1
            $pattern = substr($pattern, 0, $pos + 1);
2375
        }
2376 1
        if (!empty($flag)) {
2377 1
            $pattern .= preg_replace('/[^igmu]/', '', $flag);
2378
        }
2379
2380 1
        return $pattern;
2381
    }
2382
}
2383