HTMLTag::getName()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
/**
3
 * File containing the class {@see \AppUtils\HTMLTag}.
4
 *
5
 * @package AppUtils
6
 * @subpackage HTML
7
 * @see \AppUtils\HTMLTag
8
 */
9
10
declare(strict_types=1);
11
12
namespace AppUtils;
13
14
use AppUtils\HTMLTag\GlobalOptions;
15
16
/**
17
 * Helper class for generating individual HTML tags,
18
 * with chainable methods.
19
 *
20
 * @package AppUtils
21
 * @subpackage HTML
22
 * @author Sebastian Mordziol <[email protected]>
23
 *
24
 * @link https://github.com/Mistralys/application-utils/wiki/HTMLTag
25
 */
26
class HTMLTag implements Interface_Stringable, Interface_Classable
27
{
28
    public const SELF_CLOSE_STYLE_SLASH = 'slash';
29
    public const SELF_CLOSE_STYLE_NONE = 'none';
30
31
    public AttributeCollection $attributes;
32
    private string $name;
33
    public StringBuilder $content;
34
    private bool $selfClosing = false;
35
    private bool $allowEmpty = false;
36
    private static ?GlobalOptions $globalOptions = null;
37
38
    private function __construct(string $name, AttributeCollection $attributes)
39
    {
40
        $this->name = $name;
41
        $this->attributes = $attributes;
42
        $this->content = sb();
43
    }
44
45
    /**
46
     * @return string
47
     */
48
    public function getName() : string
49
    {
50
        return $this->name;
51
    }
52
53
    /**
54
     * @param bool $selfClosing
55
     * @return $this
56
     */
57
    public function setSelfClosing(bool $selfClosing=true) : self
58
    {
59
        $this->selfClosing = $selfClosing;
60
        return $this;
61
    }
62
63
    public function isSelfClosing() : bool
64
    {
65
        return $this->selfClosing;
66
    }
67
68
    /**
69
     * @param bool $allowed
70
     * @return $this
71
     */
72
    public function setEmptyAllowed(bool $allowed=true) : self
73
    {
74
        $this->allowEmpty = $allowed;
75
        return $this;
76
    }
77
78
    public function isEmptyAllowed() : bool
79
    {
80
        if($this->isSelfClosing())
81
        {
82
            return true;
83
        }
84
85
        return $this->allowEmpty;
86
    }
87
88
    public static function create(string $name, ?AttributeCollection $attributes=null) : HTMLTag
89
    {
90
        if($attributes === null)
91
        {
92
            $attributes = AttributeCollection::create();
93
        }
94
95
        return new HTMLTag($name, $attributes);
96
    }
97
98
    public function hasAttributes() : bool
99
    {
100
        return $this->attributes->hasAttributes();
101
    }
102
103
    /**
104
     * Returns true if the tag has no content, and no attributes.
105
     * By default, an empty tag is not rendered.
106
     *
107
     * @return bool
108
     */
109
    public function isEmpty() : bool
110
    {
111
        return !$this->hasAttributes() && $this->renderContent() === '';
112
    }
113
114
    public function render() : string
115
    {
116
        if(!$this->isEmptyAllowed() && $this->isEmpty())
117
        {
118
            return '';
119
        }
120
121
        return
122
            $this->renderOpen().
123
            $this->renderContent().
124
            $this->renderClose();
125
    }
126
127
    public static function getGlobalOptions() : GlobalOptions
128
    {
129
        if(!isset(self::$globalOptions))
130
        {
131
            self::$globalOptions = new GlobalOptions();
132
        }
133
134
        return self::$globalOptions;
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::globalOptions could return the type null which is incompatible with the type-hinted return AppUtils\HTMLTag\GlobalOptions. Consider adding an additional type-check to rule them out.
Loading history...
135
    }
136
137
    public function getSelfClosingChar() : string
138
    {
139
        if($this->selfClosing && self::getGlobalOptions()->getSelfCloseStyle() === self::SELF_CLOSE_STYLE_SLASH)
140
        {
141
            return '/';
142
        }
143
144
        return '';
145
    }
146
147
    public function renderOpen() : string
148
    {
149
        return sprintf(
150
            '<%s%s%s>',
151
            $this->name,
152
            $this->attributes,
153
            $this->getSelfClosingChar()
154
        );
155
    }
156
157
    public function renderClose() : string
158
    {
159
        if($this->selfClosing)
160
        {
161
            return '';
162
        }
163
164
        return sprintf('</%s>', $this->name);
165
    }
166
167
    /**
168
     * Adds a bit of text to the content (with an automatic space at the end).
169
     *
170
     * @param string|number|StringBuilder_Interface|NULL $content
171
     * @return $this
172
     */
173
    public function addText($content) : self
174
    {
175
        $this->content->add($content);
176
        return $this;
177
    }
178
179
    /**
180
     * Adds a bit of HTML at the end of the content.
181
     *
182
     * @param string|number|StringBuilder_Interface|NULL $content
183
     * @return $this
184
     */
185
    public function addHTML($content) : self
186
    {
187
        $this->content->html($content);
188
        return $this;
189
    }
190
191
    /**
192
     * @param string|number|Interface_Stringable|NULL $content
193
     * @return $this
194
     */
195
    public function setContent($content) : self
196
    {
197
        $this->content = sb()->add($content);
198
        return $this;
199
    }
200
201
    public function renderContent() : string
202
    {
203
        if($this->selfClosing)
204
        {
205
            return '';
206
        }
207
208
        return (string)$this->content;
209
    }
210
211
    public function __toString()
212
    {
213
        return $this->render();
214
    }
215
216
    /**
217
     * @param string $name
218
     * @param string $value
219
     * @param bool $keepIfEmpty
220
     * @return $this
221
     */
222
    public function attr(string $name, string $value, bool $keepIfEmpty=false) : self
223
    {
224
        $this->attributes->attr($name, $value);
225
226
        if($keepIfEmpty) {
227
            $this->attributes->setKeepIfEmpty($name);
228
        }
229
230
        return $this;
231
    }
232
233
    /**
234
     * @param string $name
235
     * @param bool $enabled
236
     * @return $this
237
     */
238
    public function prop(string $name, bool $enabled=true) : self
239
    {
240
        $this->attributes->prop($name, $enabled);
241
        return $this;
242
    }
243
244
    // region: Flavors
245
246
    /**
247
     * @param string $name
248
     * @return $this
249
     */
250
    public function name(string $name) : self
251
    {
252
        $this->attributes->name($name);
253
        return $this;
254
    }
255
256
    /**
257
     * @param string $id
258
     * @return $this
259
     */
260
    public function id(string $id) : self
261
    {
262
        $this->attributes->id($id);
263
        return $this;
264
    }
265
266
    /**
267
     * @param string $url
268
     * @return $this
269
     */
270
    public function href(string $url) : self
271
    {
272
        $this->attributes->href($url);
273
        return $this;
274
    }
275
276
    /**
277
     * @param string $url
278
     * @return $this
279
     */
280
    public function src(string $url) : self
281
    {
282
        $this->attributes->attrURL('src', $url);
283
        return $this;
284
    }
285
286
    // endregion
287
288
    // region: Classable interface
289
290
    /**
291
     * @param string $name
292
     * @return $this
293
     */
294
    public function addClass($name) : self
295
    {
296
        $this->attributes->addClass($name);
297
        return $this;
298
    }
299
300
    /**
301
     * @param string[] $names
302
     * @return $this
303
     */
304
    public function addClasses(array $names) : self
305
    {
306
        $this->attributes->addClasses($names);
307
        return $this;
308
    }
309
310
    public function hasClass(string $name) : bool
311
    {
312
        return $this->attributes->hasClass($name);
313
    }
314
315
    /**
316
     * @param string $name
317
     * @return $this
318
     */
319
    public function removeClass(string $name) : self
320
    {
321
        $this->attributes->removeClass($name);
322
        return $this;
323
    }
324
325
    public function getClasses() : array
326
    {
327
        return $this->attributes->getClasses();
328
    }
329
330
    public function classesToString() : string
331
    {
332
        return $this->attributes->classesToString();
333
    }
334
335
    public function classesToAttribute() : string
336
    {
337
        return $this->attributes->classesToAttribute();
338
    }
339
340
    public function hasClasses() : bool
341
    {
342
        return $this->attributes->hasClasses();
343
    }
344
345
    // endregion
346
}
347