php-wasm icon indicating copy to clipboard operation
php-wasm copied to clipboard

Docs - Simple CGI Example

Open ZebTheWizard opened this issue 10 months ago • 2 comments

How can I setup a simple working implementation of the CGI web worker?

The docs show how to instantiate PhpCgiWorker but what am I supposed to do with the service worker? Are there some simple messages I can send to the service worker to see if it works correctly?

The demo-web seems to link a bunch of shared libraries. Is that required for a simple implementation?

ZebTheWizard avatar Jan 23 '25 22:01 ZebTheWizard

The shared libs aren't required to run php-wasm, except for libxml, which should be loaded automatically.

The service worker will respond to any HTTP requests made to the domain that registered it, so the PHP handler will activate for certain paths, and handle the request without actually sending it to the server. Other requests are sent to the network as normal.

seanmorris avatar Apr 10 '25 23:04 seanmorris

Building the service worker was the most challenging thing for me. I pieced together a working service worker by using this setup. I had to use webpack instead of vite.

sw.mjs:

import { PhpCgiWorker } from "php-cgi-wasm/PhpCgiWorker.mjs";

const sharedLibs = [
  `php${PHP_VERSION}-zlib.so`,
  `php${PHP_VERSION}-zip.so`,
  `php${PHP_VERSION}-gd.so`,
  `php${PHP_VERSION}-iconv.so`,
  `php${PHP_VERSION}-intl.so`,
  `php${PHP_VERSION}-openssl.so`,
  `php${PHP_VERSION}-dom.so`,
  `php${PHP_VERSION}-mbstring.so`,
  `php${PHP_VERSION}-sqlite.so`,
  `php${PHP_VERSION}-pdo-sqlite.so`,
  `php${PHP_VERSION}-xml.so`,
  `php${PHP_VERSION}-simplexml.so`,
  { url: "libxml2.so", ini: false },
];

const files = [
  { parent: "/preload/", name: "icudt72l.dat", url: "./icudt72l.dat" },
];

// Log requests with more detail
const onRequest = (request, response) => {
  const url = new URL(request.url);
  const logLine =
    `[${new Date().toISOString()}] ` +
    `${request.method} ${url.pathname} - ${response.status}`;
  console.log(logLine);

  // Log errors in detail
  if (response.status >= 400) {
    console.error("Service Worker Error Response:", {
      url: url.pathname,
      status: response.status,
      statusText: response.statusText,
      headers: Object.fromEntries(response.headers.entries()),
    });
  }
};

// Spawn the PHP-CGI binary - revert to working prefix
const php = new PhpCgiWorker({
  onRequest,
  sharedLibs,
  files,
  staticFS: false,
  prefix: "/php-wasm/cgi-bin/",
  docroot: "/persist/www/",
  types: {
    jpeg: "image/jpeg",
    jpg: "image/jpeg",
    gif: "image/gif",
    png: "image/png",
    svg: "image/svg+xml",
  },
});

// Set up the main event handlers
self.addEventListener("install", (event) => {
  console.log("Service Worker: Install");
  php.handleInstallEvent(event);
});

self.addEventListener("activate", (event) => {
  console.log("Service Worker: Activate");
  php.handleActivateEvent(event);
});

self.addEventListener("fetch", (event) => {
  php.handleFetchEvent(event);
});

self.addEventListener("message", (event) => {
  php.handleMessageEvent(event);
});

webpack.config.mjs

import path from "node:path";
import "webpack-dev-server";
import { fileURLToPath } from "node:url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const config = {
  mode: "development",
  devtool: "inline-source-map",
  module: {
    rules: [
      {
        test: /\.mjs$/,
        exclude: /node_modules/,
        use: { loader: "babel-loader" },
      },
    ],
  },
  entry: { "service-worker": "./src/sw.mjs" },
  output: {
    path: path.resolve(__dirname, "public"),
    filename: "sw.js",
  },
  target: "webworker",
};

export default config;

bun-build.sh:

#/usr/bin/env bash

set -eux;

if [ -d 'public/static/media/mapped' ]; then {
	rm public/static/media/*.map || true
	rm -rf public/static/media/mapped
}
fi


PHP_VERSION=8.3

ls node_modules/*/*.so node_modules/php-wasm-intl/icudt72l.dat | while read FILE; do {
	BASENAME=`basename ${FILE}`;
	if [[ ${BASENAME} == php8.* ]]; then
		if [[ ${BASENAME} != php${PHP_VERSION}* ]]; then
			continue;
		fi;
	fi;
	cp ${FILE} public/;
}; done;

rm -f build/*.wasm;
rm -f build/*.data;
rm -f build/*.map;
rm -f build/*.js;

rm -f public/*.wasm;
rm -f public/*.data;
rm -f public/*.map;
rm -f public/*.js;

bunx webpack --config webpack.config.mjs;

package.json:

{
  "name": "tanstack-start-example-basic-react-query",
  "private": true,
  "sideEffects": false,
  "type": "module",
  "scripts": {
    "dev": "vite dev",
    "build": " bun run sw && vite build && tsc",
    "sw": "bash ./bun-build.sh",
    "start": "bun run .output/server/index.mjs",
    "format": "biome format --write src",
    "lint": "biome lint --write src",
    "check": "biome check --write src"
  },
  "dependencies": {
    "@codemirror/lang-php": "^6.0.1",
    "@jsdevtools/rehype-toc": "^3.0.2",
    "@mdx-js/mdx": "^3.1.0",
    "@mdx-js/react": "^3.1.0",
    "@mdx-js/rollup": "^3.1.0",
    "@radix-ui/react-dialog": "^1.1.14",
    "@radix-ui/react-select": "^2.2.5",
    "@radix-ui/react-slot": "^1.2.3",
    "@radix-ui/react-tabs": "^1.1.12",
    "@radix-ui/react-tooltip": "^1.2.7",
    "@shikijs/langs": "^3.6.0",
    "@shikijs/themes": "^3.6.0",
    "@tailwindcss/postcss": "^4.1.10",
    "@tanstack/react-query": "^5.0.0-alpha.91",
    "@tanstack/react-query-devtools": "^5.0.0-alpha.91",
    "@tanstack/react-router": "^1.121.0-alpha.27",
    "@tanstack/react-router-devtools": "^1.121.0-alpha.27",
    "@tanstack/react-router-with-query": "^1.121.0-alpha.27",
    "@tanstack/react-start": "^1.121.0-alpha.27",
    "@tanstack/server-functions-plugin": "^1.121.0-alpha.26",
    "@types/mdx": "^2.0.13",
    "babel-loader": "^10.0.0",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "codemirror": "^6.0.1",
    "lucide-react": "^0.513.0",
    "next-themes": "^0.4.6",
    "php-cgi-wasm": "0.0.9-alpha-32",
    "php-wasm-dom": "0.0.9-alpha-32",
    "php-wasm-gd": "0.0.9-alpha-32",
    "php-wasm-iconv": "0.0.9-alpha-32",
    "php-wasm-intl": "0.0.9-alpha-32",
    "php-wasm-libxml": "0.0.9-alpha-32",
    "php-wasm-libzip": "0.0.9-alpha-32",
    "php-wasm-mbstring": "0.0.9-alpha-32",
    "php-wasm-openssl": "0.0.9-alpha-32",
    "php-wasm-phar": "0.0.9-alpha-32",
    "php-wasm-simplexml": "0.0.9-alpha-32",
    "php-wasm-sqlite": "0.0.9-alpha-32",
    "php-wasm-tidy": "0.0.9-alpha-32",
    "php-wasm-xml": "0.0.9-alpha-32",
    "php-wasm-yaml": "0.0.9-alpha-32",
    "php-wasm-zlib": "0.0.9-alpha-32",
    "posthog-js": "^1.255.1",
    "react": "^19.1.0",
    "react-dom": "^19.1.0",
    "react-resizable-panels": "^3.0.2",
    "redaxios": "^0.5.1",
    "rehype-highlight": "^7.0.2",
    "rehype-slug": "^6.0.0",
    "remark-frontmatter": "^5.0.0",
    "remark-gfm": "^4.0.1",
    "remark-mdx-frontmatter": "^5.2.0",
    "shiki": "^3.6.0",
    "sonner": "^2.0.5",
    "tailwind-merge": "^3.3.1",
    "tailwindcss-animate": "^1.0.7",
    "thememirror": "^2.0.1",
    "vite": "^6.3.5"
  },
  "devDependencies": {
    "@biomejs/biome": "1.9.4",
    "@tailwindcss/typography": "^0.5.16",
    "@types/node": "^22.15.31",
    "@types/react": "^19.1.8",
    "@types/react-dom": "^19.1.6",
    "autoprefixer": "^10.4.21",
    "postcss": "^8.5.5",
    "tailwindcss": "^4.1.10",
    "tw-animate-css": "^1.3.4",
    "typescript": "^5.8.3",
    "vite-tsconfig-paths": "^5.1.4",
    "webpack-cli": "^6.0.1",
    "webpack-dev-server": "^5.2.2"
  }
}

ZebTheWizard avatar Jun 26 '25 17:06 ZebTheWizard