Support spawning PHP sub-processes
adamziel opened this issue · 1 comments
Implementing Blueprints as a PHP library requires spawning PHP processes from PHP processes as follows:
<?php proc_open(['php', 'activate_theme.php']);Rationale
Complex tasks, such as activating WordPress Plugins, involve loading WordPress via require "wp-load.php"; to run WordPress functions, such as activate_plugin(). However, loading WordPress in the same PHP process that executes a Blueprint has significant downsides:
- WordPress left in a broken state can't be recovered
- A fatal error in WordPress would instantly kill the Blueprint executor
- WordPress defines global constants that subsequent steps may change (e.g.
WP_DEBUGorMULTISITE) - Once WordPress is updated, it can't be reloaded without running into "function already defined" errors
Multiprocessing model
Goal: Playground can spawn a PHP.wasm subprocesses in the Browser, Node.js, and VS Code.
fork() isn't an option
In native PHP, proc_open calls fork() in the main process and substitutes the child process with the requested command. Emscripten, however, doesn't support fork(). Playground's PHP.wasm polyfills proc_open with a custom Asyncify-based implementation.
Reusing the current PHP instance is not an option
This *would crash:
php.setSpawnHandler(
createSpawnHandler(async function (args, processApi) {
const result = await php.run({
scriptPath: args[1]
});
processApi.exit(result.exitCode);
})
);Internally, PHP sets a bunch of global variables. Calling run() before the previous run() is finished would either fail or leave us in an undefined state.
We need to start a fresh PHP instance
This would not crash:
php1.setSpawnHandler(
createSpawnHandler(async function (args, processApi) {
const php2 = await WebPHP.load();
const result = await php2.run({
scriptPath: args[1]
});
processApi.exit(result.exitCode);
})
);php2 could be spawned either:
- In the same web worker as
php1. Upsides: Easy resource sharing much easier. Downsides: ? - In a new web worker. Upsides: ?. Downsides: Sharing resources would involve
postMessage(), transferrable objects, SharedArrayBuffers, and other advanced APIs.
Filesystem sharing
Just spawning php2 wouldn't quite solve the problem as php2 would act on a separate in-memory filesystem (MEMFS) without affecting the WordPress residing in php1.
Let's discuss this in a dedicated issue: