Passed
Push — main ( 47a406...6037b2 )
by Thierry
02:35
created

HtmlElement::setAttributes()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 9
c 1
b 0
f 0
nc 4
nop 2
dl 0
loc 11
rs 9.9666
1
<?php
2
3
namespace Lagdo\UiBuilder\Component\Base;
4
5
use Lagdo\UiBuilder\Component\Html\Element;
6
use Lagdo\UiBuilder\Html\HtmlBuilder;
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
class HtmlElement extends Element
19
{
20
    /**
21
     * @var bool
22
     */
23
    private $isShort = false;
24
25
    /**
26
     * @var bool
27
     */
28
    private $isOpened = false;
29
30
    /**
31
     * @var array
32
     */
33
    private $attributes = [];
34
35
    /**
36
     * @var array
37
     */
38
    private $escapes = [];
39
40
    /**
41
     * @var array
42
     */
43
    private $baseClasses = []; // The component classes.
44
45
    /**
46
     * @var array
47
     */
48
    private $classes = []; // The additional classes.
49
50
    /**
51
     * @var array<Element>
52
     */
53
    private $children = [];
54
55
    /**
56
     * @param HtmlBuilder $builder
57
     * @param string $tag
58
     * @param array $attributes
59
     */
60
    public function __construct(private HtmlBuilder $builder, private string $tag, array $attributes = [])
61
    {
62
        $this->setAttributes($attributes);
63
    }
64
65
    /**
66
     * @param string $method
67
     * @param array $arguments
68
     *
69
     * @return static
70
     */
71
    public function __call(string $method, array $arguments): static
72
    {
73
        $this->builder->callElementFactory($this, $method, $arguments);
74
        return $this;
75
    }
76
77
    /**
78
     * @param string $tag
79
     *
80
     * @return void
81
     */
82
    public function setTag(string $tag): void
83
    {
84
        $this->tag = $tag;
85
    }
86
87
    /**
88
     * @param array<Element> $children
89
     *
90
     * @return void
91
     */
92
    public function setChildren(array $children): void
93
    {
94
        $this->children = $children;
95
    }
96
97
    /**
98
     * @param array<Element> $children
99
     *
100
     * @return void
101
     */
102
    public function addChildren(array $children): void
103
    {
104
        $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...
105
    }
106
107
    /**
108
     * @param Element $child
109
     *
110
     * @return void
111
     */
112
    public function addChild(Element $child): void
113
    {
114
        $this->children[] = $child;
115
    }
116
117
    /**
118
     * @param string $class
119
     *
120
     * @return void
121
     */
122
    public function addBaseClass(string $class): void
123
    {
124
        $this->baseClasses[] = trim($class);
125
    }
126
127
    /**
128
     * @param int $index
129
     * @param string $class
130
     *
131
     * @return void
132
     */
133
    public function setBaseClass(int $index, string $class): void
134
    {
135
       $this->baseClasses[$index] = trim($class);
136
    }
137
138
    /**
139
     * @param string $class
140
     *
141
     * @return void
142
     */
143
    public function addClass(string $class): void
144
    {
145
        $this->classes[] = trim($class);
146
    }
147
148
    /**
149
     * @param string $class
150
     *
151
     * @return void
152
     */
153
    public function setClass(string $class): void
154
    {
155
        // Actually appends the class.
156
        $this->addClass($class);
157
    }
158
159
    /**
160
     * @param bool $isShort
161
     *
162
     * @return void
163
     */
164
    public function setShort($isShort): void
165
    {
166
        $this->isShort = $isShort;
167
    }
168
169
    /**
170
     * @param bool $isOpened
171
     *
172
     * @return void
173
     */
174
    public function setOpened($isOpened): void
175
    {
176
        $this->isOpened = $isOpened;
177
    }
178
179
    /**
180
     * @param string $name
181
     * @param string|null $value
182
     * @param bool $escape
183
     *
184
     * @return void
185
     */
186
    public function setAttribute(string $name, string|null $value = null, bool $escape = true): void
187
    {
188
        $this->attributes[$name] = $value;
189
        $this->escapes[$name] = $escape;
190
    }
191
192
    /**
193
     * @param array $attributes
194
     * @param bool $escape
195
     *
196
     * @return void
197
     */
198
    public function setAttributes(array $attributes, bool $escape = true): void
199
    {
200
        foreach ($attributes as $name => $value) {
201
            switch (true) {
202
            case is_numeric($name):
203
                $this->setAttribute($value);
204
                break;
205
            case is_string($value):
206
                $this->setAttribute($name, $value, $escape);
207
                break;
208
            default: // Any other values are ignored.
209
            }
210
        }
211
    }
212
213
    /**
214
     * @return string
215
     */
216
    private function renderAttributes(): string
217
    {
218
        // Merge the classes.
219
        $classes = [...$this->baseClasses, ...$this->classes];
220
        if (isset($this->attributes['class'])) {
221
            $classes[] = $this->attributes['class'];
222
        }
223
        if (($class = trim(implode(' ', $classes))) !== '') {
224
            $this->attributes['class'] = $class;
225
        }
226
227
        $attributes = [];
228
        foreach ($this->attributes as $name => $value) {
229
            $name = $this->escape($name);
230
            if ($value !== null && ($this->escapes[$name] ?? true) === true) {
231
                $value = $this->escape($value);
232
            }
233
            $attributes[] = $value !== null ? "$name=\"$value\"" : $name;
234
        }
235
236
        return !$attributes ? '' : ' ' . implode(' ', $attributes);
237
    }
238
239
    /**
240
     * @return string
241
     */
242
    private function renderShort(): string
243
    {
244
        return sprintf('<%s%s />', $this->tag, $this->renderAttributes());
245
    }
246
247
    /**
248
     * @return string
249
     */
250
    private function renderOpened(): string
251
    {
252
        return sprintf('<%s%s>', $this->tag, $this->renderAttributes());
253
    }
254
255
    /**
256
     * @return string
257
     */
258
    private function renderTag(): string
259
    {
260
        $children = implode('', $this->children);
261
        return sprintf('%s%s</%s>', $this->renderOpened(), $children, $this->tag);
262
    }
263
264
    /**
265
     * @param $value
266
     *
267
     * @return string
268
     */
269
    private function escape($value): string
270
    {
271
        return htmlspecialchars($value, ENT_COMPAT);
272
    }
273
274
    /**
275
     * @inheritDoc
276
     */
277
    protected function render(): string
278
    {
279
        return match(true) {
280
            $this->isShort => $this->renderShort(),
281
            $this->isOpened => $this->renderOpened(),
282
            default => $this->renderTag(),
283
        };
284
    }
285
}
286