Html   F
last analyzed

Complexity

Total Complexity 275

Size/Duplication

Total Lines 1853
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 19
Bugs 1 Features 0
Metric Value
wmc 275
eloc 494
c 19
b 1
f 0
dl 0
loc 1853
ccs 501
cts 501
cp 1
rs 2

102 Methods

Rating   Name   Duplication   Size   Complexity  
A h2() 0 7 3
A article() 0 7 3
A h1() 0 7 3
A thead() 0 4 2
A tbody() 0 4 2
A col() 0 4 2
A label() 0 10 3
A encodeUnquotedAttribute() 0 20 1
A section() 0 7 3
A td() 0 10 3
A strong() 0 7 3
A fieldset() 0 7 2
A javaScriptFile() 0 7 2
A colgroup() 0 4 2
A i() 0 7 3
A cssFile() 0 7 2
A th() 0 10 3
A form() 0 13 4
A encode() 0 7 1
A p() 0 7 3
A input() 0 17 4
A hiddenInput() 0 7 2
A closeTag() 0 3 1
A resetButton() 0 7 2
A video() 0 3 1
A tr() 0 4 2
A submitButton() 0 7 2
A h4() 0 7 3
A radioList() 0 3 1
A optgroup() 0 3 1
A h3() 0 7 3
A resetInput() 0 7 2
A ol() 0 7 2
A hgroup() 0 7 3
A h5() 0 7 3
A span() 0 7 3
A table() 0 4 2
A div() 0 7 3
A voidTag() 0 7 2
A img() 0 13 4
A textInput() 0 7 2
A escapeJavaScriptStringValue() 0 7 1
A mailto() 0 9 2
A source() 0 3 1
A ul() 0 7 2
A normalTag() 0 10 3
A br() 0 3 1
A option() 0 12 3
A openTag() 0 3 1
A checkbox() 0 7 2
A body() 0 7 3
A legend() 0 10 3
A b() 0 7 3
A select() 0 7 2
A em() 0 7 3
A html() 0 13 4
A h6() 0 7 3
A nav() 0 7 3
A small() 0 7 3
A tfoot() 0 4 2
A range() 0 7 2
A script() 0 10 3
A track() 0 4 2
A radio() 0 7 2
A header() 0 7 3
A picture() 0 3 1
A file() 0 7 2
A caption() 0 10 3
A buttonInput() 0 7 2
A generateId() 0 10 2
A audio() 0 3 1
A style() 0 10 3
A a() 0 13 4
A button() 0 7 2
A noscript() 0 4 2
A link() 0 10 3
A tag() 0 10 3
A textarea() 0 10 4
A aside() 0 7 3
A datalist() 0 7 2
A title() 0 7 3
A li() 0 4 2
A submitInput() 0 7 2
A passwordInput() 0 7 2
A checkboxList() 0 3 1
A meta() 0 7 2
A encodeAttribute() 0 11 1
A getNonArrayableName() 0 3 2
C renderTagAttributes() 0 50 16
A address() 0 7 3
A getArrayableName() 0 3 2
B addCssStyle() 0 16 8
A mergeCssClasses() 0 11 5
A removeCssClass() 0 18 5
A normalizeRegexpPattern() 0 20 5
A cssStyleFromArray() 0 9 3
B addCssClass() 0 24 7
A cssStyleToArray() 0 11 3
A renderAttribute() 0 8 2
A footer() 0 7 3
A removeCssStyle() 0 9 4
A hr() 0 7 2

How to fix   Complexity   

Complex Class

Complex classes like Html 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.

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 Html, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Html;
6
7
use InvalidArgumentException;
8
use JsonException;
9
use Stringable;
10
use Yiisoft\Html\Tag\A;
11
use Yiisoft\Html\Tag\Address;
12
use Yiisoft\Html\Tag\Article;
13
use Yiisoft\Html\Tag\Aside;
14
use Yiisoft\Html\Tag\B;
15
use Yiisoft\Html\Tag\Br;
16
use Yiisoft\Html\Tag\Button;
17
use Yiisoft\Html\Tag\Caption;
18
use Yiisoft\Html\Tag\Col;
19
use Yiisoft\Html\Tag\Colgroup;
20
use Yiisoft\Html\Tag\CustomTag;
21
use Yiisoft\Html\Tag\Datalist;
22
use Yiisoft\Html\Tag\Div;
23
use Yiisoft\Html\Tag\Em;
24
use Yiisoft\Html\Tag\Fieldset;
25
use Yiisoft\Html\Tag\Form;
26
use Yiisoft\Html\Tag\H1;
27
use Yiisoft\Html\Tag\H2;
28
use Yiisoft\Html\Tag\H3;
29
use Yiisoft\Html\Tag\H4;
30
use Yiisoft\Html\Tag\H5;
31
use Yiisoft\Html\Tag\H6;
32
use Yiisoft\Html\Tag\I;
33
use Yiisoft\Html\Tag\Img;
34
use Yiisoft\Html\Tag\Input;
35
use Yiisoft\Html\Tag\Input\Checkbox;
36
use Yiisoft\Html\Tag\Input\File;
37
use Yiisoft\Html\Tag\Input\Radio;
38
use Yiisoft\Html\Tag\Input\Range;
39
use Yiisoft\Html\Tag\Label;
40
use Yiisoft\Html\Tag\Legend;
41
use Yiisoft\Html\Tag\Li;
42
use Yiisoft\Html\Tag\Link;
43
use Yiisoft\Html\Tag\Audio;
44
use Yiisoft\Html\Tag\Body;
45
use Yiisoft\Html\Tag\Footer;
46
use Yiisoft\Html\Tag\Header;
47
use Yiisoft\Html\Tag\Hgroup;
48
use Yiisoft\Html\Tag\Hr;
49
use Yiisoft\Html\Tag\Small;
50
use Yiisoft\Html\Tag\Track;
51
use Yiisoft\Html\Tag\Video;
52
use Yiisoft\Html\Tag\Meta;
53
use Yiisoft\Html\Tag\Nav;
54
use Yiisoft\Html\Tag\Noscript;
55
use Yiisoft\Html\Tag\Ol;
56
use Yiisoft\Html\Tag\Optgroup;
57
use Yiisoft\Html\Tag\Option;
58
use Yiisoft\Html\Tag\P;
59
use Yiisoft\Html\Tag\Picture;
60
use Yiisoft\Html\Tag\Script;
61
use Yiisoft\Html\Tag\Section;
62
use Yiisoft\Html\Tag\Select;
63
use Yiisoft\Html\Tag\Source;
64
use Yiisoft\Html\Tag\Span;
65
use Yiisoft\Html\Tag\Strong;
66
use Yiisoft\Html\Tag\Style;
67
use Yiisoft\Html\Tag\Table;
68
use Yiisoft\Html\Tag\Tbody;
69
use Yiisoft\Html\Tag\Td;
70
use Yiisoft\Html\Tag\Textarea;
71
use Yiisoft\Html\Tag\Tfoot;
72
use Yiisoft\Html\Tag\Th;
73
use Yiisoft\Html\Tag\Thead;
74
use Yiisoft\Html\Tag\Title;
75
use Yiisoft\Html\Tag\Tr;
76
use Yiisoft\Html\Tag\Ul;
77
use Yiisoft\Html\Widget\CheckboxList\CheckboxList;
78
use Yiisoft\Html\Widget\RadioList\RadioList;
79
use Yiisoft\Json\Json;
80
81
use function count;
82
use function in_array;
83
use function is_array;
84
use function is_bool;
85
use function is_int;
86
use function strlen;
87
88
/**
89
 * Html provides a set of static methods for generating commonly used HTML tags.
90
 *
91
 * Nearly all the methods in this class allow setting additional HTML attributes for the HTML tags they generate.
92
 * You can specify, for example, `class`, `style` or `id` for an HTML element using the `$options` parameter. See the
93
 * documentation of the {@see tag()} method for more details.
94
 */
95
final class Html
96
{
97
    /**
98
     * The preferred order of attributes in a tag. This mainly affects the order of the attributes that are
99
     * rendered by {@see renderTagAttributes()}.
100
     */
101
    private const ATTRIBUTE_ORDER = [
102
        'type',
103
        'id',
104
        'class',
105
        'name',
106
        'value',
107
108
        'href',
109
        'src',
110
        'srcset',
111
        'form',
112
        'action',
113
        'method',
114
115
        'selected',
116
        'checked',
117
        'readonly',
118
        'disabled',
119
        'multiple',
120
121
        'size',
122
        'maxlength',
123
        'minlength',
124
        'width',
125
        'height',
126
        'rows',
127
        'cols',
128
129
        'alt',
130
        'title',
131
        'rel',
132
        'media',
133
    ];
134
135
    /**
136
     * List of tag attributes that should be specially handled when their values are of array type.
137
     * In particular, if the value of the `data` attribute is `['name' => 'xyz', 'age' => 13]`, two attributes will be
138
     * generated instead of one: `data-name="xyz" data-age="13"`.
139
     */
140
    private const DATA_ATTRIBUTES = ['data', 'data-ng', 'ng', 'aria'];
141
142
    /**
143
     * @var array<string, int>
144
     */
145
    private static array $generateIdCounter = [];
146
147
    /**
148
     * Returns an autogenerated sequential ID.
149
     *
150 8
     * @return string Autogenerated ID.
151
     */
152 8
    public static function generateId(string $prefix = 'i'): string
153 8
    {
154 1
        $prefix .= hrtime(true);
155
        if (isset(self::$generateIdCounter[$prefix])) {
156 8
            $counter = ++self::$generateIdCounter[$prefix];
157 8
        } else {
158
            $counter = 1;
159 8
            self::$generateIdCounter = [$prefix => $counter];
160
        }
161
        return $prefix . $counter;
162
    }
163
164
    /**
165
     * Encodes special characters into HTML entities for use as a tag content
166
     * i.e. `<div>tag content</div>`.
167
     * Characters encoded are: &, <, >.
168
     *
169
     * @param mixed $content The content to be encoded.
170
     * @param bool $doubleEncode If already encoded entities should be encoded.
171
     * @param string $encoding The encoding to use, defaults to "UTF-8".
172
     *
173
     * @return string Encoded content.
174
     *
175 274
     * @link https://html.spec.whatwg.org/#data-state
176
     */
177 274
    public static function encode(mixed $content, bool $doubleEncode = true, string $encoding = 'UTF-8'): string
178
    {
179
        return htmlspecialchars(
180
            (string) $content,
181
            ENT_NOQUOTES | ENT_SUBSTITUTE | ENT_HTML5,
182
            $encoding,
183
            $doubleEncode
184
        );
185
    }
186
187
    /**
188
     * Encodes special characters into HTML entities for use as HTML tag unquoted attribute value
189
     * i.e. `<input value=my-value>`.
190
     * Characters encoded are: &, <, >, ", ', `, =, tab, space, U+000A (form feed), U+0000 (null).
191
     *
192
     * @param mixed $value The attribute value to be encoded.
193
     * @param bool $doubleEncode If already encoded entities should be encoded.
194
     * @param string $encoding The encoding to use, defaults to "UTF-8".
195
     *
196
     * @return string Encoded attribute value.
197
     *
198
     * @link https://html.spec.whatwg.org/#attribute-value-(unquoted)-state
199
     * @link https://html.spec.whatwg.org/#attribute-value-(single-quoted)-state
200 8
     * @link https://html.spec.whatwg.org/#attribute-value-(double-quoted)-state
201
     */
202
    public static function encodeUnquotedAttribute(
203
        mixed $value,
204
        bool $doubleEncode = true,
205 8
        string $encoding = 'UTF-8'
206
    ): string {
207
        $value = htmlspecialchars(
208
            (string) $value,
209
            ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5,
210
            $encoding,
211
            $doubleEncode
212 8
        );
213
214
        return strtr($value, [
215
            "\t" => '&Tab;', // U+0009 CHARACTER TABULATION (tab)
216
            "\n" => '&NewLine;', // U+000A LINE FEED (LF)
217
            "\u{000c}" => '&#12;', // U+000C FORM FEED (FF)
218
            "\u{0000}" => '&#0;', // U+0000 NULL
219
            ' ' => '&#32;', // U+0020 SPACE
220
            '=' => '&equals;', // U+003D EQUALS SIGN (=)
221
            '`' => '&grave;', // U+0060 GRAVE ACCENT (`)
222
        ]);
223
    }
224
225
    /**
226
     * Encodes special characters into HTML entities for use as HTML tag quoted attribute value
227
     * i.e. `<input value="my-value">`.
228
     * Characters encoded are: &, <, >, ", ', U+0000 (null).
229
     *
230
     * @param mixed $value The attribute value to be encoded.
231
     * @param bool $doubleEncode If already encoded entities should be encoded.
232
     * @param string $encoding The encoding to use, defaults to "UTF-8".
233
     *
234
     * @return string Encoded attribute value.
235
     *
236
     * @link https://html.spec.whatwg.org/#attribute-value-(single-quoted)-state
237 521
     * @link https://html.spec.whatwg.org/#attribute-value-(double-quoted)-state
238
     */
239 521
    public static function encodeAttribute(mixed $value, bool $doubleEncode = true, string $encoding = 'UTF-8'): string
240
    {
241
        $value = htmlspecialchars(
242
            (string) $value,
243
            ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5,
244
            $encoding,
245
            $doubleEncode
246 521
        );
247
248
        return strtr($value, [
249
            "\u{0000}" => '&#0;', // U+0000 NULL
250
        ]);
251
    }
252
253
    /**
254
     * Escape special characters for use as JavaScript string value in a `<script>` tag:
255
     *
256
     * ```
257
     * <script>
258
     *     window.myVar = "<?= Html::escapeJavaScriptStringValue($myVar) ?>";
259
     * </script>
260
     * ```
261
     *
262
     * @param mixed $value JavaScript string.
263
     *
264 5
     * @return string Escaped JavaScript string.
265
     */
266 5
    public static function escapeJavaScriptStringValue(mixed $value): string
267
    {
268
        return strtr((string) $value, [
269
            '/' => '\/',
270
            '"' => '\"',
271
            "'" => "\'",
272
            '\\' => '\\\\',
273
        ]);
274
    }
275
276
    /**
277
     * Generates a complete HTML tag.
278
     *
279
     * @see CustomTag
280
     *
281
     * @param string $name The tag name.
282
     * @param string|Stringable $content The tag content.
283
     * @param array $attributes The tag attributes in terms of name-value pairs.
284
     *
285 1
     * @psalm-param non-empty-string $name
286
     */
287 1
    public static function tag(string $name, string|Stringable $content = '', array $attributes = []): CustomTag
288 1
    {
289 1
        $tag = CustomTag::name($name);
290
        if ($content !== '') {
291 1
            $tag = $tag->content($content);
292 1
        }
293
        if (!empty($attributes)) {
294 1
            $tag = $tag->attributes($attributes);
295
        }
296
        return $tag;
297
    }
298
299
    /**
300
     * Generates a normal HTML tag.
301
     *
302
     * @see CustomTag
303
     *
304
     * @param string $name The tag name.
305
     * @param string|Stringable $content The tag content.
306
     * @param array $attributes The tag attributes in terms of name-value pairs.
307
     *
308 1
     * @psalm-param non-empty-string $name
309
     */
310 1
    public static function normalTag(string $name, string|Stringable $content = '', array $attributes = []): CustomTag
311 1
    {
312 1
        $tag = CustomTag::name($name)->normal();
313
        if ($content !== '') {
314 1
            $tag = $tag->content($content);
315 1
        }
316
        if (!empty($attributes)) {
317 1
            $tag = $tag->attributes($attributes);
318
        }
319
        return $tag;
320
    }
321
322
    /**
323
     * Generates a void HTML tag.
324
     *
325
     * @see CustomTag
326
     *
327
     * @param string $name The tag name.
328
     * @param array $attributes The tag attributes in terms of name-value pairs.
329
     *
330 1
     * @psalm-param non-empty-string $name
331
     */
332 1
    public static function voidTag(string $name, array $attributes = []): CustomTag
333 1
    {
334 1
        $tag = CustomTag::name($name)->void();
335
        if (!empty($attributes)) {
336 1
            $tag = $tag->attributes($attributes);
337
        }
338
        return $tag;
339
    }
340
341
    /**
342
     * Generates a start tag.
343
     *
344
     * @see self::closeTag()
345
     *
346
     * @param string $name The tag name.
347 106
     * @param array $attributes The tag attributes in terms of name-value pairs.
348
     */
349 106
    public static function openTag(string $name, array $attributes = []): string
350
    {
351
        return '<' . $name . self::renderTagAttributes($attributes) . '>';
352
    }
353
354
    /**
355
     * Generates an end tag.
356
     *
357
     * @see self::openTag()
358
     *
359 58
     * @param string $name The tag name.
360
     */
361 58
    public static function closeTag(string $name): string
362
    {
363
        return "</$name>";
364
    }
365
366
    /**
367
     * Generates a {@see Style} tag.
368
     *
369
     * @param string $content The style content.
370 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
371
     */
372 1
    public static function style(string $content = '', array $attributes = []): Style
373 1
    {
374 1
        $tag = Style::tag();
375
        if ($content !== '') {
376 1
            $tag = $tag->content($content);
377 1
        }
378
        if (!empty($attributes)) {
379 1
            $tag = $tag->attributes($attributes);
380
        }
381
        return $tag;
382
    }
383
384
    /**
385
     * Generates a {@see Script} tag.
386
     *
387
     * @param string $content The script content.
388 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
389
     */
390 1
    public static function script(string $content = '', array $attributes = []): Script
391 1
    {
392 1
        $tag = Script::tag();
393
        if ($content !== '') {
394 1
            $tag = $tag->content($content);
395 1
        }
396
        if (!empty($attributes)) {
397 1
            $tag = $tag->attributes($attributes);
398
        }
399
        return $tag;
400
    }
401
402
    /**
403
     * Generates a {@see Noscript} tag.
404
     *
405 1
     * @param string|Stringable $content Tag content.
406
     */
407 1
    public static function noscript(string|Stringable $content = ''): Noscript
408 1
    {
409
        $tag = Noscript::tag();
410
        return $content === '' ? $tag : $tag->content($content);
411
    }
412
413
    /**
414
     * Generates a {@see Title} tag.
415
     *
416
     * @param string|Stringable $content Tag content.
417 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
418
     */
419 1
    public static function title(string|Stringable $content = '', array $attributes = []): Title
420 1
    {
421 1
        $tag = Title::tag();
422
        if (!empty($attributes)) {
423 1
            $tag = $tag->attributes($attributes);
424
        }
425
        return $content === '' ? $tag : $tag->content($content);
426
    }
427
428
    /**
429
     * Generates a {@see Meta} tag.
430
     *
431 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
432
     */
433 1
    public static function meta(array $attributes = []): Meta
434 1
    {
435 1
        $tag = Meta::tag();
436
        if (!empty($attributes)) {
437 1
            $tag = $tag->attributes($attributes);
438
        }
439
        return $tag;
440
    }
441
442
    /**
443
     * Generates a {@see Link} tag.
444
     *
445
     * @param string|null $url The destination of the link.
446 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
447
     */
448 1
    public static function link(?string $url = null, array $attributes = []): Link
449 1
    {
450 1
        $tag = Link::tag();
451
        if (!empty($attributes)) {
452 1
            $tag = $tag->attributes($attributes);
453 1
        }
454
        if ($url !== null) {
455 1
            $tag = $tag->url($url);
456
        }
457
        return $tag;
458
    }
459
460
    /**
461
     * Generates a {@see Link} tag that refers to an CSS file.
462
     *
463
     * @param string $url The URL of the CSS file.
464 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
465
     */
466 1
    public static function cssFile(string $url, array $attributes = []): Link
467 1
    {
468 1
        $tag = Link::toCssFile($url);
469
        if (!empty($attributes)) {
470 1
            $tag = $tag->addAttributes($attributes);
471
        }
472
        return $tag;
473
    }
474
475
    /**
476
     * Generates a {@see Script} tag that refers to a JavaScript file.
477
     *
478
     * @param string $url The URL of the JavaScript file.
479 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
480
     */
481 1
    public static function javaScriptFile(string $url, array $attributes = []): Script
482 1
    {
483 1
        $tag = Script::tag()->url($url);
484
        if (!empty($attributes)) {
485 1
            $tag = $tag->addAttributes($attributes);
486
        }
487
        return $tag;
488
    }
489
490
    /**
491
     * Generates a hyperlink tag.
492
     *
493
     * @param string|Stringable $content The tag content.
494
     * @param array $attributes The tag attributes in terms of name-value pairs.
495
     *
496 1
     * @see A
497
     */
498 1
    public static function a(string|Stringable $content = '', ?string $url = null, array $attributes = []): A
499 1
    {
500 1
        $tag = A::tag();
501
        if (!empty($attributes)) {
502 1
            $tag = $tag->attributes($attributes);
503 1
        }
504
        if ($content !== '') {
505 1
            $tag = $tag->content($content);
506 1
        }
507
        if ($url !== null) {
508 1
            $tag = $tag->url($url);
509
        }
510
        return $tag;
511
    }
512
513
    /**
514
     * Generates a mailto hyperlink tag.
515
     *
516
     * @param array $attributes The tag attributes in terms of name-value pairs.
517
     *
518 1
     * @see A
519
     */
520 1
    public static function mailto(string $content, ?string $mail = null, array $attributes = []): A
521 1
    {
522 1
        $tag = A::tag()
523 1
            ->content($content)
524 1
            ->mailto($mail ?? $content);
525
        if (!empty($attributes)) {
526 1
            $tag = $tag->addAttributes($attributes);
527
        }
528
        return $tag;
529
    }
530
531
    /**
532
     * Generates an {@see Img} tag.
533
     *
534
     * @param string|null $url The image URL.
535 1
     * @param string|null $alt Alt text.
536
     * @param array $attributes The tag attributes in terms of name-value pairs.
537 1
     */
538 1
    public static function img(?string $url = null, ?string $alt = '', array $attributes = []): Img
539 1
    {
540
        $tag = Img::tag();
541 1
        if (!empty($attributes)) {
542 1
            $tag = $tag->attributes($attributes);
543
        }
544 1
        if ($url !== null) {
545
            $tag = $tag->src($url);
546
        }
547
        if ($alt !== null) {
548
            $tag = $tag->alt($alt);
549
        }
550
        return $tag;
551
    }
552 1
553
    /**
554 1
     * Generates a {@see Fieldset} tag.
555 1
     *
556 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
557
     */
558 1
    public static function fieldset(array $attributes = []): Fieldset
559
    {
560
        $tag = Fieldset::tag();
561
        if (!empty($attributes)) {
562
            $tag = $tag->attributes($attributes);
563
        }
564
        return $tag;
565
    }
566
567
    /**
568 1
     * Generates a {@see Form} tag.
569
     *
570 1
     * @param string|null $action The URL to use for form submission.
571 1
     * @param string|null $method The method attribute value.
572 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
573
     */
574 1
    public static function form(?string $action = null, ?string $method = null, array $attributes = []): Form
575 1
    {
576
        $tag = Form::tag();
577 1
        if ($action !== null) {
578 1
            $attributes['action'] = $action;
579
        }
580 1
        if ($method !== null) {
581
            $attributes['method'] = $method;
582
        }
583
        if (!empty($attributes)) {
584
            $tag = $tag->attributes($attributes);
585
        }
586
        return $tag;
587
    }
588
589
    /**
590 1
     * Generates a {@see Label} tag.
591
     *
592 1
     * @param string|Stringable $content Label text.
593 1
     * @param string|null $for The ID of the HTML element that this label is associated with.
594 1
     * If this is null, the "for" attribute will not be generated.
595
     */
596 1
    public static function label(string|Stringable $content = '', ?string $for = null): Label
597 1
    {
598
        $tag = Label::tag();
599 1
        if ($for !== null) {
600
            $tag = $tag->forId($for);
601
        }
602
        if ($content !== '') {
603
            $tag = $tag->content($content);
604
        }
605
        return $tag;
606
    }
607
608 4
    /**
609
     * Generates a {@see Legend} tag.
610 4
     *
611 4
     * @param string|Stringable $content The tag content.
612 4
     * @param array $attributes The tag attributes in terms of name-value pairs.
613
     */
614 4
    public static function legend(string|Stringable $content = '', array $attributes = []): Legend
615 2
    {
616
        $tag = Legend::tag();
617 4
        if ($content !== '') {
618
            $tag = $tag->content($content);
619
        }
620
        if (!empty($attributes)) {
621
            $tag = $tag->attributes($attributes);
622
        }
623
        return $tag;
624
    }
625
626
    /**
627
     * Generates a button tag.
628 18
     *
629
     * @see Button::button()
630 18
     *
631 18
     * @param string $content The content enclosed within the button tag.
632 2
     * @param array $attributes The tag attributes in terms of name-value pairs.
633
     */
634 18
    public static function button(string $content = 'Button', array $attributes = []): Button
635
    {
636
        $tag = Button::button($content);
637
        if (!empty($attributes)) {
638
            $tag = $tag->addAttributes($attributes);
639
        }
640
        return $tag;
641
    }
642
643
    /**
644
     * Generates a submit button tag.
645 4
     *
646
     * @see Button::submit()
647 4
     *
648 4
     * @param string $content The content enclosed within the button tag.
649 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
650
     */
651 4
    public static function submitButton(string $content = 'Submit', array $attributes = []): Button
652
    {
653
        $tag = Button::submit($content);
654
        if (!empty($attributes)) {
655
            $tag = $tag->addAttributes($attributes);
656
        }
657
        return $tag;
658
    }
659
660
    /**
661
     * Generates a reset button tag.
662 4
     *
663
     * @see Button::reset()
664 4
     *
665 4
     * @param string $content The content enclosed within the button tag.
666 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
667
     */
668 4
    public static function resetButton(string $content = 'Reset', array $attributes = []): Button
669
    {
670
        $tag = Button::reset($content);
671
        if (!empty($attributes)) {
672
            $tag = $tag->addAttributes($attributes);
673
        }
674
        return $tag;
675
    }
676
677
    /**
678
     * Generates an {@see Input} type of the given type.
679
     *
680 1
     * @param string $type The type attribute.
681
     * @param string|null $name The name attribute. If it is `null`, the name attribute will not be generated.
682
     * @param bool|float|int|string|Stringable|null $value The value attribute. If it is `null`, the value
683
     * attribute will not be generated.
684
     * @param array $attributes The tag attributes in terms of name-value pairs.
685
     */
686 1
    public static function input(
687 1
        string $type,
688 1
        ?string $name = null,
689
        bool|float|int|string|Stringable|null $value = null,
690 1
        array $attributes = []
691 1
    ): Input {
692
        $tag = Input::tag()->type($type);
693 1
        if ($name !== null) {
694 1
            $tag = $tag->name($name);
695
        }
696 1
        if ($value !== null) {
697
            $tag = $tag->value($value);
698
        }
699
        if (!empty($attributes)) {
700
            $tag = $tag->addAttributes($attributes);
701
        }
702
        return $tag;
703
    }
704
705
    /**
706
     * Generates an {@see Input} button.
707 1
     *
708
     * @see Input::button()
709 1
     *
710 1
     * @param string|null $label The value attribute.
711 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
712
     */
713 1
    public static function buttonInput(?string $label = 'Button', array $attributes = []): Input
714
    {
715
        $tag = Input::button($label);
716
        if (!empty($attributes)) {
717
            $tag = $tag->addAttributes($attributes);
718
        }
719
        return $tag;
720
    }
721
722
    /**
723
     * Generates submit {@see Input} button.
724 1
     *
725
     * @see Input::submitButton()
726 1
     *
727 1
     * @param string|null $label The value attribute.
728 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
729
     */
730 1
    public static function submitInput(?string $label = 'Submit', array $attributes = []): Input
731
    {
732
        $tag = Input::submitButton($label);
733
        if (!empty($attributes)) {
734
            $tag = $tag->addAttributes($attributes);
735
        }
736
        return $tag;
737
    }
738
739
    /**
740
     * Generates a reset {@see Input} button.
741 1
     *
742
     * @see Input::resetButton()
743 1
     *
744 1
     * @param string|null $label The value attribute.
745 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
746
     */
747 1
    public static function resetInput(?string $label = 'Reset', array $attributes = []): Input
748
    {
749
        $tag = Input::resetButton($label);
750
        if (!empty($attributes)) {
751
            $tag = $tag->addAttributes($attributes);
752
        }
753
        return $tag;
754
    }
755
756
    /**
757 2
     * Generates a text {@see Input} field.
758
     *
759
     * @param string|null $name The name attribute.
760
     * @param bool|float|int|string|Stringable|null $value The value attribute.
761
     * @param array $attributes The tag attributes in terms of name-value pairs.
762 2
     */
763 2
    public static function textInput(
764
        ?string $name = null,
765
        bool|float|int|string|Stringable|null $value = null,
766
        array $attributes = []
767
    ): Input {
768
        $tag = Input::text($name, $value);
769
        return $attributes === [] ? $tag : $tag->addAttributes($attributes);
770
    }
771
772
    /**
773
     * Generates a hidden input field.
774
     *
775 7
     * @see Input::hidden()
776
     *
777
     * @param string|null $name The name attribute.
778
     * @param bool|float|int|string|Stringable|null $value The value attribute.
779
     * @param array $attributes The tag attributes in terms of name-value pairs.
780 7
     */
781 7
    public static function hiddenInput(
782
        ?string $name = null,
783
        bool|float|int|string|Stringable|null $value = null,
784
        array $attributes = []
785
    ): Input {
786
        $tag = Input::hidden($name, $value);
787
        return $attributes === [] ? $tag : $tag->addAttributes($attributes);
788
    }
789
790
    /**
791
     * Generates a password input field.
792
     *
793 1
     * @see Input::password()
794
     *
795
     * @param string|null $name The name attribute.
796
     * @param bool|float|int|string|Stringable|null $value The value attribute.
797
     * @param array $attributes The tag attributes in terms of name-value pairs.
798 1
     */
799 1
    public static function passwordInput(
800
        ?string $name = null,
801
        bool|float|int|string|Stringable|null $value = null,
802
        array $attributes = []
803
    ): Input {
804
        $tag = Input::password($name, $value);
805
        return $attributes === [] ? $tag : $tag->addAttributes($attributes);
806
    }
807
808
    /**
809
     * Generates a file input field.
810
     *
811
     * To use a file input field, you should set the enclosing form's "enctype" attribute to be "multipart/form-data".
812
     * After the form is submitted, the uploaded file information can be obtained via $_FILES[$name]
813
     * (see PHP documentation).
814
     *
815 1
     * @see Input::file()
816
     *
817
     * @param string|null $name The name attribute.
818
     * @param bool|float|int|string|Stringable|null $value The value attribute.
819
     * @param array $attributes The tag attributes in terms of name-value pairs.
820 1
     */
821 1
    public static function file(
822
        ?string $name = null,
823
        bool|float|int|string|Stringable|null $value = null,
824
        array $attributes = []
825
    ): File {
826
        $tag = Input::file($name, $value);
827
        return $attributes === [] ? $tag : $tag->addAttributes($attributes);
828
    }
829
830
    /**
831
     * Generates a radio button {@see Input}.
832
     *
833 39
     * @see Input::radio()
834
     *
835
     * @param string|null $name The name attribute.
836
     * @param bool|float|int|string|Stringable|null $value The value attribute.
837
     * @param array $attributes The tag attributes in terms of name-value pairs.
838 39
     */
839 39
    public static function radio(
840
        ?string $name = null,
841
        bool|float|int|string|Stringable|null $value = null,
842
        array $attributes = []
843
    ): Radio {
844
        $tag = Input::radio($name, $value);
845
        return $attributes === [] ? $tag : $tag->addAttributes($attributes);
846
    }
847
848
    /**
849
     * Generates a range {@see Range}.
850
     *
851 1
     * @see Input::range()
852
     *
853
     * @param string|null $name The name attribute.
854
     * @param float|int|string|Stringable|null $value The value attribute.
855
     * @param array $attributes The tag attributes in terms of name-value pairs.
856 1
     */
857 1
    public static function range(
858
        ?string $name = null,
859
        float|int|string|Stringable|null $value = null,
860
        array $attributes = []
861
    ): Range {
862
        $tag = Input::range($name, $value);
863
        return $attributes === [] ? $tag : $tag->addAttributes($attributes);
864
    }
865
866
    /**
867
     * Generates a checkbox {@see Input}.
868
     *
869 40
     * @see Input::checkbox()
870
     *
871
     * @param string|null $name The name attribute.
872
     * @param bool|float|int|string|Stringable|null $value The value attribute.
873
     * @param array $attributes The tag attributes in terms of name-value pairs.
874 40
     */
875 40
    public static function checkbox(
876
        ?string $name = null,
877
        bool|float|int|string|Stringable|null $value = null,
878
        array $attributes = []
879
    ): Checkbox {
880
        $tag = Input::checkbox($name, $value);
881
        return $attributes === [] ? $tag : $tag->addAttributes($attributes);
882
    }
883
884
    /**
885 1
     * Generates a {@see Textarea} input.
886
     *
887 1
     * @param string|null $name The input name.
888 1
     * @param string|null $value The input value.
889 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
890
     */
891 1
    public static function textarea(?string $name = null, ?string $value = null, array $attributes = []): Textarea
892 1
    {
893
        $tag = Textarea::tag();
894 1
        if ($name !== null) {
895
            $tag = $tag->name($name);
896
        }
897
        if (!empty($value)) {
898
            $tag = $tag->value($value);
899
        }
900
        return $attributes === [] ? $tag : $tag->addAttributes($attributes);
901
    }
902 1
903
    /**
904 1
     * Generates a {@see Select} tag.
905 1
     *
906 1
     * @param string|null $name The name attribute
907
     */
908 1
    public static function select(?string $name = null): Select
909
    {
910
        $tag = Select::tag();
911
        if ($name !== null) {
912
            $tag = $tag->name($name);
913
        }
914 1
        return $tag;
915
    }
916 1
917
    /**
918
     * Generates a {@see Optgroup} tag.
919
     */
920
    public static function optgroup(): Optgroup
921
    {
922
        return Optgroup::tag();
923
    }
924
925 1
    /**
926
     * Generates a {@see Option} tag.
927
     *
928
     * @param string|Stringable $content Tag content.
929 1
     * @param bool|float|int|string|Stringable|null $value The value attribute.
930 1
     */
931 1
    public static function option(
932
        string|Stringable $content = '',
933 1
        bool|float|int|string|Stringable|null $value = null
934 1
    ): Option {
935
        $tag = Option::tag();
936 1
        if ($content !== '') {
937
            $tag = $tag->content($content);
938
        }
939
        if ($value !== null) {
940
            $tag = $tag->value($value);
941
        }
942
        return $tag;
943
    }
944 1
945
    /**
946 1
     * Generates a list of checkboxes.
947
     *
948
     * @see CheckboxList
949
     */
950
    public static function checkboxList(string $name): CheckboxList
951
    {
952
        return CheckboxList::create($name);
953
    }
954 1
955
    /**
956 1
     * Generates a list of radio buttons.
957
     *
958
     * @see RadioList
959
     */
960
    public static function radioList(string $name): RadioList
961
    {
962
        return RadioList::create($name);
963
    }
964
965 1
    /**
966
     * Generates a {@see Div} tag.
967 1
     *
968 1
     * @param string|Stringable $content Tag content.
969 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
970
     */
971 1
    public static function div(string|Stringable $content = '', array $attributes = []): Div
972
    {
973
        $tag = Div::tag();
974
        if (!empty($attributes)) {
975
            $tag = $tag->attributes($attributes);
976
        }
977
        return $content === '' ? $tag : $tag->content($content);
978
    }
979
980 20
    /**
981
     * Generates a {@see Span} tag.
982 20
     *
983 20
     * @param string|Stringable $content Tag content.
984 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
985
     */
986 20
    public static function span(string|Stringable $content = '', array $attributes = []): Span
987
    {
988
        $tag = Span::tag();
989
        if (!empty($attributes)) {
990
            $tag = $tag->attributes($attributes);
991
        }
992
        return $content === '' ? $tag : $tag->content($content);
993
    }
994
995 1
    /**
996
     * Generates a {@see Em} tag.
997 1
     *
998 1
     * @param string|Stringable $content Tag content.
999 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
1000
     */
1001 1
    public static function em(string|Stringable $content = '', array $attributes = []): Em
1002
    {
1003
        $tag = Em::tag();
1004
        if (!empty($attributes)) {
1005
            $tag = $tag->attributes($attributes);
1006
        }
1007
        return $content === '' ? $tag : $tag->content($content);
1008
    }
1009
1010 1
    /**
1011
     * Generates a {@see Strong} tag.
1012 1
     *
1013 1
     * @param string|Stringable $content Tag content.
1014 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
1015
     */
1016 1
    public static function strong(string|Stringable $content = '', array $attributes = []): Strong
1017
    {
1018
        $tag = Strong::tag();
1019
        if (!empty($attributes)) {
1020
            $tag = $tag->attributes($attributes);
1021
        }
1022
        return $content === '' ? $tag : $tag->content($content);
1023
    }
1024
1025 1
    /**
1026
     * Generates a {@see Small} tag.
1027 1
     *
1028 1
     * @param string|Stringable $content Tag content.
1029 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
1030
     */
1031 1
    public static function small(string|Stringable $content = '', array $attributes = []): Small
1032
    {
1033
        $tag = Small::tag();
1034
        if (!empty($attributes)) {
1035
            $tag = $tag->attributes($attributes);
1036
        }
1037
        return $content === '' ? $tag : $tag->content($content);
1038
    }
1039
1040 1
    /**
1041
     * Generates a {@see B} tag.
1042 1
     *
1043 1
     * @param string|Stringable $content Tag content.
1044 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
1045
     */
1046 1
    public static function b(string|Stringable $content = '', array $attributes = []): B
1047
    {
1048
        $tag = B::tag();
1049
        if (!empty($attributes)) {
1050
            $tag = $tag->attributes($attributes);
1051
        }
1052
        return $content === '' ? $tag : $tag->content($content);
1053
    }
1054
1055 2
    /**
1056
     * Generates a {@see I} tag.
1057 2
     *
1058 2
     * @param string|Stringable $content Tag content.
1059 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
1060
     */
1061 2
    public static function i(string|Stringable $content = '', array $attributes = []): I
1062
    {
1063
        $tag = I::tag();
1064
        if (!empty($attributes)) {
1065
            $tag = $tag->attributes($attributes);
1066
        }
1067
        return $content === '' ? $tag : $tag->content($content);
1068
    }
1069
1070 1
    /**
1071
     * Generates a {@see H1} tag.
1072 1
     *
1073 1
     * @param string|Stringable $content Tag content.
1074 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
1075
     */
1076 1
    public static function h1(string|Stringable $content = '', array $attributes = []): H1
1077
    {
1078
        $tag = H1::tag();
1079
        if (!empty($attributes)) {
1080
            $tag = $tag->attributes($attributes);
1081
        }
1082
        return $content === '' ? $tag : $tag->content($content);
1083
    }
1084
1085 1
    /**
1086
     * Generates a {@see H2} tag.
1087 1
     *
1088 1
     * @param string|Stringable $content Tag content.
1089 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
1090
     */
1091 1
    public static function h2(string|Stringable $content = '', array $attributes = []): H2
1092
    {
1093
        $tag = H2::tag();
1094
        if (!empty($attributes)) {
1095
            $tag = $tag->attributes($attributes);
1096
        }
1097
        return $content === '' ? $tag : $tag->content($content);
1098
    }
1099
1100 1
    /**
1101
     * Generates a {@see H3} tag.
1102 1
     *
1103 1
     * @param string|Stringable $content Tag content.
1104 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
1105
     */
1106 1
    public static function h3(string|Stringable $content = '', array $attributes = []): H3
1107
    {
1108
        $tag = H3::tag();
1109
        if (!empty($attributes)) {
1110
            $tag = $tag->attributes($attributes);
1111
        }
1112
        return $content === '' ? $tag : $tag->content($content);
1113
    }
1114
1115 1
    /**
1116
     * Generates a {@see H4} tag.
1117 1
     *
1118 1
     * @param string|Stringable $content Tag content.
1119 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
1120
     */
1121 1
    public static function h4(string|Stringable $content = '', array $attributes = []): H4
1122
    {
1123
        $tag = H4::tag();
1124
        if (!empty($attributes)) {
1125
            $tag = $tag->attributes($attributes);
1126
        }
1127
        return $content === '' ? $tag : $tag->content($content);
1128
    }
1129
1130 1
    /**
1131
     * Generates a {@see H5} tag.
1132 1
     *
1133 1
     * @param string|Stringable $content Tag content.
1134 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
1135
     */
1136 1
    public static function h5(string|Stringable $content = '', array $attributes = []): H5
1137
    {
1138
        $tag = H5::tag();
1139
        if (!empty($attributes)) {
1140
            $tag = $tag->attributes($attributes);
1141
        }
1142
        return $content === '' ? $tag : $tag->content($content);
1143
    }
1144
1145 3
    /**
1146
     * Generates a {@see H6} tag.
1147 3
     *
1148 3
     * @param string|Stringable $content Tag content.
1149 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
1150
     */
1151 3
    public static function h6(string|Stringable $content = '', array $attributes = []): H6
1152
    {
1153
        $tag = H6::tag();
1154
        if (!empty($attributes)) {
1155
            $tag = $tag->attributes($attributes);
1156
        }
1157 1
        return $content === '' ? $tag : $tag->content($content);
1158
    }
1159 1
1160
    /**
1161
     * Generates a {@see P} tag.
1162
     *
1163
     * @param string|Stringable $content Tag content.
1164
     * @param array $attributes The tag attributes in terms of name-value pairs.
1165 1
     */
1166
    public static function p(string|Stringable $content = '', array $attributes = []): P
1167 1
    {
1168
        $tag = P::tag();
1169
        if (!empty($attributes)) {
1170
            $tag = $tag->attributes($attributes);
1171
        }
1172
        return $content === '' ? $tag : $tag->content($content);
1173
    }
1174
1175 1
    /**
1176
     * Generates a {@see Ul} tag.
1177 1
     *
1178 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
1179
     */
1180
    public static function ul(array $attributes = []): Ul
1181
    {
1182
        $tag = Ul::tag();
1183
        if (!empty($attributes)) {
1184
            $tag = $tag->attributes($attributes);
1185
        }
1186 1
        return $tag;
1187
    }
1188 1
1189 1
    /**
1190 1
     * Generates a {@see Ol} tag.
1191
     *
1192 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
1193
     */
1194
    public static function ol(array $attributes = []): Ol
1195
    {
1196
        $tag = Ol::tag();
1197
        if (!empty($attributes)) {
1198
            $tag = $tag->attributes($attributes);
1199
        }
1200
        return $tag;
1201 1
    }
1202
1203 1
    /**
1204 1
     * Generates a {@see Li} tag.
1205 1
     *
1206
     * @param string|Stringable $content Tag content.
1207 1
     */
1208 1
    public static function li(string|Stringable $content = ''): Li
1209
    {
1210 1
        $tag = Li::tag();
1211
        return $content === '' ? $tag : $tag->content($content);
1212
    }
1213
1214
    /**
1215
     * Generates a {@see Datalist} tag.
1216
     *
1217
     * @param array $attributes The tag attributes in terms of name-value pairs.
1218 1
     */
1219
    public static function datalist(array $attributes = []): Datalist
1220 1
    {
1221 1
        $tag = Datalist::tag();
1222
        if (!empty($attributes)) {
1223
            $tag = $tag->attributes($attributes);
1224
        }
1225
        return $tag;
1226
    }
1227
1228
    /**
1229 1
     * Generates a {@see Caption} tag.
1230
     *
1231 1
     * @param string|Stringable $content Tag content.
1232 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
1233
     */
1234
    public static function caption(string|Stringable $content = '', array $attributes = []): Caption
1235
    {
1236
        $tag = Caption::tag();
1237
        if ($content !== '') {
1238
            $tag = $tag->content($content);
1239
        }
1240 1
        if ($attributes !== []) {
1241
            $tag = $tag->attributes($attributes);
1242 1
        }
1243 1
        return $tag;
1244
    }
1245
1246
    /**
1247
     * Generates a {@see Col} tag.
1248
     *
1249
     * @param array $attributes The tag attributes in terms of name-value pairs.
1250
     */
1251 1
    public static function col(array $attributes = []): Col
1252
    {
1253 1
        $tag = Col::tag();
1254 1
        return $attributes === [] ? $tag : $tag->attributes($attributes);
1255
    }
1256
1257
    /**
1258
     * Generates a {@see Colgroup} tag.
1259
     *
1260
     * @param array $attributes The tag attributes in terms of name-value pairs.
1261
     */
1262 1
    public static function colgroup(array $attributes = []): Colgroup
1263
    {
1264 1
        $tag = Colgroup::tag();
1265 1
        return $attributes === [] ? $tag : $tag->attributes($attributes);
1266
    }
1267
1268
    /**
1269
     * Generates a {@see Table} tag.
1270
     *
1271
     * @param array $attributes The tag attributes in terms of name-value pairs.
1272
     */
1273 1
    public static function table(array $attributes = []): Table
1274
    {
1275 1
        $tag = Table::tag();
1276 1
        return $attributes === [] ? $tag : $tag->attributes($attributes);
1277
    }
1278
1279
    /**
1280
     * Generates a {@see Thead} tag.
1281
     *
1282
     * @param array $attributes The tag attributes in terms of name-value pairs.
1283
     */
1284 1
    public static function thead(array $attributes = []): Thead
1285
    {
1286 1
        $tag = Thead::tag();
1287 1
        return $attributes === [] ? $tag : $tag->attributes($attributes);
1288
    }
1289
1290
    /**
1291
     * Generates a {@see Tbody} tag.
1292
     *
1293
     * @param array $attributes The tag attributes in terms of name-value pairs.
1294
     */
1295
    public static function tbody(array $attributes = []): Tbody
1296 1
    {
1297
        $tag = Tbody::tag();
1298 1
        return $attributes === [] ? $tag : $tag->attributes($attributes);
1299 1
    }
1300 1
1301
    /**
1302 1
     * Generates a {@see Tfoot} tag.
1303 1
     *
1304
     * @param array $attributes The tag attributes in terms of name-value pairs.
1305 1
     */
1306
    public static function tfoot(array $attributes = []): Tfoot
1307
    {
1308
        $tag = Tfoot::tag();
1309
        return $attributes === [] ? $tag : $tag->attributes($attributes);
1310
    }
1311
1312
    /**
1313
     * Generates a {@see Tr} tag.
1314 1
     *
1315
     * @param array $attributes The tag attributes in terms of name-value pairs.
1316 1
     */
1317 1
    public static function tr(array $attributes = []): Tr
1318 1
    {
1319
        $tag = Tr::tag();
1320 1
        return $attributes === [] ? $tag : $tag->attributes($attributes);
1321 1
    }
1322
1323 1
    /**
1324
     * Generates a {@see Td} tag.
1325
     *
1326
     * @param string|Stringable $content Tag content.
1327
     * @param array $attributes The tag attributes in terms of name-value pairs.
1328
     */
1329 1
    public static function td(string|Stringable $content = '', array $attributes = []): Td
1330
    {
1331 1
        $tag = Td::tag();
1332
        if ($content !== '') {
1333
            $tag = $tag->content($content);
1334
        }
1335
        if ($attributes !== []) {
1336
            $tag = $tag->attributes($attributes);
1337 1
        }
1338
        return $tag;
1339 1
    }
1340
1341
    /**
1342
     * Generates a {@see Th} tag.
1343
     *
1344
     * @param string|Stringable $content Tag content.
1345 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
1346
     */
1347 1
    public static function th(string|Stringable $content = '', array $attributes = []): Th
1348
    {
1349
        $tag = Th::tag();
1350
        if ($content !== '') {
1351
            $tag = $tag->content($content);
1352
        }
1353 1
        if ($attributes !== []) {
1354
            $tag = $tag->attributes($attributes);
1355 1
        }
1356 1
        return $tag;
1357
    }
1358
1359
    /**
1360
     * Generates a {@see Br} tag.
1361
     */
1362 1
    public static function br(): Br
1363
    {
1364 1
        return Br::tag();
1365
    }
1366
1367
    /**
1368
     * Generates a {@see Video} tag.
1369
     */
1370 1
    public static function video(): Video
1371
    {
1372 1
        return Video::tag();
1373
    }
1374
1375
    /**
1376
     * Generates a {@see Audio} tag.
1377
     */
1378
    public static function audio(): Audio
1379
    {
1380
        return Audio::tag();
1381 1
    }
1382
1383 1
    /**
1384 1
     * Generates a {@see Track} tag.
1385 1
     */
1386
    public static function track(?string $src = null): Track
1387 1
    {
1388
        $tag = Track::tag();
1389
        return $src === null ? $tag : $tag->src($src);
1390
    }
1391
1392
    /**
1393
     * Generates a {@see Picture} tag.
1394
     */
1395
    public static function picture(): Picture
1396 1
    {
1397
        return Picture::tag();
1398 1
    }
1399 1
1400 1
    /**
1401
     * Generates a {@see Source} tag.
1402 1
     */
1403
    public static function source(): Source
1404
    {
1405
        return Source::tag();
1406
    }
1407
1408
    /**
1409
     * Generates a {@see Html} tag.
1410
     *
1411 1
     * @param string|Stringable $content Tag content.
1412
     * @param string|null $lang The document language.
1413 1
     * @param array $attributes The tag attributes in terms of name-value pairs.
1414 1
     */
1415 1
    public static function html(string|Stringable $content = '', ?string $lang = null, array $attributes = []): Tag\Html
1416
    {
1417 1
        $tag = Tag\Html::tag();
1418
        if (!empty($attributes)) {
1419
            $tag = $tag->attributes($attributes);
1420
        }
1421
        if ($content !== '') {
1422
            $tag = $tag->content($content);
1423
        }
1424
        if ($lang !== null) {
1425
            $tag = $tag->lang($lang);
1426 1
        }
1427
        return $tag;
1428 1
    }
1429 1
1430 1
    /**
1431
     * Generates a {@see Body} tag.
1432 1
     *
1433
     * @param string|Stringable $content Tag content.
1434
     * @param array $attributes The tag attributes in terms of name-value pairs.
1435
     */
1436
    public static function body(string|Stringable $content = '', array $attributes = []): Body
1437
    {
1438
        $tag = Body::tag();
1439
        if (!empty($attributes)) {
1440
            $tag = $tag->attributes($attributes);
1441 1
        }
1442
        return $content === '' ? $tag : $tag->content($content);
1443 1
    }
1444 1
1445 1
    /**
1446
     * Generates a {@see Article} tag.
1447 1
     *
1448
     * @param string|Stringable $content Tag content.
1449
     * @param array $attributes The tag attributes in terms of name-value pairs.
1450
     */
1451
    public static function article(string|Stringable $content = '', array $attributes = []): Article
1452
    {
1453
        $tag = Article::tag();
1454
        if (!empty($attributes)) {
1455
            $tag = $tag->attributes($attributes);
1456 1
        }
1457
        return $content === '' ? $tag : $tag->content($content);
1458 1
    }
1459 1
1460 1
    /**
1461
     * Generates a {@see Section} tag.
1462 1
     *
1463
     * @param string|Stringable $content Tag content.
1464
     * @param array $attributes The tag attributes in terms of name-value pairs.
1465
     */
1466
    public static function section(string|Stringable $content = '', array $attributes = []): Section
1467
    {
1468
        $tag = Section::tag();
1469
        if (!empty($attributes)) {
1470
            $tag = $tag->attributes($attributes);
1471 1
        }
1472
        return $content === '' ? $tag : $tag->content($content);
1473 1
    }
1474 1
1475 1
    /**
1476
     * Generates a {@see Nav} tag.
1477 1
     *
1478
     * @param string|Stringable $content Tag content.
1479
     * @param array $attributes The tag attributes in terms of name-value pairs.
1480
     */
1481
    public static function nav(string|Stringable $content = '', array $attributes = []): Nav
1482
    {
1483
        $tag = Nav::tag();
1484
        if (!empty($attributes)) {
1485
            $tag = $tag->attributes($attributes);
1486 1
        }
1487
        return $content === '' ? $tag : $tag->content($content);
1488 1
    }
1489 1
1490 1
    /**
1491
     * Generates a {@see Aside} tag.
1492 1
     *
1493
     * @param string|Stringable $content Tag content.
1494
     * @param array $attributes The tag attributes in terms of name-value pairs.
1495
     */
1496
    public static function aside(string|Stringable $content = '', array $attributes = []): Aside
1497
    {
1498
        $tag = Aside::tag();
1499
        if (!empty($attributes)) {
1500
            $tag = $tag->attributes($attributes);
1501 1
        }
1502
        return $content === '' ? $tag : $tag->content($content);
1503 1
    }
1504 1
1505 1
    /**
1506
     * Generates a {@see Hgroup} tag.
1507 1
     *
1508
     * @param string|Stringable $content Tag content.
1509
     * @param array $attributes The tag attributes in terms of name-value pairs.
1510
     */
1511
    public static function hgroup(string|Stringable $content = '', array $attributes = []): Hgroup
1512
    {
1513
        $tag = Hgroup::tag();
1514
        if (!empty($attributes)) {
1515
            $tag = $tag->attributes($attributes);
1516
        }
1517
        return $content === '' ? $tag : $tag->content($content);
1518
    }
1519
1520
    /**
1521
     * Generates a {@see Header} tag.
1522
     *
1523
     * @param string|Stringable $content Tag content.
1524
     * @param array $attributes The tag attributes in terms of name-value pairs.
1525
     */
1526
    public static function header(string|Stringable $content = '', array $attributes = []): Header
1527
    {
1528
        $tag = Header::tag();
1529
        if (!empty($attributes)) {
1530
            $tag = $tag->attributes($attributes);
1531
        }
1532
        return $content === '' ? $tag : $tag->content($content);
1533
    }
1534
1535 741
    /**
1536
     * Generates a {@see Hr} tag.
1537 741
     */
1538 272
    public static function hr(array $attributes = []): Hr
1539 272
    {
1540 272
        $tag = Hr::tag();
1541
        if (!empty($attributes)) {
1542 268
            $tag = $tag->attributes($attributes);
1543
        }
1544
        return $tag;
1545 272
    }
1546
1547
    /**
1548 741
     * Generates a {@see Footer} tag.
1549
     *
1550
     * @param string|Stringable $content Tag content.
1551
     * @param array $attributes The tag attributes in terms of name-value pairs.
1552
     */
1553 741
    public static function footer(string|Stringable $content = '', array $attributes = []): Footer
1554 626
    {
1555 158
        $tag = Footer::tag();
1556 158
        if (!empty($attributes)) {
1557
            $tag = $tag->attributes($attributes);
1558 601
        }
1559 54
        return $content === '' ? $tag : $tag->content($content);
1560
    }
1561 10
1562 10
    /**
1563 4
     * Generates a {@see Address} tag.
1564 8
     *
1565
     * @param string|Stringable $content Tag content.
1566 48
     * @param array $attributes The tag attributes in terms of name-value pairs.
1567
     */
1568 45
    public static function address(string|Stringable $content = '', array $attributes = []): Address
1569 7
    {
1570
        $tag = Address::tag();
1571 38
        if (!empty($attributes)) {
1572 7
            $tag = $tag->attributes($attributes);
1573
        }
1574 6
        return $content === '' ? $tag : $tag->content($content);
1575 2
    }
1576
1577 4
    /**
1578
     * Renders the HTML tag attributes.
1579 49
     *
1580
     * Attributes whose values are of boolean type will be treated as
1581 559
     * [boolean attributes](https://www.w3.org/TR/html5/infrastructure.html#boolean-attributes).
1582 479
     *
1583
     * Attributes whose values are null will not be rendered. The values of attributes will be HTML-encoded using
1584
     * {@see encodeAttribute()}.
1585
     *
1586 741
     * The "data" attribute is specially handled when it is receiving an array value. In this case, the array will be
1587
     * "expanded" and a list data attributes will be rendered. For example, if `'data' => ['id' => 1, 'name' => 'yii']`
1588
     * then this will be rendered `data-id="1" data-name="yii"`.
1589
     *
1590
     * Additionally, `'data' => ['params' => ['id' => 1, 'name' => 'yii'], 'status' => 'ok']` will be rendered as:
1591
     * `data-params='{"id":1,"name":"yii"}' data-status="ok"`.
1592
     *
1593
     * @param array $attributes Attributes to be rendered. The attribute values will be HTML-encoded using
1594
     * {@see encodeAttribute()}.
1595
     *
1596
     * @throws JsonException
1597
     *
1598
     * @return string The rendering result. If the attributes are not empty, they will be rendered into a string
1599
     * with a leading white space (so that it can be directly appended to the tag name in a tag). If there is no
1600
     * attribute, an empty string will be returned.
1601
     */
1602
    public static function renderTagAttributes(array $attributes): string
1603
    {
1604
        if (count($attributes) > 1) {
1605
            $sorted = [];
1606
            foreach (self::ATTRIBUTE_ORDER as $name) {
1607
                if (isset($attributes[$name])) {
1608 11
                    $sorted[$name] = $attributes[$name];
1609
                }
1610 11
            }
1611 7
            $attributes = array_merge($sorted, $attributes);
1612
        }
1613 7
1614
        $html = '';
1615
        /**
1616 1
         * @var string $name
1617 7
         */
1618
        foreach ($attributes as $name => $value) {
1619
            if (is_bool($value)) {
1620 10
                if ($value) {
1621
                    $html .= self::renderAttribute($name);
1622
                }
1623
            } elseif (is_array($value)) {
1624
                if (in_array($name, self::DATA_ATTRIBUTES, true)) {
1625
                    /** @psalm-var array<array-key, array|string|\Stringable|null> $value */
1626
                    foreach ($value as $n => $v) {
1627
                        $html .= is_array($v)
1628
                            ? self::renderAttribute($name . '-' . $n, Json::htmlEncode($v), '\'')
1629
                            : self::renderAttribute($name . '-' . $n, self::encodeAttribute($v));
1630
                    }
1631
                } elseif ($name === 'class') {
1632 1
                    /** @var string[] $value */
1633
                    if (empty($value)) {
1634 1
                        continue;
1635 1
                    }
1636 1
                    $html .= self::renderAttribute($name, self::encodeAttribute(implode(' ', $value)));
1637 1
                } elseif ($name === 'style') {
1638 1
                    /** @psalm-var array<string,string> $value */
1639
                    if (empty($value)) {
1640 1
                        continue;
1641
                    }
1642
                    $html .= self::renderAttribute($name, self::encodeAttribute(self::cssStyleFromArray($value)));
1643
                } else {
1644 1
                    $html .= self::renderAttribute($name, Json::htmlEncode($value), '\'');
1645 1
                }
1646 1
            } elseif ($value !== null) {
1647 1
                $html .= self::renderAttribute($name, self::encodeAttribute($value));
1648
            }
1649 1
        }
1650
1651
        return $html;
1652
    }
1653
1654
    /**
1655
     * Adds a CSS class (or several classes) to the specified options.
1656
     *
1657
     * If the CSS class is already in the options, it will not be added again. If class specification at given options
1658
     * is an array, and some class placed there with the named (string) key, overriding of such key will have no
1659
     * effect. For example:
1660
     *
1661
     * ```php
1662
     * $options = ['class' => ['persistent' => 'initial']];
1663
     *
1664
     * // ['class' => ['persistent' => 'initial']];
1665 7
     * Html::addCssClass($options, ['persistent' => 'override']);
1666
     * ```
1667 7
     *
1668 6
     * @see removeCssClass()
1669 4
     *
1670 3
     * @param array $options The options to be modified.
1671 1
     * @param null[]|string|string[]|null $class The CSS class(es) to be added. Null values will be ignored.
1672
     *
1673
     * @psalm-param string|array<array-key,string|null> $class
1674
     */
1675 7
    public static function addCssClass(array &$options, string|array|null $class): void
1676
    {
1677
        if ($class === null) {
0 ignored issues
show
introduced by
The condition $class === null is always false.
Loading history...
1678
            return;
1679
        }
1680
        if (is_array($class)) {
0 ignored issues
show
introduced by
The condition is_array($class) is always true.
Loading history...
1681
            $class = array_filter($class, static fn (mixed $c): bool => $c !== null);
1682
            if (empty($class)) {
1683
                return;
1684
            }
1685
        }
1686
1687
        if (isset($options['class'])) {
1688
            if (is_array($options['class'])) {
1689
                /** @psalm-var string[] $options['class'] */
1690
                $options['class'] = self::mergeCssClasses($options['class'], (array) $class);
1691
            } else {
1692
                /** @psalm-var string $options['class'] */
1693
                $classes = preg_split('/\s+/', $options['class'], -1, PREG_SPLIT_NO_EMPTY);
1694
                $classes = self::mergeCssClasses($classes, (array) $class);
1695
                $options['class'] = is_array($class) ? $classes : implode(' ', $classes);
0 ignored issues
show
introduced by
The condition is_array($class) is always true.
Loading history...
1696
            }
1697
        } else {
1698 1
            $options['class'] = $class;
1699
        }
1700 1
    }
1701
1702 1
    /**
1703 1
     * Removes a CSS class from the specified options.
1704 1
     *
1705 1
     * @see addCssClass()
1706 1
     *
1707 1
     * @param array $options The options to be modified.
1708
     * @param string|string[] $class The CSS class(es) to be removed.
1709
     */
1710
    public static function removeCssClass(array &$options, string|array $class): void
1711 1
    {
1712
        if (isset($options['class'])) {
1713 1
            if (is_array($options['class'])) {
1714
                $classes = array_diff($options['class'], (array) $class);
1715
                if (empty($classes)) {
1716
                    unset($options['class']);
1717
                } else {
1718
                    $options['class'] = $classes;
1719
                }
1720
            } else {
1721
                /** @var string[] */
1722
                $classes = preg_split('/\s+/', (string) $options['class'], -1, PREG_SPLIT_NO_EMPTY);
1723
                $classes = array_diff($classes, (array) $class);
1724
                if (empty($classes)) {
1725
                    unset($options['class']);
1726
                } else {
1727
                    $options['class'] = implode(' ', $classes);
1728
                }
1729
            }
1730
        }
1731 1
    }
1732
1733 1
    /**
1734
     * Merges already existing CSS classes with new one.
1735 1
     *
1736 1
     * This method provides the priority for named existing classes over additional.
1737 1
     *
1738
     * @param string[] $existingClasses Already existing CSS classes.
1739 1
     * @param null[]|string[] $additionalClasses CSS classes to be added.
1740
     *
1741
     * @return string[] The merge result.
1742
     *
1743
     * @psalm-param array<array-key,string> $additionalClasses
1744
     */
1745
    private static function mergeCssClasses(array $existingClasses, array $additionalClasses): array
1746
    {
1747
        foreach ($additionalClasses as $key => $class) {
1748
            if (is_int($key) && !in_array($class, $existingClasses, true)) {
1749
                $existingClasses[] = $class;
1750
            } elseif (!isset($existingClasses[$key])) {
1751
                $existingClasses[$key] = $class;
1752
            }
1753
        }
1754
1755
        return array_unique($existingClasses);
1756
    }
1757
1758
    /**
1759
     * Adds the specified CSS styles to the HTML options.
1760 7
     *
1761
     * If the options already contain a `style` element, the new style will be merged
1762 7
     * with the existing one. If a CSS property exists in both the new and the old styles,
1763 7
     * the old one may be overwritten if `$overwrite` is true.
1764 7
     *
1765
     * For example,
1766
     *
1767
     * ```php
1768 7
     * Html::addCssStyle($options, 'width: 100px; height: 200px');
1769
     * ```
1770
     *
1771
     * @see removeCssStyle()
1772
     *
1773
     * @param array $options The HTML options to be modified.
1774
     * @param array<string, string>|string $style The new style string (e.g. `'width: 100px; height: 200px'`) or array
1775
     * (e.g. `['width' => '100px', 'height' => '200px']`).
1776
     * @param bool $overwrite Whether to overwrite existing CSS properties if the new style contain them too.
1777
     */
1778
    public static function addCssStyle(array &$options, array|string $style, bool $overwrite = true): void
1779
    {
1780
        if (!empty($options['style'])) {
1781
            /** @psalm-var array<string,string>|string $options['style'] */
1782
            $oldStyle = is_array($options['style']) ? $options['style'] : self::cssStyleToArray($options['style']);
1783
            $newStyle = is_array($style) ? $style : self::cssStyleToArray($style);
0 ignored issues
show
introduced by
The condition is_array($style) is always true.
Loading history...
1784
            if (!$overwrite) {
1785
                foreach ($newStyle as $property => $_value) {
1786
                    if (isset($oldStyle[$property])) {
1787
                        unset($newStyle[$property]);
1788
                    }
1789
                }
1790 3
            }
1791
            $style = array_merge($oldStyle, $newStyle);
1792 3
        }
1793 3
        $options['style'] = is_array($style) ? self::cssStyleFromArray($style) : $style;
0 ignored issues
show
introduced by
The condition is_array($style) is always true.
Loading history...
1794 3
    }
1795 3
1796 3
    /**
1797
     * Removes the specified CSS styles from the HTML options.
1798
     *
1799
     * For example,
1800 3
     *
1801
     * ```php
1802
     * Html::removeCssStyle($options, ['width', 'height']);
1803
     * ```
1804
     *
1805
     * @see addCssStyle()
1806
     *
1807
     * @param array $options The HTML options to be modified.
1808
     * @param string|string[] $properties The CSS properties to be removed. You may use a string if you are removing a
1809
     * single property.
1810
     */
1811
    public static function removeCssStyle(array &$options, string|array $properties): void
1812
    {
1813
        if (!empty($options['style'])) {
1814
            /** @psalm-var array<string,string>|string $options['style'] */
1815
            $style = is_array($options['style']) ? $options['style'] : self::cssStyleToArray($options['style']);
1816
            foreach ((array) $properties as $property) {
1817
                unset($style[$property]);
1818
            }
1819
            $options['style'] = self::cssStyleFromArray($style);
1820
        }
1821
    }
1822
1823 13
    /**
1824
     * Converts a CSS style array into a string representation.
1825 13
     *
1826 1
     * For example,
1827
     *
1828
     * ```php
1829 12
     * // width: 100px; height: 200px;
1830
     * Html::cssStyleFromArray(['width' => '100px', 'height' => '200px']);
1831 12
     * ```
1832 8
     *
1833 4
     * @see cssStyleToArray()
1834 2
     *
1835
     * @param array<string, string> $style The CSS style array. The array keys are the CSS property names,
1836
     * and the array values are the corresponding CSS property values.
1837 10
     *
1838 10
     * @return string|null The CSS style string. If the CSS style is empty, a null will be returned.
1839 4
     */
1840
    public static function cssStyleFromArray(array $style): ?string
1841
    {
1842 6
        $result = '';
1843
        foreach ($style as $name => $value) {
1844
            $result .= "$name: $value; ";
1845 45
        }
1846
1847 45
        // Return null if empty to avoid rendering the "style" attribute.
1848
        return $result === '' ? null : rtrim($result);
1849
    }
1850 32
1851
    /**
1852 32
     * Converts a CSS style string into an array representation.
1853
     *
1854
     * The array keys are the CSS property names, and the array values are the corresponding CSS property values.
1855
     *
1856
     * For example,
1857
     *
1858
     * ```php
1859
     * // ['width' => '100px', 'height' => '200px']
1860 539
     * Html::cssStyleToArray('width: 100px; height: 200px;');
1861
     * ```
1862
     *
1863 539
     * @see cssStyleFromArray()
1864 104
     *
1865
     * @param string|\Stringable $style The CSS style string.
1866
     *
1867 504
     * @return array The array representation of the CSS style.
1868
     * @psalm-return array<string, string>
1869
     */
1870
    public static function cssStyleToArray(string|Stringable $style): array
1871
    {
1872
        $result = [];
1873
        foreach (explode(';', (string) $style) as $property) {
1874
            $property = explode(':', $property);
1875
            if (count($property) > 1) {
1876
                $result[trim($property[0])] = trim($property[1]);
1877
            }
1878
        }
1879
1880
        return $result;
1881
    }
1882
1883
    /**
1884
     * Normalize PCRE regular expression to use in the "pattern" HTML attribute:
1885
     *  - convert \x{FFFF} to \uFFFF;
1886
     *  - remove flags and delimiters.
1887
     *
1888
     * For example:
1889
     *
1890
     * ```php
1891
     * Html::normalizeRegexpPattern('/([a-z0-9-]+)/im'); // will return: ([a-z0-9-]+)
1892
     * ```
1893
     *
1894
     * @link https://html.spec.whatwg.org/multipage/input.html#the-pattern-attribute
1895
     *
1896
     * @param string $regexp PCRE regular expression.
1897
     * @param string|null $delimiter Regular expression delimiter.
1898
     *
1899
     * @throws InvalidArgumentException if incorrect regular expression or delimiter
1900
     *
1901
     * @return string Value for use in the "pattern" HTML attribute
1902
     */
1903
    public static function normalizeRegexpPattern(string $regexp, ?string $delimiter = null): string
1904
    {
1905
        if (strlen($regexp) < 2) {
1906
            throw new InvalidArgumentException('Incorrect regular expression.');
1907
        }
1908
1909
        $pattern = preg_replace('/\\\\x{?([0-9a-fA-F]+)}?/', '\u$1', $regexp);
1910
1911
        if ($delimiter === null) {
1912
            $delimiter = substr($pattern, 0, 1);
1913
        } elseif (strlen($delimiter) !== 1) {
1914
            throw new InvalidArgumentException('Incorrect delimiter.');
1915
        }
1916
1917
        $endPosition = strrpos($pattern, $delimiter, 1);
1918
        if ($endPosition === false) {
1919
            throw new InvalidArgumentException('Incorrect regular expression.');
1920
        }
1921
1922
        return substr($pattern, 1, $endPosition - 1);
1923
    }
1924
1925
    public static function getArrayableName(string $name): string
1926
    {
1927
        return !str_ends_with($name, '[]') ? $name . '[]' : $name;
1928
    }
1929
1930
    public static function getNonArrayableName(string $name): string
1931
    {
1932
        return str_ends_with($name, '[]') ? substr($name, 0, -2) : $name;
1933
    }
1934
1935
    /**
1936
     * Render attribute in HTML tag.
1937
     *
1938
     * @link https://html.spec.whatwg.org/#a-quick-introduction-to-html
1939
     */
1940
    private static function renderAttribute(string $name, string $encodedValue = '', string $quote = '"'): string
1941
    {
1942
        // The value, along with the "=" character, can be omitted altogether if the value is the empty string.
1943
        if ($encodedValue === '') {
1944
            return ' ' . $name;
1945
        }
1946
1947
        return ' ' . $name . '=' . $quote . $encodedValue . $quote;
1948
    }
1949
}
1950