Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PHP: Support proc_open #594

Closed
adamziel opened this issue Jun 25, 2023 · 2 comments · Fixed by #596
Closed

PHP: Support proc_open #594

adamziel opened this issue Jun 25, 2023 · 2 comments · Fixed by #596
Labels

Comments

@adamziel
Copy link
Collaborator

adamziel commented Jun 25, 2023

PHPUnit and wp-cli both use proc_open and will only work correctly in WordPress Playground if that function is supported.

Today proc_open does not work becuse internally it calls fork() which is not supported in Emscripten and doesn't seem possible to polyfill.

Therefore, the only way to support proc_open is to shim it using require('child_process').spawn() similarly to how we shim popen():

js_popen_to_file: function(command, mode, exitCodePtr) {

const cp = require('child_process');
let ret;
if (modestr === 'r') {
ret = cp.spawnSync(cmdstr, [], {
shell: true,
stdio: ["inherit", "pipe", "inherit"],
});
HEAPU8[exitCodePtr] = ret.status;
require('fs').writeFileSync(pipeFilePath, ret.stdout, {
encoding: 'utf8',
flag: 'w+',
});
} else if (modestr === 'w') {

proc_open has the following signature in PHP:

proc_open(
    array|string $command,
    array $descriptor_spec,
    array &$pipes,
    ?string $cwd = null,
    ?array $env_vars = null,
    ?array $options = null
): resource|false

Handling $pipes is the most challenging bit here. Emscripten documentation doesn't explain how to create pipes – the easiest way should be reading the code and looking up examples.

If we can find a way to open Emscripten pipes and pass the data from/to the child process, that's great – that would probably involve custom Emscripten streams or devices. Otherwise, we could fake pipes by opening three temporary files in Emscripten (for stdin, stdout, stderr) and creating $pipes using those file descriptor.

cc @wojtekn @sejas @danielbachhuber @schlessera

@adamziel
Copy link
Collaborator Author

adamziel commented Jun 25, 2023

PHPUnit uses proc_open to support process isolation (which WordPress relies on):

TestCase::run():

        if (!$this->shouldRunInSeparateProcess()) {
            (new TestRunner)->run($this);
        } else {
            (new TestRunner)->runInSeparateProcess(
                $this,
                $this->runClassInSeparateProcess && !$this->runTestInSeparateProcess,
                $this->preserveGlobalState,
            );
        }
    }

TestRunner::runInSeparateProcess:

$php = AbstractPhpProcess::factory();
$php->runTestJob($template->render(), $test);

DefaultPhpProcess::runProcess():

        $process = proc_open(
            $this->getCommand($settings, $this->tempFile),
            $pipeSpec,
            $pipes,
            null,
            $env,
        );

@adamziel adamziel changed the title Support proc_open PHP: Support proc_open Jun 25, 2023
@adamziel
Copy link
Collaborator Author

wp-cli proc_open usages

@adamziel adamziel mentioned this issue Oct 4, 2023
10 tasks
adamziel added a commit that referenced this issue Oct 24, 2023
## Description

Adds [support for proc_open(), popen($cmd, "w"), exec(), passthru(),
system()](b4cfb8c)
via a custom spawn handler defined in JavaScript via
`php.setSpawnHandler()`.

### `proc_open()`

This PR patches PHP to replace C calls to `proc_open()` with a custom
implementation provided with a new `proc_open7.0.c`/`proc_open7.4.c`
file. The original `proc_open()` relies on POSIX `fork()` function not
available in WebAssembly, so which is why this PR defers the command
execution to the `setSpawnHandler()` callback provided via JavaScript.

### `exec()`, `passthru()`, `system()`

This PR provides a custom `php_exec` implementation (via the
`wasm_php_exec` C function). `php_exec` is the function powering PHP
functions like `exec()`, `passthru()`, `system()`.

### `popen()`

The existing `popen()` implementation is rewired to call the
`setSpawnHandler()` callback. Also, the support for the `popen($cmd,
"w")` mode was added.

### Other notes

This PR removes support for PHP 5.6 as supporting these functions was
challenging there AND also WordPress dropped the support for PHP 5.6.


Closes #594
Closes #710
@adamziel adamziel mentioned this issue Dec 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant