HTTP リクエスト API
OxPHP は HTTP リクエストデータにアクセスするためのオブジェクト指向 API を提供します。$_GET、$_POST、$_COOKIE、$_FILES、$_SERVER を読み取る代わりに、Request オブジェクトのメソッドを呼び出すことで、要求したものだけを過不足なく取得できます。
目次
- 概要
- Request オブジェクトの取得
- RequestInterface のメソッド
- SessionInterface
- UploadedFileInterface
- AttributesInterface
- 例外
- SUPERGLOBALS_ENABLED
- ワーカーモード
- IDE サポート
- 例
概要
oxphp_http_request() は、現在のワーカースレッドに保存されている HTTP リクエストデータへの読み取り専用プロキシを返します。データは遅延して取得されます。$request->header('Accept') のような単一のメソッド呼び出しは、Rust 側のデータ構造に直接アクセスし、その値だけを返します。$request->headers() のような配列全体を返す呼び出しは、配列を一度だけ構築し、リクエストの処理中は PHP オブジェクト内にキャッシュします。
スーパーグローバルの代わりにこれを使う理由
- JSON ボディのパースが組み込まれています。
$request->payload()はapplication/json、application/x-www-form-urlencoded、multipart/form-dataを追加のコードなしでパースします。 - 配列キーのタイプミスがありません。
$request->method()は$_SERVER['REQUEST_METHOD']よりもタイプミスしにくくなっています。 - ファイルアップロードの型検出。
$request->file('avatar')->type()は、クライアントが申告した値ではなく、ファイルの実際の内容から判定した MIME タイプを返します。 - テストしやすい。 動作がインターフェースで定義されているため、ユニットテストでモック実装を注入できます。
- スーパーグローバルは引き続き利用可能です。
SUPERGLOBALS_ENABLED=falseの設定は任意です。オブジェクト API はどちらの場合でも動作します。
Request オブジェクトの取得
<?php
$request = oxphp_http_request();oxphp_http_request() は、アクティブな HTTP リクエスト内で実行されているスクリプトのどこからでも、oxphp_worker() のコールバック内を含めて呼び出せます。
<?php
oxphp_worker(function () {
$request = oxphp_http_request();
$method = $request->method();
// ...
});RequestInterface のメソッド
URI とメソッド
$request->method(): stringHTTP メソッドを大文字で返します("GET"、"POST"、"PUT"、"PATCH"、"DELETE" など)。
$request->isMethod(string $method): bool大文字・小文字を区別しないメソッドチェックです。
$request->path(): stringクエリ文字列を含まない URI パスを返します("/users/42")。
$request->fullUri(): stringスキーム、ホスト、(標準以外の場合の)ポート、パス、クエリ文字列を含む完全な URI を返します("https://example.com:8080/users/42?page=2")。標準ポート(HTTP は 80、HTTPS は 443)は省略されます。
$request->scheme(): string"https" または "http" を返します。
$request->isSecure(): boolスキームが "https" のとき true を返します。
$request->host(): stringHost ヘッダーから取得したホスト名です。ヘッダーが存在しない場合(Host ヘッダーのない HTTP/1.0 リクエストなど)は空文字列を返します。
$request->port(): intHost ヘッダーから取得したポートです。明示的に指定されていない場合は、スキームに応じたデフォルト値(HTTP は 80、HTTPS は 443)を返します。
TRUSTED_PROXIES に接続元(peer)が含まれている場合、scheme()、isSecure()、host()、port() は X-Forwarded-Proto と X-Forwarded-Host を尊重します。信頼済みプロキシが設定されていない場合は、直接接続の情報を反映します。
$request->queryString(): ?string先頭の ? を含まない生のクエリ文字列です。クエリ文字列がない場合は null を返します。
プロトコル
$request->httpProtocol(): string完全なプロトコル文字列です("HTTP/1.1" または "HTTP/2")。
$request->httpProtocolVersion(): stringバージョン番号のみです("1.1" または "2")。
クエリパラメータ
$request->query(?string $key = null, mixed $default = null): mixedクエリ文字列のパラメータにアクセスします。
| 呼び出し | 戻り値 |
|---|---|
$request->query() |
すべてのパラメータを配列として返す(ネストされた配列を含む) |
$request->query('page') |
page の値。存在しない場合は null |
$request->query('page', 1) |
page の値。存在しない場合は 1 |
ブラケット記法(?tags[]=php&tags[]=async)はネストされた配列にパースされます。
// Request: GET /search?q=oxphp&tags[]=php&tags[]=async
$q = $request->query('q'); // "oxphp"
$tags = $request->query('tags'); // ["php", "async"]
$all = $request->query(); // ["q" => "oxphp", "tags" => ["php", "async"]]見つかった値は常に文字列です。キーが存在しない場合、$default はそのまま返されます。
パース済みボディ
$request->payload(?string $key = null, mixed $default = null): mixedパースされたリクエストボディを返します。ボディは Content-Type ヘッダーに従ってパースされます。
| Content-Type | 戻り値 |
|---|---|
application/x-www-form-urlencoded |
フィールド値の連想配列 |
multipart/form-data |
テキストフィールド値の連想配列 |
application/json |
デコードされた配列またはスカラー値。不正な JSON の場合は null |
| 上記以外の値 | null |
payload() は POST リクエストに限りません。PUT、PATCH をはじめ、ボディを送信するあらゆるメソッドで動作します。パース結果は最初の呼び出し時にキャッシュされ、リクエストのライフタイム全体で再利用されます。
| 呼び出し | 戻り値 |
|---|---|
$request->payload() |
パースされたボディ全体 |
$request->payload('email') |
単一のフィールド値。存在しない場合は null |
$request->payload('email', '') |
単一のフィールド値。存在しない場合は '' |
<?php
// JSON request: POST /api/users
// Content-Type: application/json
// Body: {"name": "Alice", "role": "admin"}
$name = $request->payload('name'); // "Alice"
$role = $request->payload('role'); // "admin"
$data = $request->payload(); // ["name" => "Alice", "role" => "admin"]ヘッダー
$request->header(string $name, ?string $default = null): ?string生のヘッダー値を返します。ヘッダー名は大文字・小文字を区別しません。複数の値を持つヘッダー(Accept、X-Forwarded-For)の場合、ヘッダー行全体が単一の文字列として返されます。パースは呼び出し側の責任です。
$request->hasHeader(string $name): bool指定したヘッダーが存在する場合に true を返します。
$request->headers(): arrayすべてのヘッダーを連想配列として返します。各キーは受信したままのヘッダー名(正規化なし)で、各値は生のヘッダー文字列です。
<?php
$accept = $request->header('Accept');
// "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
if ($request->hasHeader('Authorization')) {
$token = $request->header('Authorization');
}
$all = $request->headers();
// ["Content-Type" => "application/json", "Accept" => "...", ...]Cookies
$request->cookie(string $name, ?string $default = null): ?string単一の Cookie の値を返します。Cookie が存在しない場合は $default を返します。
$request->cookies(): arrayすべての Cookie を名前と値のペアの連想配列として返します。
<?php
$theme = $request->cookie('theme', 'light'); // "dark" or "light"
$session = $request->cookie('session'); // null if absent
$all = $request->cookies(); // ["theme" => "dark", ...]生のボディ
$request->body(): string生のリクエストボディのバイト列を返します。これは OxPHP における file_get_contents('php://input') に相当します。payload() とは異なり、body() はキャッシュされません。呼び出しのたびに基盤となるデータ構造にアクセスします。
$request->contentType(): ?stringContent-Type ヘッダーの値を返します。存在しない場合は null を返します。
<?php
// Read raw body for signature verification
$raw = $request->body();
$signature = $request->header('X-Hub-Signature-256');
$valid = hash_hmac('sha256', $raw, $secret) === $signature;body() と payload() は独立しています。同じリクエスト内で両方を呼び出せます。
ファイルアップロード
$request->file(string $name): ?UploadedFileInterface指定したフィールド名のアップロードファイルを返します。フィールドが存在しない場合は null を返します。配列フィールド(name="photos[]")の場合は最初のファイルを返します。
$request->files(?string $name = null): array| 呼び出し | 戻り値 |
|---|---|
$request->files() |
すべてのアップロードファイルを UploadedFileInterface のフラットな配列として返す |
$request->files('photos') |
photos フィールドのすべてのファイル(name="photos[]" に対応) |
<?php
$avatar = $request->file('avatar');
if ($avatar && $avatar->isValid()) {
$mime = $avatar->type(); // Detected from file contents, not the client claim
$name = $avatar->name(); // Original filename
$avatar->moveTo('/var/uploads/' . basename($name));
}
// Multiple files
$photos = $request->files('photos'); // UploadedFileInterface[]
foreach ($photos as $photo) {
if ($photo->isValid()) {
$photo->moveTo('/var/uploads/' . basename($photo->name()));
}
}クライアント
$request->ip(): stringクライアントの IP アドレスを返します。TRUSTED_PROXIES が設定されていて、リクエストの接続元が信頼済みの集合に含まれる場合、これは X-Forwarded-For または RFC 7239 の Forwarded に含まれる、最も右側の信頼されていないアドレスになります。そうでない場合は直接の接続元 IP であり、通常はエンドクライアントではなくロードバランサーの IP です。
生の X-Forwarded-For ヘッダーは高度なユースケースのために $request->header('X-Forwarded-For') で引き続き利用できますが、手動でパースして正しく処理できることはまれです(最も左か最も右か、CIDR による信頼チェックの欠如など)。代わりに TRUSTED_PROXIES を設定してください。信頼済みプロキシを参照してください。
タイミング
$request->startTime(bool $asFloat = false): int|floatこのリクエストを受信した時刻の Unix タイムスタンプを返します。
| 呼び出し | 戻り値 |
|---|---|
$request->startTime() |
整数の秒数: 1711234567 |
$request->startTime(true) |
秒未満の精度を持つ浮動小数点数: 1711234567.3412 |
<?php
$elapsed = microtime(true) - $request->startTime(true);
error_log(sprintf("Request took %.3fs so far", $elapsed));属性
$request->attributes(): AttributesInterface現在のリクエストのための、変更可能な属性コンテナを返します。属性を使うと、グローバル変数を使わずに、同じリクエスト内のミドルウェア、ルートハンドラー、その他のコード間でデータを共有できます。
<?php
// In authentication middleware
$request->attributes()->set('user', $authenticatedUser);
// In the route handler
$user = $request->attributes()->get('user');属性はリクエストごとに管理され、ワーカーモードでは新しいリクエストのたびにリセットされます。ファイバーを使用する場合、属性は同じリクエストに対して同じワーカースレッド上で実行されているすべてのファイバー間で共有されます。PHP のファイバーは協調的(cooperative)であるため、並行アクセスは発生しません。
セッション
$request->session(): ?SessionInterface$_SESSION の読み取り専用ビューを返します。session_start() が呼び出されていない場合は null を返します。セッション管理(開始、保存、破棄、値の書き込み)には標準の PHP セッション関数を使用します。
<?php
session_start();
$session = $request->session();
$userId = $session->get('user_id');
$isAdmin = $session->get('is_admin', false);
// Write session data using standard PHP functions
$_SESSION['last_seen'] = time();SessionInterface
SessionInterface はアクティブなセッションの読み取り専用ビューです。
namespace OxPHP\Http;
interface SessionInterface
{
public function id(): string;
public function name(): string;
public function get(string $key, mixed $default = null): mixed;
public function has(string $key): bool;
public function all(): array;
}| メソッド | 説明 |
|---|---|
id() |
セッション ID |
name() |
セッション名(デフォルト: "PHPSESSID") |
get(key, default) |
単一のセッション値。キーが存在しない場合は $default |
has(key) |
$_SESSION にキーが存在する場合は true |
all() |
すべてのセッションデータを配列として返す |
セッションの値は、session() が最初に呼び出された時点の状態ではなく、呼び出し時点の $_SESSION の現在の状態を反映します。
UploadedFileInterface
UploadedFileInterface は単一のアップロードファイルを表します。
namespace OxPHP\Http;
interface UploadedFileInterface
{
public function name(): string;
public function clientType(): string;
public function type(): string;
public function size(): int;
public function tmpPath(): string;
public function error(): int;
public function isValid(): bool;
public function moveTo(string $destination): bool;
}| メソッド | 説明 |
|---|---|
name() |
クライアントが送信した元のファイル名 |
clientType() |
クライアントが申告した MIME タイプ — セキュリティ上の判断でこの値を信頼してはいけません |
type() |
マジックバイト検出を用いて、ファイルの実際の内容から判定した MIME タイプ。タイプを判定できない場合は "application/octet-stream" を返します。最初の呼び出し時にキャッシュされます。 |
size() |
バイト単位のファイルサイズ |
tmpPath() |
ディスク上の一時ファイルへのパス |
error() |
UPLOAD_ERR_* 定数のいずれか |
isValid() |
error() が UPLOAD_ERR_OK のとき true |
moveTo(path) |
ファイルを $path に移動します。移動前に type() を呼び出します。ファイルが無効な場合や移動に失敗した場合は false を返します。 |
アップロードファイルを使用する前に、必ず isValid() を確認してください。セキュリティに関わる判断を行う際は、clientType() ではなく type() を使用してください。
<?php
$file = $request->file('document');
if (!$file || !$file->isValid()) {
http_response_code(400);
echo json_encode(['error' => 'Upload failed or missing']);
return;
}
$detectedMime = $file->type();
$allowed = ['application/pdf', 'image/jpeg', 'image/png'];
if (!in_array($detectedMime, $allowed, true)) {
http_response_code(415);
echo json_encode(['error' => "File type not allowed: $detectedMime"]);
return;
}
$file->moveTo('/var/uploads/' . bin2hex(random_bytes(8)) . '.pdf');AttributesInterface
AttributesInterface はリクエストオブジェクトの中で唯一変更可能な部分です。認証済みユーザー、解決されたルートパラメータ、ロケール、機能フラグなど、アプリケーションの複数の箇所からアクセスする必要があるリクエストごとのメタデータを格納するためのものです。
namespace OxPHP\Http;
interface AttributesInterface
{
public function get(string $key, mixed $default = null): mixed;
public function set(string $key, mixed $value): void;
public function has(string $key): bool;
public function remove(string $key): void;
public function all(): array;
}| メソッド | 説明 |
|---|---|
get(key, default) |
$key の値を返す。存在しない場合は $default |
set(key, value) |
値を格納する |
has(key) |
キーが設定されている場合は true |
remove(key) |
キーを削除する |
all() |
すべての属性を連想配列として返す |
例外
アクティブなリクエストコンテキストの外で oxphp_http_request() を呼び出すと、OxPHP\Http\Exception 名前空間の例外がスローされます。
namespace OxPHP\Http\Exception;
class NoActiveRequestException extends \RuntimeException {}
class AsyncContextException extends NoActiveRequestException {}
class WorkerIdleException extends NoActiveRequestException {}| 例外 | スローされるタイミング |
|---|---|
NoActiveRequestException |
アクティブな HTTP リクエストがない場合: CLI、MINIT、シャットダウン後、またはリクエストより長く存続するファイバー |
AsyncContextException |
oxphp_async() コールバックの内部 — 非同期ワーカーはリクエストコンテキストを持たない別のスレッドで実行されます |
WorkerIdleException |
ワーカーモードで、リクエストとリクエストの合間 — ワーカーは次のリクエストを待機しています |
AsyncContextException と WorkerIdleException はどちらも NoActiveRequestException を継承しているため、基底クラスをキャッチすればすべてのケースを処理できます。
<?php
try {
$request = oxphp_http_request();
} catch (\OxPHP\Http\Exception\AsyncContextException $e) {
// Inside oxphp_async() — no request context here
} catch (\OxPHP\Http\Exception\WorkerIdleException $e) {
// Worker is between requests — do not call oxphp_http_request() here
} catch (\OxPHP\Http\Exception\NoActiveRequestException $e) {
// Any other case with no active request
}通常のリクエスト処理コードでは、この try/catch は必要ありません。この例外ガードは、リクエストコンテキストの外で実行される可能性のあるブートストラップコードで役立ちます。
SUPERGLOBALS_ENABLED
SUPERGLOBALS_ENABLED=true # default — full backward compatibility
SUPERGLOBALS_ENABLED=false # superglobals are empty arraysデフォルトでは、OxPHP は $_GET、$_POST、$_COOKIE、$_FILES、$_SERVER を通常どおり設定します。このモードでは、HTTP オブジェクト API がスーパーグローバルと並行して利用できます。
SUPERGLOBALS_ENABLED=false を設定すると、これらの配列は空になり、リクエストごとにそれらを構築するコストがなくなります。以下は、この設定に関係なく引き続き動作します。
| 機能 | SUPERGLOBALS_ENABLED=false のときの動作 |
|---|---|
oxphp_http_request() |
常に利用可能 |
php://input |
利用可能(スーパーグローバルではなくストリームであるため) |
$_SESSION |
利用可能(PHP のセッションモジュールが管理) |
header(), headers_list() |
利用可能(SAPI の出力関数) |
session_start(), session_*() |
利用可能(ネイティブの PHP 関数) |
$_GET, $_POST, $_COOKIE, $_FILES, $_SERVER |
空の配列 |
実行時に現在の設定を確認するには oxphp_superglobals_enabled() を使用します。
<?php
if (!oxphp_superglobals_enabled()) {
$method = oxphp_http_request()->method();
} else {
$method = $_SERVER['REQUEST_METHOD'];
}ワーカーモード
ワーカーモードでは、受信したリクエストごとに新しい Request オブジェクトが生成されます。前のリクエストのオブジェクトは、そのリクエストが完了すると無効になります。リクエストとリクエストの間でそのオブジェクトへの参照を保持しないでください。
<?php
// worker.php
require __DIR__ . '/vendor/autoload.php';
$app = new MyApp\Application();
oxphp_worker(function () use ($app) {
$request = oxphp_http_request();
$app->handle($request);
});Request オブジェクト上のすべてのキャッシュ(パース済みヘッダー、Cookie、クエリパラメータ、ペイロード)は、次のリクエストが始まると自動的にクリアされます。
IDE サポート
PhpStorm、VS Code、または LSP に対応した任意のエディタで補完と型チェックを利用するには、スタブパッケージをインストールします。
composer require --dev oxphp/stubsスタブパッケージは以下を提供します。
oxphp-stubs/
├── OxPHP/Http/
│ ├── RequestInterface.php
│ ├── SessionInterface.php
│ ├── UploadedFileInterface.php
│ ├── AttributesInterface.php
│ ├── Request.php
│ ├── Session.php
│ ├── UploadedFile.php
│ ├── Attributes.php
│ └── Exception/
│ ├── NoActiveRequestException.php
│ ├── AsyncContextException.php
│ └── WorkerIdleException.php
└── functions.php実行時の依存関係は追加されません。このパッケージは require-dev 専用です。
例
従来モード
<?php
$request = oxphp_http_request();
$method = $request->method(); // "GET"
$path = $request->path(); // "/api/articles"
$page = $request->query('page', 1); // "2" or 1 (default)
// Authorization header
if (!$request->hasHeader('Authorization')) {
http_response_code(401);
echo json_encode(['error' => 'Unauthorized']);
exit;
}
$token = $request->header('Authorization');
// Structured logging with request metadata
error_log(sprintf(
'[%s] %s %s from %s',
oxphp_request_id(),
$method,
$path,
$request->ip()
));JSON ボディを伴う POST
<?php
$request = oxphp_http_request();
if (!$request->isMethod('POST')) {
http_response_code(405);
exit;
}
$email = $request->payload('email');
$password = $request->payload('password');
if (!$email || !$password) {
http_response_code(400);
echo json_encode(['error' => 'email and password are required']);
exit;
}
// payload() handles JSON, form-urlencoded, and multipart
// No manual json_decode() or $_POST check needed
$user = authenticate($email, $password);
header('Content-Type: application/json');
echo json_encode(['token' => $user->generateToken()]);ミドルウェアの属性
<?php
// auth-middleware.php
function authenticate_request(\OxPHP\Http\RequestInterface $request): void
{
$token = $request->header('Authorization');
if (!$token) {
http_response_code(401);
exit;
}
$user = verify_token(str_replace('Bearer ', '', $token));
if (!$user) {
http_response_code(403);
exit;
}
$request->attributes()->set('user', $user);
}
// route-handler.php
$request = oxphp_http_request();
authenticate_request($request);
$user = $request->attributes()->get('user');
echo json_encode(['id' => $user->id, 'name' => $user->name]);セッションを伴うワーカーモード
<?php
// worker.php
require __DIR__ . '/vendor/autoload.php';
oxphp_worker(function () {
$request = oxphp_http_request();
if ($request->path() === '/login' && $request->isMethod('POST')) {
$username = $request->payload('username');
$password = $request->payload('password');
if (verify_credentials($username, $password)) {
session_start();
$_SESSION['user'] = $username;
$_SESSION['authenticated'] = true;
header('Location: /dashboard');
} else {
http_response_code(401);
echo 'Invalid credentials';
}
return;
}
if ($request->path() === '/dashboard') {
session_start();
$session = $request->session();
if (!$session || !$session->get('authenticated')) {
header('Location: /login');
return;
}
echo 'Welcome, ' . htmlspecialchars($session->get('user'));
}
});