Compiling PHP

The build pipeline lives in a Dockerfile. In broad strokes, it:

  • Installs all the necessary linux packages (like build-essential)
  • Downloads PHP and the required libraries, e.g. sqlite3.
  • Applies a few patches.
  • Compiles everything using Emscripten, a drop-in replacement for the C compiler.
  • Compiles php_wasm.c โ€“ a convenient API for JavaScript.
  • Outputs a php.wasm file and one or more JavaScript loaders, depending on the configuration.
  • Transforms the Emscripten’s default php.js output into an ESM module with additional features.

To find out more about each step, refer directly to the Dockerfile.

Building

To build all PHP versions, run npm run recompile:php:web (or php-wasm-node) in the repository root. You’ll find the output files in packages/php-wasm/php-web/public. To build a specific version, run npm run recompile:php:web:kitchen-sink:8.0 or npm run recompile:php:web:light:8.0 โ€“ depending on the build pack.

The build produces two files: php.wasm and php.js.

PHP.wasm WebAssembly module

PHP extensions

PHP is built with several extensions listed in the Dockerfile.

Some extensions, like zip, can be turned on or off during the build. Others, like sqlite3, are hardcoded.

If you need to turn off one of the hardcoded extensions, feel free to open an issue in this repo. Better yet, this project needs contributors. You are more than welcome to open a PR and author the change you need.

C API exposed to JavaScript

The C API exposed to JavaScript lives in the php_wasm.c file.

Refer to the source code and the inline documentation in php_wasm.c to learn more.

Build configuration

The build is configurable via the Docker --build-arg feature. You can set them up through the build.js script, just run this command to get the usage message:

npm run recompile:php:web

PHP.js JavaScript module

The php.js file generated by the WebAssembly PHP build pipeline is not a vanilla Emscripten module. Instead, it’s an ESM module that wraps the regular Emscripten output and adds some extra functionality.

Here’s the API it exposes:

// php.wasm size in bytes:
export const dependenciesTotalSize = 5644199;

// php.wasm filename:
export const dependencyFilename = 'php.wasm';

// Run Emscripten's generated module:
export default function (jsEnv, emscriptenModuleArgs) {}

The generated JavaScript module is not meant for direct use. Instead, it can be consumed through a NodePHP class in Node.js and a WebPHP class in the browser:

// In Node.js:
const php = NodePHP.load('7.4');

// On the web:
const php = await WebPHP.load('8.0');

Both of these classes extend the BasePHP class exposed by the @php-wasm/universal package and implement the UniversalPHP interface that standardizes the API across all PHP environments.

Loading the PHP runtime

The load() method handles the entire PHP initialization pipeline. In particular, it:

  • Instantiates the Emscripten PHP module
  • Wires it together with the data dependencies and loads them
  • Ensures is all happens in a correct order
  • Waits until the entire loading sequence is finished