Skip to content

Refactor xdebug + intl to use loadPHPExtension helper (draft, stacked) - #3547

Closed
adamziel wants to merge 2 commits into
adamziel/load-php-extensionfrom
adamziel/extensions-via-helper
Closed

Refactor xdebug + intl to use loadPHPExtension helper (draft, stacked)#3547
adamziel wants to merge 2 commits into
adamziel/load-php-extensionfrom
adamziel/extensions-via-helper

Conversation

@adamziel

Copy link
Copy Markdown
Collaborator

Stacked on #3545.

Why

When I tried to dogfood the loadPHPExtension() API from #3545 against the bundled extension wrappers (with-xdebug, with-intl), two gaps surfaced:

  1. The wrappers run inside Emscripten's onRuntimeInitialized where only a raw PHPRuntime.FS is available — UniversalPHP doesn't exist yet, and php.ini hasn't been written.
  2. with-intl needs to drop icu.dat next to the .so. The original API had no way to express "ship these companion files alongside the extension."

So this PR extends the API and converts both wrappers to use it.

API additions

  • installPHPExtensionFilesSync(fs, options) — sync, raw-FS sibling of loadPHPExtension. Both helpers share a buildExtensionFiles() core so the on-disk layout stays in lockstep between blueprint-loaded and bundled extensions.
  • extraFiles: Array<{ path, data }> option — companion data files. Used by intl for icudt74l.dat; future-friendly for things like MaxMind GeoIP .mmdb files.

Refactors

  • with-xdebug.ts: ~80 lines of FS plumbing → one installPHPExtensionFilesSync call plus an iniEntries map. The /.xdebug/ path-mapping block stays inline because it writes outside the extensions dir.
  • with-intl.ts: ~60 lines → one call with an extraFiles entry. ENV setup (ICU_DATA, PHP_INI_SCAN_DIR) stays at the EmscriptenOptions layer because Emscripten ENV must be set before runtime init.

Out of scope

  • redis and memcached wrappers — same shape, but I'd rather convert them in a follow-up PR to keep this one's blast radius small.

Test plan

  • `npx nx test php-wasm-node` passes
  • Boot with `--xdebug` and confirm `xdebug.so` is loaded in `phpinfo()`
  • Boot with `--intl` (default) and confirm `Intl` extension is loaded
The 'with*' wrappers needed two API tweaks before they could share code
with the blueprint step from #3545:

1. A sync, FS-level installer (installPHPExtensionFilesSync). The
   wrappers run inside Emscripten's onRuntimeInitialized, where only
   the raw PHPRuntime.FS is available — UniversalPHP doesn't exist
   yet, and php.ini hasn't been written.

2. An extraFiles option for companion data files. intl needs to drop
   icu.dat next to the .so; without this the wrappers had to keep
   their own bespoke FS-write code.

Both helpers (async loadPHPExtension and sync installPHPExtensionFilesSync)
share a buildExtensionFiles() core, so the on-disk layout stays in lockstep
between blueprint-loaded extensions and the bundled ones.

with-xdebug shrinks from ~80 lines of FS plumbing to a single call plus
its iniEntries map; with-intl from ~60 to one call with an extraFiles
entry. Behavior is unchanged.
The strict-equality tests in php-dynamic-loading.spec.ts expect the
exact original byte sequence (lines joined with \n, no trailing \n).
The previous helper in this PR added a trailing newline, which broke
14 tests across all PHP versions (XDebug + Intl ini-content checks).

Drop the trailing newline; PHP's ini parser is happy with either.
@adamziel

Copy link
Copy Markdown
Collaborator Author

Superseded by #3566, which consolidates the extension loader, Blueprint API, compile helper, and bundled extension migration into one trunk-based PR.

@adamziel adamziel closed this Apr 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment