Passed
Push — main ( 68e606...997043 )
by Thierry
02:29
created

HtmlElement::renderAttributes()   B

Complexity

Conditions 8
Paths 40

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 12
nc 40
nop 0
dl 0
loc 21
rs 8.4444
c 0
b 0
f 0
1
<?php
2
3
namespace Lagdo\UiBuilder\Component;
4
5
use Lagdo\UiBuilder\Component\Html\Element;
6
use Lagdo\UiBuilder\Engine\Engine;
7
8
use function htmlspecialchars;
9
use function implode;
10
use function is_numeric;
11
use function is_string;
12
use function sprintf;
13
use function trim;
14
15
/**
16
 * The HTML tags finally generated for a given component
17
 *
18
 * @method static setId(string $id, bool $escape = true)
19
 * @method static setClass(string $class, bool $escape = true)
20
 * @method static setFor(string $for, bool $escape = true)
21
 * @method static setName(string $name, bool $escape = true)
22
 * @method static setValue(string $value, bool $escape = true)
23
 * @method static setType(string $type, bool $escape = true)
24
 * @method static setTitle(string $type, bool $escape = true)
25
 * @method static setStyle(string $type, bool $escape = true)
26
 */
27
class HtmlElement extends Element
28
{
29
    /**
30
     * @var bool
31
     */
32
    private $isShort = false;
33
34
    /**
35
     * @var bool
36
     */
37
    private $isOpened = false;
38
39
    /**
40
     * @var array
41
     */
42
    private $attributes = [];
43
44
    /**
45
     * @var array
46
     */
47
    private $escapes = [];
48
49
    /**
50
     * @var array
51
     */
52
    private $baseClasses = []; // The component classes.
53
54
    /**
55
     * @var array
56
     */
57
    private $classes = []; // The additional classes.
58
59
    /**
60
     * @var array<Element>
61
     */
62
    private $children = [];
63
64
    /**
65
     * @param Engine $engine
66
     * @param string $tag
67
     * @param array $attributes
68
     */
69
    public function __construct(private Engine $engine, private string $tag, array $attributes = [])
70
    {
71
        $this->setAttributes($attributes);
72
    }
73
74
    /**
75
     * @param string $method
76
     * @param array $arguments
77
     *
78
     * @return static
79
     */
80
    public function __call(string $method, array $arguments): static
81
    {
82
        $this->engine->callElementHelper($this, $method, $arguments);
83
        return $this;
84
    }
85
86
    /**
87
     * @param string $tag
88
     *
89
     * @return void
90
     */
91
    public function setTag(string $tag): void
92
    {
93
        $this->tag = $tag;
94
    }
95
96
    /**
97
     * @param array<Element> $children
98
     *
99
     * @return void
100
     */
101
    public function setChildren(array $children): void
102
    {
103
        $this->children = $children;
104
    }
105
106
    /**
107
     * @param array<Element> $children
108
     *
109
     * @return void
110
     */
111
    public function addChildren(array $children): void
112
    {
113
        $this->children = [...$this->children, ...$children];
0 ignored issues
show
Documentation Bug introduced by
It seems like array($this->children, $children) of type array<integer,Lagdo\UiBu...mponent\Html\Element[]> is incompatible with the declared type Lagdo\UiBuilder\Component\Html\Element[] of property $children.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
114
    }
115
116
    /**
117
     * @param Element $child
118
     *
119
     * @return void
120
     */
121
    public function addChild(Element $child): void
122
    {
123
        $this->children[] = $child;
124
    }
125
126
    /**
127
     * @param string $class
128
     *
129
     * @return void
130
     */
131
    public function addBaseClass(string $class): void
132
    {
133
        $this->baseClasses[] = trim($class);
134
    }
135
136
    /**
137
     * @param int $index
138
     * @param string $class
139
     *
140
     * @return void
141
     */
142
    public function setBaseClass(int $index, string $class): void
143
    {
144
       $this->baseClasses[$index] = trim($class);
145
    }
146
147
    /**
148
     * @param string $class
149
     *
150
     * @return void
151
     */
152
    public function addClass(string $class): void
153
    {
154
        $this->classes[] = trim($class);
155
    }
156
157
    /**
158
     * @param string $class
159
     *
160
     * @return void
161
     */
162
    public function setClass(string $class): void
163
    {
164
        // Actually appends the class.
165
        $this->addClass($class);
166
    }
167
168
    /**
169
     * @param bool $isShort
170
     *
171
     * @return void
172
     */
173
    public function setShort($isShort): void
174
    {
175
        $this->isShort = $isShort;
176
    }
177
178
    /**
179
     * @param bool $isOpened
180
     *
181
     * @return void
182
     */
183
    public function setOpened($isOpened): void
184
    {
185
        $this->isOpened = $isOpened;
186
    }
187
188
    /**
189
     * @param string $name
190
     * @param string|null $value
191
     * @param bool $escape
192
     *
193
     * @return void
194
     */
195
    public function setAttribute(string $name, string|null $value = null, bool $escape = true): void
196
    {
197
        $this->attributes[$name] = $value;
198
        $this->escapes[$name] = $escape;
199
    }
200
201
    /**
202
     * @param array $attributes
203
     * @param bool $escape
204
     *
205
     * @return void
206
     */
207
    public function setAttributes(array $attributes, bool $escape = true): void
208
    {
209
        foreach ($attributes as $name => $value) {
210
            switch (true) {
211
            case is_numeric($name):
212
                $this->setAttribute($value);
213
                break;
214
            case is_string($value):
215
                $this->setAttribute($name, $value, $escape);
216
                break;
217
            default: // Any other values are ignored.
218
            }
219
        }
220
    }
221
222
    /**
223
     * @return string
224
     */
225
    private function renderAttributes(): string
226
    {
227
        // Merge the classes.
228
        $classes = [...$this->baseClasses, ...$this->classes];
229
        if (isset($this->attributes['class'])) {
230
            $classes[] = $this->attributes['class'];
231
        }
232
        if (($class = trim(implode(' ', $classes))) !== '') {
233
            $this->attributes['class'] = $class;
234
        }
235
236
        $attributes = [];
237
        foreach ($this->attributes as $name => $value) {
238
            $name = $this->escape($name);
239
            if ($value !== null && ($this->escapes[$name] ?? true) === true) {
240
                $value = $this->escape($value);
241
            }
242
            $attributes[] = $value !== null ? "$name=\"$value\"" : $name;
243
        }
244
245
        return !$attributes ? '' : ' ' . implode(' ', $attributes);
246
    }
247
248
    /**
249
     * @return string
250
     */
251
    private function renderShort(): string
252
    {
253
        return sprintf('<%s%s />', $this->tag, $this->renderAttributes());
254
    }
255
256
    /**
257
     * @return string
258
     */
259
    private function renderOpened(): string
260
    {
261
        return sprintf('<%s%s>', $this->tag, $this->renderAttributes());
262
    }
263
264
    /**
265
     * @return string
266
     */
267
    private function renderTag(): string
268
    {
269
        $children = implode('', $this->children);
270
        return sprintf('%s%s</%s>', $this->renderOpened(), $children, $this->tag);
271
    }
272
273
    /**
274
     * @param $value
275
     *
276
     * @return string
277
     */
278
    private function escape($value): string
279
    {
280
        return htmlspecialchars($value, ENT_COMPAT);
281
    }
282
283
    /**
284
     * @inheritDoc
285
     */
286
    protected function render(): string
287
    {
288
        return match(true) {
289
            $this->isShort => $this->renderShort(),
290
            $this->isOpened => $this->renderOpened(),
291
            default => $this->renderTag(),
292
        };
293
    }
294
}
295