├── .editorconfig ├── .env ├── .github ├── FUNDING.yml └── workflows │ ├── ci.yml │ └── demo.yml ├── .gitignore ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── NOTICE ├── README.md ├── demo ├── .gitignore ├── public │ └── index.html └── src │ └── index.php ├── docker-bake.hcl └── source └── phpw.c /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | trim_trailing_whitespace = true 3 | insert_final_newline = true 4 | indent_style = tab 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | 9 | [*.{yml,yaml}] 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | end_of_line = lf 15 | charset = utf-8 16 | 17 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | UID=1000 2 | EMCC_CORES=8 3 | ENVIRONMENT=${ENVIRONMENT:-web} 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [soyuka] 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Build Docker image 2 | on: 3 | pull_request: 4 | branches: 5 | - main 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - 11 | name: Checkout 12 | uses: actions/checkout@v4 13 | - 14 | name: Build and push 15 | uses: docker/build-push-action@v5 16 | with: 17 | context: . 18 | push: false 19 | tags: soyuka/php-wasm:latest 20 | cache-from: type=registry,ref=soyuka/php-wasm:latest 21 | cache-to: type=inline 22 | -------------------------------------------------------------------------------- /.github/workflows/demo.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: true 23 | 24 | jobs: 25 | # Single deploy job since we're just deploying 26 | deploy: 27 | environment: 28 | name: github-pages 29 | url: ${{ steps.deployment.outputs.page_url }} 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v3 34 | - 35 | name: Set up Docker Buildx 36 | uses: docker/setup-buildx-action@v3 37 | - 38 | name: Login to Docker Hub 39 | uses: docker/login-action@v3 40 | with: 41 | username: ${{ secrets.DOCKERHUB_USERNAME }} 42 | password: ${{ secrets.DOCKERHUB_TOKEN }} 43 | - 44 | name: Build PHP WASM 45 | uses: docker/build-push-action@v5 46 | with: 47 | context: . 48 | push: true 49 | tags: soyuka/php-wasm:latest 50 | cache-from: type=registry,ref=soyuka/php-wasm:latest 51 | cache-to: type=inline 52 | - name: Setup Pages 53 | uses: actions/configure-pages@v3 54 | - name: Build website 55 | working-directory: demo 56 | run: | 57 | docker create --name=php-wasm docker.io/soyuka/php-wasm:latest 58 | mkdir -p public/ dist/ 59 | docker cp php-wasm:/build/php-web.mjs ./dist 60 | docker cp php-wasm:/build/php-web.wasm ./public 61 | docker run -v $(pwd)/src:/src -v $(pwd)/public:/public -v $(pwd)/dist:/dist docker.io/soyuka/php-wasm:latest python3 /emsdk/upstream/emscripten/tools/file_packager.py /public/php-web.data --use-preload-cache --lz4 --preload "/src" --js-output=/dist/php-web.data.js --no-node --exclude '*/.*' --export-name=createPhpModule 62 | sed '/--pre-js/r dist/php-web.data.js' dist/php-web.mjs > public/php-web.mjs 63 | - name: Upload artifact 64 | uses: actions/upload-pages-artifact@v2 65 | with: 66 | path: 'demo/public' 67 | - name: Deploy to GitHub Pages 68 | id: deployment 69 | uses: actions/deploy-pages@v2 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | lib 3 | third_party 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # php-wasm aka PIB 2 | 3 | Changes 4 | 5 | ## 0.0.3 - New Horizons 6 | 7 | * php.exec() may be used to evaluate a single php expression & return its result. 8 | * php may now access & traverse the dom and access nodes. 9 | * The querySelector method is available on dom nodes. 10 | * addEventListener/removeEventListener is also available on dom nodes. 11 | * sqlite3 v3.33 is now statically linked to php & the sqlite3 extension is enabled. 12 | * The following extensions are now enabled: sqlite3, pdo, & pdo-sqlite. 13 | * Totally revamped build process that tracks build artifact relationships. 14 | * Builds for web, node, shell, worker & webview. 15 | 16 | ## 0.0.2 - Gaining Momentum 17 | 18 | * php objects now have persistent memory, may be cleared with `php.refresh();`. 19 | * php code may now access Javascript (and thus, the DOM) via the [VRZNO](https://github.com/seanmorris/vrzno) project. The extension is preinstalled with php-wasm. 20 | * ` 29 |
30 | 44 | -------------------------------------------------------------------------------- /demo/src/index.php: -------------------------------------------------------------------------------- 1 | 3 | #include 4 | 5 | #include "zend_globals_macros.h" 6 | #include "zend_exceptions.h" 7 | #include "zend_closures.h" 8 | 9 | int main() { 10 | return 0; 11 | } 12 | 13 | void phpw_flush() 14 | { 15 | fprintf(stdout, "\n"); 16 | fprintf(stderr, "\n"); 17 | } 18 | 19 | char *EMSCRIPTEN_KEEPALIVE phpw_exec(char *code) 20 | { 21 | // This sets USE_ZEND_ALLOC=0 to avoid nunmap errors 22 | setenv("USE_ZEND_ALLOC", "0", 1); 23 | php_embed_init(0, NULL); 24 | char *retVal = NULL; 25 | 26 | zend_try 27 | { 28 | zval retZv; 29 | 30 | zend_eval_string(code, &retZv, "php-wasm evaluate expression"); 31 | 32 | convert_to_string(&retZv); 33 | 34 | retVal = Z_STRVAL(retZv); 35 | } zend_catch { 36 | } zend_end_try(); 37 | 38 | phpw_flush(); 39 | php_embed_shutdown(); 40 | 41 | return retVal; 42 | } 43 | 44 | void EMSCRIPTEN_KEEPALIVE phpw_run(char *code) 45 | { 46 | setenv("USE_ZEND_ALLOC", "0", 1); 47 | php_embed_init(0, NULL); 48 | zend_try 49 | { 50 | zend_eval_string(code, NULL, "php-wasm run script"); 51 | if(EG(exception)) 52 | { 53 | zend_exception_error(EG(exception), E_ERROR); 54 | } 55 | } zend_catch { 56 | /* int exit_status = EG(exit_status); */ 57 | } zend_end_try(); 58 | 59 | phpw_flush(); 60 | php_embed_shutdown(); 61 | } 62 | 63 | int EMBED_SHUTDOWN = 1; 64 | 65 | void phpw(char *file) 66 | { 67 | setenv("USE_ZEND_ALLOC", "0", 1); 68 | if (EMBED_SHUTDOWN == 0) { 69 | php_embed_shutdown(); 70 | } 71 | 72 | php_embed_init(0, NULL); 73 | EMBED_SHUTDOWN = 0; 74 | zend_first_try { 75 | zend_file_handle file_handle; 76 | zend_stream_init_filename(&file_handle, file); 77 | // file_handle.primary_script = 1; 78 | 79 | if (php_execute_script(&file_handle) == FAILURE) { 80 | php_printf("Failed to execute PHP script.\n"); 81 | } 82 | 83 | zend_destroy_file_handle(&file_handle); 84 | } zend_catch { 85 | /* int exit_status = EG(exit_status); */ 86 | } zend_end_try(); 87 | 88 | phpw_flush(); 89 | php_embed_shutdown(); 90 | EMBED_SHUTDOWN = 1; 91 | } 92 | --------------------------------------------------------------------------------