[package]
name = "mini_php_http"
version = "0.1.0"
edition = "2021"
[dependencies]
tiny_http = "0.12"
ripht-php-sapi = "0.1.0-rc.7"use ripht_php_sapi::prelude::*;
use std::io::Read;
use tiny_http::{Header, Response, Server, StatusCode};
fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut addr = "127.0.0.1:8080".to_string();
let mut script = std::path::PathBuf::from("index.php");
let mut docroot: Option<std::path::PathBuf> = None;
let mut it = std::env::args().skip(1);
while let Some(a) = it.next() {
match a.as_str() {
"--addr" => addr = it.next().expect("--addr needs value"),
"--script" => script = std::path::PathBuf::from(it.next().expect("--script needs value")),
"--docroot" => docroot = Some(std::path::PathBuf::from(it.next().expect("--docroot needs value"))),
"--help" | "-h" => {
eprintln!("Usage: mini_php_http [--addr 127.0.0.1:8080] --script ./index.php [--docroot ./public]");
return Ok(());
}
_ => return Err(format!("Unknown arg: {a}").into()),
}
}
if !script.exists() {
return Err(format!("script not found: {}", script.display()).into());
}
let php = RiphtSapi::instance();
let server = Server::http(&addr)?;
eprintln!("Listening on http://{addr}");
eprintln!("Script: {}", script.display());
loop {
let mut req = match server.recv() {
Ok(r) => r,
Err(e) => {
eprintln!("recv error: {e}");
continue;
}
};
let method = match req.method().as_str() {
"GET" => Method::Get,
"POST" => Method::Post,
"PUT" => Method::Put,
"DELETE" => Method::Delete,
"PATCH" => Method::Patch,
"HEAD" => Method::Head,
"OPTIONS" => Method::Options,
other => {
let res = Response::from_string(format!("Method not allowed: {other}\n"))
.with_status_code(StatusCode(405));
let _ = req.respond(res);
continue;
}
};
let uri = req.url().to_string();
// body(req を消費しない)
let mut body = Vec::new();
if req.body_length().unwrap_or(0) > 0 {
if let Err(e) = req.as_reader().read_to_end(&mut body) {
eprintln!("body read error: {e}");
}
}
let mut builder = WebRequest::new(method).with_uri(uri);
if let Some(ct) = req
.headers()
.iter()
.find(|h| h.field.equiv("Content-Type"))
.map(|h| h.value.as_str().to_string())
{
builder = builder.with_content_type(ct);
}
for h in req.headers() {
builder = builder.with_header(h.field.as_str().to_string(), h.value.as_str().to_string());
}
if !body.is_empty() {
builder = builder.with_body(body);
}
if let Some(dr) = &docroot {
builder = builder.with_document_root(dr.clone());
} else if let Some(parent) = script.parent() {
builder = builder.with_document_root(parent.to_path_buf());
}
let ctx = match builder.build(&script) {
Ok(c) => c,
Err(e) => {
let res = Response::from_string(format!("WebRequest build error: {e}\n"))
.with_status_code(StatusCode(500));
let _ = req.respond(res);
continue;
}
};
let mut result = match php.execute(ctx) {
Ok(r) => r,
Err(e) => {
let res = Response::from_string(format!("PHP execute error: {e}\n"))
.with_status_code(StatusCode(500));
let _ = req.respond(res);
continue;
}
};
let status = StatusCode(result.status_code());
let body = result.take_body();
let mut resp = Response::from_data(body).with_status_code(status);
for h in result.all_headers() {
let name = h.name();
let value = h.value();
if let Ok(hh) = Header::from_bytes(name.as_bytes(), value.as_bytes()) {
resp.add_header(hh);
}
}
let _ = req.respond(resp);
}
}index.php
<?php
header("Content-Type: text/plain; charset=utf-8");
echo "Hello from PHP\n";
echo "URI: " . ($_SERVER["REQUEST_URI"] ?? "") . "\n";
echo "Method: " . ($_SERVER["REQUEST_METHOD"] ?? "") . "\n";
echo "Query: " . ($_SERVER["QUERY_STRING"] ?? "") . "\n";cargo run -- --addr 127.0.0.1:8080 --script ./index.php