Passed
Push — master ( 24b93d...97671c )
by P.R.
07:42
created

Html::echoElement()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 1
nop 4
dl 0
loc 8
ccs 4
cts 4
cp 1
crap 2
rs 10
c 0
b 0
f 0
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
   */
143 27
  public static function echoNested(?array $structure): void
144
  {
145 27
    if ($structure!==null)
146
    {
147 26
      $key = array_key_first($structure);
148 26
      if (is_int($key))
149
      {
150
        // Structure is a list of elements.
151 3
        foreach ($structure as $element)
152
        {
153 3
          self::echoNested($element);
154
        }
155
      }
156 25
      elseif ($key!==null)
157
      {
158
        // Structure is an associative array.
159 25
        if (isset($structure['tag']))
160
        {
161
          // Element with content.
162 24
          if (array_key_exists('inner', $structure))
163
          {
164 4
            self::echoTag($structure['tag'], $structure['attr'] ?? []);
165 4
            self::echoNested($structure['inner']);
166 4
            echo '</', $structure['tag'], '>';
167
          }
168 23
          elseif (array_key_exists('text', $structure))
169
          {
170 5
            self::echoElement($structure['tag'], $structure['attr'] ?? [], $structure['text']);
171
          }
172 20
          elseif (array_key_exists('html', $structure))
173
          {
174 18
            self::echoElement($structure['tag'], $structure['attr'] ?? [], $structure['html'], true);
175
          }
176
          else
177
          {
178 24
            self::echoVoidElement($structure['tag'], $structure['attr'] ?? []);
179
          }
180
        }
181 2
        elseif (array_key_exists('text', $structure))
182
        {
183 1
          echo self::txt2Html($structure['text']);
184
        }
185 2
        elseif (array_key_exists('html', $structure))
186
        {
187 1
          echo $structure['html'];
188
        }
189
        else
190
        {
191 1
          throw new \LogicException("Expected key 'tag', 'text', or 'html'");
192
        }
193
      }
194
    }
195 26
  }
196
197
  //--------------------------------------------------------------------------------------------------------------------
198
  /**
199
   * Returns a string with proper conversion of special characters to HTML entities of an attribute of a HTML tag.
200
   *
201
   * Boolean attributes (e.g. checked, disabled and draggable, autocomplete also) are set when the value is none empty.
202
   *
203
   * @param string $name  The name of the attribute.
204
   * @param mixed  $value The value of the attribute.
205
   *
206
   * @return string
207
   *
208
   * @since 1.0.0
209
   * @api
210
   */
211 43
  public static function generateAttribute(string $name, $value): string
212
  {
213 43
    $html = '';
214
215
    switch ($name)
216
    {
217
      // Boolean attributes.
218 43
      case 'autofocus':
219 43
      case 'checked':
220 43
      case 'disabled':
221 43
      case 'hidden':
222 42
      case 'ismap':
223 42
      case 'multiple':
224 42
      case 'novalidate':
225 42
      case 'readonly':
226 42
      case 'required':
227 42
      case 'selected':
228 42
      case 'spellcheck':
229 4
        if (!empty($value))
230
        {
231 3
          $html = ' ';
232 3
          $html .= $name;
233 3
          $html .= '="';
234 3
          $html .= $name;
235 3
          $html .= '"';
236
        }
237 4
        break;
238
239
      // Annoying boolean attribute exceptions.
240 39
      case 'draggable':
241 3
        if ($value!==null)
242
        {
243 3
          if ($value==='auto')
244
          {
245 1
            $html = ' draggable="auto"';
246
          }
247 3
          elseif (empty($value) || $value==='false')
248
          {
249 2
            $html = ' draggable="false"';
250
          }
251
          else
252
          {
253 2
            $html = ' draggable="true"';
254
          }
255
        }
256 3
        break;
257
258 38
      case 'contenteditable':
259 3
        if ($value!==null)
260
        {
261 3
          $html = ' ';
262 3
          $html .= $name;
263 3
          $html .= (!empty($value)) ? '="true"' : '="false"';
264
        }
265 3
        break;
266
267 35
      case 'autocomplete':
268 2
        if ($value!==null)
269
        {
270 2
          $html = ' ';
271 2
          $html .= $name;
272 2
          $html .= (!empty($value)) ? '="on"' : '="off"';
273
        }
274 2
        break;
275
276 33
      case 'translate':
277 3
        if ($value!==null)
278
        {
279 3
          $html = ' ';
280 3
          $html .= $name;
281 3
          $html .= (!empty($value)) ? '="yes"' : '="no"';
282
        }
283 3
        break;
284
285 30
      case 'class' and is_array($value):
286 13
        $classes = implode(' ', self::cleanClasses($value));
287 13
        if ($classes!=='')
288
        {
289 12
          $html = ' class="';
290 12
          $html .= htmlspecialchars($classes, ENT_QUOTES, self::$encoding);
291 12
          $html .= '"';
292
        }
293 13
        break;
294
295
      default:
296 17
        if ($value!==null && $value!=='')
297
        {
298 14
          $html = ' ';
299 14
          $html .= htmlspecialchars($name, ENT_QUOTES, self::$encoding);
300 14
          $html .= '="';
301 14
          $html .= self::txt2Html($value);
302 14
          $html .= '"';
303
        }
304
    }
305
306 43
    return $html;
307
  }
308
309
  //--------------------------------------------------------------------------------------------------------------------
310
  /**
311
   * Generates the HTML code for an element.
312
   *
313
   * Note: tags for void elements such as '<br/>' are not supported.
314
   *
315
   * @param string                     $tagName    The name of the tag, e.g. a, form.
316
   * @param array                      $attributes The attributes of the tag. Special characters in the attributes will
317
   *                                               be replaced with HTML entities.
318
   * @param bool|int|float|string|null $innerText  The inner text of the tag.
319
   * @param bool                       $isHtml     If set the inner text is a HTML snippet, otherwise special
320
   *                                               characters in the inner text will be replaced with HTML entities.
321
   *
322
   * @return string
323
   *
324
   * @since 1.0.0
325
   * @api
326
   */
327 27
  public static function generateElement(string $tagName,
328
                                         array  $attributes = [],
329
                                                $innerText = '',
330
                                         bool   $isHtml = false): string
331
  {
332 27
    $html = self::generateTag($tagName, $attributes);
333 27
    $html .= ($isHtml) ? $innerText : self::txt2Html($innerText);
334 27
    $html .= '</';
335 27
    $html .= $tagName;
336 27
    $html .= '>';
337
338 27
    return $html;
339
  }
340
341
  //--------------------------------------------------------------------------------------------------------------------
342
  /**
343
   * Returns the HTML code of nested elements.
344
   *
345
   * Example:
346
   *
347
   * $html = Html::generateNested([['tag'   => 'table',
348
   *                                'attr'  => ['class' => 'test'],
349
   *                                'inner' => [['tag'   => 'tr',
350
   *                                             'attr'  => ['id' => 'first-row'],
351
   *                                             'inner' => [['tag'  => 'td',
352
   *                                                          'text' => 'hello'],
353
   *                                                         ['tag'  => 'td',
354
   *                                                          'attr' => ['class' => 'bold'],
355
   *                                                          'html' => '<b>world</b>']]],
356
   *                                            ['tag'   => 'tr',
357
   *                                             'inner' => [['tag'  => 'td',
358
   *                                                          'text' => 'foo'],
359
   *                                                         ['tag'  => 'td',
360
   *                                                          'text' => 'bar']]],
361
   *                                            ['tag'   => 'tr',
362
   *                                             'attr'  => ['id' => 'last-row'],
363
   *                                             'inner' => [['tag'  => 'td',
364
   *                                                          'text' => 'foo'],
365
   *                                                         ['tag'  => 'td',
366
   *                                                          'text' => 'bar']]]]],
367
   *                               ['text' => 'The End'],
368
   *                               ['html' => '!']]);
369
   *
370
   * @param array|null $structure The structure of the nested elements.
371
   *
372
   * @return string
373
   *
374
   * @since 1.4.0
375
   * @api
376
   */
377 11
  public static function generateNested(?array $structure): string
378
  {
379 11
    $html = '';
380 11
    self::generateNestedHelper($structure, $html);
381
382 10
    return $html;
383
  }
384
385
  //--------------------------------------------------------------------------------------------------------------------
386
  /**
387
   * Generates the HTML code for a start tag of an element.
388
   *
389
   * @param string $tagName    The name of the tag, e.g. a, form.
390
   * @param array  $attributes The attributes of the tag. Special characters in the attributes will be replaced with
391
   *                           HTML entities.
392
   *
393
   * @return string
394
   *
395
   * @since 1.0.0
396
   * @api
397
   */
398 28
  public static function generateTag(string $tagName, array $attributes = []): string
399
  {
400 28
    $html = '<';
401 28
    $html .= $tagName;
402 28
    foreach ($attributes as $name => $value)
403
    {
404 26
      $html .= self::generateAttribute($name, $value);
405
    }
406 28
    $html .= '>';
407
408 28
    return $html;
409
  }
410
411
  //--------------------------------------------------------------------------------------------------------------------
412
  /**
413
   * Generates the HTML code for a void element.
414
   *
415
   * Void elements are: area, base, br, col, embed, hr, img, input, keygen, link, menuitem, meta, param, source, track,
416
   * wbr. See <http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements>
417
   *
418
   * @param string $tagName    The name of the tag, e.g. img, link.
419
   * @param array  $attributes The attributes of the tag. Special characters in the attributes will be replaced with
420
   *                           HTML entities.
421
   *
422
   * @return string
423
   *
424
   * @since 1.0.0
425
   * @api
426
   */
427 3
  public static function generateVoidElement(string $tagName, array $attributes = []): string
428
  {
429 3
    $html = '<';
430 3
    $html .= $tagName;
431 3
    foreach ($attributes as $name => $value)
432
    {
433 1
      $html .= self::generateAttribute($name, $value);
434
    }
435 3
    $html .= '/>';
436
437 3
    return $html;
438
  }
439
440
  //--------------------------------------------------------------------------------------------------------------------
441
  /**
442
   * 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
443
   * incremented with each call of this method.
444
   *
445
   * @return string
446
   *
447
   * @since 1.0.0
448
   * @api
449
   */
450 2
  public static function getAutoId(): string
451
  {
452 2
    self::$autoId++;
453
454 2
    return 'plaisio-id-'.self::$autoId;
455
  }
456
457
  //--------------------------------------------------------------------------------------------------------------------
458
  /**
459
   * Returns a string with special characters converted to HTML entities.
460
   * This method is a wrapper around [htmlspecialchars](http://php.net/manual/en/function.htmlspecialchars.php).
461
   *
462
   * @param bool|int|float|string|null $value The string with optionally special characters.
463
   *
464
   * @return string
465
   *
466
   * @since 1.0.0
467
   * @api
468
   */
469 48
  public static function txt2Html($value): string
470
  {
471
    switch (true)
472
    {
473 48
      case is_string($value):
474 40
        return htmlspecialchars($value, ENT_QUOTES, self::$encoding);
475
476 14
      case is_int($value):
477 11
      case is_float($value):
478 10
      case $value===null:
479 8
        return (string)$value;
480
481 8
      case $value===true:
482 1
        return '1';
483
484 7
      case $value===false:
485 5
        return '0';
486
487
      default:
488 2
        throw new FallenException('type', is_object($value) ? get_class($value) : gettype($value));
489
    }
490
  }
491
492
  //--------------------------------------------------------------------------------------------------------------------
493
  /**
494
   * Returns the slug of a string that can be safely used in an URL.
495
   *
496
   * @param string|null $string The string.
497
   *
498
   * @return string
499
   *
500
   * @since 1.1.0
501
   * @api
502
   */
503 1
  public static function txt2Slug(?string $string): string
504
  {
505 1
    if ($string===null) return '';
506
507 1
    return trim(preg_replace('/[^0-9a-z]+/', '-', strtr(mb_strtolower($string), self::$trans)), '-');
508
  }
509
510
  //--------------------------------------------------------------------------------------------------------------------
511
  /**
512
   * Removes empty and duplicate classes from an array with classes.
513
   *
514
   * @param array $classes The classes.
515
   *
516
   * @return array
517
   */
518 16
  private static function cleanClasses(array $classes): array
519
  {
520 16
    $ret = [];
521
522 16
    foreach ($classes as $class)
523
    {
524 14
      $tmp = Cast::toManString($class, '');
525 14
      if ($tmp!=='') $ret[] = $tmp;
526
    }
527
528 16
    $ret = array_unique($ret, SORT_STRING);
529 16
    sort($ret);
530
531 16
    return $ret;
532
  }
533
534
  //--------------------------------------------------------------------------------------------------------------------
535
  /**
536
   * Echos an attribute of an HTML tag with proper conversion of special characters to HTML entities.
537
   *
538
   * Boolean attributes (e.g. checked, disabled and draggable, autocomplete also) are set when the value is none empty.
539
   *
540
   * @param string $name  The name of the attribute.
541
   * @param mixed  $value The value of the attribute.
542
   */
543 23
  private static function echoAttribute(string $name, $value): void
544
  {
545
    switch ($name)
546
    {
547
      // Boolean attributes.
548 23
      case 'autofocus':
549 23
      case 'checked':
550 23
      case 'disabled':
551 23
      case 'hidden':
552 23
      case 'ismap':
553 23
      case 'multiple':
554 23
      case 'novalidate':
555 23
      case 'readonly':
556 23
      case 'required':
557 23
      case 'selected':
558 23
      case 'spellcheck':
559 2
        if (!empty($value))
560
        {
561 1
          echo ' ', $name, '="', $name, '"';
562
        }
563 2
        break;
564
565
      // Annoying boolean attribute exceptions.
566 21
      case 'draggable':
567 2
        if ($value!==null)
568
        {
569 2
          if ($value==='auto')
570
          {
571 1
            echo ' draggable="auto"';
572
          }
573 2
          elseif (empty($value) || $value==='false')
574
          {
575 1
            echo ' draggable="false"';
576
          }
577
          else
578
          {
579 1
            echo ' draggable="true"';
580
          }
581
        }
582 2
        break;
583
584 21
      case 'contenteditable':
585 2
        if ($value!==null)
586
        {
587 2
          echo ' contenteditable', (!empty($value)) ? '="true"' : '="false"';
588
        }
589 2
        break;
590
591 19
      case 'autocomplete':
592 2
        if ($value!==null)
593
        {
594 2
          echo ' autocomplete', (!empty($value)) ? '="on"' : '="off"';
595
        }
596 2
        break;
597
598 17
      case 'translate':
599 2
        if ($value!==null)
600
        {
601 2
          echo ' translate', (!empty($value)) ? '="yes"' : '="no"';
602
        }
603 2
        break;
604
605 15
      case 'class' and is_array($value):
606 3
        $classes = implode(' ', self::cleanClasses($value));
607 3
        if ($classes!=='')
608
        {
609 2
          echo ' class="', htmlspecialchars($classes, ENT_QUOTES, self::$encoding), '"';
610
        }
611 3
        break;
612
613
      default:
614 12
        if ($value!==null && $value!=='')
615
        {
616 9
          echo ' ', htmlspecialchars($name, ENT_QUOTES, self::$encoding), '="', self::txt2Html($value), '"';
617
        }
618
    }
619 23
  }
620
621
  //--------------------------------------------------------------------------------------------------------------------
622
  /**
623
   * Echos the HTML code for an element.
624
   *
625
   * Note: tags for void elements such as '<br/>' are not supported.
626
   *
627
   * @param string                     $tagName    The name of the tag, e.g. a, form.
628
   * @param array                      $attributes The attributes of the tag. Special characters in the attributes will
629
   *                                               be replaced with HTML entities.
630
   * @param bool|int|float|string|null $innerText  The inner text of the tag.
631
   * @param bool                       $isHtml     If set the inner text is a HTML snippet, otherwise special
632
   *                                               characters in the inner text will be replaced with HTML entities.
633
   */
634 22
  private static function echoElement(string $tagName,
635
                                      array  $attributes = [],
636
                                             $innerText = '',
637
                                      bool   $isHtml = false): void
638
  {
639 22
    self::echoTag($tagName, $attributes);
640 22
    echo ($isHtml) ? $innerText : self::txt2Html($innerText);
641 22
    echo '</', $tagName, '>';
642 22
  }
643
644
  //--------------------------------------------------------------------------------------------------------------------
645
  /**
646
   * Echos the HTML code for a start tag of an element.
647
   *
648
   * @param string $tagName    The name of the tag, e.g. a, form.
649
   * @param array  $attributes The attributes of the tag. Special characters in the attributes will be replaced with
650
   *                           HTML entities.
651
   */
652 23
  private static function echoTag(string $tagName, array $attributes): void
653
  {
654 23
    echo '<', $tagName;
655 23
    foreach ($attributes as $name => $value)
656
    {
657 23
      self::echoAttribute($name, $value);
658
    }
659 23
    echo '>';
660 23
  }
661
662
  //--------------------------------------------------------------------------------------------------------------------
663
  /**
664
   * Echo the HTML code for a void element.
665
   *
666
   * Void elements are: area, base, br, col, embed, hr, img, input, keygen, link, menuitem, meta, param, source, track,
667
   * wbr. See <http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements>
668
   *
669
   * @param string $tagName    The name of the tag, e.g. img, link.
670
   * @param array  $attributes The attributes of the tag. Special characters in the attributes will be replaced with
671
   *                           HTML entities.
672
   */
673 2
  private static function echoVoidElement(string $tagName, array $attributes): void
674
  {
675 2
    echo '<', $tagName;
676 2
    foreach ($attributes as $name => $value)
677
    {
678
      self::echoAttribute($name, $value);
679
    }
680 2
    echo '/>';
681 2
  }
682
683
  //--------------------------------------------------------------------------------------------------------------------
684
  /**
685
   * Helper method for method generateNested().
686
   *
687
   * @param array|null $structure The (nested) structure of the HTML code.
688
   * @param string     $html      The generated HTML code.
689
   */
690 11
  private static function generateNestedHelper(?array $structure, string &$html): void
691
  {
692 11
    if ($structure!==null)
1 ignored issue
show
introduced by
The condition $structure !== null is always true.
Loading history...
693
    {
694 10
      $key = array_key_first($structure);
695 10
      if (is_int($key))
696
      {
697
        // Structure is a list of elements.
698 3
        foreach ($structure as $element)
699
        {
700 3
          self::generateNestedHelper($element, $html);
701
        }
702
      }
703 9
      elseif ($key!==null)
704
      {
705
        // Structure is an associative array.
706 9
        if (isset($structure['tag']))
707
        {
708
          // Element with content.
709 8
          if (array_key_exists('inner', $structure))
710
          {
711 4
            $html .= self::generateTag($structure['tag'], $structure['attr'] ?? []);
712 4
            self::generateNestedHelper($structure['inner'], $html);
713 4
            $html .= '</';
714 4
            $html .= $structure['tag'];
715 4
            $html .= '>';
716
          }
717 7
          elseif (array_key_exists('text', $structure))
718
          {
719 5
            $html .= self::generateElement($structure['tag'], $structure['attr'] ?? [], $structure['text']);
720
          }
721 4
          elseif (array_key_exists('html', $structure))
722
          {
723 2
            $html .= self::generateElement($structure['tag'], $structure['attr'] ?? [], $structure['html'], true);
724
          }
725
          else
726
          {
727 8
            $html .= self::generateVoidElement($structure['tag'], $structure['attr'] ?? []);
728
          }
729
        }
730 2
        elseif (array_key_exists('text', $structure))
731
        {
732 1
          $html .= self::txt2Html(Cast::toOptString($structure['text']));
733
        }
734 2
        elseif (array_key_exists('html', $structure))
735
        {
736 1
          $html .= $structure['html'];
737
        }
738
        else
739
        {
740 1
          throw new \LogicException("Expected key 'tag', 'text', or 'html'");
741
        }
742
      }
743
    }
744 10
  }
745
746
  //--------------------------------------------------------------------------------------------------------------------
747
}
748
749
//----------------------------------------------------------------------------------------------------------------------
750