-
Notifications
You must be signed in to change notification settings - Fork 270
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
Populate php://input and other streams – JSON POST requests are broken #103
Labels
Comments
The trick is: int EMSCRIPTEN_KEEPALIVE phpwasm_init_context()
{
putenv("USE_ZEND_ALLOC=0");
SG(request_info).request_method = "POST";
SG(request_info).content_type = "application/json";
php_embed_module.read_post = *php__wasm_read_post;
return php_embed_init(0, NULL);
}
static int php__wasm_read_post(char *buffer, uint count_bytes)
{
sprintf (buffer, "{\"test\": \"foo\"}");
return 15;
} |
Merged
adamziel
added a commit
that referenced
this issue
Jan 12, 2023
# What is this PR all about? Introduces a PHP SAPI module that enables setting `$_POST`, `$_SERVER`, `php://input` and all the other PHP values from JavaScript. ## What problem does it solve? Before this PR, most superglobal values were set by prepending code snippets like `$_SERVER['DOCUMENT_ROOT'] = ${JSON.stringify(documentRoot)};` every time some code was evaluated. Unfortunately, that technique couldn't populate everything, e.g. `php://input` remained empty. ## How does it work? PHP SAPI is used to integrate PHP with webservers and runtimes. A few SAPIs you might be familiar with are `php-cgi`, `php-fpm`, and `php-cli`. A SAPI consumes the request information from the runtime, passes it to PHP, triggers the code execution, and passes the response back to the runtime. This PR introduces a WASM SAPI that accepts input information from JavaScript, sets up a PHP request, and passes a response back to JS. The most important changes are in the `php_wasm.c` file. The rest of the PR is adjusting the existing codebase to the new way of working with PHP. Briefly speaking, the SAPI module exposes a few setters like `wasm_set_query_string` or `wasm_add_SERVER_entry` and a `wasm_sapi_handle_request()` function that triggers the request execution. The output information are written to `/tmp/stdout`, `/tmp/stderr`, and `/tmp/headers.json` by the C module and read by the PHP JavaScript class. Because the request body and the query string are parsed by the same PHP internal functions as they would on a webserver, array syntax like `settings[newsletter]=1` is handled correctly. One surprising thing is the ability to set arbitrary `$_FILES` entries with `wasm_add_uploaded_file`. This is because JavaScript typically has access to any uploaded `File` objects and it would be wasteful to re-serialize them only so that PHP can parse them all over again. With `wasm_add_uploaded_file` you can first write the uploaded files to the filesystem and then simply let PHP know about their existence. Solves #103
As of #107, High-level API: const response = php.run({
method: 'POST',
body: '{"foo": "bar"}',
code: `<?php echo file_get_contents('php://input');`,
});
const bodyText = new TextDecoder().decode(response.body);
expect(bodyText).toEqual('{"foo": "bar"}'); Low-level API: this.#Runtime.ccall('wasm_set_request_body', null, [STR], ['{"test": "foo"}']);
this.#Runtime.ccall('wasm_set_request_method', null, [STR], ['POST']);
this.#Runtime.ccall('wasm_set_content_type', null, [STR], ["application/x-www-form-urlencoded"]);
this.#Runtime.ccall('wasm_set_php_code', null, [STR], ['<?php echo file_get_contents("php://input"); ']);
this.#Runtime.ccall('wasm_sapi_handle_request', NUM, [], []);
console.log(this.readFileAsBuffer('/tmp/stdout'));
// {"test": "foo"} |
Pookie717
added a commit
to Pookie717/wordpress-playground
that referenced
this issue
Oct 1, 2023
# What is this PR all about? Introduces a PHP SAPI module that enables setting `$_POST`, `$_SERVER`, `php://input` and all the other PHP values from JavaScript. ## What problem does it solve? Before this PR, most superglobal values were set by prepending code snippets like `$_SERVER['DOCUMENT_ROOT'] = ${JSON.stringify(documentRoot)};` every time some code was evaluated. Unfortunately, that technique couldn't populate everything, e.g. `php://input` remained empty. ## How does it work? PHP SAPI is used to integrate PHP with webservers and runtimes. A few SAPIs you might be familiar with are `php-cgi`, `php-fpm`, and `php-cli`. A SAPI consumes the request information from the runtime, passes it to PHP, triggers the code execution, and passes the response back to the runtime. This PR introduces a WASM SAPI that accepts input information from JavaScript, sets up a PHP request, and passes a response back to JS. The most important changes are in the `php_wasm.c` file. The rest of the PR is adjusting the existing codebase to the new way of working with PHP. Briefly speaking, the SAPI module exposes a few setters like `wasm_set_query_string` or `wasm_add_SERVER_entry` and a `wasm_sapi_handle_request()` function that triggers the request execution. The output information are written to `/tmp/stdout`, `/tmp/stderr`, and `/tmp/headers.json` by the C module and read by the PHP JavaScript class. Because the request body and the query string are parsed by the same PHP internal functions as they would on a webserver, array syntax like `settings[newsletter]=1` is handled correctly. One surprising thing is the ability to set arbitrary `$_FILES` entries with `wasm_add_uploaded_file`. This is because JavaScript typically has access to any uploaded `File` objects and it would be wasteful to re-serialize them only so that PHP can parse them all over again. With `wasm_add_uploaded_file` you can first write the uploaded files to the filesystem and then simply let PHP know about their existence. Solves WordPress/wordpress-playground#103
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
In PHP, parsing JSON data from POST is often handled with
file_get_contents( 'php://input' );
. That stream is read-only and can't be pre-populated by a PHP code inphp-server.ts
. Instead, it must happen somewhere in the C code inphp_wasm.c
.For example, submitting a POST request with
{"foo": "bar"}
as body and acontent-type: application/json
should yieldbar
in the following code:At the moment, it does not.
The text was updated successfully, but these errors were encountered: