Title: PHP extension dependencies
Published: July 15, 2026
Last modified: July 16, 2026

---

# PHP extension dependencies

## In this article

 * [Playground-built dependencies](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#playground-built-dependencies)
 * [Vendored dependencies](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#vendored-dependencies)
 * [Prebuilt static archives](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#prebuilt-static-archives)
 * [CMake dependencies](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#cmake-dependencies)
 * [Makefile dependencies](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#makefile-dependencies)
 * [Rust build systems](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#rust-build-systems)
    - [PHP version support for ext-php-rs](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#php-version-support-for-ext-php-rs)
 * [Troubleshooting](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#troubleshooting)

[ Back to top](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#wp--skip-link--target)

Custom PHP.wasm extensions can only link WebAssembly code built with the same
 Emscripten
toolchain and JSPI ABI as the PHP runtime. Native host libraries from `/usr/lib`,
Homebrew, apt, or npm packages cannot be linked into the final `.so`.

Build dependencies as static WebAssembly archives and pass their headers and
 archives
to `@php-wasm/compile-extension`.

## 󠀁[Playground-built dependencies](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#playground-built-dependencies)󠁿

Some libraries already have recipes in `packages/php-wasm/compile`. Build the
 JSPI
target and pass the mounted path inside the helper container:

    ```bash
    make -C packages/php-wasm/compile libz_jspi

    npx @php-wasm/compile-extension \
        --source ./zlib-probe \
        --name zlib_probe \
        --php-versions 8.4 \
        --extra-cflags "-I/php-wasm-compile/libz/jspi/dist/root/lib/include" \
        --extra-ldflags "/php-wasm-compile/libz/jspi/dist/root/lib/lib/libz.a"
    ```

Use the `jspi` archive for custom extension builds.

## 󠀁[Vendored dependencies](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#vendored-dependencies)󠁿

If the dependency is not built by Playground, vendor the source under your
 extension
directory and build it with Emscripten before the extension is linked. Paths from`--
source` are available under `/build` inside the helper container:

    ```bash
    npx @php-wasm/compile-extension \
        --source ./external-lib-probe \
        --name external_lib_probe \
        --php-versions 8.4 \
        --extra-cflags "-I/build/vendor/string-score/install/include" \
        --extra-ldflags "/build/vendor/string-score/install/lib/libstring_score.a"
    ```

`--extra-cflags` is visible during `./configure`. `--extra-ldflags` is applied
 
to the final side-module link so dependency archives do not break Autoconf’s compiler
smoke tests.

## 󠀁[Prebuilt static archives](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#prebuilt-static-archives)󠁿

Use `--extra-ldflags` to link prebuilt static archives such as a Rust
 `staticlib`.
The helper detects `.a` entries in `--extra-ldflags` and force-links them into the
final side module with `--whole-archive`.

    ```bash
    npx @php-wasm/compile-extension \
        --source ./my-rust-extension \
        --name my_rust_extension \
        --php-versions 8.4 \
        --extra-cflags "-I/build/include" \
        --extra-ldflags "/build/target/wasm32-unknown-emscripten/release/libmy_rust_extension.a"
    ```

The extension still needs a small `phpize` wrapper. A common Rust pattern is:

 * `config.m4` declares the PHP extension and builds a tiny C shim with
    `PHP_NEW_EXTENSION`.
 * `_shim.c` defines the PHP module entry and calls exported Rust functions over
   
   C ABI.
 * `--extra-ldflags` points at the Rust `staticlib` archive under `/build`.

Do not use `PHP_ADD_LIBRARY_WITH_PATH` for a sibling `.a` archive in
 `config.m4`.
PHP’s libtool setup can look for a matching `.so`, fail to link the archive into
the side module, and still leave a build artifact behind. Pass static archives through`--
extra-ldflags` instead.

## 󠀁[CMake dependencies](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#cmake-dependencies)󠁿

Build CMake dependencies as static archives with Emscripten and store the
 install
tree under the extension source directory:

    ```bash
    source /root/emsdk/emsdk_env.sh

    emcmake cmake \
        -S vendor/libfoo \
        -B vendor/libfoo/build \
        -DCMAKE_BUILD_TYPE=Release \
        -DCMAKE_INSTALL_PREFIX="$PWD/vendor/libfoo/install" \
        -DBUILD_SHARED_LIBS=OFF

    emmake cmake --build vendor/libfoo/build --target install

    npx @php-wasm/compile-extension \
        --source . \
        --name my_extension \
        --php-versions 8.4 \
        --extra-cflags "-I/build/vendor/libfoo/install/include" \
        --extra-ldflags "/build/vendor/libfoo/install/lib/libfoo.a"
    ```

## 󠀁[Makefile dependencies](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#makefile-dependencies)󠁿

Force Makefile dependencies to use Emscripten tools:

    ```bash
    source /root/emsdk/emsdk_env.sh

    emmake make -C vendor/libfoo \
        CC=emcc \
        CXX=em++ \
        AR=emar \
        RANLIB=emranlib \
        PREFIX="$PWD/vendor/libfoo/install" \
        install

    npx @php-wasm/compile-extension \
        --source . \
        --name my_extension \
        --php-versions 8.4 \
        --extra-cflags "-I/build/vendor/libfoo/install/include" \
        --extra-ldflags "/build/vendor/libfoo/install/lib/libfoo.a"
    ```

The final PHP extension still needs a `phpize` build recipe. If the extension
 itself
is CMake-only or Makefile-only, add a thin `config.m4` wrapper that builds the PHP
extension and treats the CMake or Make output as dependency code.

## 󠀁[Rust build systems](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#rust-build-systems)󠁿

The helper image includes a host `php` CLI because `ext-php-rs` and similar
 build
systems often shell out to `php` for version detection. The host CLI is only for
build scripts. The compiled extension must still link against the PHP.wasm headers
and side-module ABI.

Rust extensions that use `bindgen` need the same target, sysroot, and Zend
 defines
as PHP.wasm. The helper exports `BINDGEN_EXTRA_CLANG_ARGS` with:

    ```bash
    --target=wasm32-unknown-emscripten \
    --sysroot=$EMSDK_SYSROOT \
    -DZEND_ENABLE_ZVAL_LONG64 \
    -D__x86_64__
    ```

If you build the Rust archive outside the helper container, set the same value
 
before running `cargo`.

Rust `cc-rs` build scripts also need position-independent objects for side
 module
linking. The helper exports target-specific compiler variables including `CFLAGS_wasm32_unknown_emscripten
=-fPIC` and `CXXFLAGS_wasm32_unknown_emscripten=-fPIC`. Without `-fPIC`, the final
link can fail with an error like:

    ```language-text
    R_WASM_MEMORY_ADDR_SLEB cannot be used against symbol ...; recompile with -fPIC
    ```

Rust’s precompiled `std` for `wasm32-unknown-emscripten` is built with
 unwinding
support. That imports a `__cpp_exception` WebAssembly tag that PHP.wasm does not
export. Build Rust static libraries with `panic=abort` and a nightly rebuilt standard
library:

    ```bash
    RUSTFLAGS="-C panic=abort" cargo +nightly build \
        --release \
        --target wasm32-unknown-emscripten \
        -Zbuild-std=std,panic_abort
    ```

Link the resulting `lib*.a` with `--extra-ldflags`.

### 󠀁[PHP version support for ext-php-rs](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#php-version-support-for-ext-php-rs)󠁿

`ext-php-rs` `0.15` depends on PHP 8 Zend APIs and does not compile against
 PHP`
7.4` headers. Rust extensions built on top of `ext-php-rs` `0.15` should restrict
the PHP matrix to `8.0` through `8.5`:

    ```bash
    npx @php-wasm/compile-extension \
        --source ./my-rust-extension \
        --name my_rust_extension \
        --php-versions 8.0,8.1,8.2,8.3,8.4,8.5 \
        --extra-ldflags "/build/target/wasm32-unknown-emscripten/release/libmy_rust_extension.a"
    ```

The helper itself still supports PHP `7.4`. The limitation is in the Rust
 binding
layer, not in the WebAssembly side-module ABI. Hand-written Rust extensions that
bind Zend directly through `bindgen` can target PHP `7.4` if the C ABI shim does
the same.

`ext-php-rs` also generates bindings for one PHP version at a time. When you
 build
the same Rust crate for several PHP versions, rebuild the staticlib for each PHP
version with that version’s Zend API number visible to the build script, and link
the matching archive into each `--php-versions` lane.

## 󠀁[Troubleshooting](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#troubleshooting)󠁿

`configure: error: ... not found`

The dependency headers or libraries are not visible inside the container. Use
 paths
under `/build` for files copied from `--source`, or `/php-wasm-compile/<dependency
>/<mode>/dist/root/lib` for Playground-built dependencies.

`undefined symbol` when loading the extension

The extension references a function that is not exported by the PHP main module

or was not linked from a WebAssembly dependency archive. Add the dependency archive
to `--extra-ldflags`, or rebuild the main PHP.wasm runtime if the symbol must come
from PHP core.

`WebAssembly.LinkError` or startup crashes

Check that the extension loads in a JSPI runtime. The custom extension helper
 does
not build Asyncify artifacts.

`wasm-ld: unknown file type` or `file not recognized`

One of the linked libraries is a native host library. Rebuild that dependency
 with
Emscripten and link the resulting `.a` file.

`R_WASM_MEMORY_ADDR_SLEB cannot be used against symbol`

A C or C++ object in a static archive was not compiled as position-independent
 
code. Rebuild it with `-fPIC`, or make sure Rust `cc-rs` sees `CFLAGS_wasm32_unknown_emscripten
=-fPIC`.

`__cpp_exception` is undefined when loading a Rust extension

The Rust archive was built against an unwinding `std`. Rebuild it with
 `RUSTFLAGS
="-C panic=abort"` and `cargo +nightly build -Zbuild-std=std,panic_abort`.

`bad export type for 'stdin'` or another C runtime global

The side module pulled in a dependency object that expects a mutable C runtime
 
global the main PHP.wasm module does not export. Rebuild the dependency with the
unused feature disabled, link a smaller archive that excludes that object, or move
the dependency into the main PHP.wasm build so the global is provided by the runtime.

`phpize` cannot find headers

The helper image builds and installs a minimal matching PHP source tree before
 
running `phpize`. If an extension includes headers from optional PHP extensions,
copy or generate those headers in the Docker layer or include them in the extension
source.

First published

July 15, 2026

Last updated

July 16, 2026

Edit article

[ Improve it on GitHub: PHP extension dependencies ](https://raw.githubusercontent.com/WordPress/wordpress-playground/trunk/packages/docs/site/docs/developers/06-apis/javascript-api/09-php-extension-dependencies.md)

Changelog

[ See list of changes: PHP extension dependencies ](https://developer.wordpress.org/playground/developers/apis/javascript-api/php-extension-dependencies/?output_format=md#)

[  Previous: Building PHP extensions](https://developer.wordpress.org/playground/developers/apis/javascript-api/build-php-extensions/)

[  Next: Architecture overview](https://developer.wordpress.org/playground/developers/architecture/)