Skip to content

Commit

Permalink
Support for PHP->JS->PHP data transfer by returning value from post_m…
Browse files Browse the repository at this point in the history
…essage_to_js() (#732)

## Description

A part of #724

`post_message_to_js` passes a message to the JavaScript module where it
can be handled inside `onMessage()`. This commit adds support for
returning values and promises inside that handler to feed them back to
PHP. For example:

```php
$response = post_message_to_js(json_encode($request_data));
```

```ts
	await playground.onMessage(async (message: string) => {
		const requestData = JSON.parse(message);
		const response = await fetch(requestData);
		return JSON.stringify(toSimpleObject(response));
	});
```

A handler like above could be used to add support for network requests,
which is in fact what
#724 does
  • Loading branch information
adamziel authored Nov 6, 2023
1 parent 420be2b commit dbad2aa
Show file tree
Hide file tree
Showing 37 changed files with 88 additions and 69 deletions.
45 changes: 30 additions & 15 deletions packages/php-wasm/compile/build-assets/php_wasm.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,25 @@ unsigned int wasm_sleep(unsigned int time) {

extern int *wasm_setsockopt(int sockfd, int level, int optname, intptr_t optval, size_t optlen, int dummy);
extern char *js_popen_to_file(const char *cmd, const char *mode, uint8_t *exit_code_ptr);
extern char *js_module_onMessage(const char *data);

/**
* Passes a message to the JavaScript module and writes the response
* data, if any, to the response_buffer pointer.
*
* @param message The message to pass into JavaScript.
* @param response_buffer The address where the response will be stored. The
* JS module will allocate a memory block for the response buffer and write
* its address to **response_buffer. The caller is responsible for freeing
* that memory after use.
*
* @return The size of the response_buffer (it can contain null bytes).
*
* @note The caller should ensure that the memory allocated for response_buffer
* is freed after its use to prevent memory leaks. It's also recommended
* to handle exceptions and errors gracefully within the function to ensure
* the stability of the system.
*/
extern size_t js_module_onMessage(const char *data, char **response_buffer);

// popen() shim
// -----------------------------------------------------------
Expand Down Expand Up @@ -267,8 +285,7 @@ ZEND_BEGIN_ARG_INFO(arginfo_dl, 0)
ZEND_ARG_INFO(0, extension_filename)
ZEND_END_ARG_INFO()

/* {{{ proto int strcmp(string str1, string str2)
Binary safe string comparison */
/* Enable PHP to exchange messages with JavaScript */
PHP_FUNCTION(post_message_to_js)
{
char *data;
Expand All @@ -278,21 +295,19 @@ PHP_FUNCTION(post_message_to_js)
return;
}

char *result = js_module_onMessage(data);

if(result != NULL) {
// In PHP 7, the second parameter was removed and the string is always
// returned as a copy.
#if PHP_MAJOR_VERSION >= 7
RETURN_STRING(result);
#else
RETURN_STRING(result, 1);
#endif
} else {
char *response;
size_t response_len = js_module_onMessage(data, &response);
if (response != NULL)
{
zend_string *return_string = zend_string_init(response, response_len, 0);
free(response);
RETURN_NEW_STR(return_string);
}
else
{
RETURN_NULL();
}
}
/* }}} */


#if WITH_CLI_SAPI == 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@ const LibraryExample = {
});
},

js_module_onMessage: function (data) {
js_module_onMessage: function (data, bufPtr) {
if (typeof Asyncify === 'undefined') {
return;
}
Expand All @@ -638,10 +638,26 @@ const LibraryExample = {
const dataStr = UTF8ToString(data);

return Asyncify.handleSleep((wakeUp) => {
Module['onMessage'](dataStr).then((result) => {
wakeUp(allocateUTF8OnStack(result));
Module['onMessage'](dataStr).then((response) => {
const responseBytes = typeof response === "string"
? new TextEncoder().encode(response)
: response;

// Copy the response bytes to heap
const responseSize = responseBytes.byteLength;
const responsePtr = _malloc(responseSize + 1);
HEAPU8.set(responseBytes, responsePtr);
HEAPU8[responsePtr + responseSize] = 0;
HEAPU8[bufPtr] = responsePtr;
HEAPU8[bufPtr + 1] = responsePtr >> 8;
HEAPU8[bufPtr + 2] = responsePtr >> 16;
HEAPU8[bufPtr + 3] = responsePtr >> 24;

wakeUp(responseSize);
}).catch((e) => {
console.log("There's been an error in the onMessage handler:");
// Log the error and return NULL. Message passing
// separates JS context from the PHP context so we
// don't let PHP crash here.
console.error(e);
wakeUp(0);
} );
Expand Down
4 changes: 3 additions & 1 deletion packages/php-wasm/universal/src/lib/base-php.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ export abstract class BasePHP implements IsomorphicLocalPHP {
throw new Error('Invalid PHP runtime id.');
}
this[__private__dont__use] = runtime;
runtime['onMessage'] = async (data: string): Promise<string> => {
runtime['onMessage'] = async (
data: string
): Promise<string | Uint8Array> => {
for (const listener of this.#messageListeners) {
const returnData = await listener(data);

Expand Down
2 changes: 1 addition & 1 deletion packages/php-wasm/universal/src/lib/universal-php.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ export interface IsomorphicLocalPHP extends RequestHandler {

export type MessageListener = (
data: string
) => Promise<string> | Promise<void> | string | void;
) => Promise<string> | Promise<Uint8Array> | Promise<void> | string | void;
interface EventEmitter {
on(event: string, listener: (...args: any[]) => void): this;
emit(event: string, ...args: any[]): boolean;
Expand Down
18 changes: 2 additions & 16 deletions packages/php-wasm/web/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,7 @@
"executor": "nx:run-commands",
"options": {
"commands": [
"node packages/php-wasm/compile/build.js --PLATFORM=web-light --PHP_VERSION=7.0 --output-dir=packages/php-wasm/web/public/light",
"node packages/php-wasm/compile/build.js --PLATFORM=web-light --PHP_VERSION=7.1 --output-dir=packages/php-wasm/web/public/light",
"node packages/php-wasm/compile/build.js --PLATFORM=web-light --PHP_VERSION=7.2 --output-dir=packages/php-wasm/web/public/light",
"node packages/php-wasm/compile/build.js --PLATFORM=web-light --PHP_VERSION=7.3 --output-dir=packages/php-wasm/web/public/light",
"node packages/php-wasm/compile/build.js --PLATFORM=web-light --PHP_VERSION=7.4 --output-dir=packages/php-wasm/web/public/light",
"node packages/php-wasm/compile/build.js --PLATFORM=web-light --PHP_VERSION=8.0 --output-dir=packages/php-wasm/web/public/light",
"node packages/php-wasm/compile/build.js --PLATFORM=web-light --PHP_VERSION=8.1 --output-dir=packages/php-wasm/web/public/light",
"node packages/php-wasm/compile/build.js --PLATFORM=web-light --PHP_VERSION=8.2 --output-dir=packages/php-wasm/web/public/light"
"node packages/php-wasm/compile/build.js --PLATFORM=web-light --output-dir=packages/php-wasm/web/public/light"
],
"parallel": false
}
Expand All @@ -82,14 +75,7 @@
"executor": "nx:run-commands",
"options": {
"commands": [
"node packages/php-wasm/compile/build.js --PLATFORM=web-kitchen-sink --PHP_VERSION=7.0 --output-dir=packages/php-wasm/web/public/kitchen-sink",
"node packages/php-wasm/compile/build.js --PLATFORM=web-kitchen-sink --PHP_VERSION=7.1 --output-dir=packages/php-wasm/web/public/kitchen-sink",
"node packages/php-wasm/compile/build.js --PLATFORM=web-kitchen-sink --PHP_VERSION=7.2 --output-dir=packages/php-wasm/web/public/kitchen-sink",
"node packages/php-wasm/compile/build.js --PLATFORM=web-kitchen-sink --PHP_VERSION=7.3 --output-dir=packages/php-wasm/web/public/kitchen-sink",
"node packages/php-wasm/compile/build.js --PLATFORM=web-kitchen-sink --PHP_VERSION=7.4 --output-dir=packages/php-wasm/web/public/kitchen-sink",
"node packages/php-wasm/compile/build.js --PLATFORM=web-kitchen-sink --PHP_VERSION=8.0 --output-dir=packages/php-wasm/web/public/kitchen-sink",
"node packages/php-wasm/compile/build.js --PLATFORM=web-kitchen-sink --PHP_VERSION=8.1 --output-dir=packages/php-wasm/web/public/kitchen-sink",
"node packages/php-wasm/compile/build.js --PLATFORM=web-kitchen-sink --PHP_VERSION=8.2 --output-dir=packages/php-wasm/web/public/kitchen-sink"
"node packages/php-wasm/compile/build.js --PLATFORM=web-kitchen-sink --output-dir=packages/php-wasm/web/public/kitchen-sink"
],
"parallel": false
}
Expand Down
4 changes: 2 additions & 2 deletions packages/php-wasm/web/public/kitchen-sink/php_7_0.js

Large diffs are not rendered by default.

Binary file modified packages/php-wasm/web/public/kitchen-sink/php_7_0.wasm
Binary file not shown.
4 changes: 2 additions & 2 deletions packages/php-wasm/web/public/kitchen-sink/php_7_1.js

Large diffs are not rendered by default.

Binary file modified packages/php-wasm/web/public/kitchen-sink/php_7_1.wasm
Binary file not shown.
4 changes: 2 additions & 2 deletions packages/php-wasm/web/public/kitchen-sink/php_7_2.js

Large diffs are not rendered by default.

Binary file modified packages/php-wasm/web/public/kitchen-sink/php_7_2.wasm
Binary file not shown.
4 changes: 2 additions & 2 deletions packages/php-wasm/web/public/kitchen-sink/php_7_3.js

Large diffs are not rendered by default.

Binary file modified packages/php-wasm/web/public/kitchen-sink/php_7_3.wasm
Binary file not shown.
4 changes: 2 additions & 2 deletions packages/php-wasm/web/public/kitchen-sink/php_7_4.js

Large diffs are not rendered by default.

Binary file modified packages/php-wasm/web/public/kitchen-sink/php_7_4.wasm
Binary file not shown.
4 changes: 2 additions & 2 deletions packages/php-wasm/web/public/kitchen-sink/php_8_0.js

Large diffs are not rendered by default.

Binary file modified packages/php-wasm/web/public/kitchen-sink/php_8_0.wasm
Binary file not shown.
4 changes: 2 additions & 2 deletions packages/php-wasm/web/public/kitchen-sink/php_8_1.js

Large diffs are not rendered by default.

Binary file modified packages/php-wasm/web/public/kitchen-sink/php_8_1.wasm
Binary file not shown.
4 changes: 2 additions & 2 deletions packages/php-wasm/web/public/kitchen-sink/php_8_2.js

Large diffs are not rendered by default.

Binary file modified packages/php-wasm/web/public/kitchen-sink/php_8_2.wasm
Binary file not shown.
4 changes: 2 additions & 2 deletions packages/php-wasm/web/public/light/php_7_0.js

Large diffs are not rendered by default.

Binary file modified packages/php-wasm/web/public/light/php_7_0.wasm
Binary file not shown.
4 changes: 2 additions & 2 deletions packages/php-wasm/web/public/light/php_7_1.js

Large diffs are not rendered by default.

Binary file modified packages/php-wasm/web/public/light/php_7_1.wasm
Binary file not shown.
4 changes: 2 additions & 2 deletions packages/php-wasm/web/public/light/php_7_2.js

Large diffs are not rendered by default.

Binary file modified packages/php-wasm/web/public/light/php_7_2.wasm
Binary file not shown.
4 changes: 2 additions & 2 deletions packages/php-wasm/web/public/light/php_7_3.js

Large diffs are not rendered by default.

Binary file modified packages/php-wasm/web/public/light/php_7_3.wasm
Binary file not shown.
4 changes: 2 additions & 2 deletions packages/php-wasm/web/public/light/php_7_4.js

Large diffs are not rendered by default.

Binary file modified packages/php-wasm/web/public/light/php_7_4.wasm
Binary file not shown.
4 changes: 2 additions & 2 deletions packages/php-wasm/web/public/light/php_8_0.js

Large diffs are not rendered by default.

Binary file modified packages/php-wasm/web/public/light/php_8_0.wasm
Binary file not shown.
4 changes: 2 additions & 2 deletions packages/php-wasm/web/public/light/php_8_1.js

Large diffs are not rendered by default.

Loading

0 comments on commit dbad2aa

Please sign in to comment.