Completed
Push — security-enhancements ( 371440 )
by Alexander
08:04
created

BaseHtml::activeHint()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
698 1
            unset($options['uncheck']);
699 1
        } else {
700 2
            $hidden = '';
701
        }
702 2
        if (isset($options['label'])) {
703 2
            $label = $options['label'];
704 2
            $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : [];
705 2
            unset($options['label'], $options['labelOptions']);
706 2
            $content = static::label(static::input('radio', $name, $value, $options) . ' ' . $label, null, $labelOptions);
0 ignored issues
show
Bug introduced by
It seems like $labelOptions defined by isset($options['labelOpt...abelOptions'] : array() on line 704 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...
707 2
            return $hidden . $content;
708
        } else {
709 2
            return $hidden . static::input('radio', $name, $value, $options);
710
        }
711
    }
712
713
    /**
714
     * Generates a checkbox input.
715
     * @param string $name the name attribute.
716
     * @param boolean $checked whether the checkbox should be checked.
717
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
718
     *
719
     * - uncheck: string, the value associated with the uncheck state of the checkbox. When this attribute
720
     *   is present, a hidden input will be generated so that if the checkbox is not checked and is submitted,
721
     *   the value of this attribute will still be submitted to the server via the hidden input.
722
     * - label: string, a label displayed next to the checkbox.  It will NOT be HTML-encoded. Therefore you can pass
723
     *   in HTML code such as an image tag. If this is is coming from end users, you should [[encode()]] it to prevent XSS attacks.
724
     *   When this option is specified, the checkbox will be enclosed by a label tag.
725
     * - labelOptions: array, the HTML attributes for the label tag. Do not set this option unless you set the "label" option.
726
     *
727
     * The rest of the options will be rendered as the attributes of the resulting checkbox tag. The values will
728
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
729
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
730
     *
731
     * @return string the generated checkbox tag
732
     */
733 4
    public static function checkbox($name, $checked = false, $options = [])
734
    {
735 4
        $options['checked'] = (bool) $checked;
736 4
        $value = array_key_exists('value', $options) ? $options['value'] : '1';
737 4
        if (isset($options['uncheck'])) {
738
            // add a hidden field so that if the checkbox is not selected, it still submits a value
739 1
            $hidden = static::hiddenInput($name, $options['uncheck']);
0 ignored issues
show
Documentation introduced by
$options['uncheck'] is of type boolean, but the function expects a string|null.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
740 1
            unset($options['uncheck']);
741 1
        } else {
742 4
            $hidden = '';
743
        }
744 4
        if (isset($options['label'])) {
745 2
            $label = $options['label'];
746 2
            $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : [];
747 2
            unset($options['label'], $options['labelOptions']);
748 2
            $content = static::label(static::input('checkbox', $name, $value, $options) . ' ' . $label, null, $labelOptions);
0 ignored issues
show
Bug introduced by
It seems like $labelOptions defined by isset($options['labelOpt...abelOptions'] : array() on line 746 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...
749 2
            return $hidden . $content;
750
        } else {
751 4
            return $hidden . static::input('checkbox', $name, $value, $options);
752
        }
753
    }
754
755
    /**
756
     * Generates a drop-down list.
757
     * @param string $name the input name
758
     * @param string $selection the selected value
759
     * @param array $items the option data items. The array keys are option values, and the array values
760
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
761
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
762
     * If you have a list of data models, you may convert them into the format described above using
763
     * [[\yii\helpers\ArrayHelper::map()]].
764
     *
765
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
766
     * the labels will also be HTML-encoded.
767
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
768
     *
769
     * - prompt: string, a prompt text to be displayed as the first option;
770
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
771
     *   and the array values are the extra attributes for the corresponding option tags. For example,
772
     *
773
     *   ```php
774
     *   [
775
     *       'value1' => ['disabled' => true],
776
     *       'value2' => ['label' => 'value 2'],
777
     *   ];
778
     *   ```
779
     *
780
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
781
     *   except that the array keys represent the optgroup labels specified in $items.
782
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
783
     *   Defaults to false.
784
     * - encode: bool, whether to encode option prompt and option value characters.
785
     *   Defaults to `true`. This option is available since 2.0.3.
786
     *
787
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
788
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
789
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
790
     *
791
     * @return string the generated drop-down list tag
792
     */
793 1
    public static function dropDownList($name, $selection = null, $items = [], $options = [])
794
    {
795 1
        if (!empty($options['multiple'])) {
796
            return static::listBox($name, $selection, $items, $options);
797
        }
798 1
        $options['name'] = $name;
799 1
        unset($options['unselect']);
800 1
        $selectOptions = static::renderSelectOptions($selection, $items, $options);
801 1
        return static::tag('select', "\n" . $selectOptions . "\n", $options);
802
    }
803
804
    /**
805
     * Generates a list box.
806
     * @param string $name the input name
807
     * @param string|array $selection the selected value(s)
808
     * @param array $items the option data items. The array keys are option values, and the array values
809
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
810
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
811
     * If you have a list of data models, you may convert them into the format described above using
812
     * [[\yii\helpers\ArrayHelper::map()]].
813
     *
814
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
815
     * the labels will also be HTML-encoded.
816
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
817
     *
818
     * - prompt: string, a prompt text to be displayed as the first option;
819
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
820
     *   and the array values are the extra attributes for the corresponding option tags. For example,
821
     *
822
     *   ```php
823
     *   [
824
     *       'value1' => ['disabled' => true],
825
     *       'value2' => ['label' => 'value 2'],
826
     *   ];
827
     *   ```
828
     *
829
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
830
     *   except that the array keys represent the optgroup labels specified in $items.
831
     * - unselect: string, the value that will be submitted when no option is selected.
832
     *   When this attribute is set, a hidden field will be generated so that if no option is selected in multiple
833
     *   mode, we can still obtain the posted unselect value.
834
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
835
     *   Defaults to false.
836
     * - encode: bool, whether to encode option prompt and option value characters.
837
     *   Defaults to `true`. This option is available since 2.0.3.
838
     *
839
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
840
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
841
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
842
     *
843
     * @return string the generated list box tag
844
     */
845 2
    public static function listBox($name, $selection = null, $items = [], $options = [])
846
    {
847 2
        if (!array_key_exists('size', $options)) {
848 2
            $options['size'] = 4;
849 2
        }
850 2
        if (!empty($options['multiple']) && !empty($name) && substr_compare($name, '[]', -2, 2)) {
851 2
            $name .= '[]';
852 2
        }
853 2
        $options['name'] = $name;
854 2
        if (isset($options['unselect'])) {
855
            // add a hidden field so that if the list box has no option being selected, it still submits a value
856 2
            if (!empty($name) && substr_compare($name, '[]', -2, 2) === 0) {
857 1
                $name = substr($name, 0, -2);
858 1
            }
859 2
            $hidden = static::hiddenInput($name, $options['unselect']);
860 2
            unset($options['unselect']);
861 2
        } else {
862 1
            $hidden = '';
863
        }
864 2
        $selectOptions = static::renderSelectOptions($selection, $items, $options);
0 ignored issues
show
Bug introduced by
It seems like $selection defined by parameter $selection on line 845 can also be of type null; however, yii\helpers\BaseHtml::renderSelectOptions() does only seem to accept string|array, 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...
865 2
        return $hidden . static::tag('select', "\n" . $selectOptions . "\n", $options);
866
    }
867
868
    /**
869
     * Generates a list of checkboxes.
870
     * A checkbox list allows multiple selection, like [[listBox()]].
871
     * As a result, the corresponding submitted value is an array.
872
     * @param string $name the name attribute of each checkbox.
873
     * @param string|array $selection the selected value(s).
874
     * @param array $items the data item used to generate the checkboxes.
875
     * The array keys are the checkbox values, while the array values are the corresponding labels.
876
     * @param array $options options (name => config) for the checkbox list container tag.
877
     * The following options are specially handled:
878
     *
879
     * - tag: string|false, the tag name of the container element. False to render radio buttons without container.
880
     * - unselect: string, the value that should be submitted when none of the checkboxes is selected.
881
     *   By setting this option, a hidden input will be generated.
882
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
883
     *   This option is ignored if `item` option is set.
884
     * - separator: string, the HTML code that separates items.
885
     * - itemOptions: array, the options for generating the checkbox tag using [[checkbox()]].
886
     * - item: callable, a callback that can be used to customize the generation of the HTML code
887
     *   corresponding to a single item in $items. The signature of this callback must be:
888
     *
889
     *   ```php
890
     *   function ($index, $label, $name, $checked, $value)
891
     *   ```
892
     *
893
     *   where $index is the zero-based index of the checkbox in the whole list; $label
894
     *   is the label for the checkbox; and $name, $value and $checked represent the name,
895
     *   value and the checked status of the checkbox input, respectively.
896
     *
897
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
898
     *
899
     * @return string the generated checkbox list
900
     */
901 1
    public static function checkboxList($name, $selection = null, $items = [], $options = [])
902
    {
903 1
        if (substr($name, -2) !== '[]') {
904 1
            $name .= '[]';
905 1
        }
906
907 1
        $formatter = ArrayHelper::remove($options, 'item');
908 1
        $itemOptions = ArrayHelper::remove($options, 'itemOptions', []);
909 1
        $encode = ArrayHelper::remove($options, 'encode', true);
910 1
        $separator = ArrayHelper::remove($options, 'separator', "\n");
911 1
        $tag = ArrayHelper::remove($options, 'tag', 'div');
912
913 1
        $lines = [];
914 1
        $index = 0;
915 1
        foreach ($items as $value => $label) {
916 1
            $checked = $selection !== null &&
917 1
                (!ArrayHelper::isTraversable($selection) && !strcmp($value, $selection)
918 1
                    || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn($value, $selection));
0 ignored issues
show
Bug introduced by
It seems like $selection defined by parameter $selection on line 901 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...
919 1
            if ($formatter !== null) {
920 1
                $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
921 1
            } else {
922 1
                $lines[] = static::checkbox($name, $checked, array_merge($itemOptions, [
923 1
                    'value' => $value,
924 1
                    'label' => $encode ? static::encode($label) : $label,
925 1
                ]));
926
            }
927 1
            $index++;
928 1
        }
929
930 1
        if (isset($options['unselect'])) {
931
            // add a hidden field so that if the list box has no option being selected, it still submits a value
932 1
            $name2 = substr($name, -2) === '[]' ? substr($name, 0, -2) : $name;
933 1
            $hidden = static::hiddenInput($name2, $options['unselect']);
934 1
            unset($options['unselect']);
935 1
        } else {
936 1
            $hidden = '';
937
        }
938
939 1
        $visibleContent = implode($separator, $lines);
940
941 1
        if ($tag === false) {
942 1
            return $hidden . $visibleContent;
943
        }
944
945 1
        return $hidden . static::tag($tag, $visibleContent, $options);
946
    }
947
948
    /**
949
     * Generates a list of radio buttons.
950
     * A radio button list is like a checkbox list, except that it only allows single selection.
951
     * @param string $name the name attribute of each radio button.
952
     * @param string|array $selection the selected value(s).
953
     * @param array $items the data item used to generate the radio buttons.
954
     * The array keys are the radio button values, while the array values are the corresponding labels.
955
     * @param array $options options (name => config) for the radio button list container tag.
956
     * The following options are specially handled:
957
     *
958
     * - tag: string|false, the tag name of the container element. False to render radio buttons without container.
959
     * - unselect: string, the value that should be submitted when none of the radio buttons is selected.
960
     *   By setting this option, a hidden input will be generated.
961
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
962
     *   This option is ignored if `item` option is set.
963
     * - separator: string, the HTML code that separates items.
964
     * - itemOptions: array, the options for generating the radio button tag using [[radio()]].
965
     * - item: callable, a callback that can be used to customize the generation of the HTML code
966
     *   corresponding to a single item in $items. The signature of this callback must be:
967
     *
968
     *   ```php
969
     *   function ($index, $label, $name, $checked, $value)
970
     *   ```
971
     *
972
     *   where $index is the zero-based index of the radio button in the whole list; $label
973
     *   is the label for the radio button; and $name, $value and $checked represent the name,
974
     *   value and the checked status of the radio button input, respectively.
975
     *
976
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
977
     *
978
     * @return string the generated radio button list
979
     */
980 1
    public static function radioList($name, $selection = null, $items = [], $options = [])
981
    {
982 1
        $formatter = ArrayHelper::remove($options, 'item');
983 1
        $itemOptions = ArrayHelper::remove($options, 'itemOptions', []);
984 1
        $encode = ArrayHelper::remove($options, 'encode', true);
985 1
        $separator = ArrayHelper::remove($options, 'separator', "\n");
986 1
        $tag = ArrayHelper::remove($options, 'tag', 'div');
987
        // add a hidden field so that if the list box has no option being selected, it still submits a value
988 1
        $hidden = isset($options['unselect']) ? static::hiddenInput($name, $options['unselect']) : '';
989 1
        unset($options['unselect']);
990
991 1
        $lines = [];
992 1
        $index = 0;
993 1
        foreach ($items as $value => $label) {
994 1
            $checked = $selection !== null &&
995 1
                (!ArrayHelper::isTraversable($selection) && !strcmp($value, $selection)
996 1
                    || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn($value, $selection));
0 ignored issues
show
Bug introduced by
It seems like $selection defined by parameter $selection on line 980 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...
997 1
            if ($formatter !== null) {
998 1
                $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
999 1
            } else {
1000 1
                $lines[] = static::radio($name, $checked, array_merge($itemOptions, [
1001 1
                    'value' => $value,
1002 1
                    'label' => $encode ? static::encode($label) : $label,
1003 1
                ]));
1004
            }
1005 1
            $index++;
1006 1
        }
1007 1
        $visibleContent = implode($separator, $lines);
1008
1009 1
        if ($tag === false) {
1010 1
            return $hidden . $visibleContent;
1011
        }
1012
1013 1
        return $hidden . static::tag($tag, $visibleContent, $options);
1014
    }
1015
1016
    /**
1017
     * Generates an unordered list.
1018
     * @param array|\Traversable $items the items for generating the list. Each item generates a single list item.
1019
     * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true.
1020
     * @param array $options options (name => config) for the radio button list. The following options are supported:
1021
     *
1022
     * - encode: boolean, whether to HTML-encode the items. Defaults to true.
1023
     *   This option is ignored if the `item` option is specified.
1024
     * - separator: string, the HTML code that separates items. Defaults to a simple newline (`"\n"`).
1025
     *   This option is available since version 2.0.7.
1026
     * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified.
1027
     * - item: callable, a callback that is used to generate each individual list item.
1028
     *   The signature of this callback must be:
1029
     *
1030
     *   ```php
1031
     *   function ($item, $index)
1032
     *   ```
1033
     *
1034
     *   where $index is the array key corresponding to `$item` in `$items`. The callback should return
1035
     *   the whole list item tag.
1036
     *
1037
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1038
     *
1039
     * @return string the generated unordered list. An empty list tag will be returned if `$items` is empty.
1040
     */
1041 4
    public static function ul($items, $options = [])
1042
    {
1043 4
        $tag = ArrayHelper::remove($options, 'tag', 'ul');
1044 4
        $encode = ArrayHelper::remove($options, 'encode', true);
1045 4
        $formatter = ArrayHelper::remove($options, 'item');
1046 4
        $separator = ArrayHelper::remove($options, 'separator', "\n");
1047 4
        $itemOptions = ArrayHelper::remove($options, 'itemOptions', []);
1048
1049 4
        if (empty($items)) {
1050 2
            return static::tag($tag, '', $options);
1051
        }
1052
1053 4
        $results = [];
1054 4
        foreach ($items as $index => $item) {
1055 4
            if ($formatter !== null) {
1056 2
                $results[] = call_user_func($formatter, $item, $index);
1057 2
            } else {
1058 4
                $results[] = static::tag('li', $encode ? static::encode($item) : $item, $itemOptions);
1059
            }
1060 4
        }
1061
1062 4
        return static::tag(
1063 4
            $tag,
1064 4
            $separator . implode($separator, $results) . $separator,
1065
            $options
1066 4
        );
1067
    }
1068
1069
    /**
1070
     * Generates an ordered list.
1071
     * @param array|\Traversable $items the items for generating the list. Each item generates a single list item.
1072
     * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true.
1073
     * @param array $options options (name => config) for the radio button list. The following options are supported:
1074
     *
1075
     * - encode: boolean, whether to HTML-encode the items. Defaults to true.
1076
     *   This option is ignored if the `item` option is specified.
1077
     * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified.
1078
     * - item: callable, a callback that is used to generate each individual list item.
1079
     *   The signature of this callback must be:
1080
     *
1081
     *   ```php
1082
     *   function ($item, $index)
1083
     *   ```
1084
     *
1085
     *   where $index is the array key corresponding to `$item` in `$items`. The callback should return
1086
     *   the whole list item tag.
1087
     *
1088
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1089
     *
1090
     * @return string the generated ordered list. An empty string is returned if `$items` is empty.
1091
     */
1092 1
    public static function ol($items, $options = [])
1093
    {
1094 1
        $options['tag'] = 'ol';
1095 1
        return static::ul($items, $options);
1096
    }
1097
1098
    /**
1099
     * Generates a label tag for the given model attribute.
1100
     * The label text is the label associated with the attribute, obtained via [[Model::getAttributeLabel()]].
1101
     * @param Model $model the model object
1102
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1103
     * about attribute expression.
1104
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1105
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1106
     * If a value is null, the corresponding attribute will not be rendered.
1107
     * The following options are specially handled:
1108
     *
1109
     * - label: this specifies the label to be displayed. Note that this will NOT be [[encode()|encoded]].
1110
     *   If this is not set, [[Model::getAttributeLabel()]] will be called to get the label for display
1111
     *   (after encoding).
1112
     *
1113
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1114
     *
1115
     * @return string the generated label tag
1116
     */
1117 3
    public static function activeLabel($model, $attribute, $options = [])
1118
    {
1119 3
        $for = ArrayHelper::remove($options, 'for', static::getInputId($model, $attribute));
1120 3
        $attribute = static::getAttributeName($attribute);
1121 3
        $label = ArrayHelper::remove($options, 'label', static::encode($model->getAttributeLabel($attribute)));
1122 3
        return static::label($label, $for, $options);
1123
    }
1124
1125
    /**
1126
     * Generates a hint tag for the given model attribute.
1127
     * The hint text is the hint associated with the attribute, obtained via [[Model::getAttributeHint()]].
1128
     * If no hint content can be obtained, method will return an empty string.
1129
     * @param Model $model the model object
1130
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1131
     * about attribute expression.
1132
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1133
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1134
     * If a value is null, the corresponding attribute will not be rendered.
1135
     * The following options are specially handled:
1136
     *
1137
     * - hint: this specifies the hint to be displayed. Note that this will NOT be [[encode()|encoded]].
1138
     *   If this is not set, [[Model::getAttributeHint()]] will be called to get the hint for display
1139
     *   (without encoding).
1140
     *
1141
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1142
     *
1143
     * @return string the generated hint tag
1144
     * @since 2.0.4
1145
     */
1146 3
    public static function activeHint($model, $attribute, $options = [])
1147
    {
1148 3
        $attribute = static::getAttributeName($attribute);
1149 3
        $hint = isset($options['hint']) ? $options['hint'] : $model->getAttributeHint($attribute);
1150 3
        if (empty($hint)) {
1151 3
            return '';
1152
        }
1153
        $tag = ArrayHelper::remove($options, 'tag', 'div');
1154
        unset($options['hint']);
1155
        return static::tag($tag, $hint, $options);
1156
    }
1157
1158
    /**
1159
     * Generates a summary of the validation errors.
1160
     * If there is no validation error, an empty error summary markup will still be generated, but it will be hidden.
1161
     * @param Model|Model[] $models the model(s) whose validation errors are to be displayed
1162
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
1163
     *
1164
     * - header: string, the header HTML for the error summary. If not set, a default prompt string will be used.
1165
     * - footer: string, the footer HTML for the error summary.
1166
     * - encode: boolean, if set to false then the error messages won't be encoded.
1167
     *
1168
     * The rest of the options will be rendered as the attributes of the container tag. The values will
1169
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1170
     * @return string the generated error summary
1171
     */
1172
    public static function errorSummary($models, $options = [])
1173
    {
1174
        $header = isset($options['header']) ? $options['header'] : '<p>' . Yii::t('yii', 'Please fix the following errors:') . '</p>';
1175
        $footer = ArrayHelper::remove($options, 'footer', '');
1176
        $encode = ArrayHelper::remove($options, 'encode', true);
1177
        unset($options['header']);
1178
1179
        $lines = [];
1180
        if (!is_array($models)) {
1181
            $models = [$models];
1182
        }
1183
        foreach ($models as $model) {
1184
            /* @var $model Model */
1185
            foreach ($model->getFirstErrors() as $error) {
1186
                $lines[] = $encode ? Html::encode($error) : $error;
1187
            }
1188
        }
1189
1190
        if (empty($lines)) {
1191
            // still render the placeholder for client-side validation use
1192
            $content = '<ul></ul>';
1193
            $options['style'] = isset($options['style']) ? rtrim($options['style'], ';') . '; display:none' : 'display:none';
1194
        } else {
1195
            $content = '<ul><li>' . implode("</li>\n<li>", $lines) . '</li></ul>';
1196
        }
1197
        return Html::tag('div', $header . $content . $footer, $options);
1198
    }
1199
1200
    /**
1201
     * Generates a tag that contains the first validation error of the specified model attribute.
1202
     * Note that even if there is no validation error, this method will still return an empty error tag.
1203
     * @param Model $model the model object
1204
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1205
     * about attribute expression.
1206
     * @param array $options the tag options in terms of name-value pairs. The values will be HTML-encoded
1207
     * using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1208
     *
1209
     * The following options are specially handled:
1210
     *
1211
     * - tag: this specifies the tag name. If not set, "div" will be used.
1212
     * - encode: boolean, if set to false then the error message won't be encoded.
1213
     *
1214
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1215
     *
1216
     * @return string the generated label tag
1217
     */
1218 3
    public static function error($model, $attribute, $options = [])
1219
    {
1220 3
        $attribute = static::getAttributeName($attribute);
1221 3
        $error = $model->getFirstError($attribute);
1222 3
        $tag = ArrayHelper::remove($options, 'tag', 'div');
1223 3
        $encode = ArrayHelper::remove($options, 'encode', true);
1224 3
        return Html::tag($tag, $encode ? Html::encode($error) : $error, $options);
1225
    }
1226
1227
    /**
1228
     * Generates an input tag for the given model attribute.
1229
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1230
     * unless they are explicitly specified in `$options`.
1231
     * @param string $type the input type (e.g. 'text', 'password')
1232
     * @param Model $model the model object
1233
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1234
     * about attribute expression.
1235
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1236
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1237
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1238
     * @return string the generated input tag
1239
     */
1240 8
    public static function activeInput($type, $model, $attribute, $options = [])
1241
    {
1242 8
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1243 8
        $value = isset($options['value']) ? $options['value'] : static::getAttributeValue($model, $attribute);
1244 8
        if (!array_key_exists('id', $options)) {
1245 8
            $options['id'] = static::getInputId($model, $attribute);
1246 8
        }
1247 8
        return static::input($type, $name, $value, $options);
1248
    }
1249
1250
    /**
1251
     * If `maxlength` option is set true and the model attribute is validated by a string validator,
1252
     * the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
1253
     * @param Model $model the model object
1254
     * @param string $attribute the attribute name or expression.
1255
     * @param array $options the tag options in terms of name-value pairs.
1256
     */
1257 11
    private static function normalizeMaxLength($model, $attribute, &$options)
1258
    {
1259 11
        if (isset($options['maxlength']) && $options['maxlength'] === true) {
1260 3
            unset($options['maxlength']);
1261 3
            $attrName = static::getAttributeName($attribute);
1262 3
            foreach ($model->getActiveValidators($attrName) as $validator) {
1263 3
                if ($validator instanceof StringValidator && $validator->max !== null) {
1264 3
                    $options['maxlength'] = $validator->max;
1265 3
                    break;
1266
                }
1267 3
            }
1268 3
        }
1269 11
    }
1270
1271
    /**
1272
     * Generates a text input tag for the given model attribute.
1273
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1274
     * unless they are explicitly specified in `$options`.
1275
     * @param Model $model the model object
1276
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1277
     * about attribute expression.
1278
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1279
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1280
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1281
     * The following special options are recognized:
1282
     *
1283
     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
1284
     *   by a string validator, the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
1285
     *   This is available since version 2.0.3.
1286
     *
1287
     * @return string the generated input tag
1288
     */
1289 4
    public static function activeTextInput($model, $attribute, $options = [])
1290
    {
1291 4
        self::normalizeMaxLength($model, $attribute, $options);
1292 4
        return static::activeInput('text', $model, $attribute, $options);
1293
    }
1294
1295
    /**
1296
     * Generates a hidden input tag for the given model attribute.
1297
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1298
     * unless they are explicitly specified in `$options`.
1299
     * @param Model $model the model object
1300
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1301
     * about attribute expression.
1302
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1303
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1304
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1305
     * @return string the generated input tag
1306
     */
1307
    public static function activeHiddenInput($model, $attribute, $options = [])
1308
    {
1309
        return static::activeInput('hidden', $model, $attribute, $options);
1310
    }
1311
1312
    /**
1313
     * Generates a password input tag for the given model attribute.
1314
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1315
     * unless they are explicitly specified in `$options`.
1316
     * @param Model $model the model object
1317
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1318
     * about attribute expression.
1319
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1320
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1321
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1322
     * The following special options are recognized:
1323
     *
1324
     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
1325
     *   by a string validator, the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
1326
     *   This option is available since version 2.0.6.
1327
     *
1328
     * @return string the generated input tag
1329
     */
1330 3
    public static function activePasswordInput($model, $attribute, $options = [])
1331
    {
1332 3
        self::normalizeMaxLength($model, $attribute, $options);
1333 3
        return static::activeInput('password', $model, $attribute, $options);
1334
    }
1335
1336
    /**
1337
     * Generates a file input tag for the given model attribute.
1338
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1339
     * unless they are explicitly specified in `$options`.
1340
     * @param Model $model the model object
1341
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1342
     * about attribute expression.
1343
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1344
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1345
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1346
     * @return string the generated input tag
1347
     */
1348
    public static function activeFileInput($model, $attribute, $options = [])
1349
    {
1350
        // add a hidden field so that if a model only has a file field, we can
1351
        // still use isset($_POST[$modelClass]) to detect if the input is submitted
1352
        $hiddenOptions = ['id' => null, 'value' => ''];
1353
        if (isset($options['name'])) {
1354
            $hiddenOptions['name'] = $options['name'];
1355
        }
1356
        return static::activeHiddenInput($model, $attribute, $hiddenOptions)
1357
            . static::activeInput('file', $model, $attribute, $options);
1358
    }
1359
1360
    /**
1361
     * Generates a textarea tag for the given model attribute.
1362
     * The model attribute value will be used as the content in the textarea.
1363
     * @param Model $model the model object
1364
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1365
     * about attribute expression.
1366
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1367
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1368
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1369
     * The following special options are recognized:
1370
     *
1371
     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
1372
     *   by a string validator, the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
1373
     *   This option is available since version 2.0.6.
1374
     *
1375
     * @return string the generated textarea tag
1376
     */
1377 4
    public static function activeTextarea($model, $attribute, $options = [])
1378
    {
1379 4
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1380 4
        if (isset($options['value'])) {
1381 1
            $value = $options['value'];
1382 1
            unset($options['value']);
1383 1
        } else {
1384 3
            $value = static::getAttributeValue($model, $attribute);
1385
        }
1386 4
        if (!array_key_exists('id', $options)) {
1387 4
            $options['id'] = static::getInputId($model, $attribute);
1388 4
        }
1389 4
        self::normalizeMaxLength($model, $attribute, $options);
1390 4
        return static::textarea($name, $value, $options);
1391
    }
1392
1393
    /**
1394
     * Generates a radio button tag together with a label for the given model attribute.
1395
     * This method will generate the "checked" tag attribute according to the model attribute value.
1396
     * @param Model $model the model object
1397
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1398
     * about attribute expression.
1399
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
1400
     *
1401
     * - uncheck: string, the value associated with the uncheck state of the radio button. If not set,
1402
     *   it will take the default value '0'. This method will render a hidden input so that if the radio button
1403
     *   is not checked and is submitted, the value of this attribute will still be submitted to the server
1404
     *   via the hidden input. If you do not want any hidden input, you should explicitly set this option as null.
1405
     * - label: string, a label displayed next to the radio button.  It will NOT be HTML-encoded. Therefore you can pass
1406
     *   in HTML code such as an image tag. If this is is coming from end users, you should [[encode()]] it to prevent XSS attacks.
1407
     *   The radio button will be enclosed by the label tag. Note that if you do not specify this option, a default label
1408
     *   will be used based on the attribute label declaration in the model. If you do not want any label, you should
1409
     *   explicitly set this option as null.
1410
     * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified.
1411
     *
1412
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
1413
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1414
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1415
     *
1416
     * @return string the generated radio button tag
1417
     */
1418
    public static function activeRadio($model, $attribute, $options = [])
1419
    {
1420
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1421
        $value = static::getAttributeValue($model, $attribute);
1422
1423
        if (!array_key_exists('value', $options)) {
1424
            $options['value'] = '1';
1425
        }
1426
        if (!array_key_exists('uncheck', $options)) {
1427
            $options['uncheck'] = '0';
1428
        }
1429
        if (!array_key_exists('label', $options)) {
1430
            $options['label'] = static::encode($model->getAttributeLabel(static::getAttributeName($attribute)));
1431
        }
1432
1433
        $checked = "$value" === "{$options['value']}";
1434
1435
        if (!array_key_exists('id', $options)) {
1436
            $options['id'] = static::getInputId($model, $attribute);
1437
        }
1438
1439
        return static::radio($name, $checked, $options);
1440
    }
1441
1442
    /**
1443
     * Generates a checkbox tag together with a label for the given model attribute.
1444
     * This method will generate the "checked" tag attribute according to the model attribute value.
1445
     * @param Model $model the model object
1446
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1447
     * about attribute expression.
1448
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
1449
     *
1450
     * - uncheck: string, the value associated with the uncheck state of the radio button. If not set,
1451
     *   it will take the default value '0'. This method will render a hidden input so that if the radio button
1452
     *   is not checked and is submitted, the value of this attribute will still be submitted to the server
1453
     *   via the hidden input. If you do not want any hidden input, you should explicitly set this option as null.
1454
     * - label: string, a label displayed next to the checkbox.  It will NOT be HTML-encoded. Therefore you can pass
1455
     *   in HTML code such as an image tag. If this is is coming from end users, you should [[encode()]] it to prevent XSS attacks.
1456
     *   The checkbox will be enclosed by the label tag. Note that if you do not specify this option, a default label
1457
     *   will be used based on the attribute label declaration in the model. If you do not want any label, you should
1458
     *   explicitly set this option as null.
1459
     * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified.
1460
     *
1461
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
1462
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1463
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1464
     *
1465
     * @return string the generated checkbox tag
1466
     */
1467
    public static function activeCheckbox($model, $attribute, $options = [])
1468
    {
1469
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1470
        $value = static::getAttributeValue($model, $attribute);
1471
1472
        if (!array_key_exists('value', $options)) {
1473
            $options['value'] = '1';
1474
        }
1475
        if (!array_key_exists('uncheck', $options)) {
1476
            $options['uncheck'] = '0';
1477
        }
1478
        if (!array_key_exists('label', $options)) {
1479
            $options['label'] = static::encode($model->getAttributeLabel(static::getAttributeName($attribute)));
1480
        }
1481
1482
        $checked = "$value" === "{$options['value']}";
1483
1484
        if (!array_key_exists('id', $options)) {
1485
            $options['id'] = static::getInputId($model, $attribute);
1486
        }
1487
1488
        return static::checkbox($name, $checked, $options);
1489
    }
1490
1491
    /**
1492
     * Generates a drop-down list for the given model attribute.
1493
     * The selection of the drop-down list is taken from the value of the model attribute.
1494
     * @param Model $model the model object
1495
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1496
     * about attribute expression.
1497
     * @param array $items the option data items. The array keys are option values, and the array values
1498
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
1499
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
1500
     * If you have a list of data models, you may convert them into the format described above using
1501
     * [[\yii\helpers\ArrayHelper::map()]].
1502
     *
1503
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
1504
     * the labels will also be HTML-encoded.
1505
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
1506
     *
1507
     * - prompt: string, a prompt text to be displayed as the first option;
1508
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
1509
     *   and the array values are the extra attributes for the corresponding option tags. For example,
1510
     *
1511
     *   ```php
1512
     *   [
1513
     *       'value1' => ['disabled' => true],
1514
     *       'value2' => ['label' => 'value 2'],
1515
     *   ];
1516
     *   ```
1517
     *
1518
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
1519
     *   except that the array keys represent the optgroup labels specified in $items.
1520
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
1521
     *   Defaults to false.
1522
     * - encode: bool, whether to encode option prompt and option value characters.
1523
     *   Defaults to `true`. This option is available since 2.0.3.
1524
     *
1525
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
1526
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1527
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1528
     *
1529
     * @return string the generated drop-down list tag
1530
     */
1531
    public static function activeDropDownList($model, $attribute, $items, $options = [])
1532
    {
1533
        if (empty($options['multiple'])) {
1534
            return static::activeListInput('dropDownList', $model, $attribute, $items, $options);
1535
        } else {
1536
            return static::activeListBox($model, $attribute, $items, $options);
1537
        }
1538
    }
1539
1540
    /**
1541
     * Generates a list box.
1542
     * The selection of the list box is taken from the value of the model attribute.
1543
     * @param Model $model the model object
1544
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1545
     * about attribute expression.
1546
     * @param array $items the option data items. The array keys are option values, and the array values
1547
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
1548
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
1549
     * If you have a list of data models, you may convert them into the format described above using
1550
     * [[\yii\helpers\ArrayHelper::map()]].
1551
     *
1552
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
1553
     * the labels will also be HTML-encoded.
1554
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
1555
     *
1556
     * - prompt: string, a prompt text to be displayed as the first option;
1557
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
1558
     *   and the array values are the extra attributes for the corresponding option tags. For example,
1559
     *
1560
     *   ```php
1561
     *   [
1562
     *       'value1' => ['disabled' => true],
1563
     *       'value2' => ['label' => 'value 2'],
1564
     *   ];
1565
     *   ```
1566
     *
1567
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
1568
     *   except that the array keys represent the optgroup labels specified in $items.
1569
     * - unselect: string, the value that will be submitted when no option is selected.
1570
     *   When this attribute is set, a hidden field will be generated so that if no option is selected in multiple
1571
     *   mode, we can still obtain the posted unselect value.
1572
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
1573
     *   Defaults to false.
1574
     * - encode: bool, whether to encode option prompt and option value characters.
1575
     *   Defaults to `true`. This option is available since 2.0.3.
1576
     *
1577
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
1578
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1579
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1580
     *
1581
     * @return string the generated list box tag
1582
     */
1583 1
    public static function activeListBox($model, $attribute, $items, $options = [])
1584
    {
1585 1
        return static::activeListInput('listBox', $model, $attribute, $items, $options);
1586
    }
1587
1588
    /**
1589
     * Generates a list of checkboxes.
1590
     * A checkbox list allows multiple selection, like [[listBox()]].
1591
     * As a result, the corresponding submitted value is an array.
1592
     * The selection of the checkbox list is taken from the value of the model attribute.
1593
     * @param Model $model the model object
1594
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1595
     * about attribute expression.
1596
     * @param array $items the data item used to generate the checkboxes.
1597
     * The array keys are the checkbox values, and the array values are the corresponding labels.
1598
     * Note that the labels will NOT be HTML-encoded, while the values will.
1599
     * @param array $options options (name => config) for the checkbox list container tag.
1600
     * The following options are specially handled:
1601
     *
1602
     * - tag: string, the tag name of the container element.
1603
     * - unselect: string, the value that should be submitted when none of the checkboxes is selected.
1604
     *   You may set this option to be null to prevent default value submission.
1605
     *   If this option is not set, an empty string will be submitted.
1606
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
1607
     *   This option is ignored if `item` option is set.
1608
     * - separator: string, the HTML code that separates items.
1609
     * - itemOptions: array, the options for generating the checkbox tag using [[checkbox()]].
1610
     * - item: callable, a callback that can be used to customize the generation of the HTML code
1611
     *   corresponding to a single item in $items. The signature of this callback must be:
1612
     *
1613
     *   ```php
1614
     *   function ($index, $label, $name, $checked, $value)
1615
     *   ```
1616
     *
1617
     *   where $index is the zero-based index of the checkbox in the whole list; $label
1618
     *   is the label for the checkbox; and $name, $value and $checked represent the name,
1619
     *   value and the checked status of the checkbox input.
1620
     *
1621
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1622
     *
1623
     * @return string the generated checkbox list
1624
     */
1625
    public static function activeCheckboxList($model, $attribute, $items, $options = [])
1626
    {
1627
        return static::activeListInput('checkboxList', $model, $attribute, $items, $options);
1628
    }
1629
1630
    /**
1631
     * Generates a list of radio buttons.
1632
     * A radio button list is like a checkbox list, except that it only allows single selection.
1633
     * The selection of the radio buttons is taken from the value of the model attribute.
1634
     * @param Model $model the model object
1635
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1636
     * about attribute expression.
1637
     * @param array $items the data item used to generate the radio buttons.
1638
     * The array keys are the radio values, and the array values are the corresponding labels.
1639
     * Note that the labels will NOT be HTML-encoded, while the values will.
1640
     * @param array $options options (name => config) for the radio button list container tag.
1641
     * The following options are specially handled:
1642
     *
1643
     * - tag: string, the tag name of the container element.
1644
     * - unselect: string, the value that should be submitted when none of the radio buttons is selected.
1645
     *   You may set this option to be null to prevent default value submission.
1646
     *   If this option is not set, an empty string will be submitted.
1647
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
1648
     *   This option is ignored if `item` option is set.
1649
     * - separator: string, the HTML code that separates items.
1650
     * - itemOptions: array, the options for generating the radio button tag using [[radio()]].
1651
     * - item: callable, a callback that can be used to customize the generation of the HTML code
1652
     *   corresponding to a single item in $items. The signature of this callback must be:
1653
     *
1654
     *   ```php
1655
     *   function ($index, $label, $name, $checked, $value)
1656
     *   ```
1657
     *
1658
     *   where $index is the zero-based index of the radio button in the whole list; $label
1659
     *   is the label for the radio button; and $name, $value and $checked represent the name,
1660
     *   value and the checked status of the radio button input.
1661
     *
1662
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1663
     *
1664
     * @return string the generated radio button list
1665
     */
1666
    public static function activeRadioList($model, $attribute, $items, $options = [])
1667
    {
1668
        return static::activeListInput('radioList', $model, $attribute, $items, $options);
1669
    }
1670
1671
    /**
1672
     * Generates a list of input fields.
1673
     * This method is mainly called by [[activeListBox()]], [[activeRadioList()]] and [[activeCheckBoxList()]].
1674
     * @param string $type the input type. This can be 'listBox', 'radioList', or 'checkBoxList'.
1675
     * @param Model $model the model object
1676
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1677
     * about attribute expression.
1678
     * @param array $items the data item used to generate the input fields.
1679
     * The array keys are the input values, and the array values are the corresponding labels.
1680
     * Note that the labels will NOT be HTML-encoded, while the values will.
1681
     * @param array $options options (name => config) for the input list. The supported special options
1682
     * depend on the input type specified by `$type`.
1683
     * @return string the generated input list
1684
     */
1685 1
    protected static function activeListInput($type, $model, $attribute, $items, $options = [])
1686
    {
1687 1
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1688 1
        $selection = static::getAttributeValue($model, $attribute);
1689 1
        if (!array_key_exists('unselect', $options)) {
1690 1
            $options['unselect'] = '';
1691 1
        }
1692 1
        if (!array_key_exists('id', $options)) {
1693 1
            $options['id'] = static::getInputId($model, $attribute);
1694 1
        }
1695 1
        return static::$type($name, $selection, $items, $options);
1696
    }
1697
1698
    /**
1699
     * Renders the option tags that can be used by [[dropDownList()]] and [[listBox()]].
1700
     * @param string|array $selection the selected value(s). This can be either a string for single selection
1701
     * or an array for multiple selections.
1702
     * @param array $items the option data items. The array keys are option values, and the array values
1703
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
1704
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
1705
     * If you have a list of data models, you may convert them into the format described above using
1706
     * [[\yii\helpers\ArrayHelper::map()]].
1707
     *
1708
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
1709
     * the labels will also be HTML-encoded.
1710
     * @param array $tagOptions the $options parameter that is passed to the [[dropDownList()]] or [[listBox()]] call.
1711
     * This method will take out these elements, if any: "prompt", "options" and "groups". See more details
1712
     * in [[dropDownList()]] for the explanation of these elements.
1713
     *
1714
     * @return string the generated list options
1715
     */
1716 4
    public static function renderSelectOptions($selection, $items, &$tagOptions = [])
1717
    {
1718 4
        $lines = [];
1719 4
        $encodeSpaces = ArrayHelper::remove($tagOptions, 'encodeSpaces', false);
1720 4
        $encode = ArrayHelper::remove($tagOptions, 'encode', true);
1721 4
        if (isset($tagOptions['prompt'])) {
1722 1
            $prompt = $encode ? static::encode($tagOptions['prompt']) : $tagOptions['prompt'];
1723 1
            if ($encodeSpaces) {
1724 1
                $prompt = str_replace(' ', '&nbsp;', $prompt);
1725 1
            }
1726 1
            $lines[] = static::tag('option', $prompt, ['value' => '']);
1727 1
        }
1728
1729 4
        $options = isset($tagOptions['options']) ? $tagOptions['options'] : [];
1730 4
        $groups = isset($tagOptions['groups']) ? $tagOptions['groups'] : [];
1731 4
        unset($tagOptions['prompt'], $tagOptions['options'], $tagOptions['groups']);
1732 4
        $options['encodeSpaces'] = ArrayHelper::getValue($options, 'encodeSpaces', $encodeSpaces);
1733 4
        $options['encode'] = ArrayHelper::getValue($options, 'encode', $encode);
1734
1735 4
        foreach ($items as $key => $value) {
1736 4
            if (is_array($value)) {
1737 1
                $groupAttrs = isset($groups[$key]) ? $groups[$key] : [];
1738 1
                if (!isset($groupAttrs['label'])) {
1739 1
                    $groupAttrs['label'] = $key;
1740 1
                }
1741 1
                $attrs = ['options' => $options, 'groups' => $groups, 'encodeSpaces' => $encodeSpaces, 'encode' => $encode];
1742 1
                $content = static::renderSelectOptions($selection, $value, $attrs);
1743 1
                $lines[] = static::tag('optgroup', "\n" . $content . "\n", $groupAttrs);
1744 1
            } else {
1745 4
                $attrs = isset($options[$key]) ? $options[$key] : [];
1746 4
                $attrs['value'] = (string) $key;
1747 4
                $attrs['selected'] = $selection !== null &&
1748 4
                        (!ArrayHelper::isTraversable($selection) && !strcmp($key, $selection)
1749 4
                        || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn($key, $selection));
0 ignored issues
show
Bug introduced by
It seems like $selection defined by parameter $selection on line 1716 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...
1750 4
                $text = $encode ? static::encode($value) : $value;
1751 4
                if ($encodeSpaces) {
1752 2
                    $text = str_replace(' ', '&nbsp;', $text);
1753 2
                }
1754 4
                $lines[] = static::tag('option', $text, $attrs);
1755
            }
1756 4
        }
1757
1758 4
        return implode("\n", $lines);
1759
    }
1760
1761
    /**
1762
     * Renders the HTML tag attributes.
1763
     *
1764
     * Attributes whose values are of boolean type will be treated as
1765
     * [boolean attributes](http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes).
1766
     *
1767
     * Attributes whose values are null will not be rendered.
1768
     *
1769
     * The values of attributes will be HTML-encoded using [[encode()]].
1770
     *
1771
     * The "data" attribute is specially handled when it is receiving an array value. In this case,
1772
     * the array will be "expanded" and a list data attributes will be rendered. For example,
1773
     * if `'data' => ['id' => 1, 'name' => 'yii']`, then this will be rendered:
1774
     * `data-id="1" data-name="yii"`.
1775
     * Additionally `'data' => ['params' => ['id' => 1, 'name' => 'yii'], 'status' => 'ok']` will be rendered as:
1776
     * `data-params='{"id":1,"name":"yii"}' data-status="ok"`.
1777
     *
1778
     * @param array $attributes attributes to be rendered. The attribute values will be HTML-encoded using [[encode()]].
1779
     * @return string the rendering result. If the attributes are not empty, they will be rendered
1780
     * into a string with a leading white space (so that it can be directly appended to the tag name
1781
     * in a tag. If there is no attribute, an empty string will be returned.
1782
     */
1783 76
    public static function renderTagAttributes($attributes)
1784
    {
1785 76
        if (count($attributes) > 1) {
1786 59
            $sorted = [];
1787 59
            foreach (static::$attributeOrder as $name) {
1788 59
                if (isset($attributes[$name])) {
1789 59
                    $sorted[$name] = $attributes[$name];
1790 59
                }
1791 59
            }
1792 59
            $attributes = array_merge($sorted, $attributes);
1793 59
        }
1794
1795 76
        $html = '';
1796 76
        foreach ($attributes as $name => $value) {
1797 73
            if (is_bool($value)) {
1798 15
                if ($value) {
1799 13
                    $html .= " $name";
1800 13
                }
1801 73
            } elseif (is_array($value)) {
1802 2
                if (in_array($name, static::$dataAttributes)) {
1803 1
                    foreach ($value as $n => $v) {
1804 1
                        if (is_array($v)) {
1805
                            $html .= " $name-$n='" . Json::htmlEncode($v) . "'";
1806
                        } else {
1807 1
                            $html .= " $name-$n=\"" . static::encode($v) . '"';
1808
                        }
1809 1
                    }
1810 2
                } elseif ($name === 'class') {
1811 1
                    if (empty($value)) {
1812 1
                        continue;
1813
                    }
1814 1
                    $html .= " $name=\"" . static::encode(implode(' ', $value)) . '"';
1815 2
                } elseif ($name === 'style') {
1816 1
                    if (empty($value)) {
1817 1
                        continue;
1818
                    }
1819 1
                    $html .= " $name=\"" . static::encode(static::cssStyleFromArray($value)) . '"';
1820 1
                } else {
1821 1
                    $html .= " $name='" . Json::htmlEncode($value) . "'";
1822
                }
1823 73
            } elseif ($value !== null) {
1824 73
                $html .= " $name=\"" . static::encode($value) . '"';
1825 73
            }
1826 76
        }
1827
1828 76
        return $html;
1829
    }
1830
1831
    /**
1832
     * Adds a CSS class (or several classes) to the specified options.
1833
     * If the CSS class is already in the options, it will not be added again.
1834
     * If class specification at given options is an array, and some class placed there with the named (string) key,
1835
     * overriding of such key will have no effect. For example:
1836
     *
1837
     * ```php
1838
     * $options = ['class' => ['persistent' => 'initial']];
1839
     * Html::addCssClass($options, ['persistent' => 'override']);
1840
     * var_dump($options['class']); // outputs: array('persistent' => 'initial');
1841
     * ```
1842
     *
1843
     * @param array $options the options to be modified.
1844
     * @param string|array $class the CSS class(es) to be added
1845
     */
1846 3
    public static function addCssClass(&$options, $class)
1847
    {
1848 3
        if (isset($options['class'])) {
1849 2
            if (is_array($options['class'])) {
1850 2
                $options['class'] = self::mergeCssClasses($options['class'], (array) $class);
1851 2
            } else {
1852 1
                $classes = preg_split('/\s+/', $options['class'], -1, PREG_SPLIT_NO_EMPTY);
1853 1
                $options['class'] = implode(' ', self::mergeCssClasses($classes, (array) $class));
1854
            }
1855 2
        } else {
1856 2
            $options['class'] = $class;
1857
        }
1858 3
    }
1859
1860
    /**
1861
     * Merges already existing CSS classes with new one.
1862
     * This method provides the priority for named existing classes over additional.
1863
     * @param array $existingClasses already existing CSS classes.
1864
     * @param array $additionalClasses CSS classes to be added.
1865
     * @return array merge result.
1866
     */
1867 2
    private static function mergeCssClasses(array $existingClasses, array $additionalClasses)
1868
    {
1869 2
        foreach ($additionalClasses as $key => $class) {
1870 2
            if (is_int($key) && !in_array($class, $existingClasses)) {
1871 1
                $existingClasses[] = $class;
1872 2
            } elseif (!isset($existingClasses[$key])) {
1873 1
                $existingClasses[$key] = $class;
1874 1
            }
1875 2
        }
1876 2
        return array_unique($existingClasses);
1877
    }
1878
1879
    /**
1880
     * Removes a CSS class from the specified options.
1881
     * @param array $options the options to be modified.
1882
     * @param string|array $class the CSS class(es) to be removed
1883
     */
1884 1
    public static function removeCssClass(&$options, $class)
1885
    {
1886 1
        if (isset($options['class'])) {
1887 1
            if (is_array($options['class'])) {
1888 1
                $classes = array_diff($options['class'], (array) $class);
1889 1
                if (empty($classes)) {
1890 1
                    unset($options['class']);
1891 1
                } else {
1892 1
                    $options['class'] = $classes;
1893
                }
1894 1
            } else {
1895 1
                $classes = preg_split('/\s+/', $options['class'], -1, PREG_SPLIT_NO_EMPTY);
1896 1
                $classes = array_diff($classes, (array) $class);
1897 1
                if (empty($classes)) {
1898 1
                    unset($options['class']);
1899 1
                } else {
1900 1
                    $options['class'] = implode(' ', $classes);
1901
                }
1902
            }
1903 1
        }
1904 1
    }
1905
1906
    /**
1907
     * Adds the specified CSS style to the HTML options.
1908
     *
1909
     * If the options already contain a `style` element, the new style will be merged
1910
     * with the existing one. If a CSS property exists in both the new and the old styles,
1911
     * the old one may be overwritten if `$overwrite` is true.
1912
     *
1913
     * For example,
1914
     *
1915
     * ```php
1916
     * Html::addCssStyle($options, 'width: 100px; height: 200px');
1917
     * ```
1918
     *
1919
     * @param array $options the HTML options to be modified.
1920
     * @param string|array $style the new style string (e.g. `'width: 100px; height: 200px'`) or
1921
     * array (e.g. `['width' => '100px', 'height' => '200px']`).
1922
     * @param boolean $overwrite whether to overwrite existing CSS properties if the new style
1923
     * contain them too.
1924
     * @see removeCssStyle()
1925
     * @see cssStyleFromArray()
1926
     * @see cssStyleToArray()
1927
     */
1928 1
    public static function addCssStyle(&$options, $style, $overwrite = true)
1929
    {
1930 1
        if (!empty($options['style'])) {
1931 1
            $oldStyle = is_array($options['style']) ? $options['style'] : static::cssStyleToArray($options['style']);
1932 1
            $newStyle = is_array($style) ? $style : static::cssStyleToArray($style);
1933 1
            if (!$overwrite) {
1934 1
                foreach ($newStyle as $property => $value) {
1935 1
                    if (isset($oldStyle[$property])) {
1936 1
                        unset($newStyle[$property]);
1937 1
                    }
1938 1
                }
1939 1
            }
1940 1
            $style = array_merge($oldStyle, $newStyle);
1941 1
        }
1942 1
        $options['style'] = is_array($style) ? static::cssStyleFromArray($style) : $style;
1943 1
    }
1944
1945
    /**
1946
     * Removes the specified CSS style from the HTML options.
1947
     *
1948
     * For example,
1949
     *
1950
     * ```php
1951
     * Html::removeCssStyle($options, ['width', 'height']);
1952
     * ```
1953
     *
1954
     * @param array $options the HTML options to be modified.
1955
     * @param string|array $properties the CSS properties to be removed. You may use a string
1956
     * if you are removing a single property.
1957
     * @see addCssStyle()
1958
     */
1959 1
    public static function removeCssStyle(&$options, $properties)
1960
    {
1961 1
        if (!empty($options['style'])) {
1962 1
            $style = is_array($options['style']) ? $options['style'] : static::cssStyleToArray($options['style']);
1963 1
            foreach ((array) $properties as $property) {
1964 1
                unset($style[$property]);
1965 1
            }
1966 1
            $options['style'] = static::cssStyleFromArray($style);
1967 1
        }
1968 1
    }
1969
1970
    /**
1971
     * Converts a CSS style array into a string representation.
1972
     *
1973
     * For example,
1974
     *
1975
     * ```php
1976
     * print_r(Html::cssStyleFromArray(['width' => '100px', 'height' => '200px']));
1977
     * // will display: 'width: 100px; height: 200px;'
1978
     * ```
1979
     *
1980
     * @param array $style the CSS style array. The array keys are the CSS property names,
1981
     * and the array values are the corresponding CSS property values.
1982
     * @return string the CSS style string. If the CSS style is empty, a null will be returned.
1983
     */
1984 4
    public static function cssStyleFromArray(array $style)
1985
    {
1986 4
        $result = '';
1987 4
        foreach ($style as $name => $value) {
1988 4
            $result .= "$name: $value; ";
1989 4
        }
1990
        // return null if empty to avoid rendering the "style" attribute
1991 4
        return $result === '' ? null : rtrim($result);
1992
    }
1993
1994
    /**
1995
     * Converts a CSS style string into an array representation.
1996
     *
1997
     * The array keys are the CSS property names, and the array values
1998
     * are the corresponding CSS property values.
1999
     *
2000
     * For example,
2001
     *
2002
     * ```php
2003
     * print_r(Html::cssStyleToArray('width: 100px; height: 200px;'));
2004
     * // will display: ['width' => '100px', 'height' => '200px']
2005
     * ```
2006
     *
2007
     * @param string $style the CSS style string
2008
     * @return array the array representation of the CSS style
2009
     */
2010 3
    public static function cssStyleToArray($style)
2011
    {
2012 3
        $result = [];
2013 3
        foreach (explode(';', $style) as $property) {
2014 3
            $property = explode(':', $property);
2015 3
            if (count($property) > 1) {
2016 3
                $result[trim($property[0])] = trim($property[1]);
2017 3
            }
2018 3
        }
2019 3
        return $result;
2020
    }
2021
2022
    /**
2023
     * Returns the real attribute name from the given attribute expression.
2024
     *
2025
     * An attribute expression is an attribute name prefixed and/or suffixed with array indexes.
2026
     * It is mainly used in tabular data input and/or input of array type. Below are some examples:
2027
     *
2028
     * - `[0]content` is used in tabular data input to represent the "content" attribute
2029
     *   for the first model in tabular input;
2030
     * - `dates[0]` represents the first array element of the "dates" attribute;
2031
     * - `[0]dates[0]` represents the first array element of the "dates" attribute
2032
     *   for the first model in tabular input.
2033
     *
2034
     * If `$attribute` has neither prefix nor suffix, it will be returned back without change.
2035
     * @param string $attribute the attribute name or expression
2036
     * @return string the attribute name without prefix and suffix.
2037
     * @throws InvalidParamException if the attribute name contains non-word characters.
2038
     */
2039 6
    public static function getAttributeName($attribute)
2040
    {
2041 6
        if (preg_match('/(^|.*\])([\w\.]+)(\[.*|$)/', $attribute, $matches)) {
2042 6
            return $matches[2];
2043
        } else {
2044
            throw new InvalidParamException('Attribute name must contain word characters only.');
2045
        }
2046
    }
2047
2048
    /**
2049
     * Returns the value of the specified attribute name or expression.
2050
     *
2051
     * For an attribute expression like `[0]dates[0]`, this method will return the value of `$model->dates[0]`.
2052
     * See [[getAttributeName()]] for more details about attribute expression.
2053
     *
2054
     * If an attribute value is an instance of [[ActiveRecordInterface]] or an array of such instances,
2055
     * the primary value(s) of the AR instance(s) will be returned instead.
2056
     *
2057
     * @param Model $model the model object
2058
     * @param string $attribute the attribute name or expression
2059
     * @return string|array the corresponding attribute value
2060
     * @throws InvalidParamException if the attribute name contains non-word characters.
2061
     */
2062 12
    public static function getAttributeValue($model, $attribute)
2063
    {
2064 12
        if (!preg_match('/(^|.*\])([\w\.]+)(\[.*|$)/', $attribute, $matches)) {
2065
            throw new InvalidParamException('Attribute name must contain word characters only.');
2066
        }
2067 12
        $attribute = $matches[2];
2068 12
        $value = $model->$attribute;
2069 12
        if ($matches[3] !== '') {
2070
            foreach (explode('][', trim($matches[3], '[]')) as $id) {
2071
                if ((is_array($value) || $value instanceof \ArrayAccess) && isset($value[$id])) {
2072
                    $value = $value[$id];
2073
                } else {
2074
                    return null;
2075
                }
2076
            }
2077
        }
2078
2079
        // https://github.com/yiisoft/yii2/issues/1457
2080 12
        if (is_array($value)) {
2081
            foreach ($value as $i => $v) {
2082
                if ($v instanceof ActiveRecordInterface) {
2083
                    $v = $v->getPrimaryKey(false);
2084
                    $value[$i] = is_array($v) ? json_encode($v) : $v;
2085
                }
2086
            }
2087 12
        } elseif ($value instanceof ActiveRecordInterface) {
2088
            $value = $value->getPrimaryKey(false);
2089
2090
            return is_array($value) ? json_encode($value) : $value;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return is_array($value) ...ncode($value) : $value; (object|integer|double|string|null|boolean) is incompatible with the return type documented by yii\helpers\BaseHtml::getAttributeValue of type string|array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
2091
        }
2092
2093 12
        return $value;
2094
    }
2095
2096
    /**
2097
     * Generates an appropriate input name for the specified attribute name or expression.
2098
     *
2099
     * This method generates a name that can be used as the input name to collect user input
2100
     * for the specified attribute. The name is generated according to the [[Model::formName|form name]]
2101
     * of the model and the given attribute name. For example, if the form name of the `Post` model
2102
     * is `Post`, then the input name generated for the `content` attribute would be `Post[content]`.
2103
     *
2104
     * See [[getAttributeName()]] for explanation of attribute expression.
2105
     *
2106
     * @param Model $model the model object
2107
     * @param string $attribute the attribute name or expression
2108
     * @return string the generated input name
2109
     * @throws InvalidParamException if the attribute name contains non-word characters.
2110
     */
2111 13
    public static function getInputName($model, $attribute)
2112
    {
2113 13
        $formName = $model->formName();
2114 13
        if (!preg_match('/(^|.*\])([\w\.]+)(\[.*|$)/', $attribute, $matches)) {
2115
            throw new InvalidParamException('Attribute name must contain word characters only.');
2116
        }
2117 13
        $prefix = $matches[1];
2118 13
        $attribute = $matches[2];
2119 13
        $suffix = $matches[3];
2120 13
        if ($formName === '' && $prefix === '') {
2121
            return $attribute . $suffix;
2122 13
        } elseif ($formName !== '') {
2123 13
            return $formName . $prefix . "[$attribute]" . $suffix;
2124
        } else {
2125
            throw new InvalidParamException(get_class($model) . '::formName() cannot be empty for tabular inputs.');
2126
        }
2127
    }
2128
2129
    /**
2130
     * Generates an appropriate input ID for the specified attribute name or expression.
2131
     *
2132
     * This method converts the result [[getInputName()]] into a valid input ID.
2133
     * For example, if [[getInputName()]] returns `Post[content]`, this method will return `post-content`.
2134
     * @param Model $model the model object
2135
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for explanation of attribute expression.
2136
     * @return string the generated input ID
2137
     * @throws InvalidParamException if the attribute name contains non-word characters.
2138
     */
2139 13
    public static function getInputId($model, $attribute)
2140
    {
2141 13
        $name = strtolower(static::getInputName($model, $attribute));
2142 13
        return str_replace(['[]', '][', '[', ']', ' ', '.'], ['', '-', '-', '', '-', '-'], $name);
2143
    }
2144
2145
    /**
2146
     * Escapes regular expression to use in JavaScript
2147
     * @param string $regexp the regular expression to be escaped.
2148
     * @return string the escaped result.
2149
     * @since 2.0.6
2150
     */
2151
    public static function escapeJsRegularExpression($regexp)
2152
    {
2153
        $pattern = preg_replace('/\\\\x\{?([0-9a-fA-F]+)\}?/', '\u$1', $regexp);
2154
        $deliminator = substr($pattern, 0, 1);
2155
        $pos = strrpos($pattern, $deliminator, 1);
2156
        $flag = substr($pattern, $pos + 1);
2157
        if ($deliminator !== '/') {
2158
            $pattern = '/' . str_replace('/', '\\/', substr($pattern, 1, $pos - 1)) . '/';
2159
        } else {
2160
            $pattern = substr($pattern, 0, $pos + 1);
2161
        }
2162
        if (!empty($flag)) {
2163
            $pattern .= preg_replace('/[^igm]/', '', $flag);
2164
        }
2165
2166
        return $pattern;
2167
    }
2168
}
2169