Completed
Push — master ( 625d55...2d12e1 )
by Carsten
12:37
created

BaseHtml   D

Complexity

Total Complexity 257

Size/Duplication

Total Lines 2185
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 91.76%

Importance

Changes 0
Metric Value
wmc 257
lcom 1
cbo 10
dl 0
loc 2185
ccs 501
cts 546
cp 0.9176
rs 4.4102
c 0
b 0
f 0

71 Methods

Rating   Name   Duplication   Size   Complexity  
A encode() 0 4 2
A decode() 0 4 1
A tag() 0 8 4
A beginTag() 0 7 3
A endTag() 0 7 3
A style() 0 4 1
A script() 0 4 1
A jsFile() 0 11 2
A wrapIntoCondition() 0 7 2
A csrfMetaTags() 0 10 3
A endForm() 0 4 1
A mailto() 0 5 2
A label() 0 5 1
A submitButton() 0 5 1
A resetButton() 0 5 1
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
A ol() 0 5 1
A activeLabel() 0 7 1
A activeHint() 0 11 3
A error() 0 8 2
A activeTextInput() 0 5 1
A activeHiddenInput() 0 4 1
A activePasswordInput() 0 5 1
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
A getAttributeName() 0 8 2
B getInputName() 0 17 5
A getInputId() 0 5 1
C activeBooleanInput() 0 27 8
D checkboxList() 0 46 12
D errorSummary() 0 36 11
A addCssClass() 0 13 3
B cssFile() 0 18 5
C beginForm() 0 45 12
A a() 0 7 2
B img() 0 17 5
A button() 0 7 2
A input() 0 9 3
B booleanInput() 0 25 6
C listBox() 0 22 8
D radioList() 0 35 10
A activeInput() 0 9 4
B normalizeMaxLength() 0 13 6
A activeFileInput() 0 11 2
A activeTextarea() 0 15 4
B activeListInput() 0 12 5
F renderSelectOptions() 0 53 19
D renderTagAttributes() 0 47 16
B mergeCssClasses() 0 11 5
B removeCssClass() 0 21 5
B addCssStyle() 0 16 8
A removeCssStyle() 0 10 4
A cssStyleFromArray() 0 9 3
A cssStyleToArray() 0 11 3
A escapeJsRegularExpression() 0 17 3
C getAttributeValue() 0 33 13
B ul() 0 27 5

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

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

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

    return array();
}

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

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

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

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

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

An additional type check may prevent trouble.

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

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

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

An additional type check may prevent trouble.

Loading history...
1029 1
            if ($formatter !== null) {
1030 1
                $lines[] = call_user_func($formatter, $index, $label, $name, $checked, $value);
1031
            } else {
1032 1
                $lines[] = static::radio($name, $checked, array_merge($itemOptions, [
1033 1
                    'value' => $value,
1034 1
                    'label' => $encode ? static::encode($label) : $label,
1035
                ]));
1036
            }
1037 1
            $index++;
1038
        }
1039 1
        $visibleContent = implode($separator, $lines);
1040
1041 1
        if ($tag === false) {
1042 1
            return $hidden . $visibleContent;
1043
        }
1044
1045 1
        return $hidden . static::tag($tag, $visibleContent, $options);
1046
    }
1047
1048
    /**
1049
     * Generates an unordered list.
1050
     * @param array|\Traversable $items the items for generating the list. Each item generates a single list item.
1051
     * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true.
1052
     * @param array $options options (name => config) for the radio button list. The following options are supported:
1053
     *
1054
     * - encode: boolean, whether to HTML-encode the items. Defaults to true.
1055
     *   This option is ignored if the `item` option is specified.
1056
     * - separator: string, the HTML code that separates items. Defaults to a simple newline (`"\n"`).
1057
     *   This option is available since version 2.0.7.
1058
     * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified.
1059
     * - item: callable, a callback that is used to generate each individual list item.
1060
     *   The signature of this callback must be:
1061
     *
1062
     *   ```php
1063
     *   function ($item, $index)
1064
     *   ```
1065
     *
1066
     *   where $index is the array key corresponding to `$item` in `$items`. The callback should return
1067
     *   the whole list item tag.
1068
     *
1069
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1070
     *
1071
     * @return string the generated unordered list. An empty list tag will be returned if `$items` is empty.
1072
     */
1073 4
    public static function ul($items, $options = [])
1074
    {
1075 4
        $tag = ArrayHelper::remove($options, 'tag', 'ul');
1076 4
        $encode = ArrayHelper::remove($options, 'encode', true);
1077 4
        $formatter = ArrayHelper::remove($options, 'item');
1078 4
        $separator = ArrayHelper::remove($options, 'separator', "\n");
1079 4
        $itemOptions = ArrayHelper::remove($options, 'itemOptions', []);
1080
1081 4
        if (empty($items)) {
1082 2
            return static::tag($tag, '', $options);
1083
        }
1084
1085 4
        $results = [];
1086 4
        foreach ($items as $index => $item) {
1087 4
            if ($formatter !== null) {
1088 2
                $results[] = call_user_func($formatter, $item, $index);
1089
            } else {
1090 4
                $results[] = static::tag('li', $encode ? static::encode($item) : $item, $itemOptions);
1091
            }
1092
        }
1093
1094 4
        return static::tag(
1095
            $tag,
1096 4
            $separator . implode($separator, $results) . $separator,
1097 4
            $options
1098
        );
1099
    }
1100
1101
    /**
1102
     * Generates an ordered list.
1103
     * @param array|\Traversable $items the items for generating the list. Each item generates a single list item.
1104
     * Note that items will be automatically HTML encoded if `$options['encode']` is not set or true.
1105
     * @param array $options options (name => config) for the radio button list. The following options are supported:
1106
     *
1107
     * - encode: boolean, whether to HTML-encode the items. Defaults to true.
1108
     *   This option is ignored if the `item` option is specified.
1109
     * - itemOptions: array, the HTML attributes for the `li` tags. This option is ignored if the `item` option is specified.
1110
     * - item: callable, a callback that is used to generate each individual list item.
1111
     *   The signature of this callback must be:
1112
     *
1113
     *   ```php
1114
     *   function ($item, $index)
1115
     *   ```
1116
     *
1117
     *   where $index is the array key corresponding to `$item` in `$items`. The callback should return
1118
     *   the whole list item tag.
1119
     *
1120
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1121
     *
1122
     * @return string the generated ordered list. An empty string is returned if `$items` is empty.
1123
     */
1124 1
    public static function ol($items, $options = [])
1125
    {
1126 1
        $options['tag'] = 'ol';
1127 1
        return static::ul($items, $options);
1128
    }
1129
1130
    /**
1131
     * Generates a label tag for the given model attribute.
1132
     * The label text is the label associated with the attribute, obtained via [[Model::getAttributeLabel()]].
1133
     * @param Model $model the model object
1134
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1135
     * about attribute expression.
1136
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1137
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1138
     * If a value is null, the corresponding attribute will not be rendered.
1139
     * The following options are specially handled:
1140
     *
1141
     * - label: this specifies the label to be displayed. Note that this will NOT be [[encode()|encoded]].
1142
     *   If this is not set, [[Model::getAttributeLabel()]] will be called to get the label for display
1143
     *   (after encoding).
1144
     *
1145
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1146
     *
1147
     * @return string the generated label tag
1148
     */
1149 11
    public static function activeLabel($model, $attribute, $options = [])
1150
    {
1151 11
        $for = ArrayHelper::remove($options, 'for', static::getInputId($model, $attribute));
1152 11
        $attribute = static::getAttributeName($attribute);
1153 11
        $label = ArrayHelper::remove($options, 'label', static::encode($model->getAttributeLabel($attribute)));
1154 11
        return static::label($label, $for, $options);
1155
    }
1156
1157
    /**
1158
     * Generates a hint tag for the given model attribute.
1159
     * The hint text is the hint associated with the attribute, obtained via [[Model::getAttributeHint()]].
1160
     * If no hint content can be obtained, method will return an empty string.
1161
     * @param Model $model the model object
1162
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1163
     * about attribute expression.
1164
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1165
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1166
     * If a value is null, the corresponding attribute will not be rendered.
1167
     * The following options are specially handled:
1168
     *
1169
     * - hint: this specifies the hint to be displayed. Note that this will NOT be [[encode()|encoded]].
1170
     *   If this is not set, [[Model::getAttributeHint()]] will be called to get the hint for display
1171
     *   (without encoding).
1172
     *
1173
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1174
     *
1175
     * @return string the generated hint tag
1176
     * @since 2.0.4
1177
     */
1178 11
    public static function activeHint($model, $attribute, $options = [])
1179
    {
1180 11
        $attribute = static::getAttributeName($attribute);
1181 11
        $hint = isset($options['hint']) ? $options['hint'] : $model->getAttributeHint($attribute);
1182 11
        if (empty($hint)) {
1183 3
            return '';
1184
        }
1185 8
        $tag = ArrayHelper::remove($options, 'tag', 'div');
1186 8
        unset($options['hint']);
1187 8
        return static::tag($tag, $hint, $options);
1188
    }
1189
1190
    /**
1191
     * Generates a summary of the validation errors.
1192
     * If there is no validation error, an empty error summary markup will still be generated, but it will be hidden.
1193
     * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.
1194
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
1195
     *
1196
     * - header: string, the header HTML for the error summary. If not set, a default prompt string will be used.
1197
     * - footer: string, the footer HTML for the error summary. Defaults to empty string.
1198
     * - encode: boolean, if set to false then the error messages won't be encoded. Defaults to `true`.
1199
     * - showAllErrors: boolean, if set to true every error message for each attribute will be shown otherwise
1200
     *   only the first error message for each attribute will be shown. Defaults to `false`.
1201
     *   Option is available since 2.0.10.
1202
     *
1203
     * The rest of the options will be rendered as the attributes of the container tag.
1204
     *
1205
     * @return string the generated error summary
1206
     */
1207 7
    public static function errorSummary($models, $options = [])
1208
    {
1209 7
        $header = isset($options['header']) ? $options['header'] : '<p>' . Yii::t('yii', 'Please fix the following errors:') . '</p>';
1210 7
        $footer = ArrayHelper::remove($options, 'footer', '');
1211 7
        $encode = ArrayHelper::remove($options, 'encode', true);
1212 7
        $showAllErrors = ArrayHelper::remove($options, 'showAllErrors', false);
1213 7
        unset($options['header']);
1214
1215 7
        $lines = [];
1216 7
        if (!is_array($models)) {
1217 7
            $models = [$models];
1218
        }
1219 7
        foreach ($models as $model) {
1220
            /* @var $model Model */
1221 7
            foreach ($model->getErrors() as $errors) {
1222 5
                foreach ($errors as $error) {
1223 5
                    $line = $encode ? Html::encode($error) : $error;
1224 5
                    if (!in_array($line, $lines, true)) {
1225 5
                        $lines[] = $line;
1226
                    }
1227 5
                    if (!$showAllErrors) {
1228 7
                        break;
1229
                    }
1230
                }
1231
            }
1232
        }
1233
1234 7
        if (empty($lines)) {
1235
            // still render the placeholder for client-side validation use
1236 2
            $content = '<ul></ul>';
1237 2
            $options['style'] = isset($options['style']) ? rtrim($options['style'], ';') . '; display:none' : 'display:none';
1238
        } else {
1239 5
            $content = '<ul><li>' . implode("</li>\n<li>", $lines) . '</li></ul>';
1240
        }
1241 7
        return Html::tag('div', $header . $content . $footer, $options);
1242
    }
1243
1244
    /**
1245
     * Generates a tag that contains the first validation error of the specified model attribute.
1246
     * Note that even if there is no validation error, this method will still return an empty error tag.
1247
     * @param Model $model the model object
1248
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1249
     * about attribute expression.
1250
     * @param array $options the tag options in terms of name-value pairs. The values will be HTML-encoded
1251
     * using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1252
     *
1253
     * The following options are specially handled:
1254
     *
1255
     * - tag: this specifies the tag name. If not set, "div" will be used.
1256
     *   See also [[tag()]].
1257
     * - encode: boolean, if set to false then the error message won't be encoded.
1258
     *
1259
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1260
     *
1261
     * @return string the generated label tag
1262
     */
1263 9
    public static function error($model, $attribute, $options = [])
1264
    {
1265 9
        $attribute = static::getAttributeName($attribute);
1266 9
        $error = $model->getFirstError($attribute);
1267 9
        $tag = ArrayHelper::remove($options, 'tag', 'div');
1268 9
        $encode = ArrayHelper::remove($options, 'encode', true);
1269 9
        return Html::tag($tag, $encode ? Html::encode($error) : $error, $options);
1270
    }
1271
1272
    /**
1273
     * Generates an input tag for the given model attribute.
1274
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1275
     * unless they are explicitly specified in `$options`.
1276
     * @param string $type the input type (e.g. 'text', 'password')
1277
     * @param Model $model the model object
1278
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1279
     * about attribute expression.
1280
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1281
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1282
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1283
     * @return string the generated input tag
1284
     */
1285 20
    public static function activeInput($type, $model, $attribute, $options = [])
1286
    {
1287 20
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1288 20
        $value = isset($options['value']) ? $options['value'] : static::getAttributeValue($model, $attribute);
1289 20
        if (!array_key_exists('id', $options)) {
1290 18
            $options['id'] = static::getInputId($model, $attribute);
1291
        }
1292 20
        return static::input($type, $name, $value, $options);
1293
    }
1294
1295
    /**
1296
     * If `maxlength` option is set true and the model attribute is validated by a string validator,
1297
     * the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
1298
     * @param Model $model the model object
1299
     * @param string $attribute the attribute name or expression.
1300
     * @param array $options the tag options in terms of name-value pairs.
1301
     */
1302 19
    private static function normalizeMaxLength($model, $attribute, &$options)
1303
    {
1304 19
        if (isset($options['maxlength']) && $options['maxlength'] === true) {
1305 3
            unset($options['maxlength']);
1306 3
            $attrName = static::getAttributeName($attribute);
1307 3
            foreach ($model->getActiveValidators($attrName) as $validator) {
1308 3
                if ($validator instanceof StringValidator && $validator->max !== null) {
1309 3
                    $options['maxlength'] = $validator->max;
1310 3
                    break;
1311
                }
1312
            }
1313
        }
1314 19
    }
1315
1316
    /**
1317
     * Generates a text input tag for the given model attribute.
1318
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1319
     * unless they are explicitly specified in `$options`.
1320
     * @param Model $model the model object
1321
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1322
     * about attribute expression.
1323
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1324
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1325
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1326
     * The following special options are recognized:
1327
     *
1328
     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
1329
     *   by a string validator, the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
1330
     *   This is available since version 2.0.3.
1331
     *
1332
     * @return string the generated input tag
1333
     */
1334 12
    public static function activeTextInput($model, $attribute, $options = [])
1335
    {
1336 12
        self::normalizeMaxLength($model, $attribute, $options);
1337 12
        return static::activeInput('text', $model, $attribute, $options);
1338
    }
1339
1340
    /**
1341
     * Generates a hidden input tag for the given model attribute.
1342
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1343
     * unless they are explicitly specified in `$options`.
1344
     * @param Model $model the model object
1345
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1346
     * about attribute expression.
1347
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1348
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1349
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1350
     * @return string the generated input tag
1351
     */
1352 3
    public static function activeHiddenInput($model, $attribute, $options = [])
1353
    {
1354 3
        return static::activeInput('hidden', $model, $attribute, $options);
1355
    }
1356
1357
    /**
1358
     * Generates a password input tag for the given model attribute.
1359
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1360
     * unless they are explicitly specified in `$options`.
1361
     * @param Model $model the model object
1362
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1363
     * about attribute expression.
1364
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1365
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1366
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1367
     * The following special options are recognized:
1368
     *
1369
     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
1370
     *   by a string validator, the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
1371
     *   This option is available since version 2.0.6.
1372
     *
1373
     * @return string the generated input tag
1374
     */
1375 3
    public static function activePasswordInput($model, $attribute, $options = [])
1376
    {
1377 3
        self::normalizeMaxLength($model, $attribute, $options);
1378 3
        return static::activeInput('password', $model, $attribute, $options);
1379
    }
1380
1381
    /**
1382
     * Generates a file input tag for the given model attribute.
1383
     * This method will generate the "name" and "value" tag attributes automatically for the model attribute
1384
     * unless they are explicitly specified in `$options`.
1385
     * @param Model $model the model object
1386
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1387
     * about attribute expression.
1388
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1389
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1390
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1391
     * @return string the generated input tag
1392
     */
1393 1
    public static function activeFileInput($model, $attribute, $options = [])
1394
    {
1395
        // add a hidden field so that if a model only has a file field, we can
1396
        // still use isset($_POST[$modelClass]) to detect if the input is submitted
1397 1
        $hiddenOptions = ['id' => null, 'value' => ''];
1398 1
        if (isset($options['name'])) {
1399
            $hiddenOptions['name'] = $options['name'];
1400
        }
1401 1
        return static::activeHiddenInput($model, $attribute, $hiddenOptions)
1402 1
            . static::activeInput('file', $model, $attribute, $options);
1403
    }
1404
1405
    /**
1406
     * Generates a textarea tag for the given model attribute.
1407
     * The model attribute value will be used as the content in the textarea.
1408
     * @param Model $model the model object
1409
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1410
     * about attribute expression.
1411
     * @param array $options the tag options in terms of name-value pairs. These will be rendered as
1412
     * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
1413
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1414
     * The following special options are recognized:
1415
     *
1416
     * - maxlength: integer|boolean, when `maxlength` is set true and the model attribute is validated
1417
     *   by a string validator, the `maxlength` option will take the value of [[\yii\validators\StringValidator::max]].
1418
     *   This option is available since version 2.0.6.
1419
     *
1420
     * @return string the generated textarea tag
1421
     */
1422 4
    public static function activeTextarea($model, $attribute, $options = [])
1423
    {
1424 4
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1425 4
        if (isset($options['value'])) {
1426 1
            $value = $options['value'];
1427 1
            unset($options['value']);
1428
        } else {
1429 3
            $value = static::getAttributeValue($model, $attribute);
1430
        }
1431 4
        if (!array_key_exists('id', $options)) {
1432 4
            $options['id'] = static::getInputId($model, $attribute);
1433
        }
1434 4
        self::normalizeMaxLength($model, $attribute, $options);
1435 4
        return static::textarea($name, $value, $options);
1436
    }
1437
1438
    /**
1439
     * Generates a radio button tag together with a label for the given model attribute.
1440
     * This method will generate the "checked" tag attribute according to the model attribute value.
1441
     * @param Model $model the model object
1442
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1443
     * about attribute expression.
1444
     * @param array $options the tag options in terms of name-value pairs.
1445
     * See [[booleanInput()]] for details about accepted attributes.
1446
     *
1447
     * @return string the generated radio button tag
1448
     */
1449 4
    public static function activeRadio($model, $attribute, $options = [])
1450
    {
1451 4
        return static::activeBooleanInput('radio', $model, $attribute, $options);
1452
    }
1453
1454
    /**
1455
     * Generates a checkbox tag together with a label for the given model attribute.
1456
     * This method will generate the "checked" tag attribute according to the model attribute value.
1457
     * @param Model $model the model object
1458
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1459
     * about attribute expression.
1460
     * @param array $options the tag options in terms of name-value pairs.
1461
     * See [[booleanInput()]] for details about accepted attributes.
1462
     *
1463
     * @return string the generated checkbox tag
1464
     */
1465 4
    public static function activeCheckbox($model, $attribute, $options = [])
1466
    {
1467 4
        return static::activeBooleanInput('checkbox', $model, $attribute, $options);
1468
    }
1469
1470
    /**
1471
     * Generates a boolean input
1472
     * This method is mainly called by [[activeCheckbox()]] and [[activeRadio()]].
1473
     * @param string $type the input type. This can be either `radio` or `checkbox`.
1474
     * @param Model $model the model object
1475
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1476
     * about attribute expression.
1477
     * @param array $options the tag options in terms of name-value pairs.
1478
     * See [[booleanInput()]] for details about accepted attributes.
1479
     * @return string the generated input element
1480
     * @since 2.0.9
1481
     */
1482 8
    protected static function activeBooleanInput($type, $model, $attribute, $options = [])
1483
    {
1484 8
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1485 8
        $value = static::getAttributeValue($model, $attribute);
1486
1487 8
        if (!array_key_exists('value', $options)) {
1488 8
            $options['value'] = '1';
1489
        }
1490 8
        if (!array_key_exists('uncheck', $options)) {
1491 4
            $options['uncheck'] = '0';
1492 4
        } elseif ($options['uncheck'] === false) {
1493 4
            unset($options['uncheck']);
1494
        }
1495 8
        if (!array_key_exists('label', $options)) {
1496 4
            $options['label'] = static::encode($model->getAttributeLabel(static::getAttributeName($attribute)));
1497 4
        } elseif ($options['label'] === false) {
1498 4
            unset($options['label']);
1499
        }
1500
1501 8
        $checked = "$value" === "{$options['value']}";
1502
1503 8
        if (!array_key_exists('id', $options)) {
1504 8
            $options['id'] = static::getInputId($model, $attribute);
1505
        }
1506
1507 8
        return static::$type($name, $checked, $options);
1508
    }
1509
1510
    /**
1511
     * Generates a drop-down list for the given model attribute.
1512
     * The selection of the drop-down list is taken from the value of the model attribute.
1513
     * @param Model $model the model object
1514
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1515
     * about attribute expression.
1516
     * @param array $items the option data items. The array keys are option values, and the array values
1517
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
1518
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
1519
     * If you have a list of data models, you may convert them into the format described above using
1520
     * [[\yii\helpers\ArrayHelper::map()]].
1521
     *
1522
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
1523
     * the labels will also be HTML-encoded.
1524
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
1525
     *
1526
     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array
1527
     *   to override the value and to set other tag attributes:
1528
     *
1529
     *   ```php
1530
     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],
1531
     *   ```
1532
     *
1533
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
1534
     *   and the array values are the extra attributes for the corresponding option tags. For example,
1535
     *
1536
     *   ```php
1537
     *   [
1538
     *       'value1' => ['disabled' => true],
1539
     *       'value2' => ['label' => 'value 2'],
1540
     *   ];
1541
     *   ```
1542
     *
1543
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
1544
     *   except that the array keys represent the optgroup labels specified in $items.
1545
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
1546
     *   Defaults to false.
1547
     * - encode: bool, whether to encode option prompt and option value characters.
1548
     *   Defaults to `true`. This option is available since 2.0.3.
1549
     *
1550
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
1551
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1552
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1553
     *
1554
     * @return string the generated drop-down list tag
1555
     */
1556 2
    public static function activeDropDownList($model, $attribute, $items, $options = [])
1557
    {
1558 2
        if (empty($options['multiple'])) {
1559 2
            return static::activeListInput('dropDownList', $model, $attribute, $items, $options);
1560
        } else {
1561
            return static::activeListBox($model, $attribute, $items, $options);
1562
        }
1563
    }
1564
1565
    /**
1566
     * Generates a list box.
1567
     * The selection of the list box is taken from the value of the model attribute.
1568
     * @param Model $model the model object
1569
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1570
     * about attribute expression.
1571
     * @param array $items the option data items. The array keys are option values, and the array values
1572
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
1573
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
1574
     * If you have a list of data models, you may convert them into the format described above using
1575
     * [[\yii\helpers\ArrayHelper::map()]].
1576
     *
1577
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
1578
     * the labels will also be HTML-encoded.
1579
     * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
1580
     *
1581
     * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array
1582
     *   to override the value and to set other tag attributes:
1583
     *
1584
     *   ```php
1585
     *   ['text' => 'Please select', 'options' => ['value' => 'none', 'class' => 'prompt', 'label' => 'Select']],
1586
     *   ```
1587
     *
1588
     * - options: array, the attributes for the select option tags. The array keys must be valid option values,
1589
     *   and the array values are the extra attributes for the corresponding option tags. For example,
1590
     *
1591
     *   ```php
1592
     *   [
1593
     *       'value1' => ['disabled' => true],
1594
     *       'value2' => ['label' => 'value 2'],
1595
     *   ];
1596
     *   ```
1597
     *
1598
     * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
1599
     *   except that the array keys represent the optgroup labels specified in $items.
1600
     * - unselect: string, the value that will be submitted when no option is selected.
1601
     *   When this attribute is set, a hidden field will be generated so that if no option is selected in multiple
1602
     *   mode, we can still obtain the posted unselect value.
1603
     * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with `&nbsp;` character.
1604
     *   Defaults to false.
1605
     * - encode: bool, whether to encode option prompt and option value characters.
1606
     *   Defaults to `true`. This option is available since 2.0.3.
1607
     *
1608
     * The rest of the options will be rendered as the attributes of the resulting tag. The values will
1609
     * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
1610
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1611
     *
1612
     * @return string the generated list box tag
1613
     */
1614 2
    public static function activeListBox($model, $attribute, $items, $options = [])
1615
    {
1616 2
        return static::activeListInput('listBox', $model, $attribute, $items, $options);
1617
    }
1618
1619
    /**
1620
     * Generates a list of checkboxes.
1621
     * A checkbox list allows multiple selection, like [[listBox()]].
1622
     * As a result, the corresponding submitted value is an array.
1623
     * The selection of the checkbox list is taken from the value of the model attribute.
1624
     * @param Model $model the model object
1625
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1626
     * about attribute expression.
1627
     * @param array $items the data item used to generate the checkboxes.
1628
     * The array keys are the checkbox values, and the array values are the corresponding labels.
1629
     * Note that the labels will NOT be HTML-encoded, while the values will.
1630
     * @param array $options options (name => config) for the checkbox list container tag.
1631
     * The following options are specially handled:
1632
     *
1633
     * - tag: string|false, the tag name of the container element. False to render checkbox without container.
1634
     *   See also [[tag()]].
1635
     * - unselect: string, the value that should be submitted when none of the checkboxes is selected.
1636
     *   You may set this option to be null to prevent default value submission.
1637
     *   If this option is not set, an empty string will be submitted.
1638
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
1639
     *   This option is ignored if `item` option is set.
1640
     * - separator: string, the HTML code that separates items.
1641
     * - itemOptions: array, the options for generating the checkbox tag using [[checkbox()]].
1642
     * - item: callable, a callback that can be used to customize the generation of the HTML code
1643
     *   corresponding to a single item in $items. The signature of this callback must be:
1644
     *
1645
     *   ```php
1646
     *   function ($index, $label, $name, $checked, $value)
1647
     *   ```
1648
     *
1649
     *   where $index is the zero-based index of the checkbox in the whole list; $label
1650
     *   is the label for the checkbox; and $name, $value and $checked represent the name,
1651
     *   value and the checked status of the checkbox input.
1652
     *
1653
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1654
     *
1655
     * @return string the generated checkbox list
1656
     */
1657
    public static function activeCheckboxList($model, $attribute, $items, $options = [])
1658
    {
1659
        return static::activeListInput('checkboxList', $model, $attribute, $items, $options);
1660
    }
1661
1662
    /**
1663
     * Generates a list of radio buttons.
1664
     * A radio button list is like a checkbox list, except that it only allows single selection.
1665
     * The selection of the radio buttons is taken from the value of the model attribute.
1666
     * @param Model $model the model object
1667
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1668
     * about attribute expression.
1669
     * @param array $items the data item used to generate the radio buttons.
1670
     * The array keys are the radio values, and the array values are the corresponding labels.
1671
     * Note that the labels will NOT be HTML-encoded, while the values will.
1672
     * @param array $options options (name => config) for the radio button list container tag.
1673
     * The following options are specially handled:
1674
     *
1675
     * - tag: string|false, the tag name of the container element. False to render radio button without container.
1676
     *   See also [[tag()]].
1677
     * - unselect: string, the value that should be submitted when none of the radio buttons is selected.
1678
     *   You may set this option to be null to prevent default value submission.
1679
     *   If this option is not set, an empty string will be submitted.
1680
     * - encode: boolean, whether to HTML-encode the checkbox labels. Defaults to true.
1681
     *   This option is ignored if `item` option is set.
1682
     * - separator: string, the HTML code that separates items.
1683
     * - itemOptions: array, the options for generating the radio button tag using [[radio()]].
1684
     * - item: callable, a callback that can be used to customize the generation of the HTML code
1685
     *   corresponding to a single item in $items. The signature of this callback must be:
1686
     *
1687
     *   ```php
1688
     *   function ($index, $label, $name, $checked, $value)
1689
     *   ```
1690
     *
1691
     *   where $index is the zero-based index of the radio button in the whole list; $label
1692
     *   is the label for the radio button; and $name, $value and $checked represent the name,
1693
     *   value and the checked status of the radio button input.
1694
     *
1695
     * See [[renderTagAttributes()]] for details on how attributes are being rendered.
1696
     *
1697
     * @return string the generated radio button list
1698
     */
1699
    public static function activeRadioList($model, $attribute, $items, $options = [])
1700
    {
1701
        return static::activeListInput('radioList', $model, $attribute, $items, $options);
1702
    }
1703
1704
    /**
1705
     * Generates a list of input fields.
1706
     * This method is mainly called by [[activeListBox()]], [[activeRadioList()]] and [[activeCheckboxList()]].
1707
     * @param string $type the input type. This can be 'listBox', 'radioList', or 'checkBoxList'.
1708
     * @param Model $model the model object
1709
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for the format
1710
     * about attribute expression.
1711
     * @param array $items the data item used to generate the input fields.
1712
     * The array keys are the input values, and the array values are the corresponding labels.
1713
     * Note that the labels will NOT be HTML-encoded, while the values will.
1714
     * @param array $options options (name => config) for the input list. The supported special options
1715
     * depend on the input type specified by `$type`.
1716
     * @return string the generated input list
1717
     */
1718 4
    protected static function activeListInput($type, $model, $attribute, $items, $options = [])
1719
    {
1720 4
        $name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
1721 4
        $selection = isset($options['value']) ? $options['value'] : static::getAttributeValue($model, $attribute);
1722 4
        if (!array_key_exists('unselect', $options)) {
1723 4
            $options['unselect'] = '';
1724
        }
1725 4
        if (!array_key_exists('id', $options)) {
1726 2
            $options['id'] = static::getInputId($model, $attribute);
1727
        }
1728 4
        return static::$type($name, $selection, $items, $options);
1729
    }
1730
1731
    /**
1732
     * Renders the option tags that can be used by [[dropDownList()]] and [[listBox()]].
1733
     * @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s).
1734
     * @param array $items the option data items. The array keys are option values, and the array values
1735
     * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
1736
     * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
1737
     * If you have a list of data models, you may convert them into the format described above using
1738
     * [[\yii\helpers\ArrayHelper::map()]].
1739
     *
1740
     * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
1741
     * the labels will also be HTML-encoded.
1742
     * @param array $tagOptions the $options parameter that is passed to the [[dropDownList()]] or [[listBox()]] call.
1743
     * This method will take out these elements, if any: "prompt", "options" and "groups". See more details
1744
     * in [[dropDownList()]] for the explanation of these elements.
1745
     *
1746
     * @return string the generated list options
1747
     */
1748 7
    public static function renderSelectOptions($selection, $items, &$tagOptions = [])
1749
    {
1750 7
        $lines = [];
1751 7
        $encodeSpaces = ArrayHelper::remove($tagOptions, 'encodeSpaces', false);
1752 7
        $encode = ArrayHelper::remove($tagOptions, 'encode', true);
1753 7
        if (isset($tagOptions['prompt'])) {
1754 3
            $promptOptions = ['value' => ''];
1755 3
            if (is_string($tagOptions['prompt'])) {
1756 3
                $promptText = $tagOptions['prompt'];
1757
            } else {
1758 1
                $promptText = $tagOptions['prompt']['text'];
1759 1
                $promptOptions = array_merge($promptOptions, $tagOptions['prompt']['options']);
1760
            }
1761 3
            $promptText = $encode ? static::encode($promptText) : $promptText;
1762 3
            if ($encodeSpaces) {
1763 1
                $promptText = str_replace(' ', '&nbsp;', $promptText);
1764
            }
1765 3
            $lines[] = static::tag('option', $promptText, $promptOptions);
1766
        }
1767
1768 7
        $options = isset($tagOptions['options']) ? $tagOptions['options'] : [];
1769 7
        $groups = isset($tagOptions['groups']) ? $tagOptions['groups'] : [];
1770 7
        unset($tagOptions['prompt'], $tagOptions['options'], $tagOptions['groups']);
1771 7
        $options['encodeSpaces'] = ArrayHelper::getValue($options, 'encodeSpaces', $encodeSpaces);
1772 7
        $options['encode'] = ArrayHelper::getValue($options, 'encode', $encode);
1773
1774 7
        foreach ($items as $key => $value) {
1775 7
            if (is_array($value)) {
1776 1
                $groupAttrs = isset($groups[$key]) ? $groups[$key] : [];
1777 1
                if (!isset($groupAttrs['label'])) {
1778 1
                    $groupAttrs['label'] = $key;
1779
                }
1780 1
                $attrs = ['options' => $options, 'groups' => $groups, 'encodeSpaces' => $encodeSpaces, 'encode' => $encode];
1781 1
                $content = static::renderSelectOptions($selection, $value, $attrs);
1782 1
                $lines[] = static::tag('optgroup', "\n" . $content . "\n", $groupAttrs);
1783
            } else {
1784 7
                $attrs = isset($options[$key]) ? $options[$key] : [];
1785 7
                $attrs['value'] = (string) $key;
1786 7
                if (!array_key_exists('selected', $attrs)) {
1787 7
                    $attrs['selected'] = $selection !== null &&
1788 5
                        (!ArrayHelper::isTraversable($selection) && !strcmp($key, $selection)
1789 5
                        || ArrayHelper::isTraversable($selection) && ArrayHelper::isIn($key, $selection));
0 ignored issues
show
Bug introduced by
It seems like $selection defined by parameter $selection on line 1748 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...
1790
                }
1791 7
                $text = $encode ? static::encode($value) : $value;
1792 7
                if ($encodeSpaces) {
1793 2
                    $text = str_replace(' ', '&nbsp;', $text);
1794
                }
1795 7
                $lines[] = static::tag('option', $text, $attrs);
1796
            }
1797
        }
1798
1799 7
        return implode("\n", $lines);
1800
    }
1801
1802
    /**
1803
     * Renders the HTML tag attributes.
1804
     *
1805
     * Attributes whose values are of boolean type will be treated as
1806
     * [boolean attributes](http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes).
1807
     *
1808
     * Attributes whose values are null will not be rendered.
1809
     *
1810
     * The values of attributes will be HTML-encoded using [[encode()]].
1811
     *
1812
     * The "data" attribute is specially handled when it is receiving an array value. In this case,
1813
     * the array will be "expanded" and a list data attributes will be rendered. For example,
1814
     * if `'data' => ['id' => 1, 'name' => 'yii']`, then this will be rendered:
1815
     * `data-id="1" data-name="yii"`.
1816
     * Additionally `'data' => ['params' => ['id' => 1, 'name' => 'yii'], 'status' => 'ok']` will be rendered as:
1817
     * `data-params='{"id":1,"name":"yii"}' data-status="ok"`.
1818
     *
1819
     * @param array $attributes attributes to be rendered. The attribute values will be HTML-encoded using [[encode()]].
1820
     * @return string the rendering result. If the attributes are not empty, they will be rendered
1821
     * into a string with a leading white space (so that it can be directly appended to the tag name
1822
     * in a tag. If there is no attribute, an empty string will be returned.
1823
     */
1824 186
    public static function renderTagAttributes($attributes)
1825
    {
1826 186
        if (count($attributes) > 1) {
1827 137
            $sorted = [];
1828 137
            foreach (static::$attributeOrder as $name) {
1829 137
                if (isset($attributes[$name])) {
1830 137
                    $sorted[$name] = $attributes[$name];
1831
                }
1832
            }
1833 137
            $attributes = array_merge($sorted, $attributes);
1834
        }
1835
1836 186
        $html = '';
1837 186
        foreach ($attributes as $name => $value) {
1838 178
            if (is_bool($value)) {
1839 30
                if ($value) {
1840 24
                    $html .= " $name";
1841
                }
1842 178
            } elseif (is_array($value)) {
1843 10
                if (in_array($name, static::$dataAttributes)) {
1844 2
                    foreach ($value as $n => $v) {
1845 2
                        if (is_array($v)) {
1846
                            $html .= " $name-$n='" . Json::htmlEncode($v) . "'";
1847
                        } else {
1848 2
                            $html .= " $name-$n=\"" . static::encode($v) . '"';
1849
                        }
1850
                    }
1851 9
                } elseif ($name === 'class') {
1852 8
                    if (empty($value)) {
1853 8
                        continue;
1854
                    }
1855 3
                    $html .= " $name=\"" . static::encode(implode(' ', $value)) . '"';
1856 2
                } elseif ($name === 'style') {
1857 1
                    if (empty($value)) {
1858 1
                        continue;
1859
                    }
1860 1
                    $html .= " $name=\"" . static::encode(static::cssStyleFromArray($value)) . '"';
1861
                } else {
1862 1
                    $html .= " $name='" . Json::htmlEncode($value) . "'";
1863
                }
1864 172
            } elseif ($value !== null) {
1865 172
                $html .= " $name=\"" . static::encode($value) . '"';
1866
            }
1867
        }
1868
1869 186
        return $html;
1870
    }
1871
1872
    /**
1873
     * Adds a CSS class (or several classes) to the specified options.
1874
     * If the CSS class is already in the options, it will not be added again.
1875
     * If class specification at given options is an array, and some class placed there with the named (string) key,
1876
     * overriding of such key will have no effect. For example:
1877
     *
1878
     * ```php
1879
     * $options = ['class' => ['persistent' => 'initial']];
1880
     * Html::addCssClass($options, ['persistent' => 'override']);
1881
     * var_dump($options['class']); // outputs: array('persistent' => 'initial');
1882
     * ```
1883
     *
1884
     * @param array $options the options to be modified.
1885
     * @param string|array $class the CSS class(es) to be added
1886
     */
1887 13
    public static function addCssClass(&$options, $class)
1888
    {
1889 13
        if (isset($options['class'])) {
1890 7
            if (is_array($options['class'])) {
1891 3
                $options['class'] = self::mergeCssClasses($options['class'], (array) $class);
1892
            } else {
1893 5
                $classes = preg_split('/\s+/', $options['class'], -1, PREG_SPLIT_NO_EMPTY);
1894 5
                $options['class'] = implode(' ', self::mergeCssClasses($classes, (array) $class));
1895
            }
1896
        } else {
1897 12
            $options['class'] = $class;
1898
        }
1899 13
    }
1900
1901
    /**
1902
     * Merges already existing CSS classes with new one.
1903
     * This method provides the priority for named existing classes over additional.
1904
     * @param array $existingClasses already existing CSS classes.
1905
     * @param array $additionalClasses CSS classes to be added.
1906
     * @return array merge result.
1907
     */
1908 7
    private static function mergeCssClasses(array $existingClasses, array $additionalClasses)
1909
    {
1910 7
        foreach ($additionalClasses as $key => $class) {
1911 7
            if (is_int($key) && !in_array($class, $existingClasses)) {
1912 6
                $existingClasses[] = $class;
1913 2
            } elseif (!isset($existingClasses[$key])) {
1914 1
                $existingClasses[$key] = $class;
1915
            }
1916
        }
1917 7
        return array_unique($existingClasses);
1918
    }
1919
1920
    /**
1921
     * Removes a CSS class from the specified options.
1922
     * @param array $options the options to be modified.
1923
     * @param string|array $class the CSS class(es) to be removed
1924
     */
1925 1
    public static function removeCssClass(&$options, $class)
1926
    {
1927 1
        if (isset($options['class'])) {
1928 1
            if (is_array($options['class'])) {
1929 1
                $classes = array_diff($options['class'], (array) $class);
1930 1
                if (empty($classes)) {
1931 1
                    unset($options['class']);
1932
                } else {
1933 1
                    $options['class'] = $classes;
1934
                }
1935
            } else {
1936 1
                $classes = preg_split('/\s+/', $options['class'], -1, PREG_SPLIT_NO_EMPTY);
1937 1
                $classes = array_diff($classes, (array) $class);
1938 1
                if (empty($classes)) {
1939 1
                    unset($options['class']);
1940
                } else {
1941 1
                    $options['class'] = implode(' ', $classes);
1942
                }
1943
            }
1944
        }
1945 1
    }
1946
1947
    /**
1948
     * Adds the specified CSS style to the HTML options.
1949
     *
1950
     * If the options already contain a `style` element, the new style will be merged
1951
     * with the existing one. If a CSS property exists in both the new and the old styles,
1952
     * the old one may be overwritten if `$overwrite` is true.
1953
     *
1954
     * For example,
1955
     *
1956
     * ```php
1957
     * Html::addCssStyle($options, 'width: 100px; height: 200px');
1958
     * ```
1959
     *
1960
     * @param array $options the HTML options to be modified.
1961
     * @param string|array $style the new style string (e.g. `'width: 100px; height: 200px'`) or
1962
     * array (e.g. `['width' => '100px', 'height' => '200px']`).
1963
     * @param bool $overwrite whether to overwrite existing CSS properties if the new style
1964
     * contain them too.
1965
     * @see removeCssStyle()
1966
     * @see cssStyleFromArray()
1967
     * @see cssStyleToArray()
1968
     */
1969 1
    public static function addCssStyle(&$options, $style, $overwrite = true)
1970
    {
1971 1
        if (!empty($options['style'])) {
1972 1
            $oldStyle = is_array($options['style']) ? $options['style'] : static::cssStyleToArray($options['style']);
1973 1
            $newStyle = is_array($style) ? $style : static::cssStyleToArray($style);
1974 1
            if (!$overwrite) {
1975 1
                foreach ($newStyle as $property => $value) {
1976 1
                    if (isset($oldStyle[$property])) {
1977 1
                        unset($newStyle[$property]);
1978
                    }
1979
                }
1980
            }
1981 1
            $style = array_merge($oldStyle, $newStyle);
1982
        }
1983 1
        $options['style'] = is_array($style) ? static::cssStyleFromArray($style) : $style;
1984 1
    }
1985
1986
    /**
1987
     * Removes the specified CSS style from the HTML options.
1988
     *
1989
     * For example,
1990
     *
1991
     * ```php
1992
     * Html::removeCssStyle($options, ['width', 'height']);
1993
     * ```
1994
     *
1995
     * @param array $options the HTML options to be modified.
1996
     * @param string|array $properties the CSS properties to be removed. You may use a string
1997
     * if you are removing a single property.
1998
     * @see addCssStyle()
1999
     */
2000 1
    public static function removeCssStyle(&$options, $properties)
2001
    {
2002 1
        if (!empty($options['style'])) {
2003 1
            $style = is_array($options['style']) ? $options['style'] : static::cssStyleToArray($options['style']);
2004 1
            foreach ((array) $properties as $property) {
2005 1
                unset($style[$property]);
2006
            }
2007 1
            $options['style'] = static::cssStyleFromArray($style);
2008
        }
2009 1
    }
2010
2011
    /**
2012
     * Converts a CSS style array into a string representation.
2013
     *
2014
     * For example,
2015
     *
2016
     * ```php
2017
     * print_r(Html::cssStyleFromArray(['width' => '100px', 'height' => '200px']));
2018
     * // will display: 'width: 100px; height: 200px;'
2019
     * ```
2020
     *
2021
     * @param array $style the CSS style array. The array keys are the CSS property names,
2022
     * and the array values are the corresponding CSS property values.
2023
     * @return string the CSS style string. If the CSS style is empty, a null will be returned.
2024
     */
2025 4
    public static function cssStyleFromArray(array $style)
2026
    {
2027 4
        $result = '';
2028 4
        foreach ($style as $name => $value) {
2029 4
            $result .= "$name: $value; ";
2030
        }
2031
        // return null if empty to avoid rendering the "style" attribute
2032 4
        return $result === '' ? null : rtrim($result);
2033
    }
2034
2035
    /**
2036
     * Converts a CSS style string into an array representation.
2037
     *
2038
     * The array keys are the CSS property names, and the array values
2039
     * are the corresponding CSS property values.
2040
     *
2041
     * For example,
2042
     *
2043
     * ```php
2044
     * print_r(Html::cssStyleToArray('width: 100px; height: 200px;'));
2045
     * // will display: ['width' => '100px', 'height' => '200px']
2046
     * ```
2047
     *
2048
     * @param string $style the CSS style string
2049
     * @return array the array representation of the CSS style
2050
     */
2051 3
    public static function cssStyleToArray($style)
2052
    {
2053 3
        $result = [];
2054 3
        foreach (explode(';', $style) as $property) {
2055 3
            $property = explode(':', $property);
2056 3
            if (count($property) > 1) {
2057 3
                $result[trim($property[0])] = trim($property[1]);
2058
            }
2059
        }
2060 3
        return $result;
2061
    }
2062
2063
    /**
2064
     * Returns the real attribute name from the given attribute expression.
2065
     *
2066
     * An attribute expression is an attribute name prefixed and/or suffixed with array indexes.
2067
     * It is mainly used in tabular data input and/or input of array type. Below are some examples:
2068
     *
2069
     * - `[0]content` is used in tabular data input to represent the "content" attribute
2070
     *   for the first model in tabular input;
2071
     * - `dates[0]` represents the first array element of the "dates" attribute;
2072
     * - `[0]dates[0]` represents the first array element of the "dates" attribute
2073
     *   for the first model in tabular input.
2074
     *
2075
     * If `$attribute` has neither prefix nor suffix, it will be returned back without change.
2076
     * @param string $attribute the attribute name or expression
2077
     * @return string the attribute name without prefix and suffix.
2078
     * @throws InvalidParamException if the attribute name contains non-word characters.
2079
     */
2080 47
    public static function getAttributeName($attribute)
2081
    {
2082 47
        if (preg_match(static::$attributeRegex, $attribute, $matches)) {
2083 44
            return $matches[2];
2084
        } else {
2085 3
            throw new InvalidParamException('Attribute name must contain word characters only.');
2086
        }
2087
    }
2088
2089
    /**
2090
     * Returns the value of the specified attribute name or expression.
2091
     *
2092
     * For an attribute expression like `[0]dates[0]`, this method will return the value of `$model->dates[0]`.
2093
     * See [[getAttributeName()]] for more details about attribute expression.
2094
     *
2095
     * If an attribute value is an instance of [[ActiveRecordInterface]] or an array of such instances,
2096
     * the primary value(s) of the AR instance(s) will be returned instead.
2097
     *
2098
     * @param Model $model the model object
2099
     * @param string $attribute the attribute name or expression
2100
     * @return string|array the corresponding attribute value
2101
     * @throws InvalidParamException if the attribute name contains non-word characters.
2102
     */
2103 35
    public static function getAttributeValue($model, $attribute)
2104
    {
2105 35
        if (!preg_match(static::$attributeRegex, $attribute, $matches)) {
2106
            throw new InvalidParamException('Attribute name must contain word characters only.');
2107
        }
2108 35
        $attribute = $matches[2];
2109 35
        $value = $model->$attribute;
2110 35
        if ($matches[3] !== '') {
2111
            foreach (explode('][', trim($matches[3], '[]')) as $id) {
2112
                if ((is_array($value) || $value instanceof \ArrayAccess) && isset($value[$id])) {
2113
                    $value = $value[$id];
2114
                } else {
2115
                    return null;
2116
                }
2117
            }
2118
        }
2119
2120
        // https://github.com/yiisoft/yii2/issues/1457
2121 35
        if (is_array($value)) {
2122
            foreach ($value as $i => $v) {
2123
                if ($v instanceof ActiveRecordInterface) {
2124
                    $v = $v->getPrimaryKey(false);
2125
                    $value[$i] = is_array($v) ? json_encode($v) : $v;
2126
                }
2127
            }
2128 35
        } elseif ($value instanceof ActiveRecordInterface) {
2129
            $value = $value->getPrimaryKey(false);
2130
2131
            return is_array($value) ? json_encode($value) : $value;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return is_array($value) ...ncode($value) : $value; (object|integer|double|string|null|boolean) is incompatible with the return type documented by yii\helpers\BaseHtml::getAttributeValue of type string|array.

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

Let’s take a look at an example:

class Author {
    private $name;

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

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

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

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

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

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

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

Loading history...
2132
        }
2133
2134 35
        return $value;
2135
    }
2136
2137
    /**
2138
     * Generates an appropriate input name for the specified attribute name or expression.
2139
     *
2140
     * This method generates a name that can be used as the input name to collect user input
2141
     * for the specified attribute. The name is generated according to the [[Model::formName|form name]]
2142
     * of the model and the given attribute name. For example, if the form name of the `Post` model
2143
     * is `Post`, then the input name generated for the `content` attribute would be `Post[content]`.
2144
     *
2145
     * See [[getAttributeName()]] for explanation of attribute expression.
2146
     *
2147
     * @param Model $model the model object
2148
     * @param string $attribute the attribute name or expression
2149
     * @return string the generated input name
2150
     * @throws InvalidParamException if the attribute name contains non-word characters.
2151
     */
2152 47
    public static function getInputName($model, $attribute)
2153
    {
2154 47
        $formName = $model->formName();
2155 47
        if (!preg_match(static::$attributeRegex, $attribute, $matches)) {
2156
            throw new InvalidParamException('Attribute name must contain word characters only.');
2157
        }
2158 47
        $prefix = $matches[1];
2159 47
        $attribute = $matches[2];
2160 47
        $suffix = $matches[3];
2161 47
        if ($formName === '' && $prefix === '') {
2162
            return $attribute . $suffix;
2163 47
        } elseif ($formName !== '') {
2164 47
            return $formName . $prefix . "[$attribute]" . $suffix;
2165
        } else {
2166
            throw new InvalidParamException(get_class($model) . '::formName() cannot be empty for tabular inputs.');
2167
        }
2168
    }
2169
2170
    /**
2171
     * Generates an appropriate input ID for the specified attribute name or expression.
2172
     *
2173
     * This method converts the result [[getInputName()]] into a valid input ID.
2174
     * For example, if [[getInputName()]] returns `Post[content]`, this method will return `post-content`.
2175
     * @param Model $model the model object
2176
     * @param string $attribute the attribute name or expression. See [[getAttributeName()]] for explanation of attribute expression.
2177
     * @return string the generated input ID
2178
     * @throws InvalidParamException if the attribute name contains non-word characters.
2179
     */
2180 43
    public static function getInputId($model, $attribute)
2181
    {
2182 43
        $name = strtolower(static::getInputName($model, $attribute));
2183 43
        return str_replace(['[]', '][', '[', ']', ' ', '.'], ['', '-', '-', '', '-', '-'], $name);
2184
    }
2185
2186
    /**
2187
     * Escapes regular expression to use in JavaScript
2188
     * @param string $regexp the regular expression to be escaped.
2189
     * @return string the escaped result.
2190
     * @since 2.0.6
2191
     */
2192
    public static function escapeJsRegularExpression($regexp)
2193
    {
2194
        $pattern = preg_replace('/\\\\x\{?([0-9a-fA-F]+)\}?/', '\u$1', $regexp);
2195
        $deliminator = substr($pattern, 0, 1);
2196
        $pos = strrpos($pattern, $deliminator, 1);
2197
        $flag = substr($pattern, $pos + 1);
2198
        if ($deliminator !== '/') {
2199
            $pattern = '/' . str_replace('/', '\\/', substr($pattern, 1, $pos - 1)) . '/';
2200
        } else {
2201
            $pattern = substr($pattern, 0, $pos + 1);
2202
        }
2203
        if (!empty($flag)) {
2204
            $pattern .= preg_replace('/[^igm]/', '', $flag);
2205
        }
2206
2207
        return $pattern;
2208
    }
2209
}
2210