[PHP.wasm] Support web JSPI side module ABI exports - #3647
Merged
Conversation
adamziel
force-pushed
the
adamziel/fix-web-jspi-side-module-longjmp
branch
from
May 15, 2026 15:23
e6241d0 to
d85c42d
Compare
adamziel
marked this pull request as ready for review
May 15, 2026 21:58
Contributor
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds support for loading external JSPI side modules (such as the published SQLite wp_mysql_parser extension) in web PHP runtimes by exposing additional ABI symbols, providing a WebAssembly.Tag for __c_longjmp, and moving extension file staging from onRuntimeInitialized to preRun.
Changes:
- Patch JSPI web loaders to synthesize
env.__c_longjmpas aWebAssembly.Tagand add wasm longjmp / PHP/libc / pthread exports for PHP 8+ JSPI builds. - Move resolved extension installation from
onRuntimeInitializedtopreRunso generated.inifiles exist before PHP startup. - Add e2e and unit tests covering the new side-module loading path; configure web Playwright to run with
JSPI=true.
Reviewed changes
Copilot reviewed 14 out of 15 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/php-wasm/compile/php/Dockerfile | Add wasm longjmp + PHP 8+ JSPI symbol exports and inject the __c_longjmp Tag patch into the web loader. |
| packages/php-wasm/compile/php/Dockerfile-5-2 | Mirror the wasm longjmp exports and loader patch for the 5.2 build. |
| packages/php-wasm/web-builds//jspi/php_.js | Regenerated loaders containing the __c_longjmp Tag fallback. |
| packages/php-wasm/web-builds/8-3/jspi/php_8_3.js | Rebuilt 8.3.31 binary/loader exporting new wp_mysql_parser ABI symbols. |
| packages/php-wasm/universal/src/lib/load-extension.ts | Stage extension files in preRun instead of onRuntimeInitialized. |
| packages/php-wasm/universal/src/lib/load-extension.spec.ts | Unit test verifying preRun ordering for staged extension files. |
| packages/php-wasm/web/src/test/php-dynamic-loading.spec.ts | E2E test loading wp_mysql_parser manifest and exercising the lexer. |
| packages/php-wasm/web/playwright.config.ts | Run the web dev server with JSPI=true for the e2e tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
adamziel
force-pushed
the
adamziel/fix-web-jspi-side-module-longjmp
branch
from
May 15, 2026 22:06
d85c42d to
8a10166
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What it does
Makes web JSPI PHP runtimes load external side modules that import wasm longjmp symbols and the PHP/libc symbols used by the published SQLite Database Integration
wp_mysql_parserextension.It also keeps extension ini staging before PHP startup, so externally resolved extensions are available during boot.
Rationale
The
wp_mysql_parsermodule downloaded correctly but crashed or failed to instantiate in the browser runtime. The web JSPI main module did not provide the same dynamic-linking surface as the side module expected:__c_longjmpwas synthesized as a JS function instead of a WebAssembly tag, and the parser also imported PHP/libc functions that were not exported by the web 8.3 JSPI build.This PR also replaces the
onRuntimeInitializedEmscripten hook with thepreRunhook.Extension files were already written as
.so+ generated.inifiles before this PR. That worked because PHP startup is lazy:loadPHPRuntime()usesnoInitialRun: true, andphp_wasm_init()usually runs later on the first PHP request.The fragile ordering was:
That is only safe while nothing starts PHP before or during
onRuntimeInitializedextension staging. If PHP startup happens in that window, PHP scans ini files beforefoo.iniexists and the extension is missed.This PR changes the ordering to:
So the fix is not “start using ini files.” The fix is “write the already-generated ini files at the earliest filesystem-ready lifecycle point, before any runtime-initialized code can trigger PHP startup.”
Implementation
The compile Dockerfiles now add the wasm longjmp exports for JSPI builds and patch the generated web loader to provide
env.__c_longjmpas aWebAssembly.Tag.For PHP 8+ JSPI builds, the main module export list now includes the additional
wp_mysql_parserimports such aszend_hash_get_current_key_type_ex,zend_hash_get_current_data_ex,zend_hash_move_forward_ex,zend_is_true,clock_gettime,getcwd, and the pthread helpers. The committed PHP 8.3 web JSPI binary was rebuilt to include those exports.Resolved extension files are staged from
preRun, notonRuntimeInitialized, so generated ini files exist before PHP scans them.The e2e coverage now loads the published
wp_mysql_parsermanifest and runsWP_MySQL_Native_Lexerover a SQL query, so it catches both load-time and runtime symbol issues.Testing instructions
Run:
I also verified the stacked website PR locally with the SQLite
wp_mysql_parsermanifest. The WordPress admin notice reported:PHP extension check: wp_mysql_parser extension is loaded; native parser classes available: yes.