aboutsummaryrefslogtreecommitdiffhomepage
path: root/worker/php
diff options
context:
space:
mode:
Diffstat (limited to 'worker/php')
-rw-r--r--worker/php/.dockerignore1
-rw-r--r--worker/php/.gitignore2
-rw-r--r--worker/php/Dockerfile82
-rw-r--r--worker/php/biome.json30
-rw-r--r--worker/php/exec.mjs85
-rw-r--r--worker/php/index.mjs39
-rw-r--r--worker/php/package-lock.json202
-rw-r--r--worker/php/package.json17
-rw-r--r--worker/php/php-wasm.c22
9 files changed, 480 insertions, 0 deletions
diff --git a/worker/php/.dockerignore b/worker/php/.dockerignore
new file mode 100644
index 0000000..07e6e47
--- /dev/null
+++ b/worker/php/.dockerignore
@@ -0,0 +1 @@
+/node_modules
diff --git a/worker/php/.gitignore b/worker/php/.gitignore
new file mode 100644
index 0000000..89c10d6
--- /dev/null
+++ b/worker/php/.gitignore
@@ -0,0 +1,2 @@
+/lib/php-wasm.*
+/node_modules
diff --git a/worker/php/Dockerfile b/worker/php/Dockerfile
new file mode 100644
index 0000000..18e715b
--- /dev/null
+++ b/worker/php/Dockerfile
@@ -0,0 +1,82 @@
+FROM emscripten/emsdk:3.1.74 AS wasm-builder
+
+RUN git clone --depth=1 --branch=php-8.4.4 https://github.com/php/php-src
+
+RUN apt-get update && \
+ apt-get install -y --no-install-recommends \
+ autoconf \
+ bison \
+ pkg-config \
+ re2c \
+ && \
+ :
+
+# Define ZEND_MM_ERROR=0 for suppressing munmap() errors.
+RUN cd php-src && \
+ ./buildconf --force && \
+ emconfigure ./configure \
+ --disable-all \
+ --disable-mbregex \
+ --disable-fiber-asm \
+ --disable-cli \
+ --disable-cgi \
+ --disable-phpdbg \
+ --enable-embed=static \
+ --enable-short-tags \
+ --enable-mbstring \
+ --without-iconv \
+ --without-libxml \
+ --without-pcre-jit \
+ --without-pdo-sqlite \
+ --without-sqlite3 \
+ && \
+ EMCC_CFLAGS='-s ERROR_ON_UNDEFINED_SYMBOLS=0 -D ZEND_MM_ERROR=0' emmake make -j$(nproc) && \
+ mv libs/libphp.a .. && \
+ make clean && \
+ git clean -fd && \
+ :
+
+COPY php-wasm.c /src/
+
+RUN cd php-src && \
+ emcc \
+ -c \
+ -o php-wasm.o \
+ -I . \
+ -I TSRM \
+ -I Zend \
+ -I main \
+ ../php-wasm.c \
+ && \
+ mv php-wasm.o .. && \
+ make clean && \
+ git clean -fd && \
+ :
+
+RUN emcc \
+ -s ENVIRONMENT=node \
+ -s ERROR_ON_UNDEFINED_SYMBOLS=0 \
+ -s EXPORTED_RUNTIME_METHODS='["ccall"]' \
+ -s EXPORT_ES6=1 \
+ -s INITIAL_MEMORY=16777216 \
+ -s INVOKE_RUN=0 \
+ -s MODULARIZE=1 \
+ -o php-wasm.js \
+ php-wasm.o \
+ libphp.a \
+ ;
+
+FROM node:22.14
+
+WORKDIR /app
+ENV NODE_ENV=production
+
+COPY --from=wasm-builder /src/php-wasm.js /src/php-wasm.wasm /app/
+COPY package.json package-lock.json /app/
+
+RUN npm install
+
+COPY index.mjs exec.mjs /app/
+
+ENTRYPOINT ["node", "index.mjs"]
+EXPOSE 80
diff --git a/worker/php/biome.json b/worker/php/biome.json
new file mode 100644
index 0000000..2eb0751
--- /dev/null
+++ b/worker/php/biome.json
@@ -0,0 +1,30 @@
+{
+ "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
+ "vcs": {
+ "enabled": false,
+ "clientKind": "git",
+ "useIgnoreFile": false
+ },
+ "files": {
+ "ignoreUnknown": false,
+ "ignore": []
+ },
+ "formatter": {
+ "enabled": true,
+ "indentStyle": "tab"
+ },
+ "organizeImports": {
+ "enabled": true
+ },
+ "linter": {
+ "enabled": true,
+ "rules": {
+ "recommended": true
+ }
+ },
+ "javascript": {
+ "formatter": {
+ "quoteStyle": "double"
+ }
+ }
+}
diff --git a/worker/php/exec.mjs b/worker/php/exec.mjs
new file mode 100644
index 0000000..d8ca899
--- /dev/null
+++ b/worker/php/exec.mjs
@@ -0,0 +1,85 @@
+import PHPWasm from "./php-wasm.js";
+
+process.once("message", async ({ code: originalCode, input }) => {
+ const PRELUDE = `
+ define('STDIN', fopen('php://stdin', 'r'));
+ define('STDOUT', fopen('php://stdout', 'r'));
+ define('STDERR', fopen('php://stderr', 'r'));
+
+ error_reporting(E_ALL & ~E_WARNING & ~E_NOTICE & ~E_DEPRECATED);
+
+ `;
+
+ // remove php tag
+ let code;
+ if (originalCode.startsWith("<?php")) {
+ code = PRELUDE + originalCode.slice(5);
+ } else if (originalCode.startsWith("<?")) {
+ code = PRELUDE + originalCode.slice(2);
+ } else {
+ code = PRELUDE + originalCode;
+ }
+
+ const BUFFER_MAX = 10 * 1024;
+
+ let stdinPos = 0; // bytewise
+ const stdinBuf = Buffer.from(input);
+ let stdoutPos = 0; // bytewise
+ const stdoutBuf = Buffer.alloc(BUFFER_MAX);
+ let stderrPos = 0; // bytewise
+ const stderrBuf = Buffer.alloc(BUFFER_MAX);
+
+ const { ccall } = await PHPWasm({
+ stdin: () => {
+ if (stdinBuf.length <= stdinPos) {
+ return null;
+ }
+ return stdinBuf.readUInt8(stdinPos++);
+ },
+ stdout: (asciiCode) => {
+ if (asciiCode === null) {
+ return; // flush
+ }
+ if (BUFFER_MAX <= stdoutPos) {
+ return; // ignore
+ }
+ stdoutBuf.writeUInt8(
+ asciiCode < 0 ? asciiCode + 256 : asciiCode,
+ stdoutPos++,
+ );
+ },
+ stderr: (asciiCode) => {
+ if (asciiCode === null) {
+ return; // flush
+ }
+ if (BUFFER_MAX <= stderrPos) {
+ return; // ignore
+ }
+ stderrBuf.writeUInt8(
+ asciiCode < 0 ? asciiCode + 256 : asciiCode,
+ stderrPos++,
+ );
+ },
+ });
+
+ let err;
+ let result;
+ try {
+ result = ccall("php_wasm_run", "number", ["string"], [code]);
+ } catch (e) {
+ err = e;
+ }
+ if (err) {
+ process.send({
+ status: "runtime_error",
+ stdout: stdoutBuf.subarray(0, stdoutPos).toString(),
+ stderr: `${stderrBuf.subarray(0, stderrPos).toString()}\n${err.toString()}`,
+ });
+ } else {
+ process.send({
+ status: result === 0 ? "success" : "runtime_error",
+ stdout: stdoutBuf.subarray(0, stdoutPos).toString(),
+ stderr: stderrBuf.subarray(0, stderrPos).toString(),
+ });
+ }
+});
diff --git a/worker/php/index.mjs b/worker/php/index.mjs
new file mode 100644
index 0000000..9950ffa
--- /dev/null
+++ b/worker/php/index.mjs
@@ -0,0 +1,39 @@
+import { fork } from "node:child_process";
+import { serve } from "@hono/node-server";
+import { Hono } from "hono";
+
+const execPhp = (code, input, timeoutMsec) => {
+ return new Promise((resolve, _reject) => {
+ const proc = fork("./exec.mjs");
+
+ proc.send({ code, input });
+
+ proc.on("message", (result) => {
+ resolve(result);
+ proc.kill();
+ });
+
+ setTimeout(() => {
+ resolve({
+ status: "timeout",
+ stdout: "",
+ stderr: `Time Limit Exceeded: ${timeoutMsec} msec`,
+ });
+ proc.kill();
+ }, timeoutMsec);
+ });
+};
+
+const app = new Hono();
+
+app.post("/exec", async (c) => {
+ console.log("worker/exec");
+ const { code, stdin, max_duration_ms } = await c.req.json();
+ const result = await execPhp(code, stdin, max_duration_ms);
+ return c.json(result);
+});
+
+serve({
+ fetch: app.fetch,
+ port: 80,
+});
diff --git a/worker/php/package-lock.json b/worker/php/package-lock.json
new file mode 100644
index 0000000..d682e43
--- /dev/null
+++ b/worker/php/package-lock.json
@@ -0,0 +1,202 @@
+{
+ "name": "iosdc-japan-2025-albatross-worker",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "iosdc-japan-2025-albatross-worker",
+ "dependencies": {
+ "@hono/node-server": "^1.13.8",
+ "hono": "^4.7.4"
+ },
+ "devDependencies": {
+ "@biomejs/biome": "^1.9.4"
+ }
+ },
+ "node_modules/@biomejs/biome": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz",
+ "integrity": "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT OR Apache-2.0",
+ "bin": {
+ "biome": "bin/biome"
+ },
+ "engines": {
+ "node": ">=14.21.3"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/biome"
+ },
+ "optionalDependencies": {
+ "@biomejs/cli-darwin-arm64": "1.9.4",
+ "@biomejs/cli-darwin-x64": "1.9.4",
+ "@biomejs/cli-linux-arm64": "1.9.4",
+ "@biomejs/cli-linux-arm64-musl": "1.9.4",
+ "@biomejs/cli-linux-x64": "1.9.4",
+ "@biomejs/cli-linux-x64-musl": "1.9.4",
+ "@biomejs/cli-win32-arm64": "1.9.4",
+ "@biomejs/cli-win32-x64": "1.9.4"
+ }
+ },
+ "node_modules/@biomejs/cli-darwin-arm64": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz",
+ "integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-darwin-x64": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz",
+ "integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-linux-arm64": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz",
+ "integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-linux-arm64-musl": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz",
+ "integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-linux-x64": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz",
+ "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-linux-x64-musl": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz",
+ "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-win32-arm64": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz",
+ "integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@biomejs/cli-win32-x64": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz",
+ "integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT OR Apache-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=14.21.3"
+ }
+ },
+ "node_modules/@hono/node-server": {
+ "version": "1.13.8",
+ "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.13.8.tgz",
+ "integrity": "sha512-fsn8ucecsAXUoVxrUil0m13kOEq4mkX4/4QozCqmY+HpGfKl74OYSn8JcMA8GnG0ClfdRI4/ZSeG7zhFaVg+wg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.14.1"
+ },
+ "peerDependencies": {
+ "hono": "^4"
+ }
+ },
+ "node_modules/hono": {
+ "version": "4.7.4",
+ "resolved": "https://registry.npmjs.org/hono/-/hono-4.7.4.tgz",
+ "integrity": "sha512-Pst8FuGqz3L7tFF+u9Pu70eI0xa5S3LPUmrNd5Jm8nTHze9FxLTK9Kaj5g/k4UcwuJSXTP65SyHOPLrffpcAJg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.9.0"
+ }
+ }
+ }
+}
diff --git a/worker/php/package.json b/worker/php/package.json
new file mode 100644
index 0000000..c1b0569
--- /dev/null
+++ b/worker/php/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "iosdc-japan-2025-albatross-worker",
+ "private": true,
+ "type": "module",
+ "main": "index.mjs",
+ "scripts": {
+ "check": "npm run check:biome",
+ "check:biome": "biome check --write"
+ },
+ "devDependencies": {
+ "@biomejs/biome": "^1.9.4"
+ },
+ "dependencies": {
+ "@hono/node-server": "^1.13.8",
+ "hono": "^4.7.4"
+ }
+}
diff --git a/worker/php/php-wasm.c b/worker/php/php-wasm.c
new file mode 100644
index 0000000..9c71ca1
--- /dev/null
+++ b/worker/php/php-wasm.c
@@ -0,0 +1,22 @@
+#include <stdio.h>
+#include <emscripten.h>
+#include <Zend/zend_execute.h>
+#include <sapi/embed/php_embed.h>
+
+int EMSCRIPTEN_KEEPALIVE php_wasm_run(const char* code) {
+ zend_result result;
+
+ int argc = 1;
+ char* argv[] = { "php.wasm", NULL };
+
+ PHP_EMBED_START_BLOCK(argc, argv);
+
+ result = zend_eval_string_ex(code, NULL, "php.wasm code", 1);
+
+ PHP_EMBED_END_BLOCK();
+
+ fflush(stdout);
+ fflush(stderr);
+
+ return result == SUCCESS ? 0 : 1;
+}