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 WASM: Compile PHP to WASI #290

Open
eliot-akira opened this issue May 10, 2023 · 9 comments
Open

PHP WASM: Compile PHP to WASI #290

eliot-akira opened this issue May 10, 2023 · 9 comments
Labels
[Feature] PHP.wasm [Type] Enhancement New feature or request [Type] Exploration An exploration that may or may not result in mergable code [Type] Mindmap Node [Type] Project

Comments

@eliot-akira
Copy link
Collaborator

eliot-akira commented May 10, 2023

There was a pull request #113 that was exploring this direction, which was closed recently with a note to come back at a later time.

Let's look into collaborating with https://github.com/vmware-labs/webassembly-language-runtimes (see php-8.1.11) once WASI supports longjmp.

From what I understand, an advantage of compiling PHP to WASI is that it can be a standalone binary that runs without dependency on Node.js. I wanted to contribute a bit by gathering related links that I came across while studying about this topic.


In the WASI repo, there was an issue WebAssembly/WASI#490 asking about setjmp support. It was closed with the following note:

The current plan is to wait for the exception-handling proposal to be standardized and implemented in wasm engines, and then to implement setjmp and longjmp using that feature. Exception-handling is currently in phase 3.

So the feature will be implemented eventually. That implies that any workaround solution is temporary, to be replaced/removed in the future.


In the meantime, a WebAssembly / WASI port of Ruby was developed, which gets around the limitation by emulating setjmp and longjmp with Asyncify. I learned about it from these articles:

Then someone re-purposed that setjmp polyfill to integrate with PHP. Here's the as-yet-unmerged pull request:

In parallel, another person from VMware started on an ambitious effort in the PHP repo itself.


Hopefully this issue can serve as reference when there's progress in any of the above.

@adamziel
Copy link
Collaborator

adamziel commented May 11, 2023

What a great issue @eliot-akira, thank you so much for putting all this information together!

From what I understand, an advantage of compiling PHP to WASI is that it can be a standalone binary that runs without dependency on Node.js.

You're exactly right! I thought Asyncify was a JavaScript thing, but it seems like it's not. TIL! This certainly opens the door to supporting WASI. This would still be a challenge, though, unless we let Asyncify auto-wrap everything it wants to: #251

@adamziel adamziel added [Type] Enhancement New feature or request [Feature] PHP.wasm [Type] Exploration An exploration that may or may not result in mergable code labels May 28, 2023
@adamziel
Copy link
Collaborator

@adamziel
Copy link
Collaborator

The Python WASI demo shipped with container2wasm even handled os.fork():

code = '''
import os  
import time
pid = os.fork()   
if pid > 0:
    print("I am parent process:") 
    print("Process ID:", os.getpid()) 
    print("Child's process ID:", pid)
    time.sleep(3)
    print("Parent process is killed")
else:
    print("I am child process:") 
    print("Process ID:", os.getpid()) 
    print("Parent's process ID:", os.getppid()) 
    time.sleep(3)
    print("child process is killed")
'''

with open("process_test.py", "w") as file:
     file.write(code)

import process_test

Now, these demos ship an entire OS compiled to WASM and require more resources to run, but for something like a development environment that should be fine.

I wonder whether the WASI shim can handle forking a process. If so, perhaps we could ditch emscripten, migrate to WASI entirely, and get more features with less patches.

@adamziel
Copy link
Collaborator

adamziel commented Jan 18, 2024

Container2Wasm demos are impressive but also too slow for any real-world use:

  • The python demo transfers around 60MB of data (vs ~2.5MB for Playground's PHP)
  • It took ~25 seconds to boot after it was downloaded (vs ~1s for Playground)
  • It works in Chrome and Firefox, but not in Safari (Unhandled Promise Rejection: RangeError: Maximum call stack size exceeded.)

browser_wasi_shim is promising and its filesystem implementation looks much cleaner than the Emscripten's one, but it has important limitations:

  • No async IO, that's a deal breaker. I think that means no Asyncify-like stack switching, either? Maybe when the JSPI proposal gets shipped in all major browsers?
  • No fork support, so it wouldn't bring native support for proc_open etc, we'd still need patches.

On the upside, there were some explorations to support the WebAssembly Component Model.

Either way, there's nothing actionable here today.

@adamziel
Copy link
Collaborator

adamziel commented Apr 26, 2024

It is possible that recent WASM runtimes support longjmp and exceptions which means we could potentially produce a standalone WASI now – this needs investigation:

emscripten-core/emscripten#20484

I'm not sure what about async IO and fork

@adamziel adamziel moved this to Project: Up Soon in Playground Board Jun 30, 2024
@adamziel adamziel moved this from Project: Triage to Project: Not now in Playground Board Jul 1, 2024
@adamziel
Copy link
Collaborator

https://github.com/bytecodealliance/jco/ is interesting:

Jco provides a fully native JS toolchain for working with WebAssembly Components in JavaScript.

Features include:

"Transpiling" Wasm Component binaries into ES modules that can run in any JS environment.
WASI Preview2 support in Node.js & browsers (experimental).

@eliot-akira
Copy link
Collaborator Author

eliot-akira commented Jul 13, 2024

Not sure how related or relevant to this issue, but here are some developments on the topic of WASM/WASI that I found interesting.

It looks like the possibility of removing/replacing Asyncify is getting closer.

Add setjmp/longjump support based on Wasm EH proposal.


I was exploring an early prototype of Zig language playground, which compiles the Zig compiler (and language server) with Emscripten to run in the browser.

It uses a fork of the WASI shim/polyfill mentioned upthread, bjorn3/browser_wasi_shim.

I have an idea to fork this playground and integrate with the WASM-4 game engine, which has a tiny SDK for Zig, as a fun way to learn the language entirely in the browser without having to install the compiler locally.

An interesting aspect of this is a workaround to set COOP and COEP headers to make SharedArrayBuffer used by Emscripten to work on GitHub Pages. I was wondering if this trick might let WordPress Playground run on GH pages as well.


A WASI shim for VSCode:


A WASI shim written in PHP:

Do you want to code in C++ while getting absolutely zero performance gain? Do you want to code your next Wordpress plugin in Rust? Or do you want to run other programming languages on your shared PHP hosting provider? Welcome to UnWasm!


A WASI runner for the web (@runno/wasi)

There are a bunch of different WASI runners out there, some of them even work in the browser. This one is focused on sandboxed emulation. Not system integration. It has been developed for the particular requirements of Runno, but you may find it useful as well.

This package allows you to run WASI binaries on the web with an emulated filesystem. If the binary receives calls to stdin/out/err then you get callbacks you'll need to handle. In future there may be other callbacks to intercept interesting system level events, or hooks into the filesystem.


Going off topic, here and there I see people attempting to implement networking functionality in the browser..

Better Networking would be interesting, too. The SocketPlugin currently does allows HTTP(S) requests and WebSockets. How about implementing low level Socket support based on HTTP-tunneling?

One big thing I have not gotten to completely work yet is collaboration – the original Croquet was fully collaborative. I started implementing a SocketPlugin that emulates a full TCP/IP stack on top of modern Croquet as transport, meaning it emulates an IP network. The existing SqueakJS SocketPlugin only allows http/https connections. The tests appear to work, but not the Croquet multiuser mode.


This article you've probably seen already:

The main difficulty in adding this feature is that the browser does not expose access to lower-level protocols such as UDP and TCP. You can only do HTTP(S), and even then, you are severely limited by CORS policies.

What about WebSockets then? A WebSocket connection starts with an HTTP connection with an “Upgrade” header. Then, if the upgrade is accepted, the WebSocket protocol takes over. WebSocket packets also have their own header. So they can’t be used transparently to connect to regular TCP sockets.

In order to connect to arbitrary sockets, we need a proxy server to perform the protocol upgrade and unwrap the WebSocket packets before sending them to the destination (and do the opposite in the other direction).

..One of the features of Tailscale, though, is the ability to let machines connect with each other directly in a mesh network. It achieves this by providing STUN servers for NAT traversal.

Sometimes even the STUN server can’t punch through the NAT, so as a fallback mechanism, Tailscale provides DERP servers.

A DERP (Detoured Encrypted Routing Protocol ) server is used to relay traffic when two peers cannot connect directly. Since it’s designed to circumvent even the most annoying firewalls, it communicates using regular HTTPS and Websockets.

That’s exactly what we need!

@adamziel
Copy link
Collaborator

Such an amazing writeup, thank you so much for sharing all of that @eliot-akira – it's extremely useful! I'm especially wowed by the WASM runtime built in PHP :D cc @luisherranz

An interesting aspect of this is a workaround to set COOP and COEP headers to make SharedArrayBuffer used by Emscripten to work on GitHub Pages. I was wondering if this trick might let WordPress Playground run on GH pages as well.

Playground used to be hosted on GH pages and it would probably "just work" there today, it doesn't use SharedArrayBuffers.

@adamziel
Copy link
Collaborator

adamziel commented Oct 30, 2024

Wasmer 5 runs on iOS! With a WASI build we could run WordPress in native apps without a webview. We could validate the idea with the VMLabs PHP WASI build.

https://wasmer.io/posts/introducing-wasmer-v5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] PHP.wasm [Type] Enhancement New feature or request [Type] Exploration An exploration that may or may not result in mergable code [Type] Mindmap Node [Type] Project
Projects
Archived in project
Development

No branches or pull requests

2 participants