stefanwimmer128 / html-builder
A fully extensible HTML-Builder supporting emmet input
Requires
- php: ^7.4
- joshtronic/php-loremipsum: ^1.0
- symfony/polyfill-php80: ^1.18
Requires (Dev)
- ext-json: *
- phpunit/phpunit: ^9.4
- symfony/var-dumper: ^5.1
README
A fully extensible HTML-Builder supporting emmet input
Installation
composer require stefanwimmer128/html-builder
Example
use function Stefanwimmer128\HtmlBuilder\render;
use function Stefanwimmer128\HtmlBuilder\h;
use function Stefanwimmer128\HtmlBuilder\capture;
use function Stefanwimmer128\HtmlBuilder\map;
$test = '<p>TEST</p>';
$values = [
'a' => 0,
'b' => 1,
'c' => 2,
];
render(h('xml[version="1.0" standalone=yes]', [], [
h('doctype', 'html', [
h('html[lang=en]', [], [
h('head > title{Hello World!}'),
h('body', [], [
h('.container.container-content#container-content-main', [], [
h('a[href="#"][disabled]', 'LINK'),
'<p>TEST</p>',
capture('printf', '%s', $test)
]),
h('comment', 'COMMENT'),
h('p', ['data-values' => json_encode($values, JSON_THROW_ON_ERROR)], [
h('b', 'LIST:'),
h('br'),
map($values, fn($value, $key) => h('span', ['data-key' => $key], $value))
])
])
])
])
]));
<?xml version="1.0" standalone="yes" ?><!DOCTYPE html><html lang="en"><head><title>Hello World!</title></head><body><div class="container container-content" id="container-content-main"><a href="#" disabled>LINK</a><p>TEST</p><p>TEST</p></div><!-- COMMENT --><p data-values="{"a":0,"b":1,"c":2}"><b>LIST:</b><br /><span data-key="a">0</span><span data-key="b">1</span><span data-key="c">2</span></p></body></html>
Special Elements
The following elements are special.
Thus, if you want to use the keywords as simple tags, you will have to use the special tag
function:
use function Stefanwimmer128\HtmlBuilder\render;
use function Stefanwimmer128\HtmlBuilder\h;
use function Stefanwimmer128\HtmlBuilder\tag;
render(h('comment', 'Test'));
render(tag('comment', 'Test'));
<!-- Test -->
<comment>Test</comment>
comment
Generate HTML comment. Any string(s) or HtmlElement
(s) can be used as children.
use function Stefanwimmer128\HtmlBuilder\render;
use function Stefanwimmer128\HtmlBuilder\h;
render(h('comment', 'Test'));
<!-- Test -->
doctype
Generate HTML doctype. Can receive children, that will be rendered after tag.
use function Stefanwimmer128\HtmlBuilder\render;
use function Stefanwimmer128\HtmlBuilder\h;
render(h('doctype', 'html', ...$children));
<!DOCTYPE html>...children...
lorem
/ lipsum
Generates lorem ipsum text (default: 30 words).
To set the number of words to generate append it to the tag: lorem20
generates 20 words, lipsum40
generates 40 words.
use function Stefanwimmer128\HtmlBuilder\render;
use function Stefanwimmer128\HtmlBuilder\h;
render(h('lorem50'));
Generates 50 words of lorem ipsum.
xml
Create XML header. Works pretty similar to a html tag.
use function Stefanwimmer128\HtmlBuilder\render;
use function Stefanwimmer128\HtmlBuilder\h;
render(h('xml[version="1.0"]', [], ...$children));
<?xml version="1.0" ?>...children...
API Documentation
h(string $input, ...$args): HtmlElement
Create element from input using parser (default: Emmet)tag(string $tag = 'div', ...$args): HtmlTag
Create tag elementtext(string $text): HtmlText
Create text elementraw(string $value): RawString
Create raw stringcapture(callable $callable, ...$args): RawString
Captures output (echo) and returns it as raw stringmap(array $array, callable $callable): array
array_map with keysrender(...$elements): string
Render elements as HTML
h(string $input, ...$args): HtmlElement
Create element from input using parser (default: Emmet)
Also works like tag()
.
use function Stefanwimmer128\HtmlBuilder\render;
use function Stefanwimmer128\HtmlBuilder\h;
render(h('table[border=0] > thead > .row > .col', 'Content'));
render(h('div', [], 'Some text <b>with html</b>'));
<table border="0"><thead><tr class="row"><td class="col">Content</td></tr></thead></table>
<div>Some text <p>with html</b></div>
tag(string $tag = 'div', ...$args): HtmlTag
Create tag element
use function Stefanwimmer128\HtmlBuilder\render;
use function Stefanwimmer128\HtmlBuilder\tag;
render(tag('div', ['class' => 'container'], [
'Text',
tag('p', 'Some text <b>with html</b>')
]));
<div class="container">Text<p>Some text <p>with html</b></p></div>
text(string $text): HtmlText
Create text element
This is automatically applied to strings that are passed as children to h()
or tag()
.
use function Stefanwimmer128\HtmlBuilder\render;
use function Stefanwimmer128\HtmlBuilder\text;
render(text('Some text <b>with html</b>'));
Some text <p>with html</b>
raw(string $value): RawString
Create raw string
use function Stefanwimmer128\HtmlBuilder\render;
use function Stefanwimmer128\HtmlBuilder\raw;
render(raw('Some text <b>with html</b>'));
Some text <b>with html</b>
capture(callable $callable, ...$args): RawString
Captures output (echo) and returns it as raw string
Useful with functions that instead of returning the markup directly output it (eg. Wordpress).
use function Stefanwimmer128\HtmlBuilder\render;
use function Stefanwimmer128\HtmlBuilder\tag;
use function Stefanwimmer128\HtmlBuilder\capture;
render(tag('div', [], capture('the_widget', 'widget')));
<div>...widget markup...</div>
map(array $array, callable $callable): array
array_map with keys
use function Stefanwimmer128\HtmlBuilder\render;
use function Stefanwimmer128\HtmlBuilder\tag;
use function Stefanwimmer128\HtmlBuilder\map;
$values = [
'key0' => 'value0',
'key1' => 'value1',
];
render(tag('ul', [], map($values, fn($value, $key) => tag('li', ['id' => $key], $value))));
<ul><li id="key0">value0</li><li id="key1">value1</li></ul>
render(...$elements): string
Render elements as HTML
See all the above.
Writing extensions
Creating a custom element
Any custom element must extend \Stefanwimmer128\HtmlBuilder\HtmlElement
.
First you create your element class:
use Stefanwimmer128\HtmlBuilder\HtmlElement;
class CustomElement extends HtmlElement {
private string $someData;
public function __construct(string $someData) {
$this->someData = $someData;
}
public function render(): string {
return sprintf('(%s)', $this->someData);
}
}
If your element is tag-like, you might want to extend \Stefanwimmer128\HtmlBuilder\HtmlTag
:
use Stefanwimmer128\HtmlBuilder\HtmlTag;
class CustomElement extends HtmlTag {
public function __construct(...$args) {
parent::__construct('custom', ...$args);
}
public function render(): string {
return sprintf('(%s)%s', $this->renderAttributes(), $this->renderChildren());
}
}
If your custom element should receive parsed information, your element must implement \Stefanwimmer128\HtmlBuilder\Parser\ParserCompatibleElement
.
After which you have to register your element, by putting this code before your usage of the element:
use Stefanwimmer128\HtmlBuilder\HtmlElement;
HtmlElement::$ELEMENTS['custom'] = CustomElement::class;
Creating a custom parser
Any parser must extend \Stefanwimmer128\HtmlBuilder\Parser\AbstractParser
.
Since creating a custom parser is very much tied to what you want to parse, please orientate yourself using the included \Stefanwimmer128\HtmlBuilder\Parser\Emmet\EmmetParser
.
use Stefanwimmer128\HtmlBuilder\Parser\AbstractParser;
class CustomParser extends AbstractParser {
public function __construct(string $input) {
// your parsing logic goes here
}
public function getTag(): string {
// return tag here
}
public function getAttributes(): array {
// return attributes here
}
public function getClasses(): array {
// return classes here
}
public function getId(): ?string {
// return id here if provided or null
}
public function getText() : ?string {
// return text here if provided or null
}
public function getChild() : ?AbstractParser {
// return child here if provided or null
}
public function isSelfclosing() : ?bool{
// return whether tag self-closes if provided or null
}
}
After which you can set your parser by:
use Stefanwimmer128\HtmlBuilder\Parser\AbstractParser;
AbstractParser::$DEFAULT_PARSER = CustomParser::class;