Completed
Push — php7-remove-phpunit-compat ( b957db...533a50 )
by Alexander
11:13
created

BaseHtml::renderTagAttributes()   D

Complexity

Conditions 16
Paths 22

Size

Total Lines 47
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 16

Importance

Changes 0
Metric Value
dl 0
loc 47
ccs 30
cts 30
cp 1
rs 4.9897
c 0
b 0
f 0
cc 16
eloc 32
nc 22
nop 1
crap 16

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://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 http://www.w3.org/TR/html-markup/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 = ['data', 'data-ng', 'ng'];
97
98
99
    /**
100
     * Encodes special characters into HTML entities.
101
     * The [[\yii\base\Application::charset|application charset]] will be used for encoding.
102
     * @param string $content the content to be encoded
103
     * @param bool $doubleEncode whether to encode HTML entities in `$content`. If false,
104
     * HTML entities in `$content` will not be further encoded.
105
     * @return string the encoded content
106
     * @see decode()
107
     * @see http://www.php.net/manual/en/function.htmlspecialchars.php
108
     */
109 209
    public static function encode($content, $doubleEncode = true)
110
    {
111 209
        return htmlspecialchars($content, ENT_QUOTES | ENT_SUBSTITUTE, Yii::$app ? Yii::$app->charset : 'UTF-8', $doubleEncode);
112
    }
113
114
    /**
115
     * Decodes special HTML entities back to the corresponding characters.
116
     * This is the opposite of [[encode()]].
117
     * @param string $content the content to be decoded
118
     * @return string the decoded content
119
     * @see encode()
120
     * @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php
121
     */
122 1
    public static function decode($content)
123
    {
124 1
        return htmlspecialchars_decode($content, ENT_QUOTES);
125
    }
126
127
    /**
128
     * Generates a complete HTML tag.
129
     * @param string|bool|null $name the tag name. If $name is `null` or `false`, the corresponding content will be rendered without any tag.
130
     * @param string $content the content to be enclosed between the start and end tags. It will not be HTML-encoded.
131
     * If this is coming from end users, you should consider [[encode()]] it to prevent XSS attacks.
132
     * @param array $options the HTML tag attributes (HTML options) in terms of name-value pairs.
133
     * These will be rendered as the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
134
     * If a value is null, the corresponding attribute will not be rendered.
135
     *
136
     * For example when using `['class' => 'my-class', 'target' => '_blank', 'value' => null]` it will result in the
137
     * html attributes rendered like this: `class="my-class" target="_blank"`.
138
     *
139
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
140
     *
141
     * @return string the generated HTML tag
142
     * @see beginTag()
143
     * @see endTag()
144
     */
145 196
    public static function tag($name, $content = '', $options = [])
146
    {
147 196
        if ($name === null || $name === false) {
148 3
            return $content;
149
        }
150 195
        $html = "<$name" . static::renderTagAttributes($options) . '>';
151 195
        return isset(static::$voidElements[strtolower($name)]) ? $html : "$html$content</$name>";
152
    }
153
154
    /**
155
     * Generates a start tag.
156
     * @param string|bool|null $name the tag name. If $name is `null` or `false`, the corresponding content will be rendered without any tag.
157
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
158
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
159
     * If a value is null, the corresponding attribute will not be rendered.
160
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
161
     * @return string the generated start tag
162
     * @see endTag()
163
     * @see tag()
164
     */
165 41
    public static function beginTag($name, $options = [])
166
    {
167 41
        if ($name === null || $name === false) {
168 3
            return '';
169
        }
170 41
        return "<$name" . static::renderTagAttributes($options) . '>';
171
    }
172
173
    /**
174
     * Generates an end tag.
175
     * @param string|bool|null $name the tag name. If $name is `null` or `false`, the corresponding content will be rendered without any tag.
176
     * @return string the generated end tag
177
     * @see beginTag()
178
     * @see tag()
179
     */
180 12
    public static function endTag($name)
181
    {
182 12
        if ($name === null || $name === false) {
183 3
            return '';
184
        }
185 11
        return "</$name>";
186
    }
187
188
    /**
189
     * Generates a style tag.
190
     * @param string $content the style content
191
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
192
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
193
     * If a value is null, the corresponding attribute will not be rendered.
194
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
195
     * @return string the generated style tag
196
     */
197 1
    public static function style($content, $options = [])
198
    {
199 1
        return static::tag('style', $content, $options);
200
    }
201
202
    /**
203
     * Generates a script tag.
204
     * @param string $content the script content
205
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
206
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
207
     * If a value is null, the corresponding attribute will not be rendered.
208
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
209
     * @return string the generated script tag
210
     */
211 1
    public static function script($content, $options = [])
212
    {
213 1
        return static::tag('script', $content, $options);
214
    }
215
216
    /**
217
     * Generates a link tag that refers to an external CSS file.
218
     * @param array|string $url the URL of the external CSS file. This parameter will be processed by [[Url::to()]].
219
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
220
     *
221
     * - condition: specifies the conditional comments for IE, e.g., `lt IE 9`. When this is specified,
222
     *   the generated `link` tag will be enclosed within the conditional comments. This is mainly useful
223
     *   for supporting old versions of IE browsers.
224
     * - noscript: if set to true, `link` tag will be wrapped into `<noscript>` tags.
225
     *
226
     * The rest of the options will be rendered as the attributes of the resulting link tag. The values will
227
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
228
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
229
     * @return string the generated link tag
230
     * @see Url::to()
231
     */
232 20
    public static function cssFile($url, $options = [])
233
    {
234 20
        if (!isset($options['rel'])) {
235 20
            $options['rel'] = 'stylesheet';
236
        }
237 20
        $options['href'] = Url::to($url);
238
239 20
        if (isset($options['condition'])) {
240 1
            $condition = $options['condition'];
241 1
            unset($options['condition']);
242 1
            return self::wrapIntoCondition(static::tag('link', '', $options), $condition);
243 20
        } elseif (isset($options['noscript']) && $options['noscript'] === true) {
244 1
            unset($options['noscript']);
245 1
            return '<noscript>' . static::tag('link', '', $options) . '</noscript>';
246
        }
247
248 20
        return static::tag('link', '', $options);
249
    }
250
251
    /**
252
     * Generates a script tag that refers to an external JavaScript file.
253
     * @param string $url the URL of the external JavaScript file. This parameter will be processed by [[Url::to()]].
254
     * @param array $options the tag options in terms of name-value pairs. The following option is specially handled:
255
     *
256
     * - condition: specifies the conditional comments for IE, e.g., `lt IE 9`. When this is specified,
257
     *   the generated `script` tag will be enclosed within the conditional comments. This is mainly useful
258
     *   for supporting old versions of IE browsers.
259
     *
260
     * The rest of the options will be rendered as the attributes of the resulting script tag. The values will
261
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
262
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
263
     * @return string the generated script tag
264
     * @see Url::to()
265
     */
266 22
    public static function jsFile($url, $options = [])
267
    {
268 22
        $options['src'] = Url::to($url);
269 22
        if (isset($options['condition'])) {
270 1
            $condition = $options['condition'];
271 1
            unset($options['condition']);
272 1
            return self::wrapIntoCondition(static::tag('script', '', $options), $condition);
273
        }
274
275 22
        return static::tag('script', '', $options);
276
    }
277
278
    /**
279
     * Wraps given content into conditional comments for IE, e.g., `lt IE 9`.
280
     * @param string $content raw HTML content.
281
     * @param string $condition condition string.
282
     * @return string generated HTML.
283
     */
284 2
    private static function wrapIntoCondition($content, $condition)
285
    {
286 2
        if (strpos($condition, '!IE') !== false) {
287 2
            return "<!--[if $condition]><!-->\n" . $content . "\n<!--<![endif]-->";
288
        }
289 2
        return "<!--[if $condition]>\n" . $content . "\n<![endif]-->";
290
    }
291
292
    /**
293
     * Generates the meta tags containing CSRF token information.
294
     * @return string the generated meta tags
295
     * @see Request::enableCsrfValidation
296
     */
297 2
    public static function csrfMetaTags()
298
    {
299 2
        $request = Yii::$app->getRequest();
300 2
        if ($request instanceof Request && $request->enableCsrfValidation) {
301 1
            return static::tag('meta', '', ['name' => 'csrf-param', 'content' => $request->csrfParam]) . "\n    "
302 1
                . static::tag('meta', '', ['name' => 'csrf-token', 'content' => $request->getCsrfToken()]) . "\n";
303
        }
304
305 1
        return '';
306
    }
307
308
    /**
309
     * Generates a form start tag.
310
     * @param array|string $action the form action URL. This parameter will be processed by [[Url::to()]].
311
     * @param string $method the form submission method, such as "post", "get", "put", "delete" (case-insensitive).
312
     * Since most browsers only support "post" and "get", if other methods are given, they will
313
     * be simulated using "post", and a hidden input will be added which contains the actual method type.
314
     * See [[\yii\web\Request::methodParam]] for more details.
315
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
316
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
317
     * If a value is null, the corresponding attribute will not be rendered.
318
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
319
     *
320
     * Special options:
321
     *
322
     *  - `csrf`: whether to generate the CSRF hidden input. Defaults to true.
323
     *
324
     * @return string the generated form start tag.
325
     * @see endForm()
326
     */
327 39
    public static function beginForm($action = '', $method = 'post', $options = [])
328
    {
329 39
        $action = Url::to($action);
330
331 39
        $hiddenInputs = [];
332
333 39
        $request = Yii::$app->getRequest();
334 39
        if ($request instanceof Request) {
335 36
            if (strcasecmp($method, 'get') && strcasecmp($method, 'post')) {
336
                // simulate PUT, DELETE, etc. via POST
337 4
                $hiddenInputs[] = static::hiddenInput($request->methodParam, $method);
338 4
                $method = 'post';
339
            }
340 36
            $csrf = ArrayHelper::remove($options, 'csrf', true);
341
342 36
            if ($csrf && $request->enableCsrfValidation && strcasecmp($method, 'post') === 0) {
343 29
                $hiddenInputs[] = static::hiddenInput($request->csrfParam, $request->getCsrfToken());
344
            }
345
        }
346
347 39
        if (!strcasecmp($method, 'get') && ($pos = strpos($action, '?')) !== false) {
348
            // query parameters in the action are ignored for GET method
349
            // we use hidden fields to add them back
350 1
            foreach (explode('&', substr($action, $pos + 1)) as $pair) {
351 1
                if (($pos1 = strpos($pair, '=')) !== false) {
352 1
                    $hiddenInputs[] = static::hiddenInput(
353 1
                        urldecode(substr($pair, 0, $pos1)),
354 1
                        urldecode(substr($pair, $pos1 + 1))
355
                    );
356
                } else {
357 1
                    $hiddenInputs[] = static::hiddenInput(urldecode($pair), '');
358
                }
359
            }
360 1
            $action = substr($action, 0, $pos);
361
        }
362
363 39
        $options['action'] = $action;
364 39
        $options['method'] = $method;
365 39
        $form = static::beginTag('form', $options);
366 39
        if (!empty($hiddenInputs)) {
367 34
            $form .= "\n" . implode("\n", $hiddenInputs);
368
        }
369
370 39
        return $form;
371
    }
372
373
    /**
374
     * Generates a form end tag.
375
     * @return string the generated tag
376
     * @see beginForm()
377
     */
378 32
    public static function endForm()
379
    {
380 32
        return '</form>';
381
    }
382
383
    /**
384
     * Generates a hyperlink tag.
385
     * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code
386
     * such as an image tag. If this is coming from end users, you should consider [[encode()]]
387
     * it to prevent XSS attacks.
388
     * @param array|string|null $url the URL for the hyperlink tag. This parameter will be processed by [[Url::to()]]
389
     * and will be used for the "href" attribute of the tag. If this parameter is null, the "href" attribute
390
     * will not be generated.
391
     *
392
     * If you want to use an absolute url you can call [[Url::to()]] yourself, before passing the URL to this method,
393
     * like this:
394
     *
395
     * ```php
396
     * Html::a('link text', Url::to($url, true))
397
     * ```
398
     *
399
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
400
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
401
     * If a value is null, the corresponding attribute will not be rendered.
402
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
403
     * @return string the generated hyperlink
404
     * @see \yii\helpers\Url::to()
405
     */
406 14
    public static function a($text, $url = null, $options = [])
407
    {
408 14
        if ($url !== null) {
409 14
            $options['href'] = Url::to($url);
410
        }
411 14
        return static::tag('a', $text, $options);
412
    }
413
414
    /**
415
     * Generates a mailto hyperlink.
416
     * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code
417
     * such as an image tag. If this is coming from end users, you should consider [[encode()]]
418
     * it to prevent XSS attacks.
419
     * @param string $email email address. If this is null, the first parameter (link body) will be treated
420
     * as the email address and used.
421
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
422
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
423
     * If a value is null, the corresponding attribute will not be rendered.
424
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
425
     * @return string the generated mailto link
426
     */
427 2
    public static function mailto($text, $email = null, $options = [])
428
    {
429 2
        $options['href'] = 'mailto:' . ($email === null ? $text : $email);
430 2
        return static::tag('a', $text, $options);
431
    }
432
433
    /**
434
     * Generates an image tag.
435
     * @param array|string $src the image URL. This parameter will be processed by [[Url::to()]].
436
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
437
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
438
     * If a value is null, the corresponding attribute will not be rendered.
439
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
440
     *
441
     * Since version 2.0.12 It is possible to pass the `srcset` option as an array which keys are
442
     * descriptors and values are URLs. All URLs will be processed by [[Url::to()]].
443
     * @return string the generated image tag.
444
     */
445 10
    public static function img($src, $options = [])
446
    {
447 10
        $options['src'] = Url::to($src);
448
449 10
        if (isset($options['srcset']) && is_array($options['srcset'])) {
450 5
            $srcset = [];
451 5
            foreach ($options['srcset'] as $descriptor => $url) {
452 4
                $srcset[] = Url::to($url) . ' ' . $descriptor;
453
            }
454 5
            $options['srcset'] = implode(',', $srcset);
455
        }
456
457 10
        if (!isset($options['alt'])) {
458 9
            $options['alt'] = '';
459
        }
460 10
        return static::tag('img', '', $options);
461
    }
462
463
    /**
464
     * Generates a label tag.
465
     * @param string $content label text. It will NOT be HTML-encoded. Therefore you can pass in HTML code
466
     * such as an image tag. If this is is coming from end users, you should [[encode()]]
467
     * it to prevent XSS attacks.
468
     * @param string $for the ID of the HTML element that this label is associated with.
469
     * If this is null, the "for" attribute will not be generated.
470
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
471
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
472
     * If a value is null, the corresponding attribute will not be rendered.
473
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
474
     * @return string the generated label tag
475
     */
476 22
    public static function label($content, $for = null, $options = [])
477
    {
478 22
        $options['for'] = $for;
479 22
        return static::tag('label', $content, $options);
480
    }
481
482
    /**
483
     * Generates a button tag.
484
     * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
485
     * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
486
     * you should consider [[encode()]] it to prevent XSS attacks.
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 button tag
492
     */
493 3
    public static function button($content = 'Button', $options = [])
494
    {
495 3
        if (!isset($options['type'])) {
496 1
            $options['type'] = 'button';
497
        }
498 3
        return static::tag('button', $content, $options);
499
    }
500
501
    /**
502
     * Generates a submit button tag.
503
     *
504
     * Be careful when naming form elements such as submit buttons. According to the [jQuery documentation](https://api.jquery.com/submit/) there
505
     * are some reserved names that can cause conflicts, e.g. `submit`, `length`, or `method`.
506
     *
507
     * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
508
     * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
509
     * you should consider [[encode()]] it to prevent XSS attacks.
510
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
511
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
512
     * If a value is null, the corresponding attribute will not be rendered.
513
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
514
     * @return string the generated submit button tag
515
     */
516 1
    public static function submitButton($content = 'Submit', $options = [])
517
    {
518 1
        $options['type'] = 'submit';
519 1
        return static::button($content, $options);
520
    }
521
522
    /**
523
     * Generates a reset button tag.
524
     * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
525
     * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
526
     * you should consider [[encode()]] it to prevent XSS attacks.
527
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
528
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
529
     * If a value is null, the corresponding attribute will not be rendered.
530
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
531
     * @return string the generated reset button tag
532
     */
533 1
    public static function resetButton($content = 'Reset', $options = [])
534
    {
535 1
        $options['type'] = 'reset';
536 1
        return static::button($content, $options);
537
    }
538
539
    /**
540
     * Generates an input type of the given type.
541
     * @param string $type the type attribute.
542
     * @param string $name the name attribute. If it is null, the name attribute will not be generated.
543
     * @param string $value the value attribute. If it is null, the value attribute will not be generated.
544
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
545
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
546
     * If a value is null, the corresponding attribute will not be rendered.
547
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
548
     * @return string the generated input tag
549
     */
550 79
    public static function input($type, $name = null, $value = null, $options = [])
551
    {
552 79
        if (!isset($options['type'])) {
553 79
            $options['type'] = $type;
554
        }
555 79
        $options['name'] = $name;
556 79
        $options['value'] = $value === null ? null : (string) $value;
557 79
        return static::tag('input', '', $options);
558
    }
559
560
    /**
561
     * Generates an input button.
562
     * @param string $label the value attribute. If it is null, the value attribute will not be generated.
563
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
564
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
565
     * If a value is null, the corresponding attribute will not be rendered.
566
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
567
     * @return string the generated button tag
568
     */
569 1
    public static function buttonInput($label = 'Button', $options = [])
570
    {
571 1
        $options['type'] = 'button';
572 1
        $options['value'] = $label;
573 1
        return static::tag('input', '', $options);
574
    }
575
576
    /**
577
     * Generates a submit input button.
578
     *
579
     * Be careful when naming form elements such as submit buttons. According to the [jQuery documentation](https://api.jquery.com/submit/) there
580
     * are some reserved names that can cause conflicts, e.g. `submit`, `length`, or `method`.
581
     *
582
     * @param string $label the value attribute. If it is null, the value attribute will not be generated.
583
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
584
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
585
     * If a value is null, the corresponding attribute will not be rendered.
586
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
587
     * @return string the generated button tag
588
     */
589 1
    public static function submitInput($label = 'Submit', $options = [])
590
    {
591 1
        $options['type'] = 'submit';
592 1
        $options['value'] = $label;
593 1
        return static::tag('input', '', $options);
594
    }
595
596
    /**
597
     * Generates a reset input button.
598
     * @param string $label the value attribute. If it is null, the value attribute will not be generated.
599
     * @param array $options the attributes of the button tag. The values will be HTML-encoded using [[encode()]].
600
     * Attributes whose value is null will be ignored and not put in the tag returned.
601
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
602
     * @return string the generated button tag
603
     */
604 1
    public static function resetInput($label = 'Reset', $options = [])
605
    {
606 1
        $options['type'] = 'reset';
607 1
        $options['value'] = $label;
608 1
        return static::tag('input', '', $options);
609
    }
610
611
    /**
612
     * Generates a text input field.
613
     * @param string $name the name attribute.
614
     * @param string $value the value attribute. If it is null, the value attribute will not be generated.
615
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
616
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
617
     * If a value is null, the corresponding attribute will not be rendered.
618
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
619
     * @return string the generated text input tag
620
     */
621 1
    public static function textInput($name, $value = null, $options = [])
622
    {
623 1
        return static::input('text', $name, $value, $options);
624
    }
625
626
    /**
627
     * Generates a hidden input field.
628
     * @param string $name the name attribute.
629
     * @param string $value the value attribute. If it is null, the value attribute will not be generated.
630
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
631
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
632
     * If a value is null, the corresponding attribute will not be rendered.
633
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
634
     * @return string the generated hidden input tag
635
     */
636 48
    public static function hiddenInput($name, $value = null, $options = [])
637
    {
638 48
        return static::input('hidden', $name, $value, $options);
639
    }
640
641
    /**
642
     * Generates a password input field.
643
     * @param string $name the name attribute.
644
     * @param string $value the value attribute. If it is null, the value attribute will not be generated.
645
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
646
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
647
     * If a value is null, the corresponding attribute will not be rendered.
648
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
649
     * @return string the generated password input tag
650
     */
651 1
    public static function passwordInput($name, $value = null, $options = [])
652
    {
653 1
        return static::input('password', $name, $value, $options);
654
    }
655
656
    /**
657
     * Generates a file input field.
658
     * To use a file input field, you should set the enclosing form's "enctype" attribute to
659
     * be "multipart/form-data". After the form is submitted, the uploaded file information
660
     * can be obtained via $_FILES[$name] (see PHP documentation).
661
     * @param string $name the name attribute.
662
     * @param string $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 file input tag
668
     */
669 1
    public static function fileInput($name, $value = null, $options = [])
670
    {
671 1
        return static::input('file', $name, $value, $options);
672
    }
673
674
    /**
675
     * Generates a text area input.
676
     * @param string $name the input name
677
     * @param string $value the input value. Note that it will be encoded using [[encode()]].
678
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
679
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
680
     * If a value is null, the corresponding attribute will not be rendered.
681
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
682
     * The following special options are recognized:
683
     *
684
     * - `doubleEncode`: whether to double encode HTML entities in `$value`. If `false`, HTML entities in `$value` will not
685
     *   be further encoded. This option is available since version 2.0.11.
686
     *
687
     * @return string the generated text area tag
688
     */
689 8
    public static function textarea($name, $value = '', $options = [])
690
    {
691 8
        $options['name'] = $name;
692 8
        $doubleEncode = ArrayHelper::remove($options, 'doubleEncode', true);
693 8
        return static::tag('textarea', static::encode($value, $doubleEncode), $options);
694
    }
695
696
    /**
697
     * Generates a radio button input.
698
     * @param string $name the name attribute.
699
     * @param bool $checked whether the radio button should be checked.
700
     * @param array $options the tag options in terms of name-value pairs.
701
     * See [[booleanInput()]] for details about accepted attributes.
702
     *
703
     * @return string the generated radio button tag
704
     */
705 10
    public static function radio($name, $checked = false, $options = [])
706
    {
707 10
        return static::booleanInput('radio', $name, $checked, $options);
708
    }
709
710
    /**
711
     * Generates a checkbox input.
712
     * @param string $name the name attribute.
713
     * @param bool $checked whether the checkbox should be checked.
714
     * @param array $options the tag options in terms of name-value pairs.
715
     * See [[booleanInput()]] for details about accepted attributes.
716
     *
717
     * @return string the generated checkbox tag
718
     */
719 9
    public static function checkbox($name, $checked = false, $options = [])
720
    {
721 9
        return static::booleanInput('checkbox', $name, $checked, $options);
722
    }
723
724
    /**
725
     * Generates a boolean input.
726
     * @param string $type the input type. This can be either `radio` or `checkbox`.
727
     * @param string $name the name attribute.
728
     * @param bool $checked whether the checkbox should be checked.
729
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
730
     *
731
     * - uncheck: string, the value associated with the uncheck state of the checkbox. When this attribute
732
     *   is present, a hidden input will be generated so that if the checkbox is not checked and is submitted,
733
     *   the value of this attribute will still be submitted to the server via the hidden input.
734
     * - label: string, a label displayed next to the checkbox.  It will NOT be HTML-encoded. Therefore you can pass
735
     *   in HTML code such as an image tag. If this is is coming from end users, you should [[encode()]] it to prevent XSS attacks.
736
     *   When this option is specified, the checkbox will be enclosed by a label tag.
737
     * - labelOptions: array, the HTML attributes for the label tag. Do not set this option unless you set the "label" option.
738
     *
739
     * The rest of the options will be rendered as the attributes of the resulting checkbox tag. The values will
740
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
741
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
742
     *
743
     * @return string the generated checkbox tag
744
     * @since 2.0.9
745
     */
746 19
    protected static function booleanInput($type, $name, $checked = false, $options = [])
747
    {
748 19
        $options['checked'] = (bool) $checked;
749 19
        $value = array_key_exists('value', $options) ? $options['value'] : '1';
750 19
        if (isset($options['uncheck'])) {
751
            // add a hidden field so that if the checkbox is not selected, it still submits a value
752 6
            $hiddenOptions = [];
753 6
            if (isset($options['form'])) {
754 1
                $hiddenOptions['form'] = $options['form'];
755
            }
756 6
            $hidden = static::hiddenInput($name, $options['uncheck'], $hiddenOptions);
0 ignored issues
show
Documentation introduced by
$options['uncheck'] is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
757 6
            unset($options['uncheck']);
758
        } else {
759 15
            $hidden = '';
760
        }
761 19
        if (isset($options['label'])) {
762 10
            $label = $options['label'];
763 10
            $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : [];
764 10
            unset($options['label'], $options['labelOptions']);
765 10
            $content = static::label(static::input($type, $name, $value, $options) . ' ' . $label, null, $labelOptions);
0 ignored issues
show
Bug introduced by
It seems like $labelOptions defined by isset($options['labelOpt...abelOptions'] : array() on line 763 can also be of type boolean; however, yii\helpers\BaseHtml::label() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
766 10
            return $hidden . $content;
767
        }
768
769 13
        return $hidden . static::input($type, $name, $value, $options);
770
    }
771
772
    /**
773
     * Generates a drop-down list.
774
     * @param string $name the input name
775
     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).
776
     * @param array $items the option data items. The array keys are option values, and the array values
777
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
778
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
779
     * If you have a list of data models, you may convert them into the format described above using
780
     * [[\yii\helpers\ArrayHelper::map()]].
781
     *
782
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
783
     * the labels will also be HTML-encoded.
784
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
785
     *
786
     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array
787
     *   to override the value and to set other tag attributes:
788
     *
789
     *   ```php
790
     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],
791
     *   ```
792
     *
793
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
794
     *   and the array values are the extra attributes for the corresponding option tags. For example,
795
     *
796
     *   ```php
797
     *   [
798
     *       'value1' => ['disabled' => true],
799
     *       'value2' => ['label' => 'value 2'],
800
     *   ];
801
     *   ```
802
     *
803
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
804
     *   except that the array keys represent the optgroup labels specified in $items.
805
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
806
     *   Defaults to false.
807
     * - encode: bool, whether to encode option prompt and option value characters.
808
     *   Defaults to `true`. This option is available since 2.0.3.
809
     *
810
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
811
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
812
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
813
     *
814
     * @return string the generated drop-down list tag
815
     */
816 3
    public static function dropDownList($name, $selection = null, $items = [], $options = [])
817
    {
818 3
        if (!empty($options['multiple'])) {
819 1
            return static::listBox($name, $selection, $items, $options);
820
        }
821 3
        $options['name'] = $name;
822 3
        unset($options['unselect']);
823 3
        $selectOptions = static::renderSelectOptions($selection, $items, $options);
824 3
        return static::tag('select', "\n" . $selectOptions . "\n", $options);
825
    }
826
827
    /**
828
     * Generates a list box.
829
     * @param string $name the input name
830
     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).
831
     * @param array $items the option data items. The array keys are option values, and the array values
832
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
833
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
834
     * If you have a list of data models, you may convert them into the format described above using
835
     * [[\yii\helpers\ArrayHelper::map()]].
836
     *
837
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
838
     * the labels will also be HTML-encoded.
839
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
840
     *
841
     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array
842
     *   to override the value and to set other tag attributes:
843
     *
844
     *   ```php
845
     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],
846
     *   ```
847
     *
848
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
849
     *   and the array values are the extra attributes for the corresponding option tags. For example,
850
     *
851
     *   ```php
852
     *   [
853
     *       'value1' => ['disabled' => true],
854
     *       'value2' => ['label' => 'value 2'],
855
     *   ];
856
     *   ```
857
     *
858
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
859
     *   except that the array keys represent the optgroup labels specified in $items.
860
     * - unselect: string, the value that will be submitted when no option is selected.
861
     *   When this attribute is set, a hidden field will be generated so that if no option is selected in multiple
862
     *   mode, we can still obtain the posted unselect value.
863
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
864
     *   Defaults to false.
865
     * - encode: bool, whether to encode option prompt and option value characters.
866
     *   Defaults to `true`. This option is available since 2.0.3.
867
     *
868
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
869
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
870
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
871
     *
872
     * @return string the generated list box tag
873
     */
874 5
    public static function listBox($name, $selection = null, $items = [], $options = [])
875
    {
876 5
        if (!array_key_exists('size', $options)) {
877 5
            $options['size'] = 4;
878
        }
879 5
        if (!empty($options['multiple']) && !empty($name) && substr_compare($name, '[]', -2, 2)) {
880 4
            $name .= '[]';
881
        }
882 5
        $options['name'] = $name;
883 5
        if (isset($options['unselect'])) {
884
            // add a hidden field so that if the list box has no option being selected, it still submits a value
885 4
            if (!empty($name) && substr_compare($name, '[]', -2, 2) === 0) {
886 2
                $name = substr($name, 0, -2);
887
            }
888 4
            $hidden = static::hiddenInput($name, $options['unselect']);
889 4
            unset($options['unselect']);
890
        } else {
891 2
            $hidden = '';
892
        }
893 5
        $selectOptions = static::renderSelectOptions($selection, $items, $options);
894 5
        return $hidden . static::tag('select', "\n" . $selectOptions . "\n", $options);
895
    }
896
897
    /**
898
     * Generates a list of checkboxes.
899
     * A checkbox list allows multiple selection, like [[listBox()]].
900
     * As a result, the corresponding submitted value is an array.
901
     * @param string $name the name attribute of each checkbox.
902
     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).
903
     * @param array $items the data item used to generate the checkboxes.
904
     * The array keys are the checkbox values, while the array values are the corresponding labels.
905
     * @param array $options options (name => config) for the checkbox list container tag.
906
     * The following options are specially handled:
907
     *
908
     * - tag: string|false, the tag name of the container element. False to render checkbox without container.
909
     *   See also [[tag()]].
910
     * - unselect: string, the value that should be submitted when none of the checkboxes is selected.
911
     *   By setting this option, a hidden input will be generated.
912
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
913
     *   This option is ignored if `item` option is set.
914
     * - separator: string, the HTML code that separates items.
915
     * - itemOptions: array, the options for generating the checkbox tag using [[checkbox()]].
916
     * - item: callable, a callback that can be used to customize the generation of the HTML code
917
     *   corresponding to a single item in $items. The signature of this callback must be:
918
     *
919
     *   ```php
920
     *   function ($index, $label, $name, $checked, $value)
921
     *   ```
922
     *
923
     *   where $index is the zero-based index of the checkbox in the whole list; $label
924
     *   is the label for the checkbox; and $name, $value and $checked represent the name,
925
     *   value and the checked status of the checkbox input, respectively.
926
     *
927
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
928
     *
929
     * @return string the generated checkbox list
930
     */
931 2
    public static function checkboxList($name, $selection = null, $items = [], $options = [])
932
    {
933 2
        if (substr($name, -2) !== '[]') {
934 2
            $name .= '[]';
935
        }
936
937 2
        $formatter = ArrayHelper::remove($options, 'item');
938 2
        $itemOptions = ArrayHelper::remove($options, 'itemOptions', []);
939 2
        $encode = ArrayHelper::remove($options, 'encode', true);
940 2
        $separator = ArrayHelper::remove($options, 'separator', "\n");
941 2
        $tag = ArrayHelper::remove($options, 'tag', 'div');
942
943 2
        $lines = [];
944 2
        $index = 0;
945 2
        foreach ($items as $value => $label) {
946 2
            $checked = $selection !== null &&
947 1
                (!ArrayHelper::isTraversable($selection) && !strcmp($value, $selection)
948 2
                    || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn($value, $selection));
0 ignored issues
show
Bug introduced by
It seems like $selection defined by parameter $selection on line 931 can also be of type string; however, yii\helpers\BaseArrayHelper::isIn() does only seem to accept array|object<Traversable>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
949 2
            if ($formatter !== null) {
950 1
                $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
951
            } else {
952 2
                $lines[] = static::checkbox($name, $checked, array_merge($itemOptions, [
953 2
                    'value' => $value,
954 2
                    'label' => $encode ? static::encode($label) : $label,
955
                ]));
956
            }
957 2
            $index++;
958
        }
959
960 2
        if (isset($options['unselect'])) {
961
            // add a hidden field so that if the list box has no option being selected, it still submits a value
962 2
            $name2 = substr($name, -2) === '[]' ? substr($name, 0, -2) : $name;
963 2
            $hidden = static::hiddenInput($name2, $options['unselect']);
964 2
            unset($options['unselect']);
965
        } else {
966 1
            $hidden = '';
967
        }
968
969 2
        $visibleContent = implode($separator, $lines);
970
971 2
        if ($tag === false) {
972 1
            return $hidden . $visibleContent;
973
        }
974
975 2
        return $hidden . static::tag($tag, $visibleContent, $options);
976
    }
977
978
    /**
979
     * Generates a list of radio buttons.
980
     * A radio button list is like a checkbox list, except that it only allows single selection.
981
     * @param string $name the name attribute of each radio button.
982
     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).
983
     * @param array $items the data item used to generate the radio buttons.
984
     * The array keys are the radio button values, while the array values are the corresponding labels.
985
     * @param array $options options (name => config) for the radio button list container tag.
986
     * The following options are specially handled:
987
     *
988
     * - tag: string|false, the tag name of the container element. False to render radio buttons without container.
989
     *   See also [[tag()]].
990
     * - unselect: string, the value that should be submitted when none of the radio buttons is selected.
991
     *   By setting this option, a hidden input will be generated.
992
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
993
     *   This option is ignored if `item` option is set.
994
     * - separator: string, the HTML code that separates items.
995
     * - itemOptions: array, the options for generating the radio button tag using [[radio()]].
996
     * - item: callable, a callback that can be used to customize the generation of the HTML code
997
     *   corresponding to a single item in $items. The signature of this callback must be:
998
     *
999
     *   ```php
1000
     *   function ($index, $label, $name, $checked, $value)
1001
     *   ```
1002
     *
1003
     *   where $index is the zero-based index of the radio button in the whole list; $label
1004
     *   is the label for the radio button; and $name, $value and $checked represent the name,
1005
     *   value and the checked status of the radio button input, respectively.
1006
     *
1007
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1008
     *
1009
     * @return string the generated radio button list
1010
     */
1011 2
    public static function radioList($name, $selection = null, $items = [], $options = [])
1012
    {
1013 2
        $formatter = ArrayHelper::remove($options, 'item');
1014 2
        $itemOptions = ArrayHelper::remove($options, 'itemOptions', []);
1015 2
        $encode = ArrayHelper::remove($options, 'encode', true);
1016 2
        $separator = ArrayHelper::remove($options, 'separator', "\n");
1017 2
        $tag = ArrayHelper::remove($options, 'tag', 'div');
1018
        // add a hidden field so that if the list box has no option being selected, it still submits a value
1019 2
        $hidden = isset($options['unselect']) ? static::hiddenInput($name, $options['unselect']) : '';
1020 2
        unset($options['unselect']);
1021
1022 2
        $lines = [];
1023 2
        $index = 0;
1024 2
        foreach ($items as $value => $label) {
1025 2
            $checked = $selection !== null &&
1026 1
                (!ArrayHelper::isTraversable($selection) && !strcmp($value, $selection)
1027 2
                    || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn($value, $selection));
0 ignored issues
show
Bug introduced by
It seems like $selection defined by parameter $selection on line 1011 can also be of type string; however, yii\helpers\BaseArrayHelper::isIn() does only seem to accept array|object<Traversable>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1028 2
            if ($formatter !== null) {
1029 1
                $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
1030
            } else {
1031 2
                $lines[] = static::radio($name, $checked, array_merge($itemOptions, [
1032 2
                    'value' => $value,
1033 2
                    'label' => $encode ? static::encode($label) : $label,
1034
                ]));
1035
            }
1036 2
            $index++;
1037
        }
1038 2
        $visibleContent = implode($separator, $lines);
1039
1040 2
        if ($tag === false) {
1041 1
            return $hidden . $visibleContent;
1042
        }
1043
1044 2
        return $hidden . static::tag($tag, $visibleContent, $options);
1045
    }
1046
1047
    /**
1048
     * Generates an unordered list.
1049
     * @param array|\Traversable $items the items for generating the list. Each item generates a single list item.
1050
     * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true.
1051
     * @param array $options options (name => config) for the radio button list. The following options are supported:
1052
     *
1053
     * - encode: boolean, whether to HTML-encode the items. Defaults to true.
1054
     *   This option is ignored if the `item` option is specified.
1055
     * - separator: string, the HTML code that separates items. Defaults to a simple newline (`"\n"`).
1056
     *   This option is available since version 2.0.7.
1057
     * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified.
1058
     * - item: callable, a callback that is used to generate each individual list item.
1059
     *   The signature of this callback must be:
1060
     *
1061
     *   ```php
1062
     *   function ($item, $index)
1063
     *   ```
1064
     *
1065
     *   where $index is the array key corresponding to `$item` in `$items`. The callback should return
1066
     *   the whole list item tag.
1067
     *
1068
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1069
     *
1070
     * @return string the generated unordered list. An empty list tag will be returned if `$items` is empty.
1071
     */
1072 4
    public static function ul($items, $options = [])
1073
    {
1074 4
        $tag = ArrayHelper::remove($options, 'tag', 'ul');
1075 4
        $encode = ArrayHelper::remove($options, 'encode', true);
1076 4
        $formatter = ArrayHelper::remove($options, 'item');
1077 4
        $separator = ArrayHelper::remove($options, 'separator', "\n");
1078 4
        $itemOptions = ArrayHelper::remove($options, 'itemOptions', []);
1079
1080 4
        if (empty($items)) {
1081 2
            return static::tag($tag, '', $options);
1082
        }
1083
1084 4
        $results = [];
1085 4
        foreach ($items as $index => $item) {
1086 4
            if ($formatter !== null) {
1087 2
                $results[] = call_user_func($formatter, $item, $index);
1088
            } else {
1089 4
                $results[] = static::tag('li', $encode ? static::encode($item) : $item, $itemOptions);
1090
            }
1091
        }
1092
1093 4
        return static::tag(
1094 4
            $tag,
1095 4
            $separator . implode($separator, $results) . $separator,
1096 4
            $options
1097
        );
1098
    }
1099
1100
    /**
1101
     * Generates an ordered list.
1102
     * @param array|\Traversable $items the items for generating the list. Each item generates a single list item.
1103
     * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true.
1104
     * @param array $options options (name => config) for the radio button list. The following options are supported:
1105
     *
1106
     * - encode: boolean, whether to HTML-encode the items. Defaults to true.
1107
     *   This option is ignored if the `item` option is specified.
1108
     * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified.
1109
     * - item: callable, a callback that is used to generate each individual list item.
1110
     *   The signature of this callback must be:
1111
     *
1112
     *   ```php
1113
     *   function ($item, $index)
1114
     *   ```
1115
     *
1116
     *   where $index is the array key corresponding to `$item` in `$items`. The callback should return
1117
     *   the whole list item tag.
1118
     *
1119
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1120
     *
1121
     * @return string the generated ordered list. An empty string is returned if `$items` is empty.
1122
     */
1123 1
    public static function ol($items, $options = [])
1124
    {
1125 1
        $options['tag'] = 'ol';
1126 1
        return static::ul($items, $options);
1127
    }
1128
1129
    /**
1130
     * Generates a label tag for the given model attribute.
1131
     * The label text is the label associated with the attribute, obtained via [[Model::getAttributeLabel()]].
1132
     * @param Model $model the model object
1133
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1134
     * about attribute expression.
1135
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1136
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1137
     * If a value is null, the corresponding attribute will not be rendered.
1138
     * The following options are specially handled:
1139
     *
1140
     * - label: this specifies the label to be displayed. Note that this will NOT be [[encode()|encoded]].
1141
     *   If this is not set, [[Model::getAttributeLabel()]] will be called to get the label for display
1142
     *   (after encoding).
1143
     *
1144
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1145
     *
1146
     * @return string the generated label tag
1147
     */
1148 11
    public static function activeLabel($model, $attribute, $options = [])
1149
    {
1150 11
        $for = ArrayHelper::remove($options, 'for', static::getInputId($model, $attribute));
1151 11
        $attribute = static::getAttributeName($attribute);
1152 11
        $label = ArrayHelper::remove($options, 'label', static::encode($model->getAttributeLabel($attribute)));
1153 11
        return static::label($label, $for, $options);
1154
    }
1155
1156
    /**
1157
     * Generates a hint tag for the given model attribute.
1158
     * The hint text is the hint associated with the attribute, obtained via [[Model::getAttributeHint()]].
1159
     * If no hint content can be obtained, method will return an empty string.
1160
     * @param Model $model the model object
1161
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1162
     * about attribute expression.
1163
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1164
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1165
     * If a value is null, the corresponding attribute will not be rendered.
1166
     * The following options are specially handled:
1167
     *
1168
     * - hint: this specifies the hint to be displayed. Note that this will NOT be [[encode()|encoded]].
1169
     *   If this is not set, [[Model::getAttributeHint()]] will be called to get the hint for display
1170
     *   (without encoding).
1171
     *
1172
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1173
     *
1174
     * @return string the generated hint tag
1175
     * @since 2.0.4
1176
     */
1177 11
    public static function activeHint($model, $attribute, $options = [])
1178
    {
1179 11
        $attribute = static::getAttributeName($attribute);
1180 11
        $hint = isset($options['hint']) ? $options['hint'] : $model->getAttributeHint($attribute);
1181 11
        if (empty($hint)) {
1182 3
            return '';
1183
        }
1184 8
        $tag = ArrayHelper::remove($options, 'tag', 'div');
1185 8
        unset($options['hint']);
1186 8
        return static::tag($tag, $hint, $options);
1187
    }
1188
1189
    /**
1190
     * Generates a summary of the validation errors.
1191
     * If there is no validation error, an empty error summary markup will still be generated, but it will be hidden.
1192
     * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.
1193
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
1194
     *
1195
     * - header: string, the header HTML for the error summary. If not set, a default prompt string will be used.
1196
     * - footer: string, the footer HTML for the error summary. Defaults to empty string.
1197
     * - encode: boolean, if set to false then the error messages won't be encoded. Defaults to `true`.
1198
     * - showAllErrors: boolean, if set to true every error message for each attribute will be shown otherwise
1199
     *   only the first error message for each attribute will be shown. Defaults to `false`.
1200
     *   Option is available since 2.0.10.
1201
     *
1202
     * The rest of the options will be rendered as the attributes of the container tag.
1203
     *
1204
     * @return string the generated error summary
1205
     */
1206 7
    public static function errorSummary($models, $options = [])
1207
    {
1208 7
        $header = isset($options['header']) ? $options['header'] : '<p>' . Yii::t('yii', 'Please fix the following errors:') . '</p>';
1209 7
        $footer = ArrayHelper::remove($options, 'footer', '');
1210 7
        $encode = ArrayHelper::remove($options, 'encode', true);
1211 7
        $showAllErrors = ArrayHelper::remove($options, 'showAllErrors', false);
1212 7
        unset($options['header']);
1213
1214 7
        $lines = [];
1215 7
        if (!is_array($models)) {
1216 7
            $models = [$models];
1217
        }
1218 7
        foreach ($models as $model) {
1219
            /* @var $model Model */
1220 7
            foreach ($model->getErrors() as $errors) {
1221 5
                foreach ($errors as $error) {
1222 5
                    $line = $encode ? Html::encode($error) : $error;
1223 5
                    if (!in_array($line, $lines, true)) {
1224 5
                        $lines[] = $line;
1225
                    }
1226 5
                    if (!$showAllErrors) {
1227 7
                        break;
1228
                    }
1229
                }
1230
            }
1231
        }
1232
1233 7
        if (empty($lines)) {
1234
            // still render the placeholder for client-side validation use
1235 2
            $content = '<ul></ul>';
1236 2
            $options['style'] = isset($options['style']) ? rtrim($options['style'], ';') . '; display:none' : 'display:none';
1237
        } else {
1238 5
            $content = '<ul><li>' . implode("</li>\n<li>", $lines) . '</li></ul>';
1239
        }
1240 7
        return Html::tag('div', $header . $content . $footer, $options);
1241
    }
1242
1243
    /**
1244
     * Generates a tag that contains the first validation error of the specified model attribute.
1245
     * Note that even if there is no validation error, this method will still return an empty error tag.
1246
     * @param Model $model the model object
1247
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1248
     * about attribute expression.
1249
     * @param array $options the tag options in terms of name-value pairs. The values will be HTML-encoded
1250
     * using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1251
     *
1252
     * The following options are specially handled:
1253
     *
1254
     * - tag: this specifies the tag name. If not set, "div" will be used.
1255
     *   See also [[tag()]].
1256
     * - encode: boolean, if set to false then the error message won't be encoded.
1257
     *
1258
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1259
     *
1260
     * @return string the generated label tag
1261
     */
1262 9
    public static function error($model, $attribute, $options = [])
1263
    {
1264 9
        $attribute = static::getAttributeName($attribute);
1265 9
        $error = $model->getFirstError($attribute);
1266 9
        $tag = ArrayHelper::remove($options, 'tag', 'div');
1267 9
        $encode = ArrayHelper::remove($options, 'encode', true);
1268 9
        return Html::tag($tag, $encode ? Html::encode($error) : $error, $options);
1269
    }
1270
1271
    /**
1272
     * Generates an input tag for the given model attribute.
1273
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1274
     * unless they are explicitly specified in `$options`.
1275
     * @param string $type the input type (e.g. 'text', 'password')
1276
     * @param Model $model the model object
1277
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1278
     * about attribute expression.
1279
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1280
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1281
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1282
     * @return string the generated input tag
1283
     */
1284 29
    public static function activeInput($type, $model, $attribute, $options = [])
1285
    {
1286 29
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1287 29
        $value = isset($options['value']) ? $options['value'] : static::getAttributeValue($model, $attribute);
1288 29
        if (!array_key_exists('id', $options)) {
1289 19
            $options['id'] = static::getInputId($model, $attribute);
1290
        }
1291 29
        return static::input($type, $name, $value, $options);
1292
    }
1293
1294
    /**
1295
     * If `maxlength` option is set true and the model attribute is validated by a string validator,
1296
     * the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
1297
     * @param Model $model the model object
1298
     * @param string $attribute the attribute name or expression.
1299
     * @param array $options the tag options in terms of name-value pairs.
1300
     */
1301 27
    private static function normalizeMaxLength($model, $attribute, &$options)
1302
    {
1303 27
        if (isset($options['maxlength']) && $options['maxlength'] === true) {
1304 3
            unset($options['maxlength']);
1305 3
            $attrName = static::getAttributeName($attribute);
1306 3
            foreach ($model->getActiveValidators($attrName) as $validator) {
1307 3
                if ($validator instanceof StringValidator && $validator->max !== null) {
1308 3
                    $options['maxlength'] = $validator->max;
1309 3
                    break;
1310
                }
1311
            }
1312
        }
1313 27
    }
1314
1315
    /**
1316
     * Generates a text input tag for the given model attribute.
1317
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1318
     * unless they are explicitly specified in `$options`.
1319
     * @param Model $model the model object
1320
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1321
     * about attribute expression.
1322
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1323
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1324
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1325
     * The following special options are recognized:
1326
     *
1327
     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
1328
     *   by a string validator, the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
1329
     *   This is available since version 2.0.3.
1330
     *
1331
     * @return string the generated input tag
1332
     */
1333 20
    public static function activeTextInput($model, $attribute, $options = [])
1334
    {
1335 20
        self::normalizeMaxLength($model, $attribute, $options);
1336 20
        return static::activeInput('text', $model, $attribute, $options);
1337
    }
1338
1339
    /**
1340
     * Generates a hidden input tag for the given model attribute.
1341
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1342
     * unless they are explicitly specified in `$options`.
1343
     * @param Model $model the model object
1344
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1345
     * about attribute expression.
1346
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1347
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1348
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1349
     * @return string the generated input tag
1350
     */
1351 4
    public static function activeHiddenInput($model, $attribute, $options = [])
1352
    {
1353 4
        return static::activeInput('hidden', $model, $attribute, $options);
1354
    }
1355
1356
    /**
1357
     * Generates a password input tag for the given model attribute.
1358
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1359
     * unless they are explicitly specified in `$options`.
1360
     * @param Model $model the model object
1361
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1362
     * about attribute expression.
1363
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1364
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1365
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1366
     * The following special options are recognized:
1367
     *
1368
     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
1369
     *   by a string validator, the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
1370
     *   This option is available since version 2.0.6.
1371
     *
1372
     * @return string the generated input tag
1373
     */
1374 3
    public static function activePasswordInput($model, $attribute, $options = [])
1375
    {
1376 3
        self::normalizeMaxLength($model, $attribute, $options);
1377 3
        return static::activeInput('password', $model, $attribute, $options);
1378
    }
1379
1380
    /**
1381
     * Generates a file input tag for the given model attribute.
1382
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1383
     * unless they are explicitly specified in `$options`.
1384
     * @param Model $model the model object
1385
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1386
     * about attribute expression.
1387
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1388
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1389
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1390
     * @return string the generated input tag
1391
     */
1392 2
    public static function activeFileInput($model, $attribute, $options = [])
1393
    {
1394
        // add a hidden field so that if a model only has a file field, we can
1395
        // still use isset($_POST[$modelClass]) to detect if the input is submitted
1396 2
        $hiddenOptions = ['id' => null, 'value' => ''];
1397 2
        if (isset($options['name'])) {
1398 1
            $hiddenOptions['name'] = $options['name'];
1399
        }
1400 2
        return static::activeHiddenInput($model, $attribute, $hiddenOptions)
1401 2
            . static::activeInput('file', $model, $attribute, $options);
1402
    }
1403
1404
    /**
1405
     * Generates a textarea tag for the given model attribute.
1406
     * The model attribute value will be used as the content in the textarea.
1407
     * @param Model $model the model object
1408
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1409
     * about attribute expression.
1410
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1411
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1412
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1413
     * The following special options are recognized:
1414
     *
1415
     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
1416
     *   by a string validator, the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
1417
     *   This option is available since version 2.0.6.
1418
     *
1419
     * @return string the generated textarea tag
1420
     */
1421 4
    public static function activeTextarea($model, $attribute, $options = [])
1422
    {
1423 4
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1424 4
        if (isset($options['value'])) {
1425 1
            $value = $options['value'];
1426 1
            unset($options['value']);
1427
        } else {
1428 3
            $value = static::getAttributeValue($model, $attribute);
1429
        }
1430 4
        if (!array_key_exists('id', $options)) {
1431 4
            $options['id'] = static::getInputId($model, $attribute);
1432
        }
1433 4
        self::normalizeMaxLength($model, $attribute, $options);
1434 4
        return static::textarea($name, $value, $options);
1435
    }
1436
1437
    /**
1438
     * Generates a radio button tag together with a label for the given model attribute.
1439
     * This method will generate the "checked" tag attribute according to the model attribute value.
1440
     * @param Model $model the model object
1441
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1442
     * about attribute expression.
1443
     * @param array $options the tag options in terms of name-value pairs.
1444
     * See [[booleanInput()]] for details about accepted attributes.
1445
     *
1446
     * @return string the generated radio button tag
1447
     */
1448 4
    public static function activeRadio($model, $attribute, $options = [])
1449
    {
1450 4
        return static::activeBooleanInput('radio', $model, $attribute, $options);
1451
    }
1452
1453
    /**
1454
     * Generates a checkbox tag together with a label for the given model attribute.
1455
     * This method will generate the "checked" tag attribute according to the model attribute value.
1456
     * @param Model $model the model object
1457
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1458
     * about attribute expression.
1459
     * @param array $options the tag options in terms of name-value pairs.
1460
     * See [[booleanInput()]] for details about accepted attributes.
1461
     *
1462
     * @return string the generated checkbox tag
1463
     */
1464 4
    public static function activeCheckbox($model, $attribute, $options = [])
1465
    {
1466 4
        return static::activeBooleanInput('checkbox', $model, $attribute, $options);
1467
    }
1468
1469
    /**
1470
     * Generates a boolean input
1471
     * This method is mainly called by [[activeCheckbox()]] and [[activeRadio()]].
1472
     * @param string $type the input type. This can be either `radio` or `checkbox`.
1473
     * @param Model $model the model object
1474
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1475
     * about attribute expression.
1476
     * @param array $options the tag options in terms of name-value pairs.
1477
     * See [[booleanInput()]] for details about accepted attributes.
1478
     * @return string the generated input element
1479
     * @since 2.0.9
1480
     */
1481 8
    protected static function activeBooleanInput($type, $model, $attribute, $options = [])
1482
    {
1483 8
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1484 8
        $value = static::getAttributeValue($model, $attribute);
1485
1486 8
        if (!array_key_exists('value', $options)) {
1487 8
            $options['value'] = '1';
1488
        }
1489 8
        if (!array_key_exists('uncheck', $options)) {
1490 4
            $options['uncheck'] = '0';
1491 4
        } elseif ($options['uncheck'] === false) {
1492 4
            unset($options['uncheck']);
1493
        }
1494 8
        if (!array_key_exists('label', $options)) {
1495 4
            $options['label'] = static::encode($model->getAttributeLabel(static::getAttributeName($attribute)));
1496 4
        } elseif ($options['label'] === false) {
1497 4
            unset($options['label']);
1498
        }
1499
1500 8
        $checked = "$value" === "{$options['value']}";
1501
1502 8
        if (!array_key_exists('id', $options)) {
1503 8
            $options['id'] = static::getInputId($model, $attribute);
1504
        }
1505
1506 8
        return static::$type($name, $checked, $options);
1507
    }
1508
1509
    /**
1510
     * Generates a drop-down list for the given model attribute.
1511
     * The selection of the drop-down list is taken from the value of the model attribute.
1512
     * @param Model $model the model object
1513
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1514
     * about attribute expression.
1515
     * @param array $items the option data items. The array keys are option values, and the array values
1516
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
1517
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
1518
     * If you have a list of data models, you may convert them into the format described above using
1519
     * [[\yii\helpers\ArrayHelper::map()]].
1520
     *
1521
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
1522
     * the labels will also be HTML-encoded.
1523
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
1524
     *
1525
     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array
1526
     *   to override the value and to set other tag attributes:
1527
     *
1528
     *   ```php
1529
     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],
1530
     *   ```
1531
     *
1532
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
1533
     *   and the array values are the extra attributes for the corresponding option tags. For example,
1534
     *
1535
     *   ```php
1536
     *   [
1537
     *       'value1' => ['disabled' => true],
1538
     *       'value2' => ['label' => 'value 2'],
1539
     *   ];
1540
     *   ```
1541
     *
1542
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
1543
     *   except that the array keys represent the optgroup labels specified in $items.
1544
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
1545
     *   Defaults to false.
1546
     * - encode: bool, whether to encode option prompt and option value characters.
1547
     *   Defaults to `true`. This option is available since 2.0.3.
1548
     *
1549
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
1550
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1551
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1552
     *
1553
     * @return string the generated drop-down list tag
1554
     */
1555 3
    public static function activeDropDownList($model, $attribute, $items, $options = [])
1556
    {
1557 3
        if (empty($options['multiple'])) {
1558 2
            return static::activeListInput('dropDownList', $model, $attribute, $items, $options);
1559
        }
1560
1561 1
        return static::activeListBox($model, $attribute, $items, $options);
1562
    }
1563
1564
    /**
1565
     * Generates a list box.
1566
     * The selection of the list box is taken from the value of the model attribute.
1567
     * @param Model $model the model object
1568
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1569
     * about attribute expression.
1570
     * @param array $items the option data items. The array keys are option values, and the array values
1571
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
1572
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
1573
     * If you have a list of data models, you may convert them into the format described above using
1574
     * [[\yii\helpers\ArrayHelper::map()]].
1575
     *
1576
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
1577
     * the labels will also be HTML-encoded.
1578
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
1579
     *
1580
     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array
1581
     *   to override the value and to set other tag attributes:
1582
     *
1583
     *   ```php
1584
     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],
1585
     *   ```
1586
     *
1587
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
1588
     *   and the array values are the extra attributes for the corresponding option tags. For example,
1589
     *
1590
     *   ```php
1591
     *   [
1592
     *       'value1' => ['disabled' => true],
1593
     *       'value2' => ['label' => 'value 2'],
1594
     *   ];
1595
     *   ```
1596
     *
1597
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
1598
     *   except that the array keys represent the optgroup labels specified in $items.
1599
     * - unselect: string, the value that will be submitted when no option is selected.
1600
     *   When this attribute is set, a hidden field will be generated so that if no option is selected in multiple
1601
     *   mode, we can still obtain the posted unselect value.
1602
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
1603
     *   Defaults to false.
1604
     * - encode: bool, whether to encode option prompt and option value characters.
1605
     *   Defaults to `true`. This option is available since 2.0.3.
1606
     *
1607
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
1608
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1609
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1610
     *
1611
     * @return string the generated list box tag
1612
     */
1613 3
    public static function activeListBox($model, $attribute, $items, $options = [])
1614
    {
1615 3
        return static::activeListInput('listBox', $model, $attribute, $items, $options);
1616
    }
1617
1618
    /**
1619
     * Generates a list of checkboxes.
1620
     * A checkbox list allows multiple selection, like [[listBox()]].
1621
     * As a result, the corresponding submitted value is an array.
1622
     * The selection of the checkbox list is taken from the value of the model attribute.
1623
     * @param Model $model the model object
1624
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1625
     * about attribute expression.
1626
     * @param array $items the data item used to generate the checkboxes.
1627
     * The array keys are the checkbox values, and the array values are the corresponding labels.
1628
     * Note that the labels will NOT be HTML-encoded, while the values will.
1629
     * @param array $options options (name => config) for the checkbox list container tag.
1630
     * The following options are specially handled:
1631
     *
1632
     * - tag: string|false, the tag name of the container element. False to render checkbox without container.
1633
     *   See also [[tag()]].
1634
     * - unselect: string, the value that should be submitted when none of the checkboxes is selected.
1635
     *   You may set this option to be null to prevent default value submission.
1636
     *   If this option is not set, an empty string will be submitted.
1637
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
1638
     *   This option is ignored if `item` option is set.
1639
     * - separator: string, the HTML code that separates items.
1640
     * - itemOptions: array, the options for generating the checkbox tag using [[checkbox()]].
1641
     * - item: callable, a callback that can be used to customize the generation of the HTML code
1642
     *   corresponding to a single item in $items. The signature of this callback must be:
1643
     *
1644
     *   ```php
1645
     *   function ($index, $label, $name, $checked, $value)
1646
     *   ```
1647
     *
1648
     *   where $index is the zero-based index of the checkbox in the whole list; $label
1649
     *   is the label for the checkbox; and $name, $value and $checked represent the name,
1650
     *   value and the checked status of the checkbox input.
1651
     *
1652
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1653
     *
1654
     * @return string the generated checkbox list
1655
     */
1656 1
    public static function activeCheckboxList($model, $attribute, $items, $options = [])
1657
    {
1658 1
        return static::activeListInput('checkboxList', $model, $attribute, $items, $options);
1659
    }
1660
1661
    /**
1662
     * Generates a list of radio buttons.
1663
     * A radio button list is like a checkbox list, except that it only allows single selection.
1664
     * The selection of the radio buttons is taken from the value of the model attribute.
1665
     * @param Model $model the model object
1666
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1667
     * about attribute expression.
1668
     * @param array $items the data item used to generate the radio buttons.
1669
     * The array keys are the radio values, and the array values are the corresponding labels.
1670
     * Note that the labels will NOT be HTML-encoded, while the values will.
1671
     * @param array $options options (name => config) for the radio button list container tag.
1672
     * The following options are specially handled:
1673
     *
1674
     * - tag: string|false, the tag name of the container element. False to render radio button without container.
1675
     *   See also [[tag()]].
1676
     * - unselect: string, the value that should be submitted when none of the radio buttons is selected.
1677
     *   You may set this option to be null to prevent default value submission.
1678
     *   If this option is not set, an empty string will be submitted.
1679
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
1680
     *   This option is ignored if `item` option is set.
1681
     * - separator: string, the HTML code that separates items.
1682
     * - itemOptions: array, the options for generating the radio button tag using [[radio()]].
1683
     * - item: callable, a callback that can be used to customize the generation of the HTML code
1684
     *   corresponding to a single item in $items. The signature of this callback must be:
1685
     *
1686
     *   ```php
1687
     *   function ($index, $label, $name, $checked, $value)
1688
     *   ```
1689
     *
1690
     *   where $index is the zero-based index of the radio button in the whole list; $label
1691
     *   is the label for the radio button; and $name, $value and $checked represent the name,
1692
     *   value and the checked status of the radio button input.
1693
     *
1694
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1695
     *
1696
     * @return string the generated radio button list
1697
     */
1698 1
    public static function activeRadioList($model, $attribute, $items, $options = [])
1699
    {
1700 1
        return static::activeListInput('radioList', $model, $attribute, $items, $options);
1701
    }
1702
1703
    /**
1704
     * Generates a list of input fields.
1705
     * This method is mainly called by [[activeListBox()]], [[activeRadioList()]] and [[activeCheckboxList()]].
1706
     * @param string $type the input type. This can be 'listBox', 'radioList', or 'checkBoxList'.
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 data item used to generate the input fields.
1711
     * The array keys are the input values, and the array values are the corresponding labels.
1712
     * Note that the labels will NOT be HTML-encoded, while the values will.
1713
     * @param array $options options (name => config) for the input list. The supported special options
1714
     * depend on the input type specified by `$type`.
1715
     * @return string the generated input list
1716
     */
1717 7
    protected static function activeListInput($type, $model, $attribute, $items, $options = [])
1718
    {
1719 7
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1720 7
        $selection = isset($options['value']) ? $options['value'] : static::getAttributeValue($model, $attribute);
1721 7
        if (!array_key_exists('unselect', $options)) {
1722 7
            $options['unselect'] = '';
1723
        }
1724 7
        if (!array_key_exists('id', $options)) {
1725 5
            $options['id'] = static::getInputId($model, $attribute);
1726
        }
1727 7
        return static::$type($name, $selection, $items, $options);
1728
    }
1729
1730
    /**
1731
     * Renders the option tags that can be used by [[dropDownList()]] and [[listBox()]].
1732
     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).
1733
     * @param array $items the option data items. The array keys are option values, and the array values
1734
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
1735
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
1736
     * If you have a list of data models, you may convert them into the format described above using
1737
     * [[\yii\helpers\ArrayHelper::map()]].
1738
     *
1739
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
1740
     * the labels will also be HTML-encoded.
1741
     * @param array $tagOptions the $options parameter that is passed to the [[dropDownList()]] or [[listBox()]] call.
1742
     * This method will take out these elements, if any: "prompt", "options" and "groups". See more details
1743
     * in [[dropDownList()]] for the explanation of these elements.
1744
     *
1745
     * @return string the generated list options
1746
     */
1747 8
    public static function renderSelectOptions($selection, $items, &$tagOptions = [])
1748
    {
1749 8
        $lines = [];
1750 8
        $encodeSpaces = ArrayHelper::remove($tagOptions, 'encodeSpaces', false);
1751 8
        $encode = ArrayHelper::remove($tagOptions, 'encode', true);
1752 8
        if (isset($tagOptions['prompt'])) {
1753 3
            $promptOptions = ['value' => ''];
1754 3
            if (is_string($tagOptions['prompt'])) {
1755 3
                $promptText = $tagOptions['prompt'];
1756
            } else {
1757 1
                $promptText = $tagOptions['prompt']['text'];
1758 1
                $promptOptions = array_merge($promptOptions, $tagOptions['prompt']['options']);
1759
            }
1760 3
            $promptText = $encode ? static::encode($promptText) : $promptText;
1761 3
            if ($encodeSpaces) {
1762 1
                $promptText = str_replace(' ', '&nbsp;', $promptText);
1763
            }
1764 3
            $lines[] = static::tag('option', $promptText, $promptOptions);
1765
        }
1766
1767 8
        $options = isset($tagOptions['options']) ? $tagOptions['options'] : [];
1768 8
        $groups = isset($tagOptions['groups']) ? $tagOptions['groups'] : [];
1769 8
        unset($tagOptions['prompt'], $tagOptions['options'], $tagOptions['groups']);
1770 8
        $options['encodeSpaces'] = ArrayHelper::getValue($options, 'encodeSpaces', $encodeSpaces);
1771 8
        $options['encode'] = ArrayHelper::getValue($options, 'encode', $encode);
1772
1773 8
        foreach ($items as $key => $value) {
1774 7
            if (is_array($value)) {
1775 1
                $groupAttrs = isset($groups[$key]) ? $groups[$key] : [];
1776 1
                if (!isset($groupAttrs['label'])) {
1777 1
                    $groupAttrs['label'] = $key;
1778
                }
1779 1
                $attrs = ['options' => $options, 'groups' => $groups, 'encodeSpaces' => $encodeSpaces, 'encode' => $encode];
1780 1
                $content = static::renderSelectOptions($selection, $value, $attrs);
1781 1
                $lines[] = static::tag('optgroup', "\n" . $content . "\n", $groupAttrs);
1782
            } else {
1783 7
                $attrs = isset($options[$key]) ? $options[$key] : [];
1784 7
                $attrs['value'] = (string) $key;
1785 7
                if (!array_key_exists('selected', $attrs)) {
1786 7
                    $attrs['selected'] = $selection !== null &&
1787 5
                        (!ArrayHelper::isTraversable($selection) && !strcmp($key, $selection)
1788 5
                        || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn($key, $selection));
0 ignored issues
show
Bug introduced by
It seems like $selection defined by parameter $selection on line 1747 can also be of type string; however, yii\helpers\BaseArrayHelper::isIn() does only seem to accept array|object<Traversable>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1789
                }
1790 7
                $text = $encode ? static::encode($value) : $value;
1791 7
                if ($encodeSpaces) {
1792 2
                    $text = str_replace(' ', '&nbsp;', $text);
1793
                }
1794 7
                $lines[] = static::tag('option', $text, $attrs);
1795
            }
1796
        }
1797
1798 8
        return implode("\n", $lines);
1799
    }
1800
1801
    /**
1802
     * Renders the HTML tag attributes.
1803
     *
1804
     * Attributes whose values are of boolean type will be treated as
1805
     * [boolean attributes](http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes).
1806
     *
1807
     * Attributes whose values are null will not be rendered.
1808
     *
1809
     * The values of attributes will be HTML-encoded using [[encode()]].
1810
     *
1811
     * The "data" attribute is specially handled when it is receiving an array value. In this case,
1812
     * the array will be "expanded" and a list data attributes will be rendered. For example,
1813
     * if `'data' => ['id' => 1, 'name' => 'yii']`, then this will be rendered:
1814
     * `data-id="1" data-name="yii"`.
1815
     * Additionally `'data' => ['params' => ['id' => 1, 'name' => 'yii'], 'status' => 'ok']` will be rendered as:
1816
     * `data-params='{"id":1,"name":"yii"}' data-status="ok"`.
1817
     *
1818
     * @param array $attributes attributes to be rendered. The attribute values will be HTML-encoded using [[encode()]].
1819
     * @return string the rendering result. If the attributes are not empty, they will be rendered
1820
     * into a string with a leading white space (so that it can be directly appended to the tag name
1821
     * in a tag. If there is no attribute, an empty string will be returned.
1822
     */
1823 204
    public static function renderTagAttributes($attributes)
1824
    {
1825 204
        if (count($attributes) > 1) {
1826 155
            $sorted = [];
1827 155
            foreach (static::$attributeOrder as $name) {
1828 155
                if (isset($attributes[$name])) {
1829 155
                    $sorted[$name] = $attributes[$name];
1830
                }
1831
            }
1832 155
            $attributes = array_merge($sorted, $attributes);
1833
        }
1834
1835 204
        $html = '';
1836 204
        foreach ($attributes as $name => $value) {
1837 196
            if (is_bool($value)) {
1838 32
                if ($value) {
1839 32
                    $html .= " $name";
1840
                }
1841 196
            } elseif (is_array($value)) {
1842 11
                if (in_array($name, static::$dataAttributes)) {
1843 4
                    foreach ($value as $n => $v) {
1844 4
                        if (is_array($v)) {
1845 1
                            $html .= " $name-$n='" . Json::htmlEncode($v) . "'";
1846
                        } else {
1847 4
                            $html .= " $name-$n=\"" . static::encode($v) . '"';
1848
                        }
1849
                    }
1850 9
                } elseif ($name === 'class') {
1851 8
                    if (empty($value)) {
1852 8
                        continue;
1853
                    }
1854 3
                    $html .= " $name=\"" . static::encode(implode(' ', $value)) . '"';
1855 2
                } elseif ($name === 'style') {
1856 1
                    if (empty($value)) {
1857 1
                        continue;
1858
                    }
1859 1
                    $html .= " $name=\"" . static::encode(static::cssStyleFromArray($value)) . '"';
1860
                } else {
1861 6
                    $html .= " $name='" . Json::htmlEncode($value) . "'";
1862
                }
1863 190
            } elseif ($value !== null) {
1864 192
                $html .= " $name=\"" . static::encode($value) . '"';
1865
            }
1866
        }
1867
1868 204
        return $html;
1869
    }
1870
1871
    /**
1872
     * Adds a CSS class (or several classes) to the specified options.
1873
     * If the CSS class is already in the options, it will not be added again.
1874
     * If class specification at given options is an array, and some class placed there with the named (string) key,
1875
     * overriding of such key will have no effect. For example:
1876
     *
1877
     * ```php
1878
     * $options = ['class' => ['persistent' => 'initial']];
1879
     * Html::addCssClass($options, ['persistent' => 'override']);
1880
     * var_dump($options['class']); // outputs: array('persistent' => 'initial');
1881
     * ```
1882
     *
1883
     * @param array $options the options to be modified.
1884
     * @param string|array $class the CSS class(es) to be added
1885
     */
1886 13
    public static function addCssClass(&$options, $class)
1887
    {
1888 13
        if (isset($options['class'])) {
1889 7
            if (is_array($options['class'])) {
1890 3
                $options['class'] = self::mergeCssClasses($options['class'], (array) $class);
1891
            } else {
1892 5
                $classes = preg_split('/\s+/', $options['class'], -1, PREG_SPLIT_NO_EMPTY);
1893 7
                $options['class'] = implode(' ', self::mergeCssClasses($classes, (array) $class));
1894
            }
1895
        } else {
1896 12
            $options['class'] = $class;
1897
        }
1898 13
    }
1899
1900
    /**
1901
     * Merges already existing CSS classes with new one.
1902
     * This method provides the priority for named existing classes over additional.
1903
     * @param array $existingClasses already existing CSS classes.
1904
     * @param array $additionalClasses CSS classes to be added.
1905
     * @return array merge result.
1906
     */
1907 7
    private static function mergeCssClasses(array $existingClasses, array $additionalClasses)
1908
    {
1909 7
        foreach ($additionalClasses as $key => $class) {
1910 7
            if (is_int($key) && !in_array($class, $existingClasses)) {
1911 6
                $existingClasses[] = $class;
1912 2
            } elseif (!isset($existingClasses[$key])) {
1913 7
                $existingClasses[$key] = $class;
1914
            }
1915
        }
1916 7
        return array_unique($existingClasses);
1917
    }
1918
1919
    /**
1920
     * Removes a CSS class from the specified options.
1921
     * @param array $options the options to be modified.
1922
     * @param string|array $class the CSS class(es) to be removed
1923
     */
1924 1
    public static function removeCssClass(&$options, $class)
1925
    {
1926 1
        if (isset($options['class'])) {
1927 1
            if (is_array($options['class'])) {
1928 1
                $classes = array_diff($options['class'], (array) $class);
1929 1
                if (empty($classes)) {
1930 1
                    unset($options['class']);
1931
                } else {
1932 1
                    $options['class'] = $classes;
1933
                }
1934
            } else {
1935 1
                $classes = preg_split('/\s+/', $options['class'], -1, PREG_SPLIT_NO_EMPTY);
1936 1
                $classes = array_diff($classes, (array) $class);
1937 1
                if (empty($classes)) {
1938 1
                    unset($options['class']);
1939
                } else {
1940 1
                    $options['class'] = implode(' ', $classes);
1941
                }
1942
            }
1943
        }
1944 1
    }
1945
1946
    /**
1947
     * Adds the specified CSS style to the HTML options.
1948
     *
1949
     * If the options already contain a `style` element, the new style will be merged
1950
     * with the existing one. If a CSS property exists in both the new and the old styles,
1951
     * the old one may be overwritten if `$overwrite` is true.
1952
     *
1953
     * For example,
1954
     *
1955
     * ```php
1956
     * Html::addCssStyle($options, 'width: 100px; height: 200px');
1957
     * ```
1958
     *
1959
     * @param array $options the HTML options to be modified.
1960
     * @param string|array $style the new style string (e.g. `'width: 100px; height: 200px'`) or
1961
     * array (e.g. `['width' => '100px', 'height' => '200px']`).
1962
     * @param bool $overwrite whether to overwrite existing CSS properties if the new style
1963
     * contain them too.
1964
     * @see removeCssStyle()
1965
     * @see cssStyleFromArray()
1966
     * @see cssStyleToArray()
1967
     */
1968 1
    public static function addCssStyle(&$options, $style, $overwrite = true)
1969
    {
1970 1
        if (!empty($options['style'])) {
1971 1
            $oldStyle = is_array($options['style']) ? $options['style'] : static::cssStyleToArray($options['style']);
1972 1
            $newStyle = is_array($style) ? $style : static::cssStyleToArray($style);
1973 1
            if (!$overwrite) {
1974 1
                foreach ($newStyle as $property => $value) {
1975 1
                    if (isset($oldStyle[$property])) {
1976 1
                        unset($newStyle[$property]);
1977
                    }
1978
                }
1979
            }
1980 1
            $style = array_merge($oldStyle, $newStyle);
1981
        }
1982 1
        $options['style'] = is_array($style) ? static::cssStyleFromArray($style) : $style;
1983 1
    }
1984
1985
    /**
1986
     * Removes the specified CSS style from the HTML options.
1987
     *
1988
     * For example,
1989
     *
1990
     * ```php
1991
     * Html::removeCssStyle($options, ['width', 'height']);
1992
     * ```
1993
     *
1994
     * @param array $options the HTML options to be modified.
1995
     * @param string|array $properties the CSS properties to be removed. You may use a string
1996
     * if you are removing a single property.
1997
     * @see addCssStyle()
1998
     */
1999 1
    public static function removeCssStyle(&$options, $properties)
2000
    {
2001 1
        if (!empty($options['style'])) {
2002 1
            $style = is_array($options['style']) ? $options['style'] : static::cssStyleToArray($options['style']);
2003 1
            foreach ((array) $properties as $property) {
2004 1
                unset($style[$property]);
2005
            }
2006 1
            $options['style'] = static::cssStyleFromArray($style);
2007
        }
2008 1
    }
2009
2010
    /**
2011
     * Converts a CSS style array into a string representation.
2012
     *
2013
     * For example,
2014
     *
2015
     * ```php
2016
     * print_r(Html::cssStyleFromArray(['width' => '100px', 'height' => '200px']));
2017
     * // will display: 'width: 100px; height: 200px;'
2018
     * ```
2019
     *
2020
     * @param array $style the CSS style array. The array keys are the CSS property names,
2021
     * and the array values are the corresponding CSS property values.
2022
     * @return string the CSS style string. If the CSS style is empty, a null will be returned.
2023
     */
2024 4
    public static function cssStyleFromArray(array $style)
2025
    {
2026 4
        $result = '';
2027 4
        foreach ($style as $name => $value) {
2028 4
            $result .= "$name: $value; ";
2029
        }
2030
        // return null if empty to avoid rendering the "style" attribute
2031 4
        return $result === '' ? null : rtrim($result);
2032
    }
2033
2034
    /**
2035
     * Converts a CSS style string into an array representation.
2036
     *
2037
     * The array keys are the CSS property names, and the array values
2038
     * are the corresponding CSS property values.
2039
     *
2040
     * For example,
2041
     *
2042
     * ```php
2043
     * print_r(Html::cssStyleToArray('width: 100px; height: 200px;'));
2044
     * // will display: ['width' => '100px', 'height' => '200px']
2045
     * ```
2046
     *
2047
     * @param string $style the CSS style string
2048
     * @return array the array representation of the CSS style
2049
     */
2050 3
    public static function cssStyleToArray($style)
2051
    {
2052 3
        $result = [];
2053 3
        foreach (explode(';', $style) as $property) {
2054 3
            $property = explode(':', $property);
2055 3
            if (count($property) > 1) {
2056 3
                $result[trim($property[0])] = trim($property[1]);
2057
            }
2058
        }
2059 3
        return $result;
2060
    }
2061
2062
    /**
2063
     * Returns the real attribute name from the given attribute expression.
2064
     *
2065
     * An attribute expression is an attribute name prefixed and/or suffixed with array indexes.
2066
     * It is mainly used in tabular data input and/or input of array type. Below are some examples:
2067
     *
2068
     * - `[0]content` is used in tabular data input to represent the "content" attribute
2069
     *   for the first model in tabular input;
2070
     * - `dates[0]` represents the first array element of the "dates" attribute;
2071
     * - `[0]dates[0]` represents the first array element of the "dates" attribute
2072
     *   for the first model in tabular input.
2073
     *
2074
     * If `$attribute` has neither prefix nor suffix, it will be returned back without change.
2075
     * @param string $attribute the attribute name or expression
2076
     * @return string the attribute name without prefix and suffix.
2077
     * @throws InvalidArgumentException if the attribute name contains non-word characters.
2078
     */
2079 47
    public static function getAttributeName($attribute)
2080
    {
2081 47
        if (preg_match(static::$attributeRegex, $attribute, $matches)) {
2082 44
            return $matches[2];
2083
        }
2084
2085 3
        throw new InvalidArgumentException('Attribute name must contain word characters only.');
2086
    }
2087
2088
    /**
2089
     * Returns the value of the specified attribute name or expression.
2090
     *
2091
     * For an attribute expression like `[0]dates[0]`, this method will return the value of `$model->dates[0]`.
2092
     * See [[getAttributeName()]] for more details about attribute expression.
2093
     *
2094
     * If an attribute value is an instance of [[ActiveRecordInterface]] or an array of such instances,
2095
     * the primary value(s) of the AR instance(s) will be returned instead.
2096
     *
2097
     * @param Model $model the model object
2098
     * @param string $attribute the attribute name or expression
2099
     * @return string|array the corresponding attribute value
2100
     * @throws InvalidArgumentException if the attribute name contains non-word characters.
2101
     */
2102 49
    public static function getAttributeValue($model, $attribute)
2103
    {
2104 49
        if (!preg_match(static::$attributeRegex, $attribute, $matches)) {
2105 1
            throw new InvalidArgumentException('Attribute name must contain word characters only.');
2106
        }
2107 48
        $attribute = $matches[2];
2108 48
        $value = $model->$attribute;
2109 48
        if ($matches[3] !== '') {
2110
            foreach (explode('][', trim($matches[3], '[]')) as $id) {
2111
                if ((is_array($value) || $value instanceof \ArrayAccess) && isset($value[$id])) {
2112
                    $value = $value[$id];
2113
                } else {
2114
                    return null;
2115
                }
2116
            }
2117
        }
2118
2119
        // https://github.com/yiisoft/yii2/issues/1457
2120 48
        if (is_array($value)) {
2121 1
            foreach ($value as $i => $v) {
2122 1
                if ($v instanceof ActiveRecordInterface) {
2123 1
                    $v = $v->getPrimaryKey(false);
2124 1
                    $value[$i] = is_array($v) ? json_encode($v) : $v;
2125
                }
2126
            }
2127 48
        } elseif ($value instanceof ActiveRecordInterface) {
2128 1
            $value = $value->getPrimaryKey(false);
2129
2130 1
            return is_array($value) ? json_encode($value) : $value;
2131
        }
2132
2133 48
        return $value;
2134
    }
2135
2136
    /**
2137
     * Generates an appropriate input name for the specified attribute name or expression.
2138
     *
2139
     * This method generates a name that can be used as the input name to collect user input
2140
     * for the specified attribute. The name is generated according to the [[Model::formName|form name]]
2141
     * of the model and the given attribute name. For example, if the form name of the `Post` model
2142
     * is `Post`, then the input name generated for the `content` attribute would be `Post[content]`.
2143
     *
2144
     * See [[getAttributeName()]] for explanation of attribute expression.
2145
     *
2146
     * @param Model $model the model object
2147
     * @param string $attribute the attribute name or expression
2148
     * @return string the generated input name
2149
     * @throws InvalidArgumentException if the attribute name contains non-word characters.
2150
     */
2151 62
    public static function getInputName($model, $attribute)
2152
    {
2153 62
        $formName = $model->formName();
2154 62
        if (!preg_match(static::$attributeRegex, $attribute, $matches)) {
2155 1
            throw new InvalidArgumentException('Attribute name must contain word characters only.');
2156
        }
2157 61
        $prefix = $matches[1];
2158 61
        $attribute = $matches[2];
2159 61
        $suffix = $matches[3];
2160 61
        if ($formName === '' && $prefix === '') {
2161 1
            return $attribute . $suffix;
2162 60
        } elseif ($formName !== '') {
2163 59
            return $formName . $prefix . "[$attribute]" . $suffix;
2164
        }
2165
2166 1
        throw new InvalidArgumentException(get_class($model) . '::formName() cannot be empty for tabular inputs.');
2167
    }
2168
2169
    /**
2170
     * Generates an appropriate input ID for the specified attribute name or expression.
2171
     *
2172
     * This method converts the result [[getInputName()]] into a valid input ID.
2173
     * For example, if [[getInputName()]] returns `Post[content]`, this method will return `post-content`.
2174
     * @param Model $model the model object
2175
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for explanation of attribute expression.
2176
     * @return string the generated input ID
2177
     * @throws InvalidArgumentException if the attribute name contains non-word characters.
2178
     */
2179 47
    public static function getInputId($model, $attribute)
2180
    {
2181 47
        $name = strtolower(static::getInputName($model, $attribute));
2182 47
        return str_replace(['[]', '][', '[', ']', ' ', '.'], ['', '-', '-', '', '-', '-'], $name);
2183
    }
2184
2185
    /**
2186
     * Escapes regular expression to use in JavaScript
2187
     * @param string $regexp the regular expression to be escaped.
2188
     * @return string the escaped result.
2189
     * @since 2.0.6
2190
     */
2191 1
    public static function escapeJsRegularExpression($regexp)
2192
    {
2193 1
        $pattern = preg_replace('/\\\\x\{?([0-9a-fA-F]+)\}?/', '\u$1', $regexp);
2194 1
        $deliminator = substr($pattern, 0, 1);
2195 1
        $pos = strrpos($pattern, $deliminator, 1);
2196 1
        $flag = substr($pattern, $pos + 1);
2197 1
        if ($deliminator !== '/') {
2198 1
            $pattern = '/' . str_replace('/', '\\/', substr($pattern, 1, $pos - 1)) . '/';
2199
        } else {
2200 1
            $pattern = substr($pattern, 0, $pos + 1);
2201
        }
2202 1
        if (!empty($flag)) {
2203 1
            $pattern .= preg_replace('/[^igm]/', '', $flag);
2204
        }
2205
2206 1
        return $pattern;
2207
    }
2208
}
2209