Skip to content

WordPress Integration

Mikhail Deynekin edited this page Oct 11, 2026 · 1 revision

WordPress Integration

WordPress is a first-class but optional host: the core library contains zero WordPress knowledge, everything WP-specific lives in HtmlPhp\WordPress\* behind existence guards, and the render engine, caching, logging and i18n all plug into WP's own subsystems.

Note

Canonical spec: docs/WORDPRESS.md.

Two ways in

The bundled plugin

wordpress/html-php/ β€” loader with edition auto-select (Composer vendor/, else lib/php85/html.php on PHP 8.5+, else lib/php83/html.php), activation hardening (uploads cache dir + deny-all .htaccess), uninstall.php cleanup, [htmlphp] shortcode, htmlphp_render() / htmlphp_t() helpers, Tools β†’ HTML.PHP admin screen. Missing library = one admin notice, never a fatal.

// anywhere in a theme / mu-plugin once the plugin is active:
echo htmlphp_render('cards/author.html', ['author' => get_user_by('id', $id)]);

Boot the bridge yourself (plugin/theme authors)

add_action('plugins_loaded', static function (): void {
    require_once __DIR__ . '/lib/php85/html.php';

    HtmlPhp\WordPress\Bridge::boot([
        'templates' => ['paths' => [__DIR__ . '/templates']],
        'i18n'      => ['domain' => 'my-plugin'],
        'cache'     => ['ttl' => 300],
    ], setup: null, options: [
        'shortcode'     => 'mycard',  // [mycard template="…" k="v"]
        'content_ssi'   => false,     // SSI over the_content β€” opt-in!
        'admin_page'    => true,
        'admin_bar'     => null,      // default: WP_DEBUG
        'cli'           => true,      // wp htmlphp …
        'mirror_events' => true,      // engine events β†’ WP hooks
    ]);
});

boot() merges WP-flavoured defaults under your config: theme htmlphp/ dirs (child first) prepended to templates.paths, ssi.document_root = ABSPATH, cache = WP object cache (+transients) or files in {uploads}/htmlphp-cache, rotating log driver, WP i18n delegation. Bridge::engine() returns the instance; the bridge singleton is the one documented exception to explicit-state rules β€” plugin lifecycles need it.

Template discovery

$loader = new HtmlPhp\WordPress\TemplateLoader(Bridge::engine());

$loader->render(['card', 'cards/card'], ['post' => get_post()]);
// suffixes tried for bare slugs: .ssi.htm .ssi.html .ssi .htm .html .tpl
// order: child theme β†’ parent theme (locate_template) β†’ your extra dirs

$loader->renderPart('content', 'single', ['post' => get_post()]);
// get_template_part()-style, returns string; missing part = diagnostic, not fatal

Every render auto-provides: %{wp_blogname} %{wp_description} %{wp_home_url} %{wp_locale} %{wp_year} %{wp_version} %{wp_charset} %{wp_language}.

Warning

.php files are never located as templates (v3 allowed it). A template is data; rendering PHP source as text is a disclosure bug, not a feature.

Shortcodes

(new HtmlPhp\WordPress\Shortcodes(Bridge::engine()))
    ->register('author_card', 'cards/author.html', ['role' => 'guest']);
// [author_card name="Ada"]bio here[/author_card]
// β†’ name + role defaults + %{content} = "bio here"

The generic [htmlphp template="…"] shortcode (bridge option) needs no code. Attribute names are validated; render failures return '' + a wp-bridge diagnostic β€” a broken template can never white-screen a page.

Subsystem adapters

WP subsystem Adapter Behaviour
Object cache WpCache (cache.driver: wordpress) wp_cache_* group htmlphp_{blogId} plus transients tier; key registry option makes clear() enumerable; multisite-safe
Logging WpLogger (log.driver: wordpress) rotating daily files in {uploads}/html-php-logs/ + debug.log mirror under WP_DEBUG_LOG
i18n built into Translator __/_n/_x/_nx delegation with your domain, determine_locale(); misses fall through to engine catalogues
Hooks event mirroring htmlphp.render β†’ apply_filters('htmlphp_render'), htmlphp.error β†’ do_action('htmlphp_error'), htmlphp.translate β†’ apply_filters('htmlphp_translate')
Content content_ssi option the_content through the SSI processor (#exec still policy-denied) β€” trusted-author sites only
Admin bar admin_bar option RenderStats summary node (ms/templates) when debugging

Admin screen (Tools β†’ HTML.PHP)

Tabs: Health (PHP support window, debug display, memory, disk, uploads writability, DB, cron, pending updates, engine diagnostics backlog) Β· Templates (lint report across every configured root) Β· Cache (driver, clear) Β· i18n (locale, catalogue stats, audit pointer) Β· Logs (tail, rotate) Β· Info (SystemInfo snapshot).

Tip

Any tab can be overridden with a template at htmlphp/admin/htmlphp/{tab}.html in your theme/plugin roots β€” the screen is rendered by the engine itself with tab/tabs/data/nonce_field/ page_url variables (the bundled plugin ships one as an example). All POST actions are nonce + manage_options protected.

Maintenance toolkit (HtmlPhp\WordPress\Maintenance\*)

Nonces (create/verify/field) Β· Options (prefixed, typed) Β· Transients (+remember($key,$ttl,$factory)) Β· Notices (one-shot queue) Β· Db (always-prepared queries, tableSizes(), countTable()) Β· Cron (idempotent schedule/unschedule/next) Β· Logs (rotate/tail) Β· SystemInfo (WP/PHP/server/engine snapshot β€” pass the engine) Β· Updates (pending core/plugin/theme) Β· Health (actionable findings β€” pass the engine) Β· Assets (register with filemtime cache busting) Β· Rest (routes that always carry a permission callback, ok()/fail() envelopes).

Mailer

HtmlPhp\WordPress\Mailer::send(
    Bridge::engine(),
    $user->user_email,
    'emails/welcome-subject.txt',   // template | path | inline '@{key}'
    'emails/welcome.html',
    ['name' => $user->display_name]
);
// subject renders unescaped (plain text), body per escape policy,
// Content-Type: text/html added unless you provide headers

WP-CLI

wp htmlphp info [--json]
wp htmlphp render <template> [--var=k:v]…
wp htmlphp lint [<path>]                 # exit 1 on errors β†’ deploy gate
wp htmlphp cache clear
wp htmlphp i18n extract <dir> [--out=messages.pot]
wp htmlphp i18n audit <dir> [--locale=de]
wp htmlphp health

Testing without WordPress

The repo's tests/wp-stubs.php (+ wp-stubs-extra.php) stubs the entire WP surface the bridge touches β€” hooks, shortcodes, options, transients, nonces, $wpdb, cron, uploads, WP_CLI, object cache, wp_mail capture. The v4 suite runs all WordPress tests under plain CLI PHP; reuse the stubs in your own CI.

➑️ Related: CLI Β· ROADMAP β†’ Gutenberg block, FSE, multisite, warm cron

Clone this wiki locally