Repository navigation
Getting Started
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 v4', '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 v4</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. β Template Syntax -
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 # 4.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.
π Home Problem Domain Getting Started
Template Syntax Expressions & Math SSI Guide I18n Guide WordPress Integration CLI
Configuration API Cheatsheet Diagnostics & Troubleshooting
Architecture Security Model Testing & CI
πΊοΈ ROADMAP Migration 4.x β 5.0 Migration 3.x β 4.0 Contributing Repository β