Learn how to build PHP to WebAssembly in 10 minutes

Adam Zieliński Avatar

Posted by


This may be my last WordPress-related post until I’m back from my Sabbatical on September 26th. Enjoy!

WebAssembly is easier than you think. It is difficult to get started with, but then it’s not. I’ve spent the last 10 months learning that, and now I will save you that time by sharing how to build PHP into WebAssembly in 10 minutes.

And if you’ve never heard of WebAssembly, you can catch up here. Tl;dr it’s kinda a way of running regular desktop programs in the browser.

Building “Hello World” to WebAssembly is easy

Here’s a simple C program we’re going to build:

#include <stdio.h>
int main() {
	printf ("Hello, World!\n");
	return 0;

Normally, we’d use a compiler called cc from GNU build tools to build this program. It would produce an executable file called hello:

$ cc main.c -o hello
$ ls
hello  main.c
$ ./hello
Hello, World!

For WebAssembly, we’ll use a compiler called emcc from Emscripten build tools to build our program. It produces an “executable” WebAssembly file called hello.wasm and a JavaScript bindings file called hello.js:

$ emcc ./main.c -o hello.js
$ ls
hello.js  hello.wasm  main.c
$ node hello.js
Hello, World!

And since it’s just JavaScript, we can use it in the browser:

<!DOCTYPE html>
<script src="hello.js"></script>

And, on an abstract level, that’s it!

Building PHP to WebAssembly is only slightly harder

PHP interpreter may be more complex than Hello World, but ultimately it is still a C program, and we can use the same technique to build it as WebAssembly! For simplicity, we’ll focus on the CLI version of PHP.

First, we need to grab the source:

$ git clone https://github.com/php/php-src.git php-src --branch PHP-8.0 --single-branch --depth 1

Typically, PHP is built as a native executable with GNU build tools:

$ cd php-src
$ ./buildconf --force
$ ./configure --disable-all --enable-cli --enable-cgi
$ make -j8

And then it’s used in the CLI like any other terminal tool:

$ /root/build/php -v
PHP 8.0.27-dev (cli) (built: Jul  3 2023 10:10:13) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.27-dev, Copyright (c) Zend Technologies

Building PHP as a WebAssembly module is similar, except it involves Emscripten build tools like emconfigure and emmake:

$ ./buildconf --force
$ emconfigure ./configure --disable-all --enable-cli --enable-cgi --without-pcre-jit 

And that’s it!

We’ve added a few extra options to:

But other than that, we followed the same process as for a native build.

We can now run the built php file. It may have no extension but it’s a JavaScript file and runs on Node.js:

$ node /root/php-src/sapi/cli/php -v
munmap() failed: [28] Invalid argument
PHP 8.0.27-dev (cli) (built: Jul  3 2023 10:23:54) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.27-dev, Copyright (c) Zend Technologies

It worked, yay!

And since it’s just JavaScript, we can also use it in the browser (via http://):

<!DOCTYPE html>
  window.PHP = {
    arguments: ['-v']
<script type="text/javascript" src="php.js"></script>

And if you wonder what’s up with the munmap warning – I’m not actually sure, but it can be silenced by adding #define ZEND_MM_ERROR 0 to php-src/main/php_config.h after ./configure and before make.

And that’s how you run PHP as WebAssembly. Yay!

If you’d like to play with it, here’s a convenient Dockerfile for you, and if you want to learn more, MDN is a great starting point.

Limitations and a way around them

There are a few important limitations to keep in mind. First, this is just the CLI version. Second, it has no access to your files or network since WebAssembly modules are isolated from their environment. Effectively, our WebAssembly module is not ready to serve HTTP requests as it is.

I’ve spent a lot of time solving these problems so that you don’t have to. WordPress Playground ships PHP in the @php-wasm/node and @php-wasm/web npm packages. The documentation is a work in progress, but check them out – you may find them quite useful!

Leave a Reply

Blog at WordPress.com.

%d bloggers like this: