Completed
Push — remove-intl-polyfills ( 129df4...a6e727 )
by Alexander
16:22 queued 12:49
created

BaseHtml   D

Complexity

Total Complexity 249

Size/Duplication

Total Lines 2269
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 99.29%

Importance

Changes 0
Metric Value
wmc 249
lcom 1
cbo 10
dl 0
loc 2269
rs 4.4102
c 0
b 0
f 0
ccs 560
cts 564
cp 0.9929

73 Methods

Rating   Name   Duplication   Size   Complexity  
A encode() 0 4 2
A decode() 0 4 1
A tag() 0 8 4
A beginTag() 0 8 3
A endTag() 0 8 3
A style() 0 4 1
A script() 0 4 1
B cssFile() 0 18 5
A jsFile() 0 11 2
A wrapIntoCondition() 0 8 2
A csrfMetaTags() 0 10 3
C beginForm() 0 45 12
A endForm() 0 4 1
A a() 0 8 2
A mailto() 0 5 2
B img() 0 18 5
A label() 0 5 1
A button() 0 8 2
A submitButton() 0 5 1
A resetButton() 0 5 1
A input() 0 9 3
A buttonInput() 0 6 1
A submitInput() 0 6 1
A resetInput() 0 6 1
A textInput() 0 4 1
A hiddenInput() 0 4 1
A passwordInput() 0 4 1
A fileInput() 0 4 1
A textarea() 0 6 1
A radio() 0 4 1
A checkbox() 0 4 1
A dropDownList() 0 10 2
C listBox() 0 22 8
F checkboxList() 0 49 13
C radioList() 0 39 11
B ul() 0 27 5
A ol() 0 5 1
A activeLabel() 0 7 1
B collectErrors() 0 23 5
A error() 0 13 3
B normalizeMaxLength() 0 13 6
A activeTextInput() 0 5 1
A setActivePlaceholder() 0 7 3
A activeHiddenInput() 0 4 1
A activePasswordInput() 0 5 1
A activeFileInput() 0 15 2
A activeRadio() 0 4 1
A activeCheckbox() 0 4 1
A activeDropDownList() 0 8 2
A activeListBox() 0 4 1
A activeCheckboxList() 0 4 1
A activeRadioList() 0 4 1
D renderTagAttributes() 0 47 16
A addCssClass() 0 13 3
B mergeCssClasses() 0 12 5
B removeCssClass() 0 21 5
B addCssStyle() 0 16 8
A removeCssStyle() 0 10 4
A cssStyleFromArray() 0 9 3
A cssStyleToArray() 0 12 3
A getAttributeName() 0 8 2
C getAttributeValue() 0 33 13
B getInputName() 0 17 5
A getInputId() 0 5 1
A escapeJsRegularExpression() 0 17 3
B booleanInput() 0 25 5
A activeHint() 0 11 2
A errorSummary() 0 18 3
A activeInput() 0 12 2
A activeTextarea() 0 16 3
C activeBooleanInput() 0 27 7
A activeListInput() 0 13 3
F renderSelectOptions() 0 57 16

How to fix   Complexity   

Complex Class

Complex classes like BaseHtml often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BaseHtml, and based on these observations, apply Extract Interface, too.

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

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