Passed
Push — master ( 413a15...667180 )
by P.R.
02:15
created

Html::htmlNestedHelper()   B

Complexity

Conditions 11
Paths 11

Size

Total Lines 51
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 11

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 25
c 1
b 0
f 0
nc 11
nop 2
dl 0
loc 51
ccs 24
cts 24
cp 1
crap 11
rs 7.3166

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
4
namespace Plaisio\Helper;
5
6
use SetBased\Exception\FallenException;
7
use SetBased\Helper\Cast;
8
9
/**
10
 * A utility class for generating HTML elements, tags, and attributes.
11
 */
12
final class Html
13
{
14
  //--------------------------------------------------------------------------------------------------------------------
15
  /**
16
   * The encoding of the generated HTML code.
17
   *
18
   * @var string
19
   *
20
   * @since 1.0.0
21
   * @api
22
   */
23
  public static string $encoding = 'UTF-8';
24
25
  /**
26
   * Counter for generating unique element IDs.
27
   *
28
   * @var int
29
   */
30
  private static int $autoId = 0;
31
32
  /**
33
   * Map from (some) unicode characters to ASCII characters.
34
   *
35
   * @var array
36
   */
37
  private static array $trans = ['ß' => 'sz',
38
                                 'à' => 'a',
39
                                 'á' => 'a',
40
                                 'â' => 'a',
41
                                 'ã' => 'a',
42
                                 'ä' => 'a',
43
                                 'å' => 'a',
44
                                 'æ' => 'ae',
45
                                 'ç' => 'c',
46
                                 'è' => 'e',
47
                                 'é' => 'e',
48
                                 'ê' => 'e',
49
                                 'ë' => 'e',
50
                                 'ì' => 'i',
51
                                 'í' => 'i',
52
                                 'î' => 'i',
53
                                 'ï' => 'i',
54
                                 'ð' => 'e',
55
                                 'ñ' => 'n',
56
                                 'ò' => 'o',
57
                                 'ó' => 'o',
58
                                 'ô' => 'o',
59
                                 'õ' => 'o',
60
                                 'ö' => 'o',
61
                                 '÷' => 'x',
62
                                 'ø' => 'o',
63
                                 'ù' => 'u',
64
                                 'ú' => 'u',
65
                                 'û' => 'u',
66
                                 'ü' => 'u',
67
                                 'ý' => 'y',
68
                                 'þ' => 'b',
69
                                 'ÿ' => 'y',
70
                                 'č' => 'c',
71
                                 'ł' => 'l',
72
                                 'š' => 's',
73
                                 'ů' => 'u',
74
                                 'ž' => 'z',
75
                                 'а' => 'a',
76
                                 'б' => 'b',
77
                                 'в' => 'v',
78
                                 'г' => 'g',
79
                                 'д' => 'd',
80
                                 'е' => 'e',
81
                                 'ж' => 'zh',
82
                                 'з' => 'z',
83
                                 'и' => 'i',
84
                                 'й' => 'i',
85
                                 'к' => 'k',
86
                                 'л' => 'l',
87
                                 'м' => 'm',
88
                                 'н' => 'n',
89
                                 'о' => 'o',
90
                                 'п' => 'p',
91
                                 'р' => 'r',
92
                                 'с' => 's',
93
                                 'т' => 't',
94
                                 'у' => 'u',
95
                                 'ф' => 'f',
96
                                 'х' => 'kh',
97
                                 'ц' => 'ts',
98
                                 'ч' => 'ch',
99
                                 'ш' => 'sh',
100
                                 'щ' => 'shch',
101
                                 'ъ' => '',
102
                                 'ы' => 'y',
103
                                 'ь' => '',
104
                                 'э' => 'e',
105
                                 'ю' => 'iu',
106
                                 'я' => 'ia',
107
                                 'ё' => 'e'];
108
109
  //--------------------------------------------------------------------------------------------------------------------
110
  /**
111
   * Echos the HTML code of nested elements.
112
   *
113
   * Example:
114
   *
115
   * $html = Html::echoNested([['tag'   => 'table',
116
   *                            'attr'  => ['class' => 'test'],
117
   *                            'inner' => [['tag'   => 'tr',
118
   *                                         'attr'  => ['id' => 'first-row'],
119
   *                                         'inner' => [['tag'  => 'td',
120
   *                                                      'text' => 'hello'],
121
   *                                                     ['tag'  => 'td',
122
   *                                                      'attr' => ['class' => 'bold'],
123
   *                                                      'html' => '<b>world</b>']]],
124
   *                                        ['tag'   => 'tr',
125
   *                                         'inner' => [['tag'  => 'td',
126
   *                                                      'text' => 'foo'],
127
   *                                                     ['tag'  => 'td',
128
   *                                                      'text' => 'bar']]],
129
   *                                        ['tag'   => 'tr',
130
   *                                         'attr'  => ['id' => 'last-row'],
131
   *                                         'inner' => [['tag'  => 'td',
132
   *                                                      'text' => 'foo'],
133
   *                                                     ['tag'  => 'td',
134
   *                                                      'text' => 'bar']]]]],
135
   *                           ['text' => 'The End'],
136
   *                           ['html' => '!']]);
137
   *
138
   * @param array|null $structure The structure of the nested elements.
139
   *
140
   * @since 3.1.0
141
   * @api
142
   * @deprecated
143
   */
144 27
  public static function echoNested(?array $structure): void
145
  {
146 27
    if ($structure!==null)
147
    {
148 26
      $key = array_key_first($structure);
149 26
      if (is_int($key))
150
      {
151
        // Structure is a list of elements.
152 3
        foreach ($structure as $element)
153
        {
154 3
          self::echoNested($element);
0 ignored issues
show
Deprecated Code introduced by
The function Plaisio\Helper\Html::echoNested() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

154
          /** @scrutinizer ignore-deprecated */ self::echoNested($element);
Loading history...
155
        }
156
      }
157 25
      elseif ($key!==null)
158
      {
159
        // Structure is an associative array.
160 25
        if (isset($structure['tag']))
161
        {
162
          // Element with content.
163 24
          if (array_key_exists('inner', $structure))
164
          {
165 4
            self::echoTag($structure['tag'], $structure['attr'] ?? []);
166 4
            self::echoNested($structure['inner']);
0 ignored issues
show
Deprecated Code introduced by
The function Plaisio\Helper\Html::echoNested() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

166
            /** @scrutinizer ignore-deprecated */ self::echoNested($structure['inner']);
Loading history...
167 4
            echo '</', $structure['tag'], '>';
168
          }
169 23
          elseif (array_key_exists('text', $structure))
170
          {
171 5
            self::echoElement($structure['tag'], $structure['attr'] ?? [], $structure['text']);
172
          }
173 20
          elseif (array_key_exists('html', $structure))
174
          {
175 18
            self::echoElement($structure['tag'], $structure['attr'] ?? [], $structure['html'], true);
176
          }
177
          else
178
          {
179 24
            self::echoVoidElement($structure['tag'], $structure['attr'] ?? []);
180
          }
181
        }
182 2
        elseif (array_key_exists('text', $structure))
183
        {
184 1
          echo self::txt2Html($structure['text']);
185
        }
186 2
        elseif (array_key_exists('html', $structure))
187
        {
188 1
          echo $structure['html'];
189
        }
190
        else
191
        {
192 1
          throw new \LogicException("Expected key 'tag', 'text', or 'html'");
193
        }
194
      }
195
    }
196 26
  }
197
198
  //--------------------------------------------------------------------------------------------------------------------
199
  /**
200
   * Returns a string with proper conversion of special characters to HTML entities of an attribute of a HTML tag.
201
   *
202
   * Boolean attributes (e.g. checked, disabled and draggable, autocomplete also) are set when the value is none empty.
203
   *
204
   * @param string $name  The name of the attribute.
205
   * @param mixed  $value The value of the attribute.
206
   *
207
   * @return string
208
   *
209
   * @since 1.0.0
210
   * @api
211
   * @deprecated
212
   */
213 43
  public static function generateAttribute(string $name, $value): string
214
  {
215 43
    $html = '';
216
217
    switch ($name)
218
    {
219
      // Boolean attributes.
220 43
      case 'autofocus':
221 43
      case 'checked':
222 43
      case 'disabled':
223 43
      case 'hidden':
224 42
      case 'ismap':
225 42
      case 'multiple':
226 42
      case 'novalidate':
227 42
      case 'readonly':
228 42
      case 'required':
229 42
      case 'selected':
230 42
      case 'spellcheck':
231 4
        if (!empty($value))
232
        {
233 3
          $html = ' ';
234 3
          $html .= $name;
235 3
          $html .= '="';
236 3
          $html .= $name;
237 3
          $html .= '"';
238
        }
239 4
        break;
240
241
      // Annoying boolean attribute exceptions.
242 39
      case 'draggable':
243 3
        if ($value!==null)
244
        {
245 3
          if ($value==='auto')
246
          {
247 1
            $html = ' draggable="auto"';
248
          }
249 3
          elseif (empty($value) || $value==='false')
250
          {
251 2
            $html = ' draggable="false"';
252
          }
253
          else
254
          {
255 2
            $html = ' draggable="true"';
256
          }
257
        }
258 3
        break;
259
260 38
      case 'contenteditable':
261 3
        if ($value!==null)
262
        {
263 3
          $html = ' ';
264 3
          $html .= $name;
265 3
          $html .= (!empty($value)) ? '="true"' : '="false"';
266
        }
267 3
        break;
268
269 35
      case 'autocomplete':
270 2
        if ($value!==null)
271
        {
272 2
          $html = ' ';
273 2
          $html .= $name;
274 2
          $html .= (!empty($value)) ? '="on"' : '="off"';
275
        }
276 2
        break;
277
278 33
      case 'translate':
279 3
        if ($value!==null)
280
        {
281 3
          $html = ' ';
282 3
          $html .= $name;
283 3
          $html .= (!empty($value)) ? '="yes"' : '="no"';
284
        }
285 3
        break;
286
287 30
      case 'class' and is_array($value):
288 13
        $classes = implode(' ', self::cleanClasses($value));
289 13
        if ($classes!=='')
290
        {
291 12
          $html = ' class="';
292 12
          $html .= htmlspecialchars($classes, ENT_QUOTES, self::$encoding);
293 12
          $html .= '"';
294
        }
295 13
        break;
296
297
      default:
298 17
        if ($value!==null && $value!=='')
299
        {
300 14
          $html = ' ';
301 14
          $html .= htmlspecialchars($name, ENT_QUOTES, self::$encoding);
302 14
          $html .= '="';
303 14
          $html .= self::txt2Html($value);
304 14
          $html .= '"';
305
        }
306
    }
307
308 43
    return $html;
309
  }
310
311
  //--------------------------------------------------------------------------------------------------------------------
312
  /**
313
   * Generates the HTML code for an element.
314
   *
315
   * Note: tags for void elements such as '<br/>' are not supported.
316
   *
317
   * @param string                     $tagName    The name of the tag, e.g. a, form.
318
   * @param array                      $attributes The attributes of the tag. Special characters in the attributes will
319
   *                                               be replaced with HTML entities.
320
   * @param bool|int|float|string|null $innerText  The inner text of the tag.
321
   * @param bool                       $isHtml     If set the inner text is a HTML snippet, otherwise special
322
   *                                               characters in the inner text will be replaced with HTML entities.
323
   *
324
   * @return string
325
   *
326
   * @since 1.0.0
327
   * @api
328
   * @deprecated
329
   */
330 27
  public static function generateElement(string $tagName,
331
                                         array  $attributes = [],
332
                                                $innerText = '',
333
                                         bool   $isHtml = false): string
334
  {
335 27
    $html = self::generateTag($tagName, $attributes);
0 ignored issues
show
Deprecated Code introduced by
The function Plaisio\Helper\Html::generateTag() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

335
    $html = /** @scrutinizer ignore-deprecated */ self::generateTag($tagName, $attributes);
Loading history...
336 27
    $html .= ($isHtml) ? $innerText : self::txt2Html($innerText);
337 27
    $html .= '</';
338 27
    $html .= $tagName;
339 27
    $html .= '>';
340
341 27
    return $html;
342
  }
343
344
  //--------------------------------------------------------------------------------------------------------------------
345
  /**
346
   * Returns the HTML code of nested elements.
347
   *
348
   * Example:
349
   *
350
   * $html = Html::generateNested([['tag'   => 'table',
351
   *                                'attr'  => ['class' => 'test'],
352
   *                                'inner' => [['tag'   => 'tr',
353
   *                                             'attr'  => ['id' => 'first-row'],
354
   *                                             'inner' => [['tag'  => 'td',
355
   *                                                          'text' => 'hello'],
356
   *                                                         ['tag'  => 'td',
357
   *                                                          'attr' => ['class' => 'bold'],
358
   *                                                          'html' => '<b>world</b>']]],
359
   *                                            ['tag'   => 'tr',
360
   *                                             'inner' => [['tag'  => 'td',
361
   *                                                          'text' => 'foo'],
362
   *                                                         ['tag'  => 'td',
363
   *                                                          'text' => 'bar']]],
364
   *                                            ['tag'   => 'tr',
365
   *                                             'attr'  => ['id' => 'last-row'],
366
   *                                             'inner' => [['tag'  => 'td',
367
   *                                                          'text' => 'foo'],
368
   *                                                         ['tag'  => 'td',
369
   *                                                          'text' => 'bar']]]]],
370
   *                               ['text' => 'The End'],
371
   *                               ['html' => '!']]);
372
   *
373
   * @param array|null $structure The structure of the nested elements.
374
   *
375
   * @return string
376
   *
377
   * @since 1.4.0
378
   * @api
379
   * @deprecated
380
   */
381 11
  public static function generateNested(?array $structure): string
382
  {
383 11
    $html = '';
384 11
    self::htmlNestedHelper($structure, $html);
385
386 10
    return $html;
387
  }
388
389
  //--------------------------------------------------------------------------------------------------------------------
390
  /**
391
   * Generates the HTML code for a start tag of an element.
392
   *
393
   * @param string $tagName    The name of the tag, e.g. a, form.
394
   * @param array  $attributes The attributes of the tag. Special characters in the attributes will be replaced with
395
   *                           HTML entities.
396
   *
397
   * @return string
398
   *
399
   * @since 1.0.0
400
   * @api
401
   * @deprecated
402
   */
403 28
  public static function generateTag(string $tagName, array $attributes = []): string
404
  {
405 28
    $html = '<';
406 28
    $html .= $tagName;
407 28
    foreach ($attributes as $name => $value)
408
    {
409 26
      $html .= self::generateAttribute($name, $value);
0 ignored issues
show
Deprecated Code introduced by
The function Plaisio\Helper\Html::generateAttribute() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

409
      $html .= /** @scrutinizer ignore-deprecated */ self::generateAttribute($name, $value);
Loading history...
410
    }
411 28
    $html .= '>';
412
413 28
    return $html;
414
  }
415
416
  //--------------------------------------------------------------------------------------------------------------------
417
  /**
418
   * Generates the HTML code for a void element.
419
   *
420
   * Void elements are: area, base, br, col, embed, hr, img, input, keygen, link, menuitem, meta, param, source, track,
421
   * wbr. See <http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements>
422
   *
423
   * @param string $tagName    The name of the tag, e.g. img, link.
424
   * @param array  $attributes The attributes of the tag. Special characters in the attributes will be replaced with
425
   *                           HTML entities.
426
   *
427
   * @return string
428
   *
429
   * @since 1.0.0
430
   * @api
431
   * @deprecated
432
   */
433 3
  public static function generateVoidElement(string $tagName, array $attributes = []): string
434
  {
435 3
    $html = '<';
436 3
    $html .= $tagName;
437 3
    foreach ($attributes as $name => $value)
438
    {
439 1
      $html .= self::generateAttribute($name, $value);
0 ignored issues
show
Deprecated Code introduced by
The function Plaisio\Helper\Html::generateAttribute() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

439
      $html .= /** @scrutinizer ignore-deprecated */ self::generateAttribute($name, $value);
Loading history...
440
    }
441 3
    $html .= '/>';
442
443 3
    return $html;
444
  }
445
446
  //--------------------------------------------------------------------------------------------------------------------
447
  /**
448
   * Returns a string that can be safely used as an ID for an element. The format of the id is 'abc_<n>' where n is
449
   * incremented with each call of this method.
450
   *
451
   * @return string
452
   *
453
   * @since 1.0.0
454
   * @api
455
   */
456 2
  public static function getAutoId(): string
457
  {
458 2
    self::$autoId++;
459
460 2
    return 'plaisio-id-'.self::$autoId;
461
  }
462
463
  //--------------------------------------------------------------------------------------------------------------------
464
  /**
465
   * Returns the HTML code of nested elements.
466
   *
467
   * Example:
468
   *
469
   * $html = Html::generateNested([['tag'   => 'table',
470
   *                                'attr'  => ['class' => 'test'],
471
   *                                'inner' => [['tag'   => 'tr',
472
   *                                             'attr'  => ['id' => 'first-row'],
473
   *                                             'inner' => [['tag'  => 'td',
474
   *                                                          'text' => 'hello'],
475
   *                                                         ['tag'  => 'td',
476
   *                                                          'attr' => ['class' => 'bold'],
477
   *                                                          'html' => '<b>world</b>']]],
478
   *                                            ['tag'   => 'tr',
479
   *                                             'inner' => [['tag'  => 'td',
480
   *                                                          'text' => 'foo'],
481
   *                                                         ['tag'  => 'td',
482
   *                                                          'text' => 'bar']]],
483
   *                                            ['tag'   => 'tr',
484
   *                                             'attr'  => ['id' => 'last-row'],
485
   *                                             'inner' => [['tag'  => 'td',
486
   *                                                          'text' => 'foo'],
487
   *                                                         ['tag'  => 'td',
488
   *                                                          'text' => 'bar']]]]],
489
   *                               ['text' => 'The End'],
490
   *                               ['html' => '!']]);
491
   *
492
   * @param array|null $structure The structure of the nested elements.
493
   *
494
   * @return string
495
   *
496
   * @since 3.2.0
497
   * @api
498
   */
499
  public static function htmlNested(?array $structure): string
500
  {
501
    $html = '';
502
    self::htmlNestedHelper($structure, $html);
503
504
    return $html;
505
  }
506
507
  //--------------------------------------------------------------------------------------------------------------------
508
  /**
509
   * Returns a string with special characters converted to HTML entities.
510
   * This method is a wrapper around [htmlspecialchars](http://php.net/manual/en/function.htmlspecialchars.php).
511
   *
512
   * @param bool|int|float|string|null $value The string with optionally special characters.
513
   *
514
   * @return string
515
   *
516
   * @since 1.0.0
517
   * @api
518
   */
519 48
  public static function txt2Html($value): string
520
  {
521
    switch (true)
522
    {
523 48
      case is_string($value):
524 40
        return htmlspecialchars($value, ENT_QUOTES, self::$encoding);
525
526 14
      case is_int($value):
527 11
      case is_float($value):
528 10
      case $value===null:
529 8
        return (string)$value;
530
531 8
      case $value===true:
532 1
        return '1';
533
534 7
      case $value===false:
535 5
        return '0';
536
537
      default:
538 2
        throw new FallenException('type', is_object($value) ? get_class($value) : gettype($value));
539
    }
540
  }
541
542
  //--------------------------------------------------------------------------------------------------------------------
543
  /**
544
   * Returns the slug of a string that can be safely used in a URL.
545
   *
546
   * @param string|null $string The string.
547
   *
548
   * @return string
549
   *
550
   * @since 1.1.0
551
   * @api
552
   */
553 1
  public static function txt2Slug(?string $string): string
554
  {
555 1
    if ($string===null) return '';
556
557 1
    return trim(preg_replace('/[^0-9a-z]+/', '-', strtr(mb_strtolower($string), self::$trans)), '-');
558
  }
559
560
  //--------------------------------------------------------------------------------------------------------------------
561
  /**
562
   * Removes empty and duplicate classes from an array with classes.
563
   *
564
   * @param array $classes The classes.
565
   *
566
   * @return array
567
   */
568 16
  private static function cleanClasses(array $classes): array
569
  {
570 16
    $ret = [];
571
572 16
    foreach ($classes as $class)
573
    {
574 14
      $tmp = Cast::toManString($class, '');
575 14
      if ($tmp!=='') $ret[] = $tmp;
576
    }
577
578 16
    $ret = array_unique($ret, SORT_STRING);
579 16
    sort($ret);
580
581 16
    return $ret;
582
  }
583
584
  //--------------------------------------------------------------------------------------------------------------------
585
  /**
586
   * Echos an attribute of an HTML tag with proper conversion of special characters to HTML entities.
587
   *
588
   * Boolean attributes (e.g. checked, disabled and draggable, autocomplete also) are set when the value is none empty.
589
   *
590
   * @param string $name  The name of the attribute.
591
   * @param mixed  $value The value of the attribute.
592
   */
593 23
  private static function echoAttribute(string $name, $value): void
594
  {
595
    switch ($name)
596
    {
597
      // Boolean attributes.
598 23
      case 'autofocus':
599 23
      case 'checked':
600 23
      case 'disabled':
601 23
      case 'hidden':
602 23
      case 'ismap':
603 23
      case 'multiple':
604 23
      case 'novalidate':
605 23
      case 'readonly':
606 23
      case 'required':
607 23
      case 'selected':
608 23
      case 'spellcheck':
609 2
        if (!empty($value))
610
        {
611 1
          echo ' ', $name, '="', $name, '"';
612
        }
613 2
        break;
614
615
      // Annoying boolean attribute exceptions.
616 21
      case 'draggable':
617 2
        if ($value!==null)
618
        {
619 2
          if ($value==='auto')
620
          {
621 1
            echo ' draggable="auto"';
622
          }
623 2
          elseif (empty($value) || $value==='false')
624
          {
625 1
            echo ' draggable="false"';
626
          }
627
          else
628
          {
629 1
            echo ' draggable="true"';
630
          }
631
        }
632 2
        break;
633
634 21
      case 'contenteditable':
635 2
        if ($value!==null)
636
        {
637 2
          echo ' contenteditable', (!empty($value)) ? '="true"' : '="false"';
638
        }
639 2
        break;
640
641 19
      case 'autocomplete':
642 2
        if ($value!==null)
643
        {
644 2
          echo ' autocomplete', (!empty($value)) ? '="on"' : '="off"';
645
        }
646 2
        break;
647
648 17
      case 'translate':
649 2
        if ($value!==null)
650
        {
651 2
          echo ' translate', (!empty($value)) ? '="yes"' : '="no"';
652
        }
653 2
        break;
654
655 15
      case 'class' and is_array($value):
656 3
        $classes = implode(' ', self::cleanClasses($value));
657 3
        if ($classes!=='')
658
        {
659 2
          echo ' class="', htmlspecialchars($classes, ENT_QUOTES, self::$encoding), '"';
660
        }
661 3
        break;
662
663
      default:
664 12
        if ($value!==null && $value!=='')
665
        {
666 9
          echo ' ', htmlspecialchars($name, ENT_QUOTES, self::$encoding), '="', self::txt2Html($value), '"';
667
        }
668
    }
669 23
  }
670
671
  //--------------------------------------------------------------------------------------------------------------------
672
  /**
673
   * Echos the HTML code for an element.
674
   *
675
   * Note: tags for void elements such as '<br/>' are not supported.
676
   *
677
   * @param string                     $tagName    The name of the tag, e.g. a, form.
678
   * @param array                      $attributes The attributes of the tag. Special characters in the attributes will
679
   *                                               be replaced with HTML entities.
680
   * @param bool|int|float|string|null $innerText  The inner text of the tag.
681
   * @param bool                       $isHtml     If set the inner text is a HTML snippet, otherwise special
682
   *                                               characters in the inner text will be replaced with HTML entities.
683
   */
684 22
  private static function echoElement(string $tagName,
685
                                      array  $attributes = [],
686
                                             $innerText = '',
687
                                      bool   $isHtml = false): void
688
  {
689 22
    self::echoTag($tagName, $attributes);
690 22
    echo ($isHtml) ? $innerText : self::txt2Html($innerText);
691 22
    echo '</', $tagName, '>';
692 22
  }
693
694
  //--------------------------------------------------------------------------------------------------------------------
695
  /**
696
   * Echos the HTML code for a start tag of an element.
697
   *
698
   * @param string $tagName    The name of the tag, e.g. a, form.
699
   * @param array  $attributes The attributes of the tag. Special characters in the attributes will be replaced with
700
   *                           HTML entities.
701
   */
702 23
  private static function echoTag(string $tagName, array $attributes): void
703
  {
704 23
    echo '<', $tagName;
705 23
    foreach ($attributes as $name => $value)
706
    {
707 23
      self::echoAttribute($name, $value);
708
    }
709 23
    echo '>';
710 23
  }
711
712
  //--------------------------------------------------------------------------------------------------------------------
713
  /**
714
   * Echo the HTML code for a void element.
715
   *
716
   * Void elements are: area, base, br, col, embed, hr, img, input, keygen, link, menuitem, meta, param, source, track,
717
   * wbr. See <http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements>
718
   *
719
   * @param string $tagName    The name of the tag, e.g. img, link.
720
   * @param array  $attributes The attributes of the tag. Special characters in the attributes will be replaced with
721
   *                           HTML entities.
722
   */
723 2
  private static function echoVoidElement(string $tagName, array $attributes): void
724
  {
725 2
    echo '<', $tagName;
726 2
    foreach ($attributes as $name => $value)
727
    {
728
      self::echoAttribute($name, $value);
729
    }
730 2
    echo '/>';
731 2
  }
732
733
  //--------------------------------------------------------------------------------------------------------------------
734
  /**
735
   * Helper method for method generateNested().
736
   *
737
   * @param array|null $structure The (nested) structure of the HTML code.
738
   * @param string     $html      The generated HTML code.
739
   */
740 11
  private static function htmlNestedHelper(?array $structure, string &$html): void
741
  {
742 11
    if ($structure!==null)
743
    {
744 10
      $key = array_key_first($structure);
745 10
      if (is_int($key))
746
      {
747
        // Structure is a list of elements.
748 3
        foreach ($structure as $element)
749
        {
750 3
          self::htmlNestedHelper($element, $html);
751
        }
752
      }
753 9
      elseif ($key!==null)
754
      {
755
        // Structure is an associative array.
756 9
        if (isset($structure['tag']))
757
        {
758
          // Element with content.
759 8
          if (array_key_exists('inner', $structure))
760
          {
761 4
            $html .= self::generateTag($structure['tag'], $structure['attr'] ?? []);
0 ignored issues
show
Deprecated Code introduced by
The function Plaisio\Helper\Html::generateTag() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

761
            $html .= /** @scrutinizer ignore-deprecated */ self::generateTag($structure['tag'], $structure['attr'] ?? []);
Loading history...
762 4
            self::htmlNestedHelper($structure['inner'], $html);
763 4
            $html .= '</';
764 4
            $html .= $structure['tag'];
765 4
            $html .= '>';
766
          }
767 7
          elseif (array_key_exists('text', $structure))
768
          {
769 5
            $html .= self::generateElement($structure['tag'], $structure['attr'] ?? [], $structure['text']);
0 ignored issues
show
Deprecated Code introduced by
The function Plaisio\Helper\Html::generateElement() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

769
            $html .= /** @scrutinizer ignore-deprecated */ self::generateElement($structure['tag'], $structure['attr'] ?? [], $structure['text']);
Loading history...
770
          }
771 4
          elseif (array_key_exists('html', $structure))
772
          {
773 2
            $html .= self::generateElement($structure['tag'], $structure['attr'] ?? [], $structure['html'], true);
0 ignored issues
show
Deprecated Code introduced by
The function Plaisio\Helper\Html::generateElement() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

773
            $html .= /** @scrutinizer ignore-deprecated */ self::generateElement($structure['tag'], $structure['attr'] ?? [], $structure['html'], true);
Loading history...
774
          }
775
          else
776
          {
777 8
            $html .= self::generateVoidElement($structure['tag'], $structure['attr'] ?? []);
0 ignored issues
show
Deprecated Code introduced by
The function Plaisio\Helper\Html::generateVoidElement() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

777
            $html .= /** @scrutinizer ignore-deprecated */ self::generateVoidElement($structure['tag'], $structure['attr'] ?? []);
Loading history...
778
          }
779
        }
780 2
        elseif (array_key_exists('text', $structure))
781
        {
782 1
          $html .= self::txt2Html(Cast::toOptString($structure['text']));
783
        }
784 2
        elseif (array_key_exists('html', $structure))
785
        {
786 1
          $html .= $structure['html'];
787
        }
788
        else
789
        {
790 1
          throw new \LogicException("Expected key 'tag', 'text', or 'html'");
791
        }
792
      }
793
    }
794 10
  }
795
796
  //--------------------------------------------------------------------------------------------------------------------
797
}
798
799
//----------------------------------------------------------------------------------------------------------------------
800