Completed
Push — master ( 9459ea...b238af )
by Alexander
159:54 queued 119:26
created

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