Skip to content

Instantly share code, notes, and snippets.

@masakielastic
Created June 21, 2025 19:55
Show Gist options
  • Select an option

  • Save masakielastic/3d13a5906ba5e57dcda9516971d0e52c to your computer and use it in GitHub Desktop.

Select an option

Save masakielastic/3d13a5906ba5e57dcda9516971d0e52c to your computer and use it in GitHub Desktop.
PHP libuv 拡張 (amphp/ext-uv) で HTTP/1 サーバー
<?php
// PHP libuv拡張を使ったシンプルなHTTP/1.1サーバー
function createHttpResponse($statusCode, $statusText, $body, $headers = []) {
$response = "HTTP/1.1 {$statusCode} {$statusText}\r\n";
// デフォルトヘッダー
$defaultHeaders = [
'Content-Type' => 'text/html; charset=utf-8',
'Content-Length' => strlen($body),
'Connection' => 'close',
'Server' => 'PHP-libuv/1.0'
];
$allHeaders = array_merge($defaultHeaders, $headers);
foreach ($allHeaders as $name => $value) {
$response .= "{$name}: {$value}\r\n";
}
$response .= "\r\n{$body}";
return $response;
}
function parseHttpRequest($data) {
$lines = explode("\r\n", $data);
$requestLine = array_shift($lines);
if (empty($requestLine)) {
return null;
}
$parts = explode(' ', $requestLine);
if (count($parts) < 3) {
return null;
}
$method = $parts[0];
$uri = $parts[1];
$version = $parts[2];
$headers = [];
foreach ($lines as $line) {
if (empty($line)) {
break;
}
$colonPos = strpos($line, ':');
if ($colonPos !== false) {
$name = trim(substr($line, 0, $colonPos));
$value = trim(substr($line, $colonPos + 1));
$headers[strtolower($name)] = $value;
}
}
return [
'method' => $method,
'uri' => $uri,
'version' => $version,
'headers' => $headers
];
}
function handleRequest($request) {
$method = $request['method'];
$uri = $request['uri'];
echo "Request: {$method} {$uri}\n";
switch ($uri) {
case '/':
return createHttpResponse(200, 'OK',
'<h1>Welcome to PHP libuv Server!</h1>' .
'<p>Current time: ' . date('Y-m-d H:i:s') . '</p>' .
'<p><a href="/hello">Hello page</a></p>' .
'<p><a href="/json">JSON API</a></p>'
);
case '/hello':
return createHttpResponse(200, 'OK',
'<h1>Hello, World!</h1>' .
'<p><a href="/">Back to home</a></p>'
);
case '/json':
$data = json_encode([
'message' => 'Hello from PHP libuv server',
'timestamp' => time(),
'method' => $method
]);
return createHttpResponse(200, 'OK', $data, [
'Content-Type' => 'application/json'
]);
default:
return createHttpResponse(404, 'Not Found',
'<h1>404 - Page Not Found</h1>' .
'<p>The requested page was not found.</p>' .
'<p><a href="/">Back to home</a></p>'
);
}
}
// TCPサーバーを作成
$server = uv_tcp_init();
// サーバーをバインド
$bind_result = uv_tcp_bind($server, uv_ip4_addr('127.0.0.1', 8080));
if ($bind_result !== 0) {
echo "Warning: Bind returned code $bind_result (but may still work)\n";
}
// リスナーを開始
$listen_result = uv_listen($server, 100, function($server) {
$client = uv_tcp_init();
$accept_result = uv_accept($server, $client);
if ($accept_result !== 0) {
echo "Warning: Accept returned code $accept_result (but may still work)\n";
}
echo "New connection accepted\n";
// クライアントからのデータを読み取り
// この拡張では引数が2つの場合があります
uv_read_start($client, function($client, $data) {
if (!empty($data)) {
echo "Received data: " . substr($data, 0, 100) . "...\n";
// HTTPリクエストをパース
$request = parseHttpRequest($data);
if ($request) {
// リクエストを処理してレスポンスを生成
$response = handleRequest($request);
// レスポンスを送信
uv_write($client, $response, function($client, $status) {
if ($status == 0) {
echo "Response sent successfully\n";
} else {
echo "Failed to send response: $status\n";
}
uv_close($client, function() {
echo "Client connection closed\n";
});
});
} else {
// 不正なリクエストの場合
echo "Invalid request received\n";
$errorResponse = createHttpResponse(400, 'Bad Request',
'<h1>400 - Bad Request</h1>'
);
uv_write($client, $errorResponse, function($client, $status) {
uv_close($client, function() {
echo "Error response sent and connection closed\n";
});
});
}
} else {
// 接続が閉じられた場合またはデータが空の場合
echo "Connection closed by client or empty data\n";
uv_close($client, function() {
echo "Client connection properly closed\n";
});
}
});
});
echo "HTTP Server started on http://127.0.0.1:8080\n";
echo "Press Ctrl+C to stop the server\n";
// シグナルハンドラーを設定(Ctrl+Cで停止)
$signal = uv_signal_init();
uv_signal_start($signal, function() use ($server) {
echo "\nShutting down server...\n";
uv_close($server);
uv_stop();
}, SIGINT);
// イベントループを開始
uv_run();
echo "Server stopped\n";
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment