GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (910)

framework/helpers/BaseHtml.php (12 issues)

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

991
                (!ArrayHelper::isTraversable($selection) && !strcmp($value, /** @scrutinizer ignore-type */ $selection)
Loading history...
992 4
                    || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn((string)$value, $selection, $strict));
0 ignored issues
show
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

992
                    || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn((string)$value, /** @scrutinizer ignore-type */ $selection, $strict));
Loading history...
993 4
            if ($formatter !== null) {
994 1
                $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
995
            } else {
996 4
                $lines[] = static::checkbox($name, $checked, array_merge([
997 4
                    'value' => $value,
998 4
                    'label' => $encode ? static::encode($label) : $label,
999 4
                ], $itemOptions));
0 ignored issues
show
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

999
                ], /** @scrutinizer ignore-type */ $itemOptions));
Loading history...
1000
            }
1001 4
            $index++;
1002
        }
1003
1004 4
        if (isset($options['unselect'])) {
1005
            // add a hidden field so that if the list box has no option being selected, it still submits a value
1006 3
            $name2 = substr($name, -2) === '[]' ? substr($name, 0, -2) : $name;
1007 3
            $hiddenOptions = [];
1008
            // make sure disabled input is not sending any value
1009 3
            if (!empty($options['disabled'])) {
1010 1
                $hiddenOptions['disabled'] = $options['disabled'];
1011
            }
1012 3
            $hidden = static::hiddenInput($name2, $options['unselect'], $hiddenOptions);
1013 3
            unset($options['unselect'], $options['disabled']);
1014
        } else {
1015 2
            $hidden = '';
1016
        }
1017
1018 4
        $visibleContent = implode($separator, $lines);
0 ignored issues
show
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

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

1093
                (!ArrayHelper::isTraversable($selection) && !strcmp($value, /** @scrutinizer ignore-type */ $selection)
Loading history...
1094 4
                    || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn((string)$value, $selection, $strict));
0 ignored issues
show
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

1094
                    || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn((string)$value, /** @scrutinizer ignore-type */ $selection, $strict));
Loading history...
1095 4
            if ($formatter !== null) {
1096 1
                $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
1097
            } else {
1098 4
                $lines[] = static::radio($name, $checked, array_merge([
1099 4
                    'value' => $value,
1100 4
                    'label' => $encode ? static::encode($label) : $label,
1101 4
                ], $itemOptions));
0 ignored issues
show
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

1101
                ], /** @scrutinizer ignore-type */ $itemOptions));
Loading history...
1102
            }
1103 4
            $index++;
1104
        }
1105 4
        $visibleContent = implode($separator, $lines);
0 ignored issues
show
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

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

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

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

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