Completed
Push — master ( 36960d...148007 )
by Alexander
24:32 queued 21:36
created

framework/helpers/BaseHtml.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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\InvalidParamException;
12
use yii\db\ActiveRecordInterface;
13
use yii\validators\StringValidator;
14
use yii\web\Request;
15
use yii\base\Model;
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 array list of void elements (element name => 1)
29
     * @see http://www.w3.org/TR/html-markup/syntax.html#void-element
30
     */
31
    public static $voidElements = [
32
        'area' => 1,
33
        'base' => 1,
34
        'br' => 1,
35
        'col' => 1,
36
        'command' => 1,
37
        'embed' => 1,
38
        'hr' => 1,
39
        'img' => 1,
40
        'input' => 1,
41
        'keygen' => 1,
42
        'link' => 1,
43
        'meta' => 1,
44
        'param' => 1,
45
        'source' => 1,
46
        'track' => 1,
47
        'wbr' => 1,
48
    ];
49
    /**
50
     * @var array the preferred order of attributes in a tag. This mainly affects the order of the attributes
51
     * that are rendered by [[renderTagAttributes()]].
52
     */
53
    public static $attributeOrder = [
54
        'type',
55
        'id',
56
        'class',
57
        'name',
58
        'value',
59
60
        'href',
61
        'src',
62
        'srcset',
63
        'action',
64
        'method',
65
66
        'selected',
67
        'checked',
68
        'readonly',
69
        'disabled',
70
        'multiple',
71
72
        'size',
73
        'maxlength',
74
        'width',
75
        'height',
76
        'rows',
77
        'cols',
78
79
        'alt',
80
        'title',
81
        'rel',
82
        'media',
83
    ];
84
    /**
85
     * @var array list of tag attributes that should be specially handled when their values are of array type.
86
     * In particular, if the value of the `data` attribute is `['name' => 'xyz', 'age' => 13]`, two attributes
87
     * will be generated instead of one: `data-name="xyz" data-age="13"`.
88
     * @since 2.0.3
89
     */
90
    public static $dataAttributes = ['data', 'data-ng', 'ng'];
91
92
93
    /**
94
     * Encodes special characters into HTML entities.
95
     * The [[\yii\base\Application::charset|application charset]] will be used for encoding.
96
     * @param string $content the content to be encoded
97
     * @param bool $doubleEncode whether to encode HTML entities in `$content`. If false,
98
     * HTML entities in `$content` will not be further encoded.
99
     * @return string the encoded content
100
     * @see decode()
101
     * @see http://www.php.net/manual/en/function.htmlspecialchars.php
102 182
     */
103
    public static function encode($content, $doubleEncode = true)
104 182
    {
105
        return htmlspecialchars($content, ENT_QUOTES | ENT_SUBSTITUTE, Yii::$app ? Yii::$app->charset : 'UTF-8', $doubleEncode);
106
    }
107
108
    /**
109
     * Decodes special HTML entities back to the corresponding characters.
110
     * This is the opposite of [[encode()]].
111
     * @param string $content the content to be decoded
112
     * @return string the decoded content
113
     * @see encode()
114
     * @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php
115 1
     */
116
    public static function decode($content)
117 1
    {
118
        return htmlspecialchars_decode($content, ENT_QUOTES);
119
    }
120
121
    /**
122
     * Generates a complete HTML tag.
123
     * @param string|bool|null $name the tag name. If $name is `null` or `false`, the corresponding content will be rendered without any tag.
124
     * @param string $content the content to be enclosed between the start and end tags. It will not be HTML-encoded.
125
     * If this is coming from end users, you should consider [[encode()]] it to prevent XSS attacks.
126
     * @param array $options the HTML tag attributes (HTML options) in terms of name-value pairs.
127
     * These will be rendered as the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
128
     * If a value is null, the corresponding attribute will not be rendered.
129
     *
130
     * For example when using `['class' => 'my-class', 'target' => '_blank', 'value' => null]` it will result in the
131
     * html attributes rendered like this: `class="my-class" target="_blank"`.
132
     *
133
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
134
     *
135
     * @return string the generated HTML tag
136
     * @see beginTag()
137
     * @see endTag()
138 171
     */
139
    public static function tag($name, $content = '', $options = [])
140 171
    {
141 3
        if ($name === null || $name === false) {
142
            return $content;
143 170
        }
144 170
        $html = "<$name" . static::renderTagAttributes($options) . '>';
145
        return isset(static::$voidElements[strtolower($name)]) ? $html : "$html$content</$name>";
146
    }
147
148
    /**
149
     * Generates a start tag.
150
     * @param string|bool|null $name the tag name. If $name is `null` or `false`, the corresponding content will be rendered without any tag.
151
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
152
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
153
     * If a value is null, the corresponding attribute will not be rendered.
154
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
155
     * @return string the generated start tag
156
     * @see endTag()
157
     * @see tag()
158 35
     */
159
    public static function beginTag($name, $options = [])
160 35
    {
161 3
        if ($name === null || $name === false) {
162
            return '';
163 35
        }
164
        return "<$name" . static::renderTagAttributes($options) . '>';
165
    }
166
167
    /**
168
     * Generates an end tag.
169
     * @param string|bool|null $name the tag name. If $name is `null` or `false`, the corresponding content will be rendered without any tag.
170
     * @return string the generated end tag
171
     * @see beginTag()
172
     * @see tag()
173 12
     */
174
    public static function endTag($name)
175 12
    {
176 3
        if ($name === null || $name === false) {
177
            return '';
178 11
        }
179
        return "</$name>";
180
    }
181
182
    /**
183
     * Generates a style tag.
184
     * @param string $content the style content
185
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
186
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
187
     * If a value is null, the corresponding attribute will not be rendered.
188
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
189
     * @return string the generated style tag
190 1
     */
191
    public static function style($content, $options = [])
192 1
    {
193
        return static::tag('style', $content, $options);
194
    }
195
196
    /**
197
     * Generates a script tag.
198
     * @param string $content the script content
199
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
200
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
201
     * If a value is null, the corresponding attribute will not be rendered.
202
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
203
     * @return string the generated script tag
204 1
     */
205
    public static function script($content, $options = [])
206 1
    {
207
        return static::tag('script', $content, $options);
208
    }
209
210
    /**
211
     * Generates a link tag that refers to an external CSS file.
212
     * @param array|string $url the URL of the external CSS file. This parameter will be processed by [[Url::to()]].
213
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
214
     *
215
     * - condition: specifies the conditional comments for IE, e.g., `lt IE 9`. When this is specified,
216
     *   the generated `link` tag will be enclosed within the conditional comments. This is mainly useful
217
     *   for supporting old versions of IE browsers.
218
     * - noscript: if set to true, `link` tag will be wrapped into `<noscript>` tags.
219
     *
220
     * The rest of the options will be rendered as the attributes of the resulting link tag. The values will
221
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
222
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
223
     * @return string the generated link tag
224
     * @see Url::to()
225 20
     */
226
    public static function cssFile($url, $options = [])
227 20
    {
228 20
        if (!isset($options['rel'])) {
229 20
            $options['rel'] = 'stylesheet';
230 20
        }
231
        $options['href'] = Url::to($url);
232 20
233 1
        if (isset($options['condition'])) {
234 1
            $condition = $options['condition'];
235 1
            unset($options['condition']);
236 20
            return self::wrapIntoCondition(static::tag('link', '', $options), $condition);
237
        } elseif (isset($options['noscript']) && $options['noscript'] === true) {
238
            unset($options['noscript']);
239
            return '<noscript>' . static::tag('link', '', $options) . '</noscript>';
240 20
        } else {
241
            return static::tag('link', '', $options);
242
        }
243
    }
244
245
    /**
246
     * Generates a script tag that refers to an external JavaScript file.
247
     * @param string $url the URL of the external JavaScript file. This parameter will be processed by [[Url::to()]].
248
     * @param array $options the tag options in terms of name-value pairs. The following option is specially handled:
249
     *
250
     * - condition: specifies the conditional comments for IE, e.g., `lt IE 9`. When this is specified,
251
     *   the generated `script` tag will be enclosed within the conditional comments. This is mainly useful
252
     *   for supporting old versions of IE browsers.
253
     *
254
     * The rest of the options will be rendered as the attributes of the resulting script tag. The values will
255
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
256
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
257
     * @return string the generated script tag
258
     * @see Url::to()
259 22
     */
260
    public static function jsFile($url, $options = [])
261 22
    {
262 22
        $options['src'] = Url::to($url);
263 1
        if (isset($options['condition'])) {
264 1
            $condition = $options['condition'];
265 1
            unset($options['condition']);
266
            return self::wrapIntoCondition(static::tag('script', '', $options), $condition);
267 22
        } else {
268
            return static::tag('script', '', $options);
269
        }
270
    }
271
272
    /**
273
     * Wraps given content into conditional comments for IE, e.g., `lt IE 9`.
274
     * @param string $content raw HTML content.
275
     * @param string $condition condition string.
276
     * @return string generated HTML.
277 2
     */
278
    private static function wrapIntoCondition($content, $condition)
279 2
    {
280 2
        if (strpos($condition, '!IE') !== false) {
281
            return "<!--[if $condition]><!-->\n" . $content . "\n<!--<![endif]-->";
282 2
        }
283
        return "<!--[if $condition]>\n" . $content . "\n<![endif]-->";
284
    }
285
286
    /**
287
     * Generates the meta tags containing CSRF token information.
288
     * @return string the generated meta tags
289
     * @see Request::enableCsrfValidation
290
     */
291
    public static function csrfMetaTags()
292
    {
293
        $request = Yii::$app->getRequest();
294
        if ($request instanceof Request && $request->enableCsrfValidation) {
295
            return static::tag('meta', '', ['name' => 'csrf-param', 'content' => $request->csrfParam]) . "\n    "
296
                . static::tag('meta', '', ['name' => 'csrf-token', 'content' => $request->getCsrfToken()]) . "\n";
297
        } else {
298
            return '';
299
        }
300
    }
301
302
    /**
303
     * Generates a form start tag.
304
     * @param array|string $action the form action URL. This parameter will be processed by [[Url::to()]].
305
     * @param string $method the form submission method, such as "post", "get", "put", "delete" (case-insensitive).
306
     * Since most browsers only support "post" and "get", if other methods are given, they will
307
     * be simulated using "post", and a hidden input will be added which contains the actual method type.
308
     * See [[\yii\web\Request::methodParam]] for more details.
309
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
310
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
311
     * If a value is null, the corresponding attribute will not be rendered.
312
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
313
     *
314
     * Special options:
315
     *
316
     *  - `csrf`: whether to generate the CSRF hidden input. Defaults to true.
317
     *
318
     * @return string the generated form start tag.
319
     * @see endForm()
320 33
     */
321
    public static function beginForm($action = '', $method = 'post', $options = [])
322 33
    {
323
        $action = Url::to($action);
324 33
325
        $hiddenInputs = [];
326 33
327 33
        $request = Yii::$app->getRequest();
328 30
        if ($request instanceof Request) {
329
            if (strcasecmp($method, 'get') && strcasecmp($method, 'post')) {
330
                // simulate PUT, DELETE, etc. via POST
331
                $hiddenInputs[] = static::hiddenInput($request->methodParam, $method);
332
                $method = 'post';
333 30
            }
334
            $csrf = ArrayHelper::remove($options, 'csrf', true);
335 30
336 29
            if ($csrf && $request->enableCsrfValidation && strcasecmp($method, 'post') === 0) {
337 29
                $hiddenInputs[] = static::hiddenInput($request->csrfParam, $request->getCsrfToken());
338 30
            }
339
        }
340 33
341
        if (!strcasecmp($method, 'get') && ($pos = strpos($action, '?')) !== false) {
342
            // query parameters in the action are ignored for GET method
343 1
            // we use hidden fields to add them back
344 1
            foreach (explode('&', substr($action, $pos + 1)) as $pair) {
345 1
                if (($pos1 = strpos($pair, '=')) !== false) {
346 1
                    $hiddenInputs[] = static::hiddenInput(
347 1
                        urldecode(substr($pair, 0, $pos1)),
348 1
                        urldecode(substr($pair, $pos1 + 1))
349 1
                    );
350
                } else {
351
                    $hiddenInputs[] = static::hiddenInput(urldecode($pair), '');
352 1
                }
353 1
            }
354 1
            $action = substr($action, 0, $pos);
355
        }
356 33
357 33
        $options['action'] = $action;
358 33
        $options['method'] = $method;
359 33
        $form = static::beginTag('form', $options);
360 30
        if (!empty($hiddenInputs)) {
361 30
            $form .= "\n" . implode("\n", $hiddenInputs);
362
        }
363 33
364
        return $form;
365
    }
366
367
    /**
368
     * Generates a form end tag.
369
     * @return string the generated tag
370
     * @see beginForm()
371 32
     */
372
    public static function endForm()
373 32
    {
374
        return '</form>';
375
    }
376
377
    /**
378
     * Generates a hyperlink tag.
379
     * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code
380
     * such as an image tag. If this is coming from end users, you should consider [[encode()]]
381
     * it to prevent XSS attacks.
382
     * @param array|string|null $url the URL for the hyperlink tag. This parameter will be processed by [[Url::to()]]
383
     * and will be used for the "href" attribute of the tag. If this parameter is null, the "href" attribute
384
     * will not be generated.
385
     *
386
     * If you want to use an absolute url you can call [[Url::to()]] yourself, before passing the URL to this method,
387
     * like this:
388
     *
389
     * ```php
390
     * Html::a('link text', Url::to($url, true))
391
     * ```
392
     *
393
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
394
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
395
     * If a value is null, the corresponding attribute will not be rendered.
396
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
397
     * @return string the generated hyperlink
398
     * @see \yii\helpers\Url::to()
399 13
     */
400
    public static function a($text, $url = null, $options = [])
401 13
    {
402 13
        if ($url !== null) {
403 13
            $options['href'] = Url::to($url);
404 13
        }
405
        return static::tag('a', $text, $options);
406
    }
407
408
    /**
409
     * Generates a mailto hyperlink.
410
     * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code
411
     * such as an image tag. If this is coming from end users, you should consider [[encode()]]
412
     * it to prevent XSS attacks.
413
     * @param string $email email address. If this is null, the first parameter (link body) will be treated
414
     * as the email address and used.
415
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
416
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
417
     * If a value is null, the corresponding attribute will not be rendered.
418
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
419
     * @return string the generated mailto link
420 2
     */
421
    public static function mailto($text, $email = null, $options = [])
422 2
    {
423 2
        $options['href'] = 'mailto:' . ($email === null ? $text : $email);
424
        return static::tag('a', $text, $options);
425
    }
426
427
    /**
428
     * Generates an image tag.
429
     * @param array|string $src the image URL. This parameter will be processed by [[Url::to()]].
430
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
431
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
432
     * If a value is null, the corresponding attribute will not be rendered.
433
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
434
     * @since 2.0.12 It is possible to pass the "srcset" option as an array which keys are
435
     * descriptors and values are URLs. All URLs will be processed by [[Url::to()]].
436
     * @return string the generated image tag
437 10
     */
438
    public static function img($src, $options = [])
439 10
    {
440
        $options['src'] = Url::to($src);
441 10
442 5
        if (isset($options['srcset']) && is_array($options['srcset'])) {
443 5
            $srcset = [];
444 4
            foreach ($options['srcset'] as $descriptor => $url) {
445 5
                $srcset[] = Url::to($url) . ' ' . $descriptor;
446 5
            }
447 5
            $options['srcset'] = implode(',', $srcset);
448
        }
449 10
450 9
        if (!isset($options['alt'])) {
451 9
            $options['alt'] = '';
452 10
        }
453
        return static::tag('img', '', $options);
454
    }
455
456
    /**
457
     * Generates a label tag.
458
     * @param string $content label text. It will NOT be HTML-encoded. Therefore you can pass in HTML code
459
     * such as an image tag. If this is is coming from end users, you should [[encode()]]
460
     * it to prevent XSS attacks.
461
     * @param string $for the ID of the HTML element that this label is associated with.
462
     * If this is null, the "for" attribute will not be generated.
463
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
464
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
465
     * If a value is null, the corresponding attribute will not be rendered.
466
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
467
     * @return string the generated label tag
468 20
     */
469
    public static function label($content, $for = null, $options = [])
470 20
    {
471 20
        $options['for'] = $for;
472
        return static::tag('label', $content, $options);
473
    }
474
475
    /**
476
     * Generates a button tag.
477
     * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
478
     * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
479
     * you should consider [[encode()]] it to prevent XSS attacks.
480
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
481
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
482
     * If a value is null, the corresponding attribute will not be rendered.
483
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
484
     * @return string the generated button tag
485 3
     */
486
    public static function button($content = 'Button', $options = [])
487 3
    {
488 1
        if (!isset($options['type'])) {
489 1
            $options['type'] = 'button';
490 3
        }
491
        return static::tag('button', $content, $options);
492
    }
493
494
    /**
495
     * Generates a submit button tag.
496
     *
497
     * Be careful when naming form elements such as submit buttons. According to the [jQuery documentation](https://api.jquery.com/submit/) there
498
     * are some reserved names that can cause conflicts, e.g. `submit`, `length`, or `method`.
499
     *
500
     * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
501
     * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
502
     * you should consider [[encode()]] it to prevent XSS attacks.
503
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
504
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
505
     * If a value is null, the corresponding attribute will not be rendered.
506
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
507
     * @return string the generated submit button tag
508 1
     */
509
    public static function submitButton($content = 'Submit', $options = [])
510 1
    {
511 1
        $options['type'] = 'submit';
512
        return static::button($content, $options);
513
    }
514
515
    /**
516
     * Generates a reset button tag.
517
     * @param string $content the content enclosed within the button tag. It will NOT be HTML-encoded.
518
     * Therefore you can pass in HTML code such as an image tag. If this is is coming from end users,
519
     * you should consider [[encode()]] it to prevent XSS attacks.
520
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
521
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
522
     * If a value is null, the corresponding attribute will not be rendered.
523
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
524
     * @return string the generated reset button tag
525 1
     */
526
    public static function resetButton($content = 'Reset', $options = [])
527 1
    {
528 1
        $options['type'] = 'reset';
529
        return static::button($content, $options);
530
    }
531
532
    /**
533
     * Generates an input type of the given type.
534
     * @param string $type the type attribute.
535
     * @param string $name the name attribute. If it is null, the name attribute will not be generated.
536
     * @param string $value the value attribute. If it is null, the value attribute will not be generated.
537
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
538
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
539
     * If a value is null, the corresponding attribute will not be rendered.
540
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
541
     * @return string the generated input tag
542 63
     */
543
    public static function input($type, $name = null, $value = null, $options = [])
544 63
    {
545 63
        if (!isset($options['type'])) {
546 63
            $options['type'] = $type;
547 63
        }
548 63
        $options['name'] = $name;
549 63
        $options['value'] = $value === null ? null : (string) $value;
550
        return static::tag('input', '', $options);
551
    }
552
553
    /**
554
     * Generates an input button.
555
     * @param string $label the value attribute. If it is null, the value attribute will not be generated.
556
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
557
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
558
     * If a value is null, the corresponding attribute will not be rendered.
559
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
560
     * @return string the generated button tag
561 1
     */
562
    public static function buttonInput($label = 'Button', $options = [])
563 1
    {
564 1
        $options['type'] = 'button';
565 1
        $options['value'] = $label;
566
        return static::tag('input', '', $options);
567
    }
568
569
    /**
570
     * Generates a submit input button.
571
     *
572
     * Be careful when naming form elements such as submit buttons. According to the [jQuery documentation](https://api.jquery.com/submit/) there
573
     * are some reserved names that can cause conflicts, e.g. `submit`, `length`, or `method`.
574
     *
575
     * @param string $label the value attribute. If it is null, the value attribute will not be generated.
576
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
577
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
578
     * If a value is null, the corresponding attribute will not be rendered.
579
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
580
     * @return string the generated button tag
581 1
     */
582
    public static function submitInput($label = 'Submit', $options = [])
583 1
    {
584 1
        $options['type'] = 'submit';
585 1
        $options['value'] = $label;
586
        return static::tag('input', '', $options);
587
    }
588
589
    /**
590
     * Generates a reset input button.
591
     * @param string $label the value attribute. If it is null, the value attribute will not be generated.
592
     * @param array $options the attributes of the button tag. The values will be HTML-encoded using [[encode()]].
593
     * Attributes whose value is null will be ignored and not put in the tag returned.
594
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
595
     * @return string the generated button tag
596 1
     */
597
    public static function resetInput($label = 'Reset', $options = [])
598 1
    {
599 1
        $options['type'] = 'reset';
600 1
        $options['value'] = $label;
601
        return static::tag('input', '', $options);
602
    }
603
604
    /**
605
     * Generates a text input field.
606
     * @param string $name the name attribute.
607
     * @param string $value the value attribute. If it is null, the value attribute will not be generated.
608
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
609
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
610
     * If a value is null, the corresponding attribute will not be rendered.
611
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
612
     * @return string the generated text input tag
613 1
     */
614
    public static function textInput($name, $value = null, $options = [])
615 1
    {
616
        return static::input('text', $name, $value, $options);
617
    }
618
619
    /**
620
     * Generates a hidden input field.
621
     * @param string $name the name attribute.
622
     * @param string $value the value attribute. If it is null, the value attribute will not be generated.
623
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
624
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
625
     * If a value is null, the corresponding attribute will not be rendered.
626
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
627
     * @return string the generated hidden input tag
628 41
     */
629
    public static function hiddenInput($name, $value = null, $options = [])
630 41
    {
631
        return static::input('hidden', $name, $value, $options);
632
    }
633
634
    /**
635
     * Generates a password input field.
636
     * @param string $name the name attribute.
637
     * @param string $value the value attribute. If it is null, the value attribute will not be generated.
638
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
639
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
640
     * If a value is null, the corresponding attribute will not be rendered.
641
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
642
     * @return string the generated password input tag
643 1
     */
644
    public static function passwordInput($name, $value = null, $options = [])
645 1
    {
646
        return static::input('password', $name, $value, $options);
647
    }
648
649
    /**
650
     * Generates a file input field.
651
     * To use a file input field, you should set the enclosing form's "enctype" attribute to
652
     * be "multipart/form-data". After the form is submitted, the uploaded file information
653
     * can be obtained via $_FILES[$name] (see PHP documentation).
654
     * @param string $name the name attribute.
655
     * @param string $value the value attribute. If it is null, the value attribute will not be generated.
656
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
657
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
658
     * If a value is null, the corresponding attribute will not be rendered.
659
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
660
     * @return string the generated file input tag
661 1
     */
662
    public static function fileInput($name, $value = null, $options = [])
663 1
    {
664
        return static::input('file', $name, $value, $options);
665
    }
666
667
    /**
668
     * Generates a text area input.
669
     * @param string $name the input name
670
     * @param string $value the input value. Note that it will be encoded using [[encode()]].
671
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
672
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
673
     * If a value is null, the corresponding attribute will not be rendered.
674
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
675
     * The following special options are recognized:
676
     *
677
     * - `doubleEncode`: whether to double encode HTML entities in `$value`. If `false`, HTML entities in `$value` will not
678
     *   be further encoded. This option is available since version 2.0.11.
679
     *
680
     * @return string the generated text area tag
681 8
     */
682
    public static function textarea($name, $value = '', $options = [])
683 8
    {
684 8
        $options['name'] = $name;
685 8
        $doubleEncode = ArrayHelper::remove($options, 'doubleEncode', true);
686
        return static::tag('textarea', static::encode($value, $doubleEncode), $options);
687
    }
688
689
    /**
690
     * Generates a radio button input.
691
     * @param string $name the name attribute.
692
     * @param bool $checked whether the radio button should be checked.
693
     * @param array $options the tag options in terms of name-value pairs.
694
     * See [[booleanInput()]] for details about accepted attributes.
695
     *
696
     * @return string the generated radio button tag
697 9
     */
698
    public static function radio($name, $checked = false, $options = [])
699 9
    {
700
        return static::booleanInput('radio', $name, $checked, $options);
701
    }
702
703
    /**
704
     * Generates a checkbox input.
705
     * @param string $name the name attribute.
706
     * @param bool $checked whether the checkbox should be checked.
707
     * @param array $options the tag options in terms of name-value pairs.
708
     * See [[booleanInput()]] for details about accepted attributes.
709
     *
710
     * @return string the generated checkbox tag
711 8
     */
712
    public static function checkbox($name, $checked = false, $options = [])
713 8
    {
714
        return static::booleanInput('checkbox', $name, $checked, $options);
715
    }
716
717
    /**
718
     * Generates a boolean input.
719
     * @param string $type the input type. This can be either `radio` or `checkbox`.
720
     * @param string $name the name attribute.
721
     * @param bool $checked whether the checkbox should be checked.
722
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
723
     *
724
     * - uncheck: string, the value associated with the uncheck state of the checkbox. When this attribute
725
     *   is present, a hidden input will be generated so that if the checkbox is not checked and is submitted,
726
     *   the value of this attribute will still be submitted to the server via the hidden input.
727
     * - label: string, a label displayed next to the checkbox.  It will NOT be HTML-encoded. Therefore you can pass
728
     *   in HTML code such as an image tag. If this is is coming from end users, you should [[encode()]] it to prevent XSS attacks.
729
     *   When this option is specified, the checkbox will be enclosed by a label tag.
730
     * - labelOptions: array, the HTML attributes for the label tag. Do not set this option unless you set the "label" option.
731
     *
732
     * The rest of the options will be rendered as the attributes of the resulting checkbox tag. The values will
733
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
734
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
735
     *
736
     * @return string the generated checkbox tag
737
     * @since 2.0.9
738 17
     */
739
    protected static function booleanInput($type, $name, $checked = false, $options = [])
740 17
    {
741 17
        $options['checked'] = (bool) $checked;
742 17
        $value = array_key_exists('value', $options) ? $options['value'] : '1';
743
        if (isset($options['uncheck'])) {
744 6
            // add a hidden field so that if the checkbox is not selected, it still submits a value
745 6
            $hidden = static::hiddenInput($name, $options['uncheck']);
0 ignored issues
show
$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...
746 6
            unset($options['uncheck']);
747 13
        } else {
748
            $hidden = '';
749 17
        }
750 8
        if (isset($options['label'])) {
751 8
            $label = $options['label'];
752 8
            $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : [];
753 8
            unset($options['label'], $options['labelOptions']);
754 8
            $content = static::label(static::input($type, $name, $value, $options) . ' ' . $label, null, $labelOptions);
0 ignored issues
show
It seems like $labelOptions defined by isset($options['labelOpt...abelOptions'] : array() on line 752 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...
755
            return $hidden . $content;
756 13
        } else {
757
            return $hidden . static::input($type, $name, $value, $options);
758
        }
759
    }
760
761
    /**
762
     * Generates a drop-down list.
763
     * @param string $name the input name
764
     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).
765
     * @param array $items the option data items. The array keys are option values, and the array values
766
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
767
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
768
     * If you have a list of data models, you may convert them into the format described above using
769
     * [[\yii\helpers\ArrayHelper::map()]].
770
     *
771
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
772
     * the labels will also be HTML-encoded.
773
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
774
     *
775
     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array
776
     *   to override the value and to set other tag attributes:
777
     *
778
     *   ```php
779
     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],
780
     *   ```
781
     *
782
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
783
     *   and the array values are the extra attributes for the corresponding option tags. For example,
784
     *
785
     *   ```php
786
     *   [
787
     *       'value1' => ['disabled' => true],
788
     *       'value2' => ['label' => 'value 2'],
789
     *   ];
790
     *   ```
791
     *
792
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
793
     *   except that the array keys represent the optgroup labels specified in $items.
794
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
795
     *   Defaults to false.
796
     * - encode: bool, whether to encode option prompt and option value characters.
797
     *   Defaults to `true`. This option is available since 2.0.3.
798
     *
799
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
800
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
801
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
802
     *
803
     * @return string the generated drop-down list tag
804 2
     */
805
    public static function dropDownList($name, $selection = null, $items = [], $options = [])
806 2
    {
807
        if (!empty($options['multiple'])) {
808
            return static::listBox($name, $selection, $items, $options);
809 2
        }
810 2
        $options['name'] = $name;
811 2
        unset($options['unselect']);
812 2
        $selectOptions = static::renderSelectOptions($selection, $items, $options);
813
        return static::tag('select', "\n" . $selectOptions . "\n", $options);
814
    }
815
816
    /**
817
     * Generates a list box.
818
     * @param string $name the input name
819
     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).
820
     * @param array $items the option data items. The array keys are option values, and the array values
821
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
822
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
823
     * If you have a list of data models, you may convert them into the format described above using
824
     * [[\yii\helpers\ArrayHelper::map()]].
825
     *
826
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
827
     * the labels will also be HTML-encoded.
828
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
829
     *
830
     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array
831
     *   to override the value and to set other tag attributes:
832
     *
833
     *   ```php
834
     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],
835
     *   ```
836
     *
837
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
838
     *   and the array values are the extra attributes for the corresponding option tags. For example,
839
     *
840
     *   ```php
841
     *   [
842
     *       'value1' => ['disabled' => true],
843
     *       'value2' => ['label' => 'value 2'],
844
     *   ];
845
     *   ```
846
     *
847
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
848
     *   except that the array keys represent the optgroup labels specified in $items.
849
     * - unselect: string, the value that will be submitted when no option is selected.
850
     *   When this attribute is set, a hidden field will be generated so that if no option is selected in multiple
851
     *   mode, we can still obtain the posted unselect value.
852
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
853
     *   Defaults to false.
854
     * - encode: bool, whether to encode option prompt and option value characters.
855
     *   Defaults to `true`. This option is available since 2.0.3.
856
     *
857
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
858
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
859
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
860
     *
861
     * @return string the generated list box tag
862 3
     */
863
    public static function listBox($name, $selection = null, $items = [], $options = [])
864 3
    {
865 3
        if (!array_key_exists('size', $options)) {
866 3
            $options['size'] = 4;
867 3
        }
868 2
        if (!empty($options['multiple']) && !empty($name) && substr_compare($name, '[]', -2, 2)) {
869 2
            $name .= '[]';
870 3
        }
871 3
        $options['name'] = $name;
872
        if (isset($options['unselect'])) {
873 3
            // add a hidden field so that if the list box has no option being selected, it still submits a value
874 1
            if (!empty($name) && substr_compare($name, '[]', -2, 2) === 0) {
875 1
                $name = substr($name, 0, -2);
876 3
            }
877 3
            $hidden = static::hiddenInput($name, $options['unselect']);
878 3
            unset($options['unselect']);
879 1
        } else {
880
            $hidden = '';
881 3
        }
882 3
        $selectOptions = static::renderSelectOptions($selection, $items, $options);
883
        return $hidden . static::tag('select', "\n" . $selectOptions . "\n", $options);
884
    }
885
886
    /**
887
     * Generates a list of checkboxes.
888
     * A checkbox list allows multiple selection, like [[listBox()]].
889
     * As a result, the corresponding submitted value is an array.
890
     * @param string $name the name attribute of each checkbox.
891
     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).
892
     * @param array $items the data item used to generate the checkboxes.
893
     * The array keys are the checkbox values, while the array values are the corresponding labels.
894
     * @param array $options options (name => config) for the checkbox list container tag.
895
     * The following options are specially handled:
896
     *
897
     * - tag: string|false, the tag name of the container element. False to render checkbox without container.
898
     *   See also [[tag()]].
899
     * - unselect: string, the value that should be submitted when none of the checkboxes is selected.
900
     *   By setting this option, a hidden input will be generated.
901
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
902
     *   This option is ignored if `item` option is set.
903
     * - separator: string, the HTML code that separates items.
904
     * - itemOptions: array, the options for generating the checkbox tag using [[checkbox()]].
905
     * - item: callable, a callback that can be used to customize the generation of the HTML code
906
     *   corresponding to a single item in $items. The signature of this callback must be:
907
     *
908
     *   ```php
909
     *   function ($index, $label, $name, $checked, $value)
910
     *   ```
911
     *
912
     *   where $index is the zero-based index of the checkbox in the whole list; $label
913
     *   is the label for the checkbox; and $name, $value and $checked represent the name,
914
     *   value and the checked status of the checkbox input, respectively.
915
     *
916
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
917
     *
918
     * @return string the generated checkbox list
919 1
     */
920
    public static function checkboxList($name, $selection = null, $items = [], $options = [])
921 1
    {
922 1
        if (substr($name, -2) !== '[]') {
923 1
            $name .= '[]';
924
        }
925 1
926 1
        $formatter = ArrayHelper::remove($options, 'item');
927 1
        $itemOptions = ArrayHelper::remove($options, 'itemOptions', []);
928 1
        $encode = ArrayHelper::remove($options, 'encode', true);
929 1
        $separator = ArrayHelper::remove($options, 'separator', "\n");
930
        $tag = ArrayHelper::remove($options, 'tag', 'div');
931 1
932 1
        $lines = [];
933 1
        $index = 0;
934 1
        foreach ($items as $value => $label) {
935 1
            $checked = $selection !== null &&
936 1
                (!ArrayHelper::isTraversable($selection) && !strcmp($value, $selection)
937 1
                    || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn($value, $selection));
0 ignored issues
show
It seems like $selection defined by parameter $selection on line 920 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...
938 1
            if ($formatter !== null) {
939 1
                $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
940 1
            } else {
941 1
                $lines[] = static::checkbox($name, $checked, array_merge($itemOptions, [
942 1
                    'value' => $value,
943 1
                    'label' => $encode ? static::encode($label) : $label,
944
                ]));
945 1
            }
946 1
            $index++;
947
        }
948 1
949
        if (isset($options['unselect'])) {
950 1
            // add a hidden field so that if the list box has no option being selected, it still submits a value
951 1
            $name2 = substr($name, -2) === '[]' ? substr($name, 0, -2) : $name;
952 1
            $hidden = static::hiddenInput($name2, $options['unselect']);
953 1
            unset($options['unselect']);
954 1
        } else {
955
            $hidden = '';
956
        }
957 1
958
        $visibleContent = implode($separator, $lines);
959 1
960 1
        if ($tag === false) {
961
            return $hidden . $visibleContent;
962
        }
963 1
964
        return $hidden . static::tag($tag, $visibleContent, $options);
965
    }
966
967
    /**
968
     * Generates a list of radio buttons.
969
     * A radio button list is like a checkbox list, except that it only allows single selection.
970
     * @param string $name the name attribute of each radio button.
971
     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).
972
     * @param array $items the data item used to generate the radio buttons.
973
     * The array keys are the radio button values, while the array values are the corresponding labels.
974
     * @param array $options options (name => config) for the radio button list container tag.
975
     * The following options are specially handled:
976
     *
977
     * - tag: string|false, the tag name of the container element. False to render radio buttons without container.
978
     *   See also [[tag()]].
979
     * - unselect: string, the value that should be submitted when none of the radio buttons is selected.
980
     *   By setting this option, a hidden input will be generated.
981
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
982
     *   This option is ignored if `item` option is set.
983
     * - separator: string, the HTML code that separates items.
984
     * - itemOptions: array, the options for generating the radio button tag using [[radio()]].
985
     * - item: callable, a callback that can be used to customize the generation of the HTML code
986
     *   corresponding to a single item in $items. The signature of this callback must be:
987
     *
988
     *   ```php
989
     *   function ($index, $label, $name, $checked, $value)
990
     *   ```
991
     *
992
     *   where $index is the zero-based index of the radio button in the whole list; $label
993
     *   is the label for the radio button; and $name, $value and $checked represent the name,
994
     *   value and the checked status of the radio button input, respectively.
995
     *
996
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
997
     *
998
     * @return string the generated radio button list
999 1
     */
1000
    public static function radioList($name, $selection = null, $items = [], $options = [])
1001 1
    {
1002 1
        $formatter = ArrayHelper::remove($options, 'item');
1003 1
        $itemOptions = ArrayHelper::remove($options, 'itemOptions', []);
1004 1
        $encode = ArrayHelper::remove($options, 'encode', true);
1005 1
        $separator = ArrayHelper::remove($options, 'separator', "\n");
1006
        $tag = ArrayHelper::remove($options, 'tag', 'div');
1007 1
        // add a hidden field so that if the list box has no option being selected, it still submits a value
1008 1
        $hidden = isset($options['unselect']) ? static::hiddenInput($name, $options['unselect']) : '';
1009
        unset($options['unselect']);
1010 1
1011 1
        $lines = [];
1012 1
        $index = 0;
1013 1
        foreach ($items as $value => $label) {
1014 1
            $checked = $selection !== null &&
1015 1
                (!ArrayHelper::isTraversable($selection) && !strcmp($value, $selection)
1016 1
                    || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn($value, $selection));
0 ignored issues
show
It seems like $selection defined by parameter $selection on line 1000 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...
1017 1
            if ($formatter !== null) {
1018 1
                $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
1019 1
            } else {
1020 1
                $lines[] = static::radio($name, $checked, array_merge($itemOptions, [
1021 1
                    'value' => $value,
1022 1
                    'label' => $encode ? static::encode($label) : $label,
1023
                ]));
1024 1
            }
1025 1
            $index++;
1026 1
        }
1027
        $visibleContent = implode($separator, $lines);
1028 1
1029 1
        if ($tag === false) {
1030
            return $hidden . $visibleContent;
1031
        }
1032 1
1033
        return $hidden . static::tag($tag, $visibleContent, $options);
1034
    }
1035
1036
    /**
1037
     * Generates an unordered list.
1038
     * @param array|\Traversable $items the items for generating the list. Each item generates a single list item.
1039
     * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true.
1040
     * @param array $options options (name => config) for the radio button list. The following options are supported:
1041
     *
1042
     * - encode: boolean, whether to HTML-encode the items. Defaults to true.
1043
     *   This option is ignored if the `item` option is specified.
1044
     * - separator: string, the HTML code that separates items. Defaults to a simple newline (`"\n"`).
1045
     *   This option is available since version 2.0.7.
1046
     * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified.
1047
     * - item: callable, a callback that is used to generate each individual list item.
1048
     *   The signature of this callback must be:
1049
     *
1050
     *   ```php
1051
     *   function ($item, $index)
1052
     *   ```
1053
     *
1054
     *   where $index is the array key corresponding to `$item` in `$items`. The callback should return
1055
     *   the whole list item tag.
1056
     *
1057
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1058
     *
1059
     * @return string the generated unordered list. An empty list tag will be returned if `$items` is empty.
1060 4
     */
1061
    public static function ul($items, $options = [])
1062 4
    {
1063 4
        $tag = ArrayHelper::remove($options, 'tag', 'ul');
1064 4
        $encode = ArrayHelper::remove($options, 'encode', true);
1065 4
        $formatter = ArrayHelper::remove($options, 'item');
1066 4
        $separator = ArrayHelper::remove($options, 'separator', "\n");
1067
        $itemOptions = ArrayHelper::remove($options, 'itemOptions', []);
1068 4
1069 2
        if (empty($items)) {
1070
            return static::tag($tag, '', $options);
1071
        }
1072 4
1073 4
        $results = [];
1074 4
        foreach ($items as $index => $item) {
1075 2
            if ($formatter !== null) {
1076 2
                $results[] = call_user_func($formatter, $item, $index);
1077 4
            } else {
1078
                $results[] = static::tag('li', $encode ? static::encode($item) : $item, $itemOptions);
1079 4
            }
1080
        }
1081 4
1082 4
        return static::tag(
1083 4
            $tag,
1084
            $separator . implode($separator, $results) . $separator,
1085 4
            $options
1086
        );
1087
    }
1088
1089
    /**
1090
     * Generates an ordered list.
1091
     * @param array|\Traversable $items the items for generating the list. Each item generates a single list item.
1092
     * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true.
1093
     * @param array $options options (name => config) for the radio button list. The following options are supported:
1094
     *
1095
     * - encode: boolean, whether to HTML-encode the items. Defaults to true.
1096
     *   This option is ignored if the `item` option is specified.
1097
     * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified.
1098
     * - item: callable, a callback that is used to generate each individual list item.
1099
     *   The signature of this callback must be:
1100
     *
1101
     *   ```php
1102
     *   function ($item, $index)
1103
     *   ```
1104
     *
1105
     *   where $index is the array key corresponding to `$item` in `$items`. The callback should return
1106
     *   the whole list item tag.
1107
     *
1108
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1109
     *
1110
     * @return string the generated ordered list. An empty string is returned if `$items` is empty.
1111 1
     */
1112
    public static function ol($items, $options = [])
1113 1
    {
1114 1
        $options['tag'] = 'ol';
1115
        return static::ul($items, $options);
1116
    }
1117
1118
    /**
1119
     * Generates a label tag for the given model attribute.
1120
     * The label text is the label associated with the attribute, obtained via [[Model::getAttributeLabel()]].
1121
     * @param Model $model the model object
1122
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1123
     * about attribute expression.
1124
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1125
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1126
     * If a value is null, the corresponding attribute will not be rendered.
1127
     * The following options are specially handled:
1128
     *
1129
     * - label: this specifies the label to be displayed. Note that this will NOT be [[encode()|encoded]].
1130
     *   If this is not set, [[Model::getAttributeLabel()]] will be called to get the label for display
1131
     *   (after encoding).
1132
     *
1133
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1134
     *
1135
     * @return string the generated label tag
1136 11
     */
1137
    public static function activeLabel($model, $attribute, $options = [])
1138 11
    {
1139 11
        $for = ArrayHelper::remove($options, 'for', static::getInputId($model, $attribute));
1140 11
        $attribute = static::getAttributeName($attribute);
1141 11
        $label = ArrayHelper::remove($options, 'label', static::encode($model->getAttributeLabel($attribute)));
1142
        return static::label($label, $for, $options);
1143
    }
1144
1145
    /**
1146
     * Generates a hint tag for the given model attribute.
1147
     * The hint text is the hint associated with the attribute, obtained via [[Model::getAttributeHint()]].
1148
     * If no hint content can be obtained, method will return an empty string.
1149
     * @param Model $model the model object
1150
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1151
     * about attribute expression.
1152
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1153
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1154
     * If a value is null, the corresponding attribute will not be rendered.
1155
     * The following options are specially handled:
1156
     *
1157
     * - hint: this specifies the hint to be displayed. Note that this will NOT be [[encode()|encoded]].
1158
     *   If this is not set, [[Model::getAttributeHint()]] will be called to get the hint for display
1159
     *   (without encoding).
1160
     *
1161
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1162
     *
1163
     * @return string the generated hint tag
1164
     * @since 2.0.4
1165 11
     */
1166
    public static function activeHint($model, $attribute, $options = [])
1167 11
    {
1168 11
        $attribute = static::getAttributeName($attribute);
1169 11
        $hint = isset($options['hint']) ? $options['hint'] : $model->getAttributeHint($attribute);
1170 3
        if (empty($hint)) {
1171
            return '';
1172 8
        }
1173 8
        $tag = ArrayHelper::remove($options, 'tag', 'div');
1174 8
        unset($options['hint']);
1175
        return static::tag($tag, $hint, $options);
1176
    }
1177
1178
    /**
1179
     * Generates a summary of the validation errors.
1180
     * If there is no validation error, an empty error summary markup will still be generated, but it will be hidden.
1181
     * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.
1182
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
1183
     *
1184
     * - header: string, the header HTML for the error summary. If not set, a default prompt string will be used.
1185
     * - footer: string, the footer HTML for the error summary. Defaults to empty string.
1186
     * - encode: boolean, if set to false then the error messages won't be encoded. Defaults to `true`.
1187
     * - showAllErrors: boolean, if set to true every error message for each attribute will be shown otherwise
1188
     *   only the first error message for each attribute will be shown. Defaults to `false`.
1189
     *   Option is available since 2.0.10.
1190
     *
1191
     * The rest of the options will be rendered as the attributes of the container tag.
1192
     *
1193
     * @return string the generated error summary
1194 7
     */
1195
    public static function errorSummary($models, $options = [])
1196 7
    {
1197 7
        $header = isset($options['header']) ? $options['header'] : '<p>' . Yii::t('yii', 'Please fix the following errors:') . '</p>';
1198 7
        $footer = ArrayHelper::remove($options, 'footer', '');
1199 7
        $encode = ArrayHelper::remove($options, 'encode', true);
1200 7
        $showAllErrors = ArrayHelper::remove($options, 'showAllErrors', false);
1201
        unset($options['header']);
1202 7
1203 7
        $lines = [];
1204 7
        if (!is_array($models)) {
1205 7
            $models = [$models];
1206 7
        }
1207
        foreach ($models as $model) {
1208 7
            /* @var $model Model */
1209 5
            foreach ($model->getErrors() as $errors) {
1210 5
                foreach ($errors as $error) {
1211 5
                    $line = $encode ? Html::encode($error) : $error;
1212 5
                    if (array_search($line, $lines) === false) {
1213 5
                        $lines[] = $line;
1214 5
                    }
1215 4
                    if (!$showAllErrors) {
1216
                        break;
1217 5
                    }
1218 7
                }
1219 7
            }
1220
        }
1221 7
1222
        if (empty($lines)) {
1223 2
            // still render the placeholder for client-side validation use
1224 2
            $content = '<ul></ul>';
1225 2
            $options['style'] = isset($options['style']) ? rtrim($options['style'], ';') . '; display:none' : 'display:none';
1226 5
        } else {
1227
            $content = '<ul><li>' . implode("</li>\n<li>", $lines) . '</li></ul>';
1228 7
        }
1229
        return Html::tag('div', $header . $content . $footer, $options);
1230
    }
1231
1232
    /**
1233
     * Generates a tag that contains the first validation error of the specified model attribute.
1234
     * Note that even if there is no validation error, this method will still return an empty error tag.
1235
     * @param Model $model the model object
1236
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1237
     * about attribute expression.
1238
     * @param array $options the tag options in terms of name-value pairs. The values will be HTML-encoded
1239
     * using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1240
     *
1241
     * The following options are specially handled:
1242
     *
1243
     * - tag: this specifies the tag name. If not set, "div" will be used.
1244
     *   See also [[tag()]].
1245
     * - encode: boolean, if set to false then the error message won't be encoded.
1246
     *
1247
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1248
     *
1249
     * @return string the generated label tag
1250 9
     */
1251
    public static function error($model, $attribute, $options = [])
1252 9
    {
1253 9
        $attribute = static::getAttributeName($attribute);
1254 9
        $error = $model->getFirstError($attribute);
1255 9
        $tag = ArrayHelper::remove($options, 'tag', 'div');
1256 9
        $encode = ArrayHelper::remove($options, 'encode', true);
1257
        return Html::tag($tag, $encode ? Html::encode($error) : $error, $options);
1258
    }
1259
1260
    /**
1261
     * Generates an input tag for the given model attribute.
1262
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1263
     * unless they are explicitly specified in `$options`.
1264
     * @param string $type the input type (e.g. 'text', 'password')
1265
     * @param Model $model the model object
1266
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1267
     * about attribute expression.
1268
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1269
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1270
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1271
     * @return string the generated input tag
1272 20
     */
1273
    public static function activeInput($type, $model, $attribute, $options = [])
1274 20
    {
1275 20
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1276 20
        $value = isset($options['value']) ? $options['value'] : static::getAttributeValue($model, $attribute);
1277 18
        if (!array_key_exists('id', $options)) {
1278 18
            $options['id'] = static::getInputId($model, $attribute);
1279 20
        }
1280
        return static::input($type, $name, $value, $options);
1281
    }
1282
1283
    /**
1284
     * If `maxlength` option is set true and the model attribute is validated by a string validator,
1285
     * the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
1286
     * @param Model $model the model object
1287
     * @param string $attribute the attribute name or expression.
1288
     * @param array $options the tag options in terms of name-value pairs.
1289 19
     */
1290
    private static function normalizeMaxLength($model, $attribute, &$options)
1291 19
    {
1292 3
        if (isset($options['maxlength']) && $options['maxlength'] === true) {
1293 3
            unset($options['maxlength']);
1294 3
            $attrName = static::getAttributeName($attribute);
1295 3
            foreach ($model->getActiveValidators($attrName) as $validator) {
1296 3
                if ($validator instanceof StringValidator && $validator->max !== null) {
1297 3
                    $options['maxlength'] = $validator->max;
1298
                    break;
1299 3
                }
1300 3
            }
1301 19
        }
1302
    }
1303
1304
    /**
1305
     * Generates a text input tag for the given model attribute.
1306
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1307
     * unless they are explicitly specified in `$options`.
1308
     * @param Model $model the model object
1309
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1310
     * about attribute expression.
1311
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1312
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1313
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1314
     * The following special options are recognized:
1315
     *
1316
     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
1317
     *   by a string validator, the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
1318
     *   This is available since version 2.0.3.
1319
     *
1320
     * @return string the generated input tag
1321 12
     */
1322
    public static function activeTextInput($model, $attribute, $options = [])
1323 12
    {
1324 12
        self::normalizeMaxLength($model, $attribute, $options);
1325
        return static::activeInput('text', $model, $attribute, $options);
1326
    }
1327
1328
    /**
1329
     * Generates a hidden input tag for the given model attribute.
1330
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1331
     * unless they are explicitly specified in `$options`.
1332
     * @param Model $model the model object
1333
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1334
     * about attribute expression.
1335
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1336
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1337
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1338
     * @return string the generated input tag
1339 3
     */
1340
    public static function activeHiddenInput($model, $attribute, $options = [])
1341 3
    {
1342
        return static::activeInput('hidden', $model, $attribute, $options);
1343
    }
1344
1345
    /**
1346
     * Generates a password input tag for the given model attribute.
1347
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1348
     * unless they are explicitly specified in `$options`.
1349
     * @param Model $model the model object
1350
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1351
     * about attribute expression.
1352
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1353
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1354
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1355
     * The following special options are recognized:
1356
     *
1357
     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
1358
     *   by a string validator, the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
1359
     *   This option is available since version 2.0.6.
1360
     *
1361
     * @return string the generated input tag
1362 3
     */
1363
    public static function activePasswordInput($model, $attribute, $options = [])
1364 3
    {
1365 3
        self::normalizeMaxLength($model, $attribute, $options);
1366
        return static::activeInput('password', $model, $attribute, $options);
1367
    }
1368
1369
    /**
1370
     * Generates a file input tag for the given model attribute.
1371
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1372
     * unless they are explicitly specified in `$options`.
1373
     * @param Model $model the model object
1374
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1375
     * about attribute expression.
1376
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1377
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1378
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1379
     * @return string the generated input tag
1380 1
     */
1381
    public static function activeFileInput($model, $attribute, $options = [])
1382
    {
1383
        // add a hidden field so that if a model only has a file field, we can
1384 1
        // still use isset($_POST[$modelClass]) to detect if the input is submitted
1385 1
        $hiddenOptions = ['id' => null, 'value' => ''];
1386
        if (isset($options['name'])) {
1387
            $hiddenOptions['name'] = $options['name'];
1388 1
        }
1389 1
        return static::activeHiddenInput($model, $attribute, $hiddenOptions)
1390
            . static::activeInput('file', $model, $attribute, $options);
1391
    }
1392
1393
    /**
1394
     * Generates a textarea tag for the given model attribute.
1395
     * The model attribute value will be used as the content in the textarea.
1396
     * @param Model $model the model object
1397
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1398
     * about attribute expression.
1399
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1400
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1401
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1402
     * The following special options are recognized:
1403
     *
1404
     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
1405
     *   by a string validator, the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
1406
     *   This option is available since version 2.0.6.
1407
     *
1408
     * @return string the generated textarea tag
1409 4
     */
1410
    public static function activeTextarea($model, $attribute, $options = [])
1411 4
    {
1412 4
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1413 1
        if (isset($options['value'])) {
1414 1
            $value = $options['value'];
1415 1
            unset($options['value']);
1416 3
        } else {
1417
            $value = static::getAttributeValue($model, $attribute);
1418 4
        }
1419 4
        if (!array_key_exists('id', $options)) {
1420 4
            $options['id'] = static::getInputId($model, $attribute);
1421 4
        }
1422 4
        self::normalizeMaxLength($model, $attribute, $options);
1423
        return static::textarea($name, $value, $options);
1424
    }
1425
1426
    /**
1427
     * Generates a radio button tag together with a label for the given model attribute.
1428
     * This method will generate the "checked" tag attribute according to the model attribute value.
1429
     * @param Model $model the model object
1430
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1431
     * about attribute expression.
1432
     * @param array $options the tag options in terms of name-value pairs.
1433
     * See [[booleanInput()]] for details about accepted attributes.
1434
     *
1435
     * @return string the generated radio button tag
1436 4
     */
1437
    public static function activeRadio($model, $attribute, $options = [])
1438 4
    {
1439
        return static::activeBooleanInput('radio', $model, $attribute, $options);
1440
    }
1441
1442
    /**
1443
     * Generates a checkbox tag together with a label for the given model attribute.
1444
     * This method will generate the "checked" tag attribute according to the model attribute value.
1445
     * @param Model $model the model object
1446
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1447
     * about attribute expression.
1448
     * @param array $options the tag options in terms of name-value pairs.
1449
     * See [[booleanInput()]] for details about accepted attributes.
1450
     *
1451
     * @return string the generated checkbox tag
1452 4
     */
1453
    public static function activeCheckbox($model, $attribute, $options = [])
1454 4
    {
1455
        return static::activeBooleanInput('checkbox', $model, $attribute, $options);
1456
    }
1457
1458
    /**
1459
     * Generates a boolean input
1460
     * This method is mainly called by [[activeCheckbox()]] and [[activeRadio()]].
1461
     * @param string $type the input type. This can be either `radio` or `checkbox`.
1462
     * @param Model $model the model object
1463
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1464
     * about attribute expression.
1465
     * @param array $options the tag options in terms of name-value pairs.
1466
     * See [[booleanInput()]] for details about accepted attributes.
1467
     * @return string the generated input element
1468
     * @since 2.0.9
1469 8
     */
1470
    protected static function activeBooleanInput($type, $model, $attribute, $options = [])
1471 8
    {
1472 8
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1473
        $value = static::getAttributeValue($model, $attribute);
1474 8
1475 8
        if (!array_key_exists('value', $options)) {
1476 8
            $options['value'] = '1';
1477 8
        }
1478 4
        if (!array_key_exists('uncheck', $options)) {
1479 8
            $options['uncheck'] = '0';
1480 4
        } elseif ($options['uncheck'] === false) {
1481 4
            unset($options['uncheck']);
1482 8
        }
1483 4
        if (!array_key_exists('label', $options)) {
1484 8
            $options['label'] = static::encode($model->getAttributeLabel(static::getAttributeName($attribute)));
1485 4
        } elseif ($options['label'] === false) {
1486 4
            unset($options['label']);
1487
        }
1488 8
1489
        $checked = "$value" === "{$options['value']}";
1490 8
1491 8
        if (!array_key_exists('id', $options)) {
1492 8
            $options['id'] = static::getInputId($model, $attribute);
1493
        }
1494 8
1495
        return static::$type($name, $checked, $options);
1496
    }
1497
1498
    /**
1499
     * Generates a drop-down list for the given model attribute.
1500
     * The selection of the drop-down list is taken from the value of the model attribute.
1501
     * @param Model $model the model object
1502
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1503
     * about attribute expression.
1504
     * @param array $items the option data items. The array keys are option values, and the array values
1505
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
1506
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
1507
     * If you have a list of data models, you may convert them into the format described above using
1508
     * [[\yii\helpers\ArrayHelper::map()]].
1509
     *
1510
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
1511
     * the labels will also be HTML-encoded.
1512
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
1513
     *
1514
     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array
1515
     *   to override the value and to set other tag attributes:
1516
     *
1517
     *   ```php
1518
     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],
1519
     *   ```
1520
     *
1521
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
1522
     *   and the array values are the extra attributes for the corresponding option tags. For example,
1523
     *
1524
     *   ```php
1525
     *   [
1526
     *       'value1' => ['disabled' => true],
1527
     *       'value2' => ['label' => 'value 2'],
1528
     *   ];
1529
     *   ```
1530
     *
1531
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
1532
     *   except that the array keys represent the optgroup labels specified in $items.
1533
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
1534
     *   Defaults to false.
1535
     * - encode: bool, whether to encode option prompt and option value characters.
1536
     *   Defaults to `true`. This option is available since 2.0.3.
1537
     *
1538
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
1539
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1540
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1541
     *
1542
     * @return string the generated drop-down list tag
1543 1
     */
1544
    public static function activeDropDownList($model, $attribute, $items, $options = [])
1545 1
    {
1546 1
        if (empty($options['multiple'])) {
1547
            return static::activeListInput('dropDownList', $model, $attribute, $items, $options);
1548
        } else {
1549
            return static::activeListBox($model, $attribute, $items, $options);
1550
        }
1551
    }
1552
1553
    /**
1554
     * Generates a list box.
1555
     * The selection of the list box is taken from the value of the model attribute.
1556
     * @param Model $model the model object
1557
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1558
     * about attribute expression.
1559
     * @param array $items the option data items. The array keys are option values, and the array values
1560
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
1561
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
1562
     * If you have a list of data models, you may convert them into the format described above using
1563
     * [[\yii\helpers\ArrayHelper::map()]].
1564
     *
1565
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
1566
     * the labels will also be HTML-encoded.
1567
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
1568
     *
1569
     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array
1570
     *   to override the value and to set other tag attributes:
1571
     *
1572
     *   ```php
1573
     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],
1574
     *   ```
1575
     *
1576
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
1577
     *   and the array values are the extra attributes for the corresponding option tags. For example,
1578
     *
1579
     *   ```php
1580
     *   [
1581
     *       'value1' => ['disabled' => true],
1582
     *       'value2' => ['label' => 'value 2'],
1583
     *   ];
1584
     *   ```
1585
     *
1586
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
1587
     *   except that the array keys represent the optgroup labels specified in $items.
1588
     * - unselect: string, the value that will be submitted when no option is selected.
1589
     *   When this attribute is set, a hidden field will be generated so that if no option is selected in multiple
1590
     *   mode, we can still obtain the posted unselect value.
1591
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
1592
     *   Defaults to false.
1593
     * - encode: bool, whether to encode option prompt and option value characters.
1594
     *   Defaults to `true`. This option is available since 2.0.3.
1595
     *
1596
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
1597
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1598
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1599
     *
1600
     * @return string the generated list box tag
1601 2
     */
1602
    public static function activeListBox($model, $attribute, $items, $options = [])
1603 2
    {
1604
        return static::activeListInput('listBox', $model, $attribute, $items, $options);
1605
    }
1606
1607
    /**
1608
     * Generates a list of checkboxes.
1609
     * A checkbox list allows multiple selection, like [[listBox()]].
1610
     * As a result, the corresponding submitted value is an array.
1611
     * The selection of the checkbox list is taken from the value of the model attribute.
1612
     * @param Model $model the model object
1613
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1614
     * about attribute expression.
1615
     * @param array $items the data item used to generate the checkboxes.
1616
     * The array keys are the checkbox values, and the array values are the corresponding labels.
1617
     * Note that the labels will NOT be HTML-encoded, while the values will.
1618
     * @param array $options options (name => config) for the checkbox list container tag.
1619
     * The following options are specially handled:
1620
     *
1621
     * - tag: string|false, the tag name of the container element. False to render checkbox without container.
1622
     *   See also [[tag()]].
1623
     * - unselect: string, the value that should be submitted when none of the checkboxes is selected.
1624
     *   You may set this option to be null to prevent default value submission.
1625
     *   If this option is not set, an empty string will be submitted.
1626
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
1627
     *   This option is ignored if `item` option is set.
1628
     * - separator: string, the HTML code that separates items.
1629
     * - itemOptions: array, the options for generating the checkbox tag using [[checkbox()]].
1630
     * - item: callable, a callback that can be used to customize the generation of the HTML code
1631
     *   corresponding to a single item in $items. The signature of this callback must be:
1632
     *
1633
     *   ```php
1634
     *   function ($index, $label, $name, $checked, $value)
1635
     *   ```
1636
     *
1637
     *   where $index is the zero-based index of the checkbox in the whole list; $label
1638
     *   is the label for the checkbox; and $name, $value and $checked represent the name,
1639
     *   value and the checked status of the checkbox input.
1640
     *
1641
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1642
     *
1643
     * @return string the generated checkbox list
1644
     */
1645
    public static function activeCheckboxList($model, $attribute, $items, $options = [])
1646
    {
1647
        return static::activeListInput('checkboxList', $model, $attribute, $items, $options);
1648
    }
1649
1650
    /**
1651
     * Generates a list of radio buttons.
1652
     * A radio button list is like a checkbox list, except that it only allows single selection.
1653
     * The selection of the radio buttons is taken from the value of the model attribute.
1654
     * @param Model $model the model object
1655
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1656
     * about attribute expression.
1657
     * @param array $items the data item used to generate the radio buttons.
1658
     * The array keys are the radio values, and the array values are the corresponding labels.
1659
     * Note that the labels will NOT be HTML-encoded, while the values will.
1660
     * @param array $options options (name => config) for the radio button list container tag.
1661
     * The following options are specially handled:
1662
     *
1663
     * - tag: string|false, the tag name of the container element. False to render radio button without container.
1664
     *   See also [[tag()]].
1665
     * - unselect: string, the value that should be submitted when none of the radio buttons is selected.
1666
     *   You may set this option to be null to prevent default value submission.
1667
     *   If this option is not set, an empty string will be submitted.
1668
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
1669
     *   This option is ignored if `item` option is set.
1670
     * - separator: string, the HTML code that separates items.
1671
     * - itemOptions: array, the options for generating the radio button tag using [[radio()]].
1672
     * - item: callable, a callback that can be used to customize the generation of the HTML code
1673
     *   corresponding to a single item in $items. The signature of this callback must be:
1674
     *
1675
     *   ```php
1676
     *   function ($index, $label, $name, $checked, $value)
1677
     *   ```
1678
     *
1679
     *   where $index is the zero-based index of the radio button in the whole list; $label
1680
     *   is the label for the radio button; and $name, $value and $checked represent the name,
1681
     *   value and the checked status of the radio button input.
1682
     *
1683
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1684
     *
1685
     * @return string the generated radio button list
1686
     */
1687
    public static function activeRadioList($model, $attribute, $items, $options = [])
1688
    {
1689
        return static::activeListInput('radioList', $model, $attribute, $items, $options);
1690
    }
1691
1692
    /**
1693
     * Generates a list of input fields.
1694
     * This method is mainly called by [[activeListBox()]], [[activeRadioList()]] and [[activeCheckboxList()]].
1695
     * @param string $type the input type. This can be 'listBox', 'radioList', or 'checkBoxList'.
1696
     * @param Model $model the model object
1697
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1698
     * about attribute expression.
1699
     * @param array $items the data item used to generate the input fields.
1700
     * The array keys are the input values, and the array values are the corresponding labels.
1701
     * Note that the labels will NOT be HTML-encoded, while the values will.
1702
     * @param array $options options (name => config) for the input list. The supported special options
1703
     * depend on the input type specified by `$type`.
1704
     * @return string the generated input list
1705 3
     */
1706
    protected static function activeListInput($type, $model, $attribute, $items, $options = [])
1707 3
    {
1708 3
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1709 3
        $selection = isset($options['value']) ? $options['value'] : static::getAttributeValue($model, $attribute);
1710 3
        if (!array_key_exists('unselect', $options)) {
1711 3
            $options['unselect'] = '';
1712 3
        }
1713 2
        if (!array_key_exists('id', $options)) {
1714 2
            $options['id'] = static::getInputId($model, $attribute);
1715 3
        }
1716
        return static::$type($name, $selection, $items, $options);
1717
    }
1718
1719
    /**
1720
     * Renders the option tags that can be used by [[dropDownList()]] and [[listBox()]].
1721
     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).
1722
     * @param array $items the option data items. The array keys are option values, and the array values
1723
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
1724
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
1725
     * If you have a list of data models, you may convert them into the format described above using
1726
     * [[\yii\helpers\ArrayHelper::map()]].
1727
     *
1728
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
1729
     * the labels will also be HTML-encoded.
1730
     * @param array $tagOptions the $options parameter that is passed to the [[dropDownList()]] or [[listBox()]] call.
1731
     * This method will take out these elements, if any: "prompt", "options" and "groups". See more details
1732
     * in [[dropDownList()]] for the explanation of these elements.
1733
     *
1734
     * @return string the generated list options
1735 6
     */
1736
    public static function renderSelectOptions($selection, $items, &$tagOptions = [])
1737 6
    {
1738 6
        $lines = [];
1739 6
        $encodeSpaces = ArrayHelper::remove($tagOptions, 'encodeSpaces', false);
1740 6
        $encode = ArrayHelper::remove($tagOptions, 'encode', true);
1741 2
        if (isset($tagOptions['prompt'])) {
1742 2
            $promptOptions = ['value' => ''];
1743 2
            if (is_string($tagOptions['prompt'])) {
1744 2
                $promptText = $tagOptions['prompt'];
1745 1
            } else {
1746 1
                $promptText = $tagOptions['prompt']['text'];
1747
                $promptOptions = array_merge($promptOptions, $tagOptions['prompt']['options']);
1748 2
            }
1749 2
            $promptText = $encode ? static::encode($promptText) : $promptText;
1750 1
            if ($encodeSpaces) {
1751 1
                $promptText = str_replace(' ', '&nbsp;', $promptText);
1752 2
            }
1753 2
            $lines[] = static::tag('option', $promptText, $promptOptions);
1754
        }
1755 6
1756 6
        $options = isset($tagOptions['options']) ? $tagOptions['options'] : [];
1757 6
        $groups = isset($tagOptions['groups']) ? $tagOptions['groups'] : [];
1758 6
        unset($tagOptions['prompt'], $tagOptions['options'], $tagOptions['groups']);
1759 6
        $options['encodeSpaces'] = ArrayHelper::getValue($options, 'encodeSpaces', $encodeSpaces);
1760
        $options['encode'] = ArrayHelper::getValue($options, 'encode', $encode);
1761 6
1762 6
        foreach ($items as $key => $value) {
1763 1
            if (is_array($value)) {
1764 1
                $groupAttrs = isset($groups[$key]) ? $groups[$key] : [];
1765 1
                if (!isset($groupAttrs['label'])) {
1766 1
                    $groupAttrs['label'] = $key;
1767 1
                }
1768 1
                $attrs = ['options' => $options, 'groups' => $groups, 'encodeSpaces' => $encodeSpaces, 'encode' => $encode];
1769 1
                $content = static::renderSelectOptions($selection, $value, $attrs);
1770 1
                $lines[] = static::tag('optgroup', "\n" . $content . "\n", $groupAttrs);
1771 6
            } else {
1772 6
                $attrs = isset($options[$key]) ? $options[$key] : [];
1773 6
                $attrs['value'] = (string) $key;
1774 6
                if (!array_key_exists('selected', $attrs)) {
1775 5
                    $attrs['selected'] = $selection !== null &&
1776 5
                        (!ArrayHelper::isTraversable($selection) && !strcmp($key, $selection)
1777 6
                        || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn($key, $selection));
1778 6
                }
1779 6
                $text = $encode ? static::encode($value) : $value;
1780 2
                if ($encodeSpaces) {
1781 2
                    $text = str_replace(' ', '&nbsp;', $text);
1782 6
                }
1783
                $lines[] = static::tag('option', $text, $attrs);
1784 6
            }
1785
        }
1786 6
1787
        return implode("\n", $lines);
1788
    }
1789
1790
    /**
1791
     * Renders the HTML tag attributes.
1792
     *
1793
     * Attributes whose values are of boolean type will be treated as
1794
     * [boolean attributes](http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes).
1795
     *
1796
     * Attributes whose values are null will not be rendered.
1797
     *
1798
     * The values of attributes will be HTML-encoded using [[encode()]].
1799
     *
1800
     * The "data" attribute is specially handled when it is receiving an array value. In this case,
1801
     * the array will be "expanded" and a list data attributes will be rendered. For example,
1802
     * if `'data' => ['id' => 1, 'name' => 'yii']`, then this will be rendered:
1803
     * `data-id="1" data-name="yii"`.
1804
     * Additionally `'data' => ['params' => ['id' => 1, 'name' => 'yii'], 'status' => 'ok']` will be rendered as:
1805
     * `data-params='{"id":1,"name":"yii"}' data-status="ok"`.
1806
     *
1807
     * @param array $attributes attributes to be rendered. The attribute values will be HTML-encoded using [[encode()]].
1808
     * @return string the rendering result. If the attributes are not empty, they will be rendered
1809
     * into a string with a leading white space (so that it can be directly appended to the tag name
1810
     * in a tag. If there is no attribute, an empty string will be returned.
1811 177
     */
1812
    public static function renderTagAttributes($attributes)
1813 177
    {
1814 133
        if (count($attributes) > 1) {
1815 133
            $sorted = [];
1816 133
            foreach (static::$attributeOrder as $name) {
1817 133
                if (isset($attributes[$name])) {
1818 133
                    $sorted[$name] = $attributes[$name];
1819 133
                }
1820 133
            }
1821 133
            $attributes = array_merge($sorted, $attributes);
1822
        }
1823 177
1824 177
        $html = '';
1825 165
        foreach ($attributes as $name => $value) {
1826 29
            if (is_bool($value)) {
1827 24
                if ($value) {
1828 24
                    $html .= " $name";
1829 165
                }
1830 3
            } elseif (is_array($value)) {
1831 2
                if (in_array($name, static::$dataAttributes)) {
1832 2
                    foreach ($value as $n => $v) {
1833
                        if (is_array($v)) {
1834
                            $html .= " $name-$n='" . Json::htmlEncode($v) . "'";
1835 2
                        } else {
1836
                            $html .= " $name-$n=\"" . static::encode($v) . '"';
1837 2
                        }
1838 3
                    }
1839 1
                } elseif ($name === 'class') {
1840 1
                    if (empty($value)) {
1841
                        continue;
1842 1
                    }
1843 2
                    $html .= " $name=\"" . static::encode(implode(' ', $value)) . '"';
1844 1
                } elseif ($name === 'style') {
1845 1
                    if (empty($value)) {
1846
                        continue;
1847 1
                    }
1848 1
                    $html .= " $name=\"" . static::encode(static::cssStyleFromArray($value)) . '"';
1849 1
                } else {
1850
                    $html .= " $name='" . Json::htmlEncode($value) . "'";
1851 165
                }
1852 165
            } elseif ($value !== null) {
1853 165
                $html .= " $name=\"" . static::encode($value) . '"';
1854 177
            }
1855
        }
1856 177
1857
        return $html;
1858
    }
1859
1860
    /**
1861
     * Adds a CSS class (or several classes) to the specified options.
1862
     * If the CSS class is already in the options, it will not be added again.
1863
     * If class specification at given options is an array, and some class placed there with the named (string) key,
1864
     * overriding of such key will have no effect. For example:
1865
     *
1866
     * ```php
1867
     * $options = ['class' => ['persistent' => 'initial']];
1868
     * Html::addCssClass($options, ['persistent' => 'override']);
1869
     * var_dump($options['class']); // outputs: array('persistent' => 'initial');
1870
     * ```
1871
     *
1872
     * @param array $options the options to be modified.
1873
     * @param string|array $class the CSS class(es) to be added
1874 5
     */
1875
    public static function addCssClass(&$options, $class)
1876 5
    {
1877 4
        if (isset($options['class'])) {
1878 2
            if (is_array($options['class'])) {
1879 2
                $options['class'] = self::mergeCssClasses($options['class'], (array) $class);
1880 3
            } else {
1881 3
                $classes = preg_split('/\s+/', $options['class'], -1, PREG_SPLIT_NO_EMPTY);
1882
                $options['class'] = implode(' ', self::mergeCssClasses($classes, (array) $class));
1883 4
            }
1884 4
        } else {
1885
            $options['class'] = $class;
1886 5
        }
1887
    }
1888
1889
    /**
1890
     * Merges already existing CSS classes with new one.
1891
     * This method provides the priority for named existing classes over additional.
1892
     * @param array $existingClasses already existing CSS classes.
1893
     * @param array $additionalClasses CSS classes to be added.
1894
     * @return array merge result.
1895 4
     */
1896
    private static function mergeCssClasses(array $existingClasses, array $additionalClasses)
1897 4
    {
1898 4
        foreach ($additionalClasses as $key => $class) {
1899 3
            if (is_int($key) && !in_array($class, $existingClasses)) {
1900 4
                $existingClasses[] = $class;
1901 1
            } elseif (!isset($existingClasses[$key])) {
1902 1
                $existingClasses[$key] = $class;
1903 4
            }
1904 4
        }
1905
        return array_unique($existingClasses);
1906
    }
1907
1908
    /**
1909
     * Removes a CSS class from the specified options.
1910
     * @param array $options the options to be modified.
1911
     * @param string|array $class the CSS class(es) to be removed
1912 1
     */
1913
    public static function removeCssClass(&$options, $class)
1914 1
    {
1915 1
        if (isset($options['class'])) {
1916 1
            if (is_array($options['class'])) {
1917 1
                $classes = array_diff($options['class'], (array) $class);
1918 1
                if (empty($classes)) {
1919 1
                    unset($options['class']);
1920 1
                } else {
1921
                    $options['class'] = $classes;
1922 1
                }
1923 1
            } else {
1924 1
                $classes = preg_split('/\s+/', $options['class'], -1, PREG_SPLIT_NO_EMPTY);
1925 1
                $classes = array_diff($classes, (array) $class);
1926 1
                if (empty($classes)) {
1927 1
                    unset($options['class']);
1928 1
                } else {
1929
                    $options['class'] = implode(' ', $classes);
1930
                }
1931 1
            }
1932 1
        }
1933
    }
1934
1935
    /**
1936
     * Adds the specified CSS style to the HTML options.
1937
     *
1938
     * If the options already contain a `style` element, the new style will be merged
1939
     * with the existing one. If a CSS property exists in both the new and the old styles,
1940
     * the old one may be overwritten if `$overwrite` is true.
1941
     *
1942
     * For example,
1943
     *
1944
     * ```php
1945
     * Html::addCssStyle($options, 'width: 100px; height: 200px');
1946
     * ```
1947
     *
1948
     * @param array $options the HTML options to be modified.
1949
     * @param string|array $style the new style string (e.g. `'width: 100px; height: 200px'`) or
1950
     * array (e.g. `['width' => '100px', 'height' => '200px']`).
1951
     * @param bool $overwrite whether to overwrite existing CSS properties if the new style
1952
     * contain them too.
1953
     * @see removeCssStyle()
1954
     * @see cssStyleFromArray()
1955
     * @see cssStyleToArray()
1956 1
     */
1957
    public static function addCssStyle(&$options, $style, $overwrite = true)
1958 1
    {
1959 1
        if (!empty($options['style'])) {
1960 1
            $oldStyle = is_array($options['style']) ? $options['style'] : static::cssStyleToArray($options['style']);
1961 1
            $newStyle = is_array($style) ? $style : static::cssStyleToArray($style);
1962 1
            if (!$overwrite) {
1963 1
                foreach ($newStyle as $property => $value) {
1964 1
                    if (isset($oldStyle[$property])) {
1965 1
                        unset($newStyle[$property]);
1966 1
                    }
1967 1
                }
1968 1
            }
1969 1
            $style = array_merge($oldStyle, $newStyle);
1970 1
        }
1971 1
        $options['style'] = is_array($style) ? static::cssStyleFromArray($style) : $style;
1972
    }
1973
1974
    /**
1975
     * Removes the specified CSS style from the HTML options.
1976
     *
1977
     * For example,
1978
     *
1979
     * ```php
1980
     * Html::removeCssStyle($options, ['width', 'height']);
1981
     * ```
1982
     *
1983
     * @param array $options the HTML options to be modified.
1984
     * @param string|array $properties the CSS properties to be removed. You may use a string
1985
     * if you are removing a single property.
1986
     * @see addCssStyle()
1987 1
     */
1988
    public static function removeCssStyle(&$options, $properties)
1989 1
    {
1990 1
        if (!empty($options['style'])) {
1991 1
            $style = is_array($options['style']) ? $options['style'] : static::cssStyleToArray($options['style']);
1992 1
            foreach ((array) $properties as $property) {
1993 1
                unset($style[$property]);
1994 1
            }
1995 1
            $options['style'] = static::cssStyleFromArray($style);
1996 1
        }
1997
    }
1998
1999
    /**
2000
     * Converts a CSS style array into a string representation.
2001
     *
2002
     * For example,
2003
     *
2004
     * ```php
2005
     * print_r(Html::cssStyleFromArray(['width' => '100px', 'height' => '200px']));
2006
     * // will display: 'width: 100px; height: 200px;'
2007
     * ```
2008
     *
2009
     * @param array $style the CSS style array. The array keys are the CSS property names,
2010
     * and the array values are the corresponding CSS property values.
2011
     * @return string the CSS style string. If the CSS style is empty, a null will be returned.
2012 4
     */
2013
    public static function cssStyleFromArray(array $style)
2014 4
    {
2015 4
        $result = '';
2016 4
        foreach ($style as $name => $value) {
2017 4
            $result .= "$name: $value; ";
2018
        }
2019 4
        // return null if empty to avoid rendering the "style" attribute
2020
        return $result === '' ? null : rtrim($result);
2021
    }
2022
2023
    /**
2024
     * Converts a CSS style string into an array representation.
2025
     *
2026
     * The array keys are the CSS property names, and the array values
2027
     * are the corresponding CSS property values.
2028
     *
2029
     * For example,
2030
     *
2031
     * ```php
2032
     * print_r(Html::cssStyleToArray('width: 100px; height: 200px;'));
2033
     * // will display: ['width' => '100px', 'height' => '200px']
2034
     * ```
2035
     *
2036
     * @param string $style the CSS style string
2037
     * @return array the array representation of the CSS style
2038 3
     */
2039
    public static function cssStyleToArray($style)
2040 3
    {
2041 3
        $result = [];
2042 3
        foreach (explode(';', $style) as $property) {
2043 3
            $property = explode(':', $property);
2044 3
            if (count($property) > 1) {
2045 3
                $result[trim($property[0])] = trim($property[1]);
2046 3
            }
2047 3
        }
2048
        return $result;
2049
    }
2050
2051
    /**
2052
     * Returns the real attribute name from the given attribute expression.
2053
     *
2054
     * An attribute expression is an attribute name prefixed and/or suffixed with array indexes.
2055
     * It is mainly used in tabular data input and/or input of array type. Below are some examples:
2056
     *
2057
     * - `[0]content` is used in tabular data input to represent the "content" attribute
2058
     *   for the first model in tabular input;
2059
     * - `dates[0]` represents the first array element of the "dates" attribute;
2060
     * - `[0]dates[0]` represents the first array element of the "dates" attribute
2061
     *   for the first model in tabular input.
2062
     *
2063
     * If `$attribute` has neither prefix nor suffix, it will be returned back without change.
2064
     * @param string $attribute the attribute name or expression
2065
     * @return string the attribute name without prefix and suffix.
2066
     * @throws InvalidParamException if the attribute name contains non-word characters.
2067 30
     */
2068
    public static function getAttributeName($attribute)
2069 30
    {
2070 30
        if (preg_match('/(^|.*\])([\w\.]+)(\[.*|$)/', $attribute, $matches)) {
2071
            return $matches[2];
2072
        } else {
2073
            throw new InvalidParamException('Attribute name must contain word characters only.');
2074
        }
2075
    }
2076
2077
    /**
2078
     * Returns the value of the specified attribute name or expression.
2079
     *
2080
     * For an attribute expression like `[0]dates[0]`, this method will return the value of `$model->dates[0]`.
2081
     * See [[getAttributeName()]] for more details about attribute expression.
2082
     *
2083
     * If an attribute value is an instance of [[ActiveRecordInterface]] or an array of such instances,
2084
     * the primary value(s) of the AR instance(s) will be returned instead.
2085
     *
2086
     * @param Model $model the model object
2087
     * @param string $attribute the attribute name or expression
2088
     * @return string|array the corresponding attribute value
2089
     * @throws InvalidParamException if the attribute name contains non-word characters.
2090 34
     */
2091
    public static function getAttributeValue($model, $attribute)
2092 34
    {
2093
        if (!preg_match('/(^|.*\])([\w\.]+)(\[.*|$)/', $attribute, $matches)) {
2094
            throw new InvalidParamException('Attribute name must contain word characters only.');
2095 34
        }
2096 34
        $attribute = $matches[2];
2097 34
        $value = $model->$attribute;
2098
        if ($matches[3] !== '') {
2099
            foreach (explode('][', trim($matches[3], '[]')) as $id) {
2100
                if ((is_array($value) || $value instanceof \ArrayAccess) && isset($value[$id])) {
2101
                    $value = $value[$id];
2102
                } else {
2103
                    return null;
2104
                }
2105
            }
2106
        }
2107
2108 34
        // https://github.com/yiisoft/yii2/issues/1457
2109
        if (is_array($value)) {
2110
            foreach ($value as $i => $v) {
2111
                if ($v instanceof ActiveRecordInterface) {
2112
                    $v = $v->getPrimaryKey(false);
2113
                    $value[$i] = is_array($v) ? json_encode($v) : $v;
2114
                }
2115 34
            }
2116
        } elseif ($value instanceof ActiveRecordInterface) {
2117
            $value = $value->getPrimaryKey(false);
2118
2119
            return is_array($value) ? json_encode($value) : $value;
2120
        }
2121 34
2122
        return $value;
2123
    }
2124
2125
    /**
2126
     * Generates an appropriate input name for the specified attribute name or expression.
2127
     *
2128
     * This method generates a name that can be used as the input name to collect user input
2129
     * for the specified attribute. The name is generated according to the [[Model::formName|form name]]
2130
     * of the model and the given attribute name. For example, if the form name of the `Post` model
2131
     * is `Post`, then the input name generated for the `content` attribute would be `Post[content]`.
2132
     *
2133
     * See [[getAttributeName()]] for explanation of attribute expression.
2134
     *
2135
     * @param Model $model the model object
2136
     * @param string $attribute the attribute name or expression
2137
     * @return string the generated input name
2138
     * @throws InvalidParamException if the attribute name contains non-word characters.
2139 46
     */
2140
    public static function getInputName($model, $attribute)
2141 46
    {
2142 46
        $formName = $model->formName();
2143
        if (!preg_match('/(^|.*\])([\w\.]+)(\[.*|$)/', $attribute, $matches)) {
2144
            throw new InvalidParamException('Attribute name must contain word characters only.');
2145 46
        }
2146 46
        $prefix = $matches[1];
2147 46
        $attribute = $matches[2];
2148 46
        $suffix = $matches[3];
2149
        if ($formName === '' && $prefix === '') {
2150 46
            return $attribute . $suffix;
2151 46
        } elseif ($formName !== '') {
2152
            return $formName . $prefix . "[$attribute]" . $suffix;
2153
        } else {
2154
            throw new InvalidParamException(get_class($model) . '::formName() cannot be empty for tabular inputs.');
2155
        }
2156
    }
2157
2158
    /**
2159
     * Generates an appropriate input ID for the specified attribute name or expression.
2160
     *
2161
     * This method converts the result [[getInputName()]] into a valid input ID.
2162
     * For example, if [[getInputName()]] returns `Post[content]`, this method will return `post-content`.
2163
     * @param Model $model the model object
2164
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for explanation of attribute expression.
2165
     * @return string the generated input ID
2166
     * @throws InvalidParamException if the attribute name contains non-word characters.
2167 43
     */
2168
    public static function getInputId($model, $attribute)
2169 43
    {
2170 43
        $name = strtolower(static::getInputName($model, $attribute));
2171
        return str_replace(['[]', '][', '[', ']', ' ', '.'], ['', '-', '-', '', '-', '-'], $name);
2172
    }
2173
2174
    /**
2175
     * Escapes regular expression to use in JavaScript
2176
     * @param string $regexp the regular expression to be escaped.
2177
     * @return string the escaped result.
2178
     * @since 2.0.6
2179
     */
2180
    public static function escapeJsRegularExpression($regexp)
2181
    {
2182
        $pattern = preg_replace('/\\\\x\{?([0-9a-fA-F]+)\}?/', '\u$1', $regexp);
2183
        $deliminator = substr($pattern, 0, 1);
2184
        $pos = strrpos($pattern, $deliminator, 1);
2185
        $flag = substr($pattern, $pos + 1);
2186
        if ($deliminator !== '/') {
2187
            $pattern = '/' . str_replace('/', '\\/', substr($pattern, 1, $pos - 1)) . '/';
2188
        } else {
2189
            $pattern = substr($pattern, 0, $pos + 1);
2190
        }
2191
        if (!empty($flag)) {
2192
            $pattern .= preg_replace('/[^igm]/', '', $flag);
2193
        }
2194
2195
        return $pattern;
2196
    }
2197
}
2198