Repository navigation
Getting Started
Mikhail Deynekin edited this page Oct 11, 2026
·
2 revisions
Get from zero to a rendering, translating, SSI-processing page in five minutes. No Composer required (but supported).
| PHP | 8.3+ (portable edition) · 8.5+ (modern edition) |
| Extensions | none required — mbstring improves truncation/slugs, intl improves transliteration, apcu enables a cache driver |
| OS | Windows / Linux / macOS |
Copy one file next to your code and require it:
require_once __DIR__ . '/lib/php83/html.php'; // PHP 8.3 / 8.4
// or
require_once __DIR__ . '/lib/php85/html.php'; // PHP 8.5+The bundles are in php83/
and php85/. Each
starts with a version guard: wrong PHP version = one clear message, not a
parse-error mystery.
composer require paulmann/html-php # PHP 8.3/8.4 — PSR-4 source
composer require paulmann/html-php85 # PHP 8.5 — modern bundleInstall the bundled plugin (instructions) — it auto-selects the right edition.
use HtmlPhp\Engine;
$engine = Engine::create([
'templates' => ['paths' => [__DIR__ . '/templates']],
]);
echo $engine->render('hello.html', [
'name' => 'World <3',
'rows' => [['task' => 'Read docs', 'done' => true],
['task' => 'Ship v5', 'done' => false]],
]);<!-- templates/hello.html -->
<h1>Hello %{name}!</h1>
<ul>
<!--@Loop rows-->
<li <!--@If done-->class="done"<!--@EndIf-->>%{_position}. %{task}</li>
<!--@Else-->
<li>@{todo.empty}</li>
<!--@EndLoop-->
</ul>Rendered with the data above:
<h1>Hello World <3!</h1>
<ul>
<li class="done">1. Read docs</li>
<li >2. Ship v5</li>
</ul>-
%{name}was auto-escaped (<3→<3). -
@{todo.empty}never ran — the list wasn't empty. When it is, that line becomes the translation oftodo.empty(or the literal key, untranslated). - Ordinary HTML comments in templates are left untouched; only the
<!--@Tag-->and<!--#ssi-->forms are special.
-
One engine, explicit config.
Engine::create(array|Config, ?setup)builds an immutable, validated configuration and a lazy service container. No globals; two engines never share state. → Configuration -
Substitutions escape by default.
%{var},#{var},@{translation_key}, filter chains%{var|upper|truncate:20}. Opt out per variable with|raw. Since v5 a tag can also compute:%{= done / total * 100 | round:1 }, with nested/dynamic keys (%{= rows[i].name},%{= prices[currency]},%{= get(fieldName)}). → Template Syntax · Expressions & Math -
Structure lives in HTML comments.
@Loop,@If/@Elif/@Else,@Extends/@Sectionlayouts,@Includepartials,@Set,@Trans. Unbalanced tags are hard errors — runbin/htmlphp lintin CI. -
SSI runs last, over the assembled document — exactly like Apache would:
<!--#include virtual="/footer.html" -->,#if,#echo,#set…#execis denied unless you build an explicit policy. → SSI Guide - i18n is a first-class citizen. PO/MO/JSON/PHP catalogues, plurals, contexts, fallback chains, and CLI extraction/audit. → I18n Guide
-
Nothing fails silently. Misses land in
$engine->diagnostics(), optionally forwarded to a logger; renders emitRenderStats. → Troubleshooting
project/
├── lib/php85/html.php # the single-file edition (or vendor/ via Composer)
├── templates/
│ ├── layouts/base.html # <!--@Section …--> skeletons
│ ├── pages/…
│ └── parts/… # @Include partials, SSI includes
├── languages/
│ ├── messages-de.po
│ └── messages-ru.json
├── config/htmlphp.php # return [ … ] — feed to Engine::create / --config=
└── public/index.php
use HtmlPhp\Engine;
use HtmlPhp\RequestContext;
use HtmlPhp\Template\RenderOptions;
$config = require __DIR__ . '/config/htmlphp.php';
$engine = Engine::create($config, function (HtmlPhp\Container $c): void {
// swap any service: cache, logger, translator, clock, escaper…
// $c->instance(Psr\SimpleCache\CacheInterface::class, $myCache);
});
// Build the request context ONCE at the edge — the engine never reads $_SERVER.
$request = RequestContext::fromServer($_SERVER);
echo $engine->render('pages/article.html', [
'article' => $article,
], new RenderOptions(
file: $_SERVER['SCRIPT_FILENAME'] ?? null, // anchors SSI includes
request: $request, // feeds DATE_LOCAL, HTTP_* …
cache: true,
ttl: 300,
));php bin/htmlphp version # 5.0.0 (8.3 edition) · PHP 8.3.x
php bin/htmlphp lint templates/ # exit 0 = ship-able structure
php bin/htmlphp render templates/pages/article.html --var=title=Hi
php tools/bench.php 500 # throughput sanity check| You want… | Go to |
|---|---|
| the complete template language | Template Syntax |
| Apache SSI under PHP | SSI Guide |
| translations with real tooling | I18n Guide |
| WordPress: shortcodes, cache, admin, CLI | WordPress Integration |
| every config key & bound | Configuration Reference |
| to understand why it's shaped like this | Problem Domain · Architecture |
| to upgrade a 3.x site | Migration Guide |
Tip
Runnable versions of everything above live in
examples/ —
bash examples/run-all.sh php8.3 walks through substitution, layouts,
SSI and i18n against temp fixtures.
HTML.PHP 5.0.0 · MIT © Mikhail Deynekin · Repository · Releases · Report a security issue
Wiki pages mirror docs/ — found a mistake? Edit the page or open an issue.