WordPress/wordpress-playground

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_DEBUG or MULTISITE)
  • 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:

Done in #1069