[PHP] Add PHP.wasm extension compile helper - #3567
Closed
adamziel wants to merge 1 commit into
Closed
Conversation
adamziel
force-pushed
the
codex/unified-php-extension-loading
branch
from
April 29, 2026 17:28
8dc2bd7 to
0a01f5d
Compare
adamziel
force-pushed
the
codex/compile-extension-helper-stack
branch
from
April 29, 2026 17:32
2e5916a to
e8d7a6c
Compare
adamziel
changed the base branch from
codex/unified-php-extension-loading
to
codex/php-extension-blueprint-loading
April 29, 2026 17:33
adamziel
force-pushed
the
codex/php-extension-blueprint-loading
branch
from
April 29, 2026 17:59
38515ca to
cdbef2d
Compare
adamziel
force-pushed
the
codex/compile-extension-helper-stack
branch
from
April 29, 2026 18:00
e8d7a6c to
a98003e
Compare
Collaborator
Author
|
Superseded by #3582, which is retargeted to trunk and updates the custom-extension workflow for the current startup-time .so loading API. |
adamziel
added a commit
that referenced
this pull request
May 2, 2026
…extensions (#3582) ## What it does Adds `@php-wasm/compile-extension`, a Docker-backed helper for compiling `phpize` extension source directories into PHP.wasm JSPI side modules. Developers can now build a custom extension, publish the generated `.so` files and `manifest.json`, and load them at startup through the `extensions` API. For example, given a `wp_mysql_parser` extension with a normal `config.m4`: ```bash npx @php-wasm/compile-extension \ --source ./wp-mysql-parser \ --name wp_mysql_parser \ --php-versions 8.4 \ --out ./dist/wp_mysql_parser ``` The output is directly loadable by PHP.wasm: ```text dist/wp_mysql_parser/ |-- manifest.json `-- wp_mysql_parser-php8.4-jspi.so ``` ```ts import { PHP } from '@php-wasm/universal'; import { loadNodeRuntime } from '@php-wasm/node'; const php = new PHP( await loadNodeRuntime('8.4', { extensions: [ { source: { format: 'manifest', manifestUrl: './dist/wp_mysql_parser/manifest.json', }, }, ], }) ); ``` Rust extensions are supported through the same workflow when the Rust crate is wrapped as a `phpize` extension: ```bash RUSTFLAGS="-C panic=abort" cargo +nightly build \ --release \ --target wasm32-unknown-emscripten \ -Zbuild-std=std,panic_abort npx @php-wasm/compile-extension \ --source ./my-rust-extension \ --name my_rust_extension \ --php-versions 8.4 \ --extra-ldflags "/build/target/wasm32-unknown-emscripten/release/libmy_rust_extension.a" ``` Rust-specific caveats: - The final extension still needs a tiny `config.m4` + C shim that exposes the PHP module entry and calls Rust over C ABI. - Custom extension builds are **JSPI-only**. Link JSPI-compatible dependency archives and load the result in a JSPI runtime. - Pass Rust `staticlib` archives through `--extra-ldflags`, not `PHP_ADD_LIBRARY_WITH_PATH`. - `bindgen` needs the PHP.wasm target/sysroot/Zend defines; the helper exports `BINDGEN_EXTRA_CLANG_ARGS` for builds run inside the container. - `cc-rs` needs `-fPIC` for side modules; the helper exports target-specific `CFLAGS_wasm32_unknown_emscripten` and `CXXFLAGS_wasm32_unknown_emscripten`. - Rust `std` must be rebuilt with `panic=abort`; the precompiled `wasm32-unknown-emscripten` `std` imports unwinding support PHP.wasm does not provide. ## Rationale Custom extensions should be a real developer workflow, not just a runtime API. This PR gives developers a documented path from extension source code to loadable PHP.wasm artifacts, including manifest metadata and `sha256` hashes. This supersedes #3567 and retargets the work to `trunk`. The docs and examples match the recent `.so` loading changes: startup-time loading via `extensions`, PHP-version manifest selection, direct URLs, and generated `.ini` files instead of post-startup `dl()` or older Blueprint-oriented APIs. ## Implementation - Adds the `@php-wasm/compile-extension` CLI, Docker build flow, manifest generation, and artifact selection helpers. - Builds custom extensions for JSPI runtimes only. The CLI no longer exposes an `--async-modes` matrix, and generated manifests no longer include `asyncMode`. - Documents static archive linking, Rust `staticlib` wrappers, vendored libraries, CMake/Make dependencies, and common WebAssembly linking failures. - Adds unit tests, Docker integration fixtures, and CI coverage for the helper. ## Testing instructions ```bash npm exec nx test php-wasm-compile-extension npm exec nx run php-wasm-compile-extension:typecheck npm exec nx run php-wasm-compile-extension:lint npm exec nx run php-wasm-compile-extension:build npm exec nx run docs-site:check-orphan-pages bash -n packages/php-wasm/compile-extension/scripts/build-in-docker.sh packages/php-wasm/compile-extension/tests/run-integration-tests.sh git diff --check PATH="$HOME/.nvm/versions/node/v24.14.0/bin:$PATH" npm exec nx run php-wasm-compile-extension:test-compile-extension-integration ``` The Docker integration needs a Node build with JSPI support; Node 22 reports JSPI unavailable locally, while Node 24 matches the PR CI job.
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
Adds
@php-wasm/compile-extension, stacked on top of #3568 and #3566. The CLI builds a PHP extension source directory into PHP8.4.soartifacts plusmanifest.json.The package includes:
phpize/emconfigure/emmakerecipesha256entriesloadPHPExtension()Rationale
The runtime API in #3566 and Blueprint step in #3568 do not require a build helper. Keeping this separate makes those reviews smaller while still proving that externally built artifacts can target the same manifest contract.
Extension authors still need a supported way to produce compatible side modules without re-deriving Emscripten flags, PHP headers, and async-mode settings.
Implementation
The CLI keeps the public surface flag-only for v1:
The Docker layer extends the existing compile image and adds the
phpizetoolchain.scripts/build-in-docker.shis the source of truth for the actual build recipe.The integration job runs only when
packages/php-wasm/compile-extension/**changes. It builds and loads a hello extension,php-srcext/calendar,redis, a zlib-backed extension, and an extension backed by a vendored non-Playground library.Testing instructions
npx nx test php-wasm-compile-extension npx nx typecheck php-wasm-compile-extension npx nx lint php-wasm-compile-extension npx nx build php-wasm-compile-extensionCI runs the Docker-backed integration coverage via
test-compile-extension-helper.