Skip to content

Build wp_mysql_parser to wasm and gate it in CI#395

Open
adamziel wants to merge 7 commits intotrunkfrom
adamziel/wasm-pipeline
Open

Build wp_mysql_parser to wasm and gate it in CI#395
adamziel wants to merge 7 commits intotrunkfrom
adamziel/wasm-pipeline

Conversation

@adamziel
Copy link
Copy Markdown
Collaborator

@adamziel adamziel commented May 1, 2026

Spike confirms the new Rust MySQL parser can be compiled to a PHP-wasm side module via Playground's @php-wasm/compile-extension (PR #3582) and loaded at runtime. This PR lands the build recipe plus a CI job that builds the side module on every parser change and verifies it loads inside a freshly-checked-out Playground.

The full investigation lives in packages/php-ext-wp-mysql-parser/wasm-spike/RESULT.md — every workaround the build needs (host-PHP graft into the compile-extension image, bindgen sysroot, -fPIC, -Zbuild-std=std,panic_abort to drop Rust's panic=unwind libstd) is documented there with the underlying error each one fixes.

The CI job is intentionally narrow: PHP 8.4, JSPI only. The point isn't a release matrix — it's to gate the green path. Expanding to 7.2 → 8.5 is a follow-up once the headline blocker lands.

That blocker: MAIN_MODULE=2 in PHP-wasm exports a 30-line SAPI list, which doesn't include zend_ce_traversable and the other Zend class-entry globals that ext-php-rs imports. No existing Playground extension hits this because everything shipping today is statically linked into MAIN_MODULE — this PR's side module is the first to need those exports. The Playground-side fix is a small __attribute__((used)) keep-alive C file in packages/php-wasm/compile/php/; details and the exact symbol list (measured with wasm-objdump) are in RESULT.md.

Until the Playground export-list change merges and the CI job's playground-ref default is bumped to a build that includes it, this workflow will fail at the node run-spike.mjs step. That's the intended behavior — the failure is the whole signal that A1 is still pending.

The workflow accepts a playground-ref workflow_dispatch input so you can point it at a specific Playground branch (e.g. the export-list PR) for ad-hoc verification before that PR merges to trunk.

adamziel added 7 commits May 2, 2026 01:58
Spike confirms the Rust extension can be compiled to a PHP-wasm side module
via @php-wasm/compile-extension and loaded into Playground. Build recipe and
findings live in wasm-spike/RESULT.md; the new GitHub Actions workflow runs
the build against wordpress-playground trunk on every PR that touches the
extension and verifies the produced .so loads and parses a query.

The workflow currently fails on a Playground-side blocker (zend_ce_traversable
not exported from PHP-wasm's MAIN_MODULE=2 export set). RESULT.md documents
the blocker and the proposed fix in WordPress/wordpress-playground.
The previous workflow assumed playground-php-wasm:compile-extension-php8-4-jspi
was already present in the Docker daemon (which it is locally, after running
the @php-wasm/compile-extension CLI once). On a fresh runner it isn't, so
Dockerfile.rust failed at FROM with 'pull access denied'.

Build it explicitly: 'make base-image' for the Emscripten toolchain layer,
then docker build of compile-extension/docker/Dockerfile.ext with the same
PHP_VERSION the CLI's resolvePHPRelease() would pick, tagged the way the
CLI tags it (dots replaced with dashes).
The wasm-spike workflow needs @php-wasm/compile-extension's source map
and load-built-extension.mjs harness, neither of which exist on
wordpress-playground trunk. They live on the adamziel/compile-extension
branch (PR #3582). Default the checkout there so the resolvePHPRelease
step can read compile.ts and run-spike.mjs can find the harness.
The base-image build fails inside the daemon's default bridge network
with 'Could not resolve archive.ubuntu.com', causing apt-get to skip
every package. Configure /etc/docker/daemon.json with public resolvers
(8.8.8.8, 1.1.1.1) and restart the daemon so the build container can
reach Ubuntu mirrors.
The build script never wrote dist/manifest.json, so the verify step
failed. Generate it from the @php-wasm/compile-extension manifest
schema (name/version/artifacts[]) right after the side-module link,
fix the workflow's sha-extraction (manifest.artifacts[0], not
manifest.extensions[0].artifacts[0]), and drop the stale ASYNC_MODE
positional arg from run-spike.mjs so the args line up with
load-built-extension.mjs's <manifest> <php> <code> <expected> shape.
External extensions only load under JSPI, but loadNodeRuntime decides
the mode by asking wasm-feature-detect, not by trusting our flag. Run
the same probe up front so a failure surfaces with a clear message
instead of the runtime's generic 'External PHP extensions require JSPI'.
wasm-feature-detect's jspi probe checks for WebAssembly.Suspending in
the WebAssembly namespace. Node 22.22.2 with --experimental-wasm-jspi
does not expose it, so loadNodeRuntime falls back to asyncify and
rejects external extensions. Node 24 has JSPI enabled, so the probe
succeeds and the runtime accepts our side module.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant