Repository navigation
Testing and CI
The suite is the contract: 386 tests / 1286 assertions run against every combination of PHP version and artifact, and several tests are the architecture documentation (no-eval/no-globals scan, engine independence, security containment).
php8.3 tests/run.php # source tree (default)
php8.5 tests/run.php # source tree, newest PHP
php8.3 tests/run.php --lib=php83/html.php # against the 8.3 bundle
php8.5 tests/run.php --lib=php85/html.php # against the 8.5 bundle
php8.5 tests/run.php --filter=ssi --verbose # filtered
php8.3 tests/run.php --list # inventory
bash tools/run-matrix.sh # whole matrix: 14 combos, both intl
# modes; drift-checks bundles first,
# never rewrites them (--build to opt in);
# hard error when a PHP version is missingExit codes: 0 green Β· 1 failures Β· 2 harness error. Two tests skip in
bundle mode (the per-file architecture scans) β everything else is
artifact-independent.
Every combination runs twice: with ext-intl and with it disabled
(mbstring, :intl in CI, -d disable_functions=transliterator_transliterate
locally) β 14 CI jobs. The suite is extension-independent by design
(Str::slugify is deterministic); the two legs exist to keep it that way.
| src | php83 bundle | php85 bundle | |
|---|---|---|---|
| PHP 8.3 | β 386 | β 384 (+2 skip) | β (parse error by design) |
| PHP 8.4 | β 386 | β 384 (+2 skip) | β |
| PHP 8.5 | β 386 | β 384 (+2 skip) | β 384 (+2 skip) |
Identical numbers with intl on and off.
01 config/container/scope/request Β· 02 support/io/pathguard Β·
03 escaper/cleaner Β· 04 cache/log/events Β· 05 SSI core Β·
06 SSI conditionals/exec Β· 07 substitution/filters Β· 08 structure tags Β·
09 i18n (incl. binary MO built in-test) Β· 10 render API/cache/stats Β·
11 linter Β· 12 WordPress (bridge/adapters/toolkit/CLI/admin/plugin files
against a 60-function WP stub set) Β· 13 security (containment, budgets,
token-level no-eval/no-globals scan, strict-types scan) Β· 14 CLI Β·
15 v5 expressions & math (inline %{= β¦}, index/get() containment,
INF/NAN guards, AST-memo bound, infinite-generator budgets, linter codes)
(subprocess exit codes).
Determinism: UTC pinned, FrozenClock injection, fixture tree under
tests/tmp/ (auto-cleaned), per-test $GLOBALS/superglobal
snapshot-restore, no network.
<?php
// tests/cases/15_my_feature.test.php
declare(strict_types=1);
group('My feature');
test('does the thing', function (): void {
$engine = test_engine(['syntax' => ['auto_escape' => false]]); // bootstrap helper
assertSame('x', $engine->renderString('%{v}', ['v' => 'x']));
});Runner API: group() test() assertSame/True/False/Null assertContains/ NotContains assertMatches assertThrows assertInstanceOf skip/skipIf
fail. Helpers from tests/bootstrap.php: test_engine(array $config, ?FrozenClock), test_request(), test_options(), fixture_write/path/dir,
run_isolated_php(), ssi() (in 05).
Rules of the house:
- New behaviour β new assertions in the matching case file (or a new one).
- Security-relevant change β a
13_securitycase proving the guard. - Bundle-visible change β run
tools/build.php+ both--liblegs. - WordPress-touching change β must pass against
tests/wp-stubs.php(extend the stubs rather than skipping).
flowchart LR
PR[push / PR] --> T["tests job Γ9\nPHP 8.3Β·8.4Β·8.5 Γ srcΒ·php83Β·php85\n(8.5 bundle excluded below 8.5)"]
PR --> B[build bundles] --> C["build.php --check\n(drift gate)"] --> T
PR --> S["PHPStan level 6 β mandatory\n(empty baseline, WP layer included)"]
PR --> F["PHP-CS-Fixer β mandatory\n(pinned, dry run)"]
PR --> W["wiki links\n(tools/check-wiki-links.php)"]
-
Drift gate: bundles are rebuilt in CI and compared byte-for-byte with
the committed ones β a PR that edits
src/without rebuilding fails. -
PHPStan: level 6 over the full
src/tree β the WordPress layer is analysed against the project's own WP stubs (bootstrapFiles), no exclusions.phpstan-baseline.neonis empty by policy (fix findings in code, never baseline them) andreportUnmatchedIgnoredErrors: true. The phar is version-pinned in CI (PHPSTAN_VERSION). -
CS: PER-CS2.x + strict types + alpha imports via
.php-cs-fixer.dist.php; the fixer version is pinned in CI (PHP_CS_FIXER_VERSION) and the dry-run is a hard gate β runphp-cs-fixer fixlocally before pushing. -
Smoke:
tests/smoke.php(end-to-end public-API tour) runs as its own job on PHP 8.3 and 8.5 (composer run smokelocally). -
Wiki links:
tools/check-wiki-links.phpvalidates every internal link and heading anchor of the wiki repository (cloned at check time).
# PHPStan (phar, no install)
curl -sSLo phpstan.phar https://github.com/phpstan/phpstan/releases/latest/download/phpstan.phar
php8.3 phpstan.phar analyse -c phpstan.neon --memory-limit=1G
# PHP-CS-Fixer
PHP_CS_FIXER_IGNORE_ENV=1 php-cs-fixer fix --dry-run --diffphp8.3 tools/bench.php 2000
# render (loops+ssi+i18n) ~0.72 ms/op Β· ~1.4k ops/s
# renderString (small) ~0.014 ms/op Β· ~69k ops/s
# processSsi (5 directives) ~0.018 ms/op Β· ~54k ops/s
# t() with catalogue ~0.002 ms/op Β· ~626k ops/sReference box: 2-vCPU container, PHP 8.3, opcache CLI off. A CI perf gate (Β±10 % regression fail) is on the ROADMAP.
php tools/build.php && php tools/build.php --check-
bash tools/run-matrix.sh(install 8.4 locally or let CI cover it) - Bump
src/Version.php(+src-8.5/Version.php),CHANGELOG.md, README badges, plugin header/readme.txt. - Tag
v4.x.y; attach both bundles to the GitHub release (shared-hosting users download, not clone). - Publish wiki sync:
bash tools/publish-wiki.sh --push.
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 β