phun
Version:
Seamlessly integrate PHP execution within your JavaScript/TypeScript projects with Phun.
124 lines (111 loc) • 3.17 kB
text/typescript
import { unlinkSync } from "node:fs";
import {
type Cwd,
type Env,
type Result,
register as boss_register,
run as boss_run,
load,
} from "boss.sh";
import type { Server } from "bun";
export type Data = Record<string, any>;
export async function run(path: string, cwd?: Cwd, env?: Env): Promise<Result> {
const json = JSON.stringify(data ?? {});
return await boss_run(path, ["php", "-f", path], cwd, env);
}
export async function serve(
path: string,
request: Request,
server: Server,
data?: Data,
cwd?: Cwd,
env?: Env,
): Promise<Response> {
env = env ?? {};
const url = new URL(request.url);
const remote = server.requestIP(request);
const pathname = decodeURIComponent(url.pathname);
env.SERVER_ADDR = server.hostname;
env.SERVER_NAME =
server.hostname in ["localhost", "127.0.0.1", "::1", "0.0.0.0"]
? "localhost"
: server.hostname;
env.SERVER_SOFTWARE = "Phun";
env.SERVER_PORT = server.port.toString();
env.GATEWAY_INTERFACE = "CGI/1.1";
env.SERVER_PROTOCOL = "HTTP/1.1";
env.REQUEST_URI = pathname;
env.REQUEST_METHOD = request.method || "GET";
env.REQUEST_TIME = Math.floor(Date.now() / 1000).toString();
env.REQUEST_TIME_FLOAT = (Date.now() / 1000).toString();
env.REMOTE_ADDR = remote?.address;
env.REMOTE_PORT = remote?.port.toString();
env.REMOTE_FAMILY = remote?.family;
env.DOCUMENT_ROOT = cwd || process.cwd();
env.SCRIPT_FILENAME = path;
env.SCRIPT_NAME = pathname;
env.PHP_SELF = pathname;
env.QUERY_STRING = url.searchParams.toString();
env.HTTPS = server.url.protocol === "https" ? "on" : "";
for (let [key, value] of request.headers) {
if (!key.toLowerCase().startsWith("content-")) {
key = `HTTP_${key}`;
}
env[key.replace(/-/g, "_").toUpperCase()] = value;
}
const result = await use(path, data, cwd, env);
return new Response(result.default, {
headers: { "Content-Type": "text/html; charset=utf-8" },
});
}
export async function render(
code: string,
data?: Data,
cwd?: Cwd,
env?: Env,
): Promise<any> {
const path = `${process.cwd()}/phun-${Date.now()}-${Math.random()
.toString(36)
.split(".")
.pop()}.php`;
Bun.write(path, code);
const render = await use(path, data, cwd, env);
unlinkSync(path);
return render.default;
}
export async function use(
path: string,
data?: Data,
cwd?: Cwd,
env?: Env,
): Promise<any> {
const json = JSON.stringify(data ?? {});
return await load(
path,
[
"php",
"-r",
`$data = json_decode('${json}', true); if(is_array($data) && !\\array_is_list($data)) extract($data); ob_start(); $default = require "${path}"; if($default === 1) $default = ob_get_clean(); else ob_end_clean(); echo json_encode($default);`,
],
cwd,
env,
);
}
export function register(
name = "PHP Loader",
filter: RegExp = /\.php$/,
cwd?: Cwd,
env?: Env,
) {
boss_register(
name,
filter,
(path) => [
"php",
"-r",
`$data = ob_start(); $default = require "${path}"; if($default === 1) $default = ob_get_clean(); else ob_end_clean(); echo json_encode($default);`,
],
cwd,
env,
);
}