Skip to content

Commit 1f5741d

Browse files
Add helper HTMLBuilder::class. (#4)
1 parent 49ace1d commit 1f5741d

File tree

7 files changed

+373
-0
lines changed

7 files changed

+373
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
- Initial release
88
- Enh #2: Add helper `Template::class` (@terabytesoftw)
99
- Bug #3: Fix `README.md` (@terabytesoftw)
10+
- Enh #4: Add helper `HTMLBuilder::class` (@terabytesoftw)

README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,52 @@ private array $attributes = ['class' => 'btn'];
8787
$classes = CssClasses::add($this->attributes, ['btn-primary', 'btn-lg'], true);
8888
```
8989

90+
### Create a new `HTML` element
91+
92+
To create a new `HTML` element, you can use the `HTMLBuilder::class` with the `createTag()` method.
93+
94+
Allowed arguments are:
95+
96+
- `tag` (string) - The tag name.
97+
- `content` (string) - The content of the tag.
98+
- `attributes` (array) - The attributes of the tag.
99+
100+
```php
101+
<?php
102+
103+
declare(strict_types=1);
104+
105+
use UIAwesome\Html\Helper\HTMLBuilder;
106+
?>
107+
108+
<?= HTMLBuilder::createTag('div', 'Hello, World!', ['class' => 'container']) ?>
109+
```
110+
111+
### Create a new `HTML` block element
112+
113+
To create a new `HTML` block element, you can use the `HTMLBuilder::class` with the `beginTag()` and `endTag()` methods.
114+
115+
Allowed arguments for `beginTag()` method are:
116+
117+
- `tag` (string) - The tag name.
118+
- `attributes` (array) - The attributes of the tag.
119+
120+
Allowed arguments for `endTag()` method are:
121+
122+
- `tag` (string) - The tag name.
123+
124+
```php
125+
<?php
126+
127+
declare(strict_types=1);
128+
129+
use UIAwesome\Html\Helper\HTMLBuilder;
130+
131+
<?= HTMLBuilder::beginTag('div', ['class' => 'container']) ?>
132+
Hello, World!
133+
<?= HTMLBuilder::endTag('div') ?>
134+
```
135+
90136
### Convert regular expression to pattern
91137

92138
The `Utils::class` helper can be used to normalize a regular expression.

src/Base/AbstractHTMLBuilder.php

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace UIAwesome\Html\Helper\Base;
6+
7+
use UIAwesome\Html\Helper\Attributes;
8+
9+
use function in_array;
10+
use function strtolower;
11+
12+
/**
13+
* Provides common functionality for generate HTML code fragments programmatically.
14+
*
15+
* Concrete classes should extend this class to implement specific HTML elements and their generation logic.
16+
*/
17+
abstract class AbstractHTMLBuilder
18+
{
19+
/**
20+
* @psalm-var string[]
21+
*/
22+
private const INLINE_ELEMENTS = [
23+
'a',
24+
'abbr',
25+
'acronym',
26+
'audio',
27+
'b',
28+
'bdi',
29+
'bdo',
30+
'big',
31+
'br',
32+
'button',
33+
'canvas',
34+
'cite',
35+
'code',
36+
'data',
37+
'datalist',
38+
'del',
39+
'dfn',
40+
'em',
41+
'embed',
42+
'i',
43+
'iframe',
44+
'img',
45+
'input',
46+
'ins',
47+
'kbd',
48+
'label',
49+
'map',
50+
'mark',
51+
'meter',
52+
'noscript',
53+
'object',
54+
'option',
55+
'output',
56+
'picture',
57+
'progress',
58+
'q',
59+
'ruby',
60+
's',
61+
'samp',
62+
'script',
63+
'select',
64+
'slot',
65+
'small',
66+
'span',
67+
'strong',
68+
'sub',
69+
'sup',
70+
'svg',
71+
'template',
72+
'textarea',
73+
'time',
74+
'u',
75+
'td',
76+
'th',
77+
'tt',
78+
'var',
79+
'video',
80+
'wbr',
81+
];
82+
83+
/**
84+
* @psalm-var string[]
85+
*/
86+
private const VOID_ELEMENT = [
87+
'area',
88+
'base',
89+
'br',
90+
'col',
91+
'command',
92+
'embed',
93+
'hr',
94+
'img',
95+
'input',
96+
'keygen',
97+
'link',
98+
'meta',
99+
'param',
100+
'source',
101+
'track',
102+
'wbr',
103+
];
104+
105+
/**
106+
* This method creates a new HTML begin tag with the specified tag name and attributes.
107+
*
108+
* @param string $tag The tag name.
109+
* @param array $attributes The tag attributes.
110+
*
111+
* @return string The begin tag.
112+
*/
113+
public static function beginTag(string $tag, array $attributes = []): string
114+
{
115+
$helperAttributes = new Attributes();
116+
$tag = self::validateTag($tag);
117+
118+
if (self::inlinedElements($tag)) {
119+
throw new \InvalidArgumentException('Inline elements cannot be used with begin/end syntax.');
120+
}
121+
122+
return '<' . $tag . $helperAttributes->render($attributes) . '>';
123+
}
124+
125+
/**
126+
* This method creates a new HTML tag with the specified tag name, content, and attributes.
127+
*
128+
* @param string $tag The tag name.
129+
* @param string $content The content of the tag.
130+
* @param array $attributes The attributes of the tag.
131+
*
132+
* @return string The tag.
133+
*/
134+
public static function createTag(string $tag, string $content = '', array $attributes = []): string
135+
{
136+
$tag = self::validateTag($tag);
137+
$voidElement = "<$tag" . Attributes::render($attributes) . '>';
138+
139+
if (self::voidElements($tag)) {
140+
return $voidElement;
141+
}
142+
143+
if (self::inlinedElements($tag)) {
144+
return "$voidElement$content</$tag>";
145+
}
146+
147+
$content = $content === '' ? '' : $content . PHP_EOL;
148+
149+
return "$voidElement\n$content</$tag>";
150+
}
151+
152+
/**
153+
* This method creates a new HTML end tag with the specified tag name.
154+
*
155+
* @param string $tag The tag name.
156+
*
157+
* @return string The closing tag.
158+
*/
159+
public static function endTag(string $tag): string
160+
{
161+
if (self::inlinedElements($tag)) {
162+
throw new \InvalidArgumentException('Inline elements cannot be used with begin/end syntax.');
163+
}
164+
165+
$tag = self::validateTag($tag);
166+
167+
return "</$tag>";
168+
}
169+
170+
/**
171+
* @return bool True if tag is inlined element.
172+
*
173+
* @link https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements
174+
*/
175+
private static function inlinedElements(string $tag): bool
176+
{
177+
return in_array($tag, self::INLINE_ELEMENTS, true);
178+
}
179+
180+
/**
181+
* @return bool True if tag is void element.
182+
*
183+
* @link http://www.w3.org/TR/html-markup/syntax.html#void-element
184+
*/
185+
private static function voidElements(string $tag): bool
186+
{
187+
return in_array($tag, self::VOID_ELEMENT, true);
188+
}
189+
190+
/**
191+
* @throws \InvalidArgumentException
192+
*/
193+
private static function validateTag(string $tag): string
194+
{
195+
$tag = strtolower($tag);
196+
197+
if ($tag === '') {
198+
throw new \InvalidArgumentException('Tag name cannot be empty.');
199+
}
200+
201+
return $tag;
202+
}
203+
}

src/HTMLBuilder.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace UIAwesome\Html\Helper;
6+
7+
/**
8+
* This class provides methods to generate `HTML` code fragments programmatically.
9+
*
10+
* @link https://developer.mozilla.org/en-US/docs/Glossary/Element
11+
*/
12+
final class HTMLBuilder extends Base\AbstractHTMLBuilder {}

tests/HTMLBuilderExceptionTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace UIAwesome\Html\Tests\Helper;
6+
7+
use UIAwesome\Html\Helper\HTMLBuilder;
8+
9+
final class HTMLBuilderExceptionTest extends \PHPUnit\Framework\TestCase
10+
{
11+
public function testBeginInlineElement(): void
12+
{
13+
$this->expectException(\InvalidArgumentException::class);
14+
$this->expectExceptionMessage('Inline elements cannot be used with begin/end syntax.');
15+
16+
HTMLBuilder::beginTag('br');
17+
}
18+
19+
public function testEndInlineElement(): void
20+
{
21+
$this->expectException(\InvalidArgumentException::class);
22+
$this->expectExceptionMessage('Inline elements cannot be used with begin/end syntax.');
23+
24+
HTMLBuilder::endTag('br');
25+
}
26+
27+
public function testTagEmpty(): void
28+
{
29+
$this->expectException(\InvalidArgumentException::class);
30+
$this->expectExceptionMessage('Tag name cannot be empty.');
31+
32+
HTMLBuilder::createTag('');
33+
}
34+
}

tests/HTMLBuilderTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace UIAwesome\Html\Tests;
6+
7+
use PHPForge\Support\Assert;
8+
use UIAwesome\Html\Helper\HTMLBuilder;
9+
10+
final class HTMLBuilderTest extends \PHPUnit\Framework\TestCase
11+
{
12+
public function testBegin(): void
13+
{
14+
$this->assertSame('<div>', HTMLBuilder::beginTag('div'));
15+
$this->assertSame('<div class="class">', HTMLBuilder::beginTag('div', ['class' => 'class']));
16+
}
17+
18+
/**
19+
* @dataProvider UIAwesome\Html\Helper\Tests\Provider\TagProvider::create
20+
*
21+
* @param string $tagName Tag name.
22+
* @param string $content Tag content.
23+
* @param array $attributes Tag attributes.
24+
* @param string $expected Expected result.
25+
*/
26+
public function testCreate(string $tagName, string $content, array $attributes, string $expected): void
27+
{
28+
Assert::equalsWithoutLE($expected, HTMLBuilder::createTag($tagName, $content, $attributes));
29+
}
30+
31+
public function testEnd(): void
32+
{
33+
$this->assertSame('</div>', HTMLBuilder::endTag('div'));
34+
}
35+
}

tests/Provider/TagProvider.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace UIAwesome\Html\Helper\Tests\Provider;
6+
7+
final class TagProvider
8+
{
9+
public static function create(): array
10+
{
11+
return [
12+
[
13+
'article',
14+
'',
15+
[
16+
'id' => 'id-1',
17+
'class' => 'class',
18+
],
19+
'<article class="class" id="id-1">' . PHP_EOL . '</article>',
20+
],
21+
['br', '', [], '<br>'],
22+
['BR', '', [], '<br>'],
23+
['hr', '', [], '<hr>'],
24+
['HR', '', [], '<hr>'],
25+
['div', 'Content', [], '<div>' . PHP_EOL . 'Content' . PHP_EOL . '</div>'],
26+
[
27+
'input',
28+
'',
29+
[
30+
'type' => 'text',
31+
'name' => 'test',
32+
'value' => '<>',
33+
],
34+
'<input name="test" type="text" value="&lt;&gt;">',
35+
],
36+
['span', '', [], '<span></span>'],
37+
['span', '', [
38+
'disabled' => true,
39+
], '<span disabled></span>'],
40+
];
41+
}
42+
}

0 commit comments

Comments
 (0)