Changelog¶
0.12.2 (2026-11-05)¶
Fixes¶
- In a worker,
FS.mkdir(),FS.symlink()and what else creates a file resolve their promise: they worked, and rejected it, as their node can not be copied. What can not be copied says so, in a message that can be read. - What
FSthrows has amessage, asNo such file or directory: it only had itserrno.
0.12.1 (2026-11-05)¶
Fixes¶
- With
OPCACHE, a file with attributes on its constants, assymfony/polyfill-uuidhas, does not crash PHP anymore: PHP 8.5.12 and before get the fix of PHP 8.5.13.
0.12.0 (2026-11-05)¶
Features¶
- BC break: PHP allocates with Zend MM, its own allocator, instead of
malloc():memory_limitworks, andmemory_get_usage()too. Going over it, or filling the memory of the module, is a fatal error of PHP: the module does not abort, and the next run starts with all of its memory. - BC break:
memory_limitdefaults to 7/8 of what the module has after its data and what it embeds,107Mwith the 128 MB by default, instead of128M. - Add the
pharextension. - Add the
sodiumextension, with libsodium 1.0.22 (LIBSODIUM_VERSION). - Add the
zipextension, with libzip 1.12.1 (LIBZIP_VERSION), and only deflate. - Add the
zlibextension. - Add
serveInFrame(), inphp-web.mjs: it shows an application in an<iframe>, with a service worker. - Add
php-sw.js, built for the web next tophp-web.mjs: the service worker of a project imports it, and callsforwardToPhp(). - Add
keepCookies()andwithCookies(), inphp-web.mjs: the cookies of the application, for a service worker that runs PHP itself. - Add
Target::files()andStack::targetArtifacts().artifacts()listsphp-sw.jstoo. - The Symfony example uses them:
public/cookies.mjsis gone, and the cookies of its framed page are under another key: a visitor starts a new session.
Fixes¶
- A module does not run out of memory by itself anymore: what a run leaves is gone with it. The Symfony example aborted after about 1,900 requests.
Minor¶
- Document what PHP can not do with the network, and what the backward compatibility covers.
0.11.0 (2026-11-05)¶
Features¶
- Add the
gdextension, with zlib 1.3.2, libpng 1.6.59, libjpeg-turbo 3.2.1 and libwebp 1.6.0 (ZLIB_VERSION,LIBPNG_VERSION,LIBJPEG_TURBO_VERSION,LIBWEBP_VERSION). - Add the
exifextension.
0.10.0 (2026-11-05)¶
Features¶
- BC break:
targets()takesTargets, andartifact()aTarget:->targets(Target::Web).Stack::TARGETSis gone. - BC break:
icuData()takes anIcuData:->icuData(IcuData::None). - BC break: the stacks are named after what they are:
php-8.5instead ofdefault,php-8.5-intlinstead ofintl.intl-without-dataandfull-without-intl-dataare gone: the playground has every extension. - BC break:
castor wasm:servetakes--php,--withand--opcache, instead of--stack. - BC break: nothing is embedded by default:
EMBED_ATis empty, andexamples/embed/is gone. - Add
request(): it runs PHP as a web server does, for an HTTP request.PHP_SAPIis thenwasm. - Add
createPhpInWorker(): PHP in a Web Worker, off the main thread. - Add the
WARMUPbuild argument,warmup()of a stack: a file of the embedded directory runs when PHP is built, and what it writes is embedded. - Add the
OPCACHEbuild argument,opcache()of a stack, PHP 8.5 and later: opcache keeps what it compiles in files, as they run (runtime), or when PHP is built (precompiled). - Add the Symfony example: a Symfony 8 application with Doctrine and SQLite, in a frame, and on its own page, with PHP in its service worker.
- Add the website, https://lyrixx.github.io/php-wasm-builder/:
castor site:build,castor site:serve, and.github/workflows/pages.yml. castor wasm:sizesgives the gzipped size too, and--jsonprints JSON.castor wasm:stackslists what each stack embeds, its warmup and its opcache.
Fixes¶
- The C stack is 1 MB, and checked: PHP overflowed the 64 KB of emscripten, and wrote over its own memory. An overflow aborts the module now.
- opcache is disabled unless it has its files: it can not start without them.
Minor¶
- Each example is a directory, laid out as a project:
stack.php,public/,app/and a README. The stacks areexample-playgroundandexample-symfony. - The notes example is gone: the Symfony one keeps its database too.
- The documentation is in
docs/, in Markdown, rendered by zensical. It shows how to use the builder with castor, and with Composer and Docker only. - The website shows this changelog.
- The README is short, and in Markdown.
- Document that PHP can write over what is embedded, in memory.
- A link to the website shows an image, that
castor site:social-imagerenders.
0.9.0 (2026-11-02)¶
Features¶
- Add the
fileinfoextension. - Add the
iconvextension, with GNU libiconv 1.19 (LIBICONV_VERSION)://TRANSLITand//IGNOREwork. - Add the
opensslextension, with OpenSSL 3.5.9 (OPENSSL_VERSION). - Add the
sessionextension. The headers are still never sent, but sessions start. - Add the
full-without-intl-datastack: every extension, intl without ICU's data. PhpWasm\check_stack()checks iconv, OpenSSL and the sessions.
Fixes¶
proc_open()with an array, as Symfony Process gives, aborted the module: it fails with a warning now, asexec()andpopen()do.runFile()andrunCode()do not print the#!line of a script anymore.$_SERVERhasPHP_SELF,SCRIPT_NAME,SCRIPT_FILENAME,PATH_TRANSLATEDandDOCUMENT_ROOT.
Minor¶
- Document that a Fiber aborts the module, and that integers are 32 bits.
0.8.0 (2026-10-22)¶
Features¶
- BC break: the JavaScript API is
createPhp(), withrunFile()andrunCode(): they take arguments and stdin, and resolve with the exit status, stdout and stderr.createPhpModule()andccall('phpw', …)are gone. - BC break:
phpw.cisdocker/run.c, and the module only exports what the API needs. - BC break: the build for node is
php-node.mjsandphp-node.wasm, instead ofphp-cli.mjsandphp-cli.wasm. - BC break:
->embed('build/embed', at: '/app')replaces->embed('build/embed', 'app'),$embedAtreplaces$embedPath, andEMBED_AT, with the/, replacesEMBED_PATH. - BC break:
PhpWasm\run_wasm()isrun_code(): it keeps the line breaks at the end, and takes stdin. - BC break:
castor wasm:testiscastor wasm:check, and checks the JavaScript API too. - BC break:
Dockerfileisdocker/Dockerfile,run.mjsisnode/run.mjs, andpage.mjsisnode/fetch-files.mjs. - Add the
pdo,pdo_sqliteandsqlite3extensions, with SQLite 3.54.1 (SQLITE_VERSION,SQLITE_YEAR), FTS5 and its math functions. - Add
createPhp({ persist }): a directory stays in IndexedDB, across the reloads and the tabs of the page. - Add
examples/notes/, andcastor wasm:serveto serve it. - Add
examples/playground/, and itsplaygroundstack. node/run.mjsgives PHP its arguments and stdin.castor wasm:runoffers to build the stack when it is not built yet.
Fixes¶
- A big script does not crash the module anymore: its code is not copied on the stack.
Minor¶
- The castor tasks say what they do, and what to do next.
- The scripts of
examples/embed/say which stack they need. - In
examples/,embed/is what is embedded,public/what is served. - The README embeds
build/embed/in place ofbuild/app/. - The CI does not comment the sizes of the
.wasmfiles on the pull requests anymore.
0.7.1 (2026-10-22)¶
Minor¶
- Build PHP 8.5.12 by default.
0.7.0 (2026-10-21)¶
Features¶
- Add
PhpWasm\test_page()andpage.mjs, to test the JavaScript of a page in node:fetch()reads the files from the disk.
Minor¶
run.mjsloads the build for the web withpage.mjs.
0.6.0 (2026-10-21)¶
Features¶
- Add
PhpWasm\check_stack()andPhpWasm\run_wasm(), to check in CI that a build boots and answers, without a browser. run.mjsruns the build for the web too.
Minor¶
castor wasm:runruns the build of the first target of the stack.castor wasm:testchecks every build of the stacks, and does not build the node target for them anymore.
0.5.0 (2026-10-21)¶
Features¶
- BC break:
PhpWasm\Buildis gone:build()returns the stacks it is given. - BC break: the getters of
Stackare gone, its fields are public and read-only.embedandoutputDirare absolute,targetsis never null. - BC break:
renamed()builds inbuild/<name>/: two stacks can not share their output directory. castor wasm:build --alltakes the overrides (--php,--target…).
Fixes¶
extensions()rejects an unknown extension at once, instead of the build failing.EXTENSIONSandTARGETSare separated by spaces only, as documented.phpw_exec()returns a copy of the value: it pointed to a string that PHP freed.phpw()does not print "Failed to execute PHP script." anymore: PHP already says why.
Minor¶
- The
./configureflags are sorted: the same extensions always give the same flags.
0.4.0 (2026-10-21)¶
Features¶
- BC break:
build()takes a list of stacks:build([$stack]). Its second argument goes todocker buildx bake --set, for a cache. - Add
ICU_DATA(->icuData(),--icu-data):nonelinks intl without ICU's data, 2 MB bigger instead of 33 MB. Add theintl-without-datastack. phpw()andphpw_run()return the exit status of PHP, andrun.mjsandcastor wasm:runexit with it.embed()takes the name of the directory in the WASM filesystem.- Add
->outputDir(), to build a stack somewhere else.
Fixes¶
- The output directory gets the default permissions back: buildx gave it 0700.
Minor¶
- The bake file is
build/bake.json, not in the directory of the stack anymore. - Drop the
--llvm-ltoandMAXIMUM_MEMORYoptions of emcc: they did nothing.
0.3.0 (2026-10-20)¶
Features¶
- BC break:
build()takes the stacks,build(Stack ...$stacks), instead of their names. - BC break: a stack never changes:
renamed()derives it,from()is gone.embed()is relative to the root of the project. - BC break: the
wasm:*tasks only build the stacks of this repository, and ofstacks.override.php: they can not be imported withcomposer://anymore.
0.2.0 (2026-10-20)¶
Features¶
- Add
PhpWasm\build(string ...$stacks), to build stacks from a project. Thewasm:*tasks needimport('composer://lyrixx/php-wasm-builder').
Fixes¶
- Build on the
emscripten/emsdkimage: the download of the SDK hung some cold builds.EMSDK_VERSIONis a tag of the image. - Allow buildx to read the builder and the stacks, and to write the builds: it waited forever for a confirmation.
0.1.0 (2026-10-20)¶
First release of this fork of derickr/php-wasm-builder.
Features¶
- Build PHP as WebAssembly with
docker buildx bake: PHP 8.5.11 by default, for the browser and for node (TARGETS). - Choose the extensions with
EXTENSIONS: only the libraries they need are built. Add bcmath, filter, tokenizer and intl, with all of ICU's data. - Choose the memory with
MEMORY, and the directory to embed. - Declare stacks in
stacks.php, and use them with the castor taskswasm:build,wasm:stacks,wasm:run,wasm:testandwasm:sizes. - Run PHP in what is built with
node run.mjs.
Minor¶
- Pin the emscripten SDK: the same arguments always give the same build.
- Build and test every stack in CI, with a cache.