早期レスポンス
oxphp_finish_request() は、完全な HTTP レスポンスを即座にクライアントへ送信し、その後も PHP スクリプトをバックグラウンド処理のために実行し続けます。これは PHP-FPM の fastcgi_finish_request() に相当する OxPHP の機能です。
仕組み
- レスポンスを構築します。 スクリプトは通常どおりヘッダー、ステータスコードを設定し、レスポンスボディを出力します。
- リクエストを完了します。
oxphp_finish_request()を呼び出します。OxPHP はすべての出力バッファをフラッシュし、リクエストを完了済みとしてマークし、完全な HTTP レスポンスをクライアントへ配信します。 - バックグラウンド処理を実行します。 スクリプトはメール送信、キャッシュエントリの書き込み、Webhook の送信といったバックグラウンド処理のために実行を続けます。
- 以降の出力は破棄されます。 呼び出しの後に生成された出力(
echo、print、var_dump)は暗黙のうちに破棄されます。 - 呼び出しは冪等です。
oxphp_finish_request()は最初の呼び出しではtrueを返し、同一リクエスト内での 2 回目以降の呼び出しではfalseを返します。
ユースケース
早期レスポンスは、リクエストを即座に受理しつつ、重要でない処理を後回しにしたいあらゆる場面で役立ちます。
- メール送信 — 即座に「受理」を返し、メールはバックグラウンドで送信する
- キャッシュのウォーミング — キャッシュ済みデータでレスポンスを返し、その後キャッシュエントリを再生成する
- アナリティクスとロギング — リクエストを受理し、その後で詳細なアナリティクスレコードを書き込む
- Webhook の送信 — 呼び出し元に受信を確認し、その後で Webhook 配信をファンアウトする
- 画像処理 — URL を即座に返し、その後でフルサイズ画像を処理する
PHP の例
基本的な使い方
<?php
header('Content-Type: application/json');
echo json_encode(['status' => 'accepted', 'id' => uniqid()]);
// Send response now — client receives the full response at this point
oxphp_finish_request();
// Background work runs here; client is no longer waiting
file_put_contents('/tmp/audit.log', date('c') . " request processed\n", FILE_APPEND);
send_notification_email($user);二重呼び出しを防ぐ
oxphp_finish_request() は 2 回目以降の呼び出しで false を返します。複数のレイヤーがこの関数を呼び出す可能性があるミドルウェアの多いアプリケーションでは、戻り値を確認してください。
<?php
function finish_and_cleanup(): void
{
if (!oxphp_finish_request()) {
// Already finished — background work was already scheduled
return;
}
// First call — safe to run cleanup
flush_metrics_buffer();
close_external_connections();
}条件付きのバックグラウンド処理
<?php
header('Content-Type: application/json');
$payload = json_decode(file_get_contents('php://input'), true);
$result = handle_request($payload);
echo json_encode($result);
if ($result['needs_sync']) {
oxphp_finish_request();
sync_to_external_service($result);
}
// No early finish if sync is not needed — script exits normallyワーカーモード
ワーカーモードでは、PHP ワーカーはスクリプト全体(すべてのバックグラウンド処理を含む)が完了するまで占有され続けます。ワーカーはコールバックが返るまで新しいリクエストを受け付けません。
<?php
oxphp_worker(function () {
$order = json_decode(file_get_contents('php://input'), true);
$result = process_order($order);
header('Content-Type: application/json');
echo json_encode(['order_id' => $result['id'], 'status' => 'accepted']);
oxphp_finish_request();
// Worker is still occupied during this background work
send_confirmation_email($result);
update_inventory($result);
notify_warehouse($result);
// Worker becomes available after this point
});ワーカープールのサイズを決める際は、バックグラウンド処理の時間を見込んでおいてください。すべてのリクエストの後にレスポンス後処理へ 3 秒を費やすワーカーは、実質的に同時に処理できるリクエスト数が少なくなります。
トラブルシューティング
バックグラウンド処理が完了しない
PHP の max_execution_time は、oxphp_finish_request() が呼び出された後も引き続き適用されます。バックグラウンド処理を含むスクリプト全体の実行時間が上限を超えると、リクエストは Request cancelled (timeout) の致命的エラーでキャンセルされます。
対処法: max_execution_time を引き上げる(php.ini で、またはスクリプトから set_time_limit() で)か、長時間かかるバックグラウンドタスクをメッセージキューへ移してください。
set_time_limit(300);
oxphp_finish_request();
// ... long-running work ...数秒以上を常に要する処理については、Redis、RabbitMQ、あるいは同様のキューへメッセージを発行し、専用のコンシューマーに非同期で処理させてください。
セッションの変更が失われる
セッションデータは oxphp_finish_request() を呼び出す前に書き込む必要があります。呼び出しの後に加えた変更は破棄されます。
対処法: oxphp_finish_request() の前に session_write_close() を呼び出してください。
<?php
$_SESSION['last_seen'] = time();
session_write_close(); // Persist session before finishing
oxphp_finish_request(); // Send responseoxphp_finish_request() の呼び出し後にレスポンスボディが空になる
echo による出力の前に oxphp_finish_request() を呼び出すと、クライアントは空のボディを受け取ります。まずレスポンスを構築して出力し、それから関数を呼び出してください。
注意事項
oxphp_finish_request()は最初の呼び出しではtrueを返し、同一リクエスト内での以降の呼び出しではfalseを返します。- 最初の呼び出しの後のすべての出力(
echo、print、var_dump)は暗黙のうちに破棄されます。 - ワーカーモードでは、ワーカーはレスポンス後のコードをすべて含むコールバック全体が完了するまで占有され続けます。
- リクエストのタイムアウトは、
oxphp_finish_request()の後に実行されるバックグラウンドコードにも引き続き適用されます。 oxphp_finish_request()とoxphp_stream_flush()は排他的です。ストリーム開始前にoxphp_finish_request()を呼び出すとストリーミングができなくなり、oxphp_stream_flush()の後に呼び出すとストリームが閉じられます。