-
Notifications
You must be signed in to change notification settings - Fork 1
/
htmlBuilder.php
114 lines (108 loc) · 3.21 KB
/
htmlBuilder.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
<?php
/**
* An array containing the names of the HTML tags which must not have a closing tag.
*
* @var array
*/
$VOID_ELEMENTS = [
'area' => 1,
'base' => 1,
'br' => 1,
'col' => 1,
'command' => 1,
'embed' => 1,
'hr' => 1,
'img' => 1,
'input' => 1,
'keygen' => 1,
'link' => 1,
'meta' => 1,
'param' => 1,
'source' => 1,
'track' => 1,
'wbr' => 1,
];
/**
* Creates an array representation of an html tag.
*
* @param string $selector Syntax: 'tag#id.class1.class2...classN', all elements are optional. Default tag is
* 'div'.
* @param array|string $attrs It can also receive the value of $content, but BEWARE: the content array MUST have
* integer keys, otherwise it will be interpreted as an attributes array.
* @param array|string $content
* @return array
*/
function h ($selector, $attrs = [], $content = [])
{
$id = str_extract ($selector, '/#([\w\-]*)/');
$classes = str_extract ($selector, '/\.([\w\-\.]*)/');
$tag = $selector === '' ? 'div' : $selector;
$classes = array_filter (explode ('.', $classes));
if (is_string ($attrs)) {
$content = $attrs;
$attrs = [];
} // Check if $content is specified instead of $attrs and adjust arguments.
else if (array_key_exists (0, $attrs)) { // supports having a null as the first array item.
$content = $attrs;
$attrs = [];
}
$class = isset($attrs['class']) ? $attrs['class'] : null;
if ($class !== null && $class !== '')
$classes = array_merge ($classes, explode (' ', $class));
unset ($attrs['class']); // must be unset even if the attribute is null or ''
$outAttrs = [];
if ($id)
$outAttrs['id'] = $id;
if ($classes)
$outAttrs['class'] = implode (' ', $classes);
// Put ID and CLASS attributes first.
if ($outAttrs) $outAttrs = array_merge ($outAttrs, $attrs);
else $outAttrs = $attrs;
return [
'<' => $tag,
'@' => $outAttrs,
'[' => $content,
];
}
/**
* Render a tree-like structure into HTML.
*
* @param array|string|null $e
* @param int $d Depth.
* @return string
* @throws \InvalidArgumentException
*/
function html ($e, $d = 0)
{
global $VOID_ELEMENTS;
if (is_null ($e)) return '';
if (is_string ($e)) return $e;
if (isset($e['<'])) {
$tag = $e['<'];
$attrs = $e['@'];
$content = $e['['];
$s = str_repeat (' ', $d);
$o = ($d ? "\n" : '') . "$s<$tag";
foreach ($attrs as $k => $v) {
if (is_null ($v)) continue;
if (is_bool ($v)) {
if ($v) $o .= " $k";
}
elseif (!is_scalar ($v))
throw new \InvalidArgumentException("Non-scalar properties are not supported");
else {
$v = htmlspecialchars ($v);
$o .= " $k=\"$v\"";
}
}
$o .= ">";
$c = isset($VOID_ELEMENTS[$tag]) ? '' : "</$tag>";
if (empty($content))
return "$o$c";
$o .= html ($content, $d + 1);
return substr ($o, -1) == '>' ? "$o\n$s$c" : "$o$c";
}
if (is_array ($e))
return implode ('', map ($e, function ($v) use ($d) { return html ($v, $d + 1); }));
throw new \InvalidArgumentException("Unsupported argument type for html(): " . gettype ($e));
}