Skip to content

Contributing

Mikhail Deynekin edited this page Oct 11, 2026 · 2 revisions

Contributing

HTML.PHP is a small, opinionated codebase β€” the opinions are the point. Read this before the first PR; it's short and it will save a round trip.

Constitution (non-negotiable)

  1. Zero runtime dependencies. PSR-shaped contracts ship in-library; interop happens through adapters (Psr3Adapter, Psr16Adapter).
  2. Templates are data. No eval, no dynamic includes of template-provided code paths, no network I/O triggered by template content.
  3. Explicit state. No $GLOBALS, no superglobals, no new statics below the WordPress bridge. Time and request data are injected.
  4. Two editions, one behaviour. src/ is 8.3-floor; src-8.5/ overlays may use newer syntax but must be behaviour-identical (matrix-tested).
  5. Fail loud, degrade soft. Structural errors throw; data errors diagnose with stable codes.

Every one of these is enforced by a test (13_security.test.php scans tokens for #1–#3). A PR that breaks the constitution needs an RFC issue first, not a diff.

Development setup

git clone https://github.com/paulmann/HTML.PHP && cd HTML.PHP

# PHP 8.3 + 8.5 (Debian/Ubuntu via sury):
sudo apt install -y php8.3-cli php8.3-mbstring php8.5-cli php8.5-mbstring

php8.3 tests/run.php                 # must be green before AND after
php8.3 tools/build.php               # regenerate bundles after src/ changes
bash tools/run-matrix.sh             # the full matrix

No Composer needed for the core workflow; vendor/ is dev-tooling only.

Repository layout

src/            PSR-4 source (PHP 8.3 floor)     ← change things here
src-8.5/        whole-file 8.5 overlays          ← mirror behaviour-critical edits
php83/ php85/   built bundles                    ← never edit by hand (CI drift gate)
tests/          runner + bootstrap + cases + WP stubs
tools/          build.php Β· bench.php Β· run-matrix.sh Β· check-wiki-links.php Β· publish-wiki.sh
wordpress/      plugin scaffold
docs/           canonical specs (versioned with code)
wiki/           tutorial/reference layer (published to the GitHub wiki)
examples/       runnable tours
bin/htmlphp     CLI

Docs rule: behaviour changes update docs/ in the same PR; user-facing narrative updates wiki/ in the same PR. The wiki drifts, CI can't check prose β€” reviewers do.

Conventions

  • declare(strict_types=1); everywhere (test-enforced), final by default, readonly wherever possible, enums for policies.
  • PER-CS2.0 via .php-cs-fixer.dist.php; alpha-sorted imports; no unused imports.
  • PHPStan level 6 clean over the full src/ tree; the baseline is empty by policy β€” fix findings in code.
  • Docblocks: @param/@return with real types (list<string>, array<string, mixed> β€” the linter-level 6 iterable rules bite otherwise).
  • Diagnostics: reuse existing codes; new codes go in Diagnostics + the troubleshooting table.
  • Events: htmlphp.<area>[.<detail>] tags; document payloads in API-Reference β†’ Events.
  • Exceptions: pick the narrowest existing class; context arrays, not message-interpolated data.

Adding things (recipes)

A filter
  1. Template\Filters::BUILTIN + a match arm in builtin() (mixed in, scalar/array out; escape-producing filters go in Substitution::ESCAPING_FILTERS).
  2. Tests in 07_variables_filters.test.php.
  3. Row in docs/TEMPLATE-SYNTAX.md + wiki syntax table.
An SSI directive

Host-side: implement Ssi\DirectiveInterface and register β€” no library change needed. Library-side (parity with mod_include only): add to Scanner::dispatch + Linter known list + docs/SSI.md + tests in 05/06.

A cache/log driver

Implement Contracts\CacheInterface / Log\LoggerInterface; wire into the matching factory + CacheDriver/LogDriver enum + config docs. Extension- guarded drivers must fall back with a diagnostic (see ApcuCache).

A catalogue format

Catalog::load() extension sniff + a from*() constructor + merge semantics via mergeFrom + tests (see the in-test MO builder in 09_i18n.test.php for fixture style).

An 8.5 overlay

Copy the src file into src-8.5/ (same relative path), apply the language feature at a genuine seam, keep behaviour identical, note it in docs/ARCHITECTURE.md's overlay table. The matrix runs both bundles β€” overlays are not exempt from any test.

Good first contributions

Labeled 🟒 on the ROADMAP: linter --fix rules, RenderOptions named constructors, XLIFF I/O, CLDR sync tool, bench --profile, the VS Code grammar, doc fixes. Each is scoped to ≀ a few hundred lines with tests.

PR checklist

  • php8.3 tests/run.php green Β· new behaviour has new assertions
  • php tools/build.php ran; --check clean (bundles committed)
  • PHPStan level 6 clean; phpstan-baseline.neon still empty
  • docs/ (+ wiki/ if user-facing narrative) updated in the same PR
  • No new globals/statics/superglobals/eval (the scanner will tell you)
  • CHANGELOG entry under Unreleased
  • Security-relevant? β†’ a 13_security case proving the guard, and mention it in the PR description

Process

  • Bugs: issue with version() banner, minimal template + data, expected vs actual, diagnostics dump. A failing test snippet is a fast lane.
  • Ideas: Discussions β†’ Ideas; accepted items move onto the ROADMAP with a label.
  • Security: private report only β€” see Security β†’ Reporting.
  • Conduct: be excellent to each other; critique code, not people.

Clone this wiki locally