HTTP リクエスト API

OxPHP は HTTP リクエストデータにアクセスするためのオブジェクト指向 API を提供します。$_GET、$_POST、$_COOKIE、$_FILES、$_SERVER を読み取る代わりに、Request オブジェクトのメソッドを呼び出すことで、要求したものだけを過不足なく取得できます。

目次

概要

oxphp_http_request() は、現在のワーカースレッドに保存されている HTTP リクエストデータへの読み取り専用プロキシを返します。データは遅延して取得されます。$request->header('Accept') のような単一のメソッド呼び出しは、Rust 側のデータ構造に直接アクセスし、その値だけを返します。配列全体を返す呼び出しのうち、query() と payload() はパース結果を呼び出し元のオブジェクトに保持しますが、headers()、cookies()、files() は呼び出すたびに配列を新しく構築します。リクエスト単位で保持されるものはありません。oxphp_http_request() は呼び出すたびに新しいオブジェクトを返し、それらのキャッシュも新しい組になります。

スーパーグローバルの代わりにこれを使う理由

  • JSON ボディのパースが組み込まれています。 $request->payload() は、リクエストメソッドに関係なくデコード済みの application/json ボディを返し、POST のフォームボディ(application/x-www-form-urlencoded または multipart/form-data)のフィールドも、追加のコードなしで返します。正確な戻り値はパース済みボディを参照してください。
  • 配列キーのタイプミスがありません。 $request->method() は $_SERVER['REQUEST_METHOD'] よりもタイプミスしにくくなっています。
  • ファイルアップロードの型検出。 $request->file('avatar')->type() は、クライアントが申告した値ではなく、ファイルの実際の内容から判定した MIME タイプを返します。
  • テストしやすい。 動作がインターフェースで定義されているため、ユニットテストでモック実装を注入できます。
  • スーパーグローバルは引き続き利用可能です。 SUPERGLOBALS_ENABLED=false の設定は任意です。オブジェクト API はどちらの場合でも動作します。

Request オブジェクトの取得

php
<?php $request = oxphp_http_request();

oxphp_http_request() は、アクティブな HTTP リクエスト内で実行されているスクリプトのどこからでも、oxphp_worker() のコールバック内を含めて呼び出せます。

php
<?php oxphp_worker(function () { $request = oxphp_http_request(); $method = $request->method(); // ... });

RequestInterface のメソッド

URI とメソッド

php
$request->method(): string

HTTP メソッドを大文字で返します("GET"、"POST"、"PUT"、"PATCH"、"DELETE" など)。

php
$request->isMethod(string $method): bool

大文字・小文字を区別しないメソッドチェックです。

php
$request->path(): string

クエリ文字列を含まない URI パスを返します("/users/42")。

php
$request->fullUri(): string

スキーム、ホスト、(標準以外の場合の)ポート、パス、クエリ文字列を含む完全な URI を返します("https://example.com:8080/users/42?page=2")。標準ポート(HTTP は 80、HTTPS は 443)は省略されます。

php
$request->scheme(): string

"https" または "http" を返します。

php
$request->isSecure(): bool

スキームが "https" のとき true を返します。

php
$request->host(): string

Host ヘッダーから取得したホスト名です。ヘッダーが存在しない場合(Host ヘッダーのない HTTP/1.0 リクエストなど)は空文字列を返します。

php
$request->port(): int

Host ヘッダーから取得したポートです。明示的に指定されていない場合は、スキームに応じたデフォルト値(HTTP は 80、HTTPS は 443)を返します。

リバースプロキシの背後で運用する場合

TRUSTED_PROXIES に接続元(peer)が含まれている場合、scheme()、isSecure()、host()、port() は X-Forwarded-Proto と X-Forwarded-Host を尊重します。信頼済みプロキシが設定されていない場合は、直接接続の情報を反映します。

php
$request->queryString(): ?string

先頭の ? を含まない生のクエリ文字列です。クエリ文字列がない場合は null を返します。

プロトコル

php
$request->httpProtocol(): string

完全なプロトコル文字列です("HTTP/1.1" または "HTTP/2")。

php
$request->httpProtocolVersion(): string

バージョン番号のみです("1.1" または "2")。

クエリパラメータ

php
$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 は現在、tags 配列ではなく tags[] という名前のフラットなエントリになります。また、繰り返された名前は、query('name') では最初の出現に解決される一方、query() は最後の出現を保持します。これが修正されるまでは、配列パラメータには $_GET を使用してください。

php
// Request: GET /search?q=oxphp&tags[]=php&tags[]=async $q = $request->query('q'); // "oxphp" $tags = $_GET['tags']; // ["php", "async"]

見つかった値は常に文字列です。キーが存在しない場合、$default はそのまま返されます。値なしで送られたパラメータ(?flag)も、空で送られたパラメータ(?flag=)も、どちらも '' として読み取られます。null になるのはパラメータが存在しない場合だけです。

デコード

名前と値はパーセントデコードされ、+ はスペースとして読み取られます。PHP が $_GET を構築するときに適用するのと同じルールです。

php
// Request: GET /search?q=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82&tag=a+b $request->query('q'); // "Привет" $request->query('tag'); // "a b"

値はバイト文字列なので、有効な UTF-8 でないものをエンコードしたエスケープもそのまま生き残ります。署名付きトークンやバイナリの ID は、送られてきたままのバイト列に対して検証できます。クエリ文字列を届いたままの未デコードの形で読む手段は、引き続き queryString() です。

名前はクライアントが送ったとおりに報告されます。一方 $_GET は PHP がファイリングしたとおりに報告します。PHP はスーパーグローバルの名前の中の ' '、'.'、'[' を書き換えるため、?a.b=1 は $_GET には a_b として入ります。

php
// Request: GET /?a.b=1 $_GET['a_b']; // "1" $request->query('a.b'); // "1"

PHP が名前を切り詰める箇所でも、名前は丸ごと保持されます。PHP はスーパーグローバルの名前を C 文字列として扱うため、?a%00b=1 は $_GET['a'] になりますが、query() は a\0b を保持します。これを、片方の API で検証してもう片方で読んでよい理由と読まないでください。この切り詰めによってあるパラメータが別のパラメータのキーに着地しうるため、?a=1&a%00x=2 では $_GET['a'] === '2' に対して query('a') === '1' となり、両者は名前の集合だけでなく値そのものについて食い違います。

パースされるパラメータは最大 1000 個です。PHP には独自の上限 max_input_vars があり、デフォルトは同じ 1000 ですが設定可能です。これを引き上げると、query() が止まる地点を越えたパラメータも $_GET には保持されます。

パース済みボディ

php
$request->payload(?string $key = null, mixed $default = null): mixed

パースされたリクエストボディを返します。ボディは Content-Type ヘッダーに従ってパースされます。

Content-Type 戻り値
application/x-www-form-urlencoded POST リクエストではフィールド値の連想配列。それ以外のメソッドでは空の配列
multipart/form-data POST リクエストではテキストフィールド値の連想配列。それ以外のメソッドでは空の配列
application/json デコードされた値。オブジェクトまたは配列の場合は配列、それ以外はスカラー値そのもの(string、int、float、bool)。不正な JSON の場合と、ボディがリテラルの null の場合は null
上記以外の値 null。判定は前方一致で行われるため、application/json で始まるもの(たとえば application/json-patch+json)は上の JSON の行に該当し、text/json や application/vnd.api+json はこの行に該当します
フォームボディは POST 専用

JSON ボディはリクエストメソッドに関係なくデコードされます。フォームボディはそうではありません。フォームの 2 行は PHP がボディをパースした結果を読み取るものであり、PHP がそのパースを行うのは POST のときだけです。そのため、フォームフィールドを含む PUT や PATCH で payload() を呼び出すと空の配列が返ります。そのようなボディは JSON で送るか、body() から読み取って自分でパースしてください。

どちらのフォームタイプでも、payload() は PHP がボディからパースしたとおりのフィールドを返します。後から $_POST に書き込んでも変わるのはスクリプト側の配列のコピーだけで、payload() にはその変更は反映されません。

パース結果は最初の呼び出し時にキャッシュされ、同じオブジェクトに対する以降の呼び出しで再利用されます。oxphp_http_request() の呼び出しはそれぞれ独自のキャッシュを持つ別のオブジェクトを返すため、2 つ目のオブジェクトでは結果が改めて構築されます。

呼び出し 戻り値
$request->payload() パースされたボディ全体
$request->payload(null, []) パースされたボディ全体。ボディがない場合は []。ボディがない場合とは、空のボディ、上の表の最後の行に該当する Content-Type、不正な JSON、リテラルの JSON null のいずれかです
$request->payload('email') 単一のフィールド値。存在しない場合は null
$request->payload('email', '') 単一のフィールド値。存在しない場合は ''

キーによる参照が届くのは配列のペイロードだけです。JSON ボディがスカラー値にデコードされた場合はキーが存在しないため、payload('anything') はデフォルト値を返します。false や 0 にデコードされるボディはその値のまま返され、デフォルト値に置き換えられることはありません。

php
<?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"]

ヘッダー

php
$request->header(string $name, ?string $default = null): ?string

生のヘッダー値を返します。ヘッダー名は大文字・小文字を区別しません。複数の値を持つヘッダー(Accept、X-Forwarded-For)の場合、ヘッダー行全体が単一の文字列として返されます。パースは呼び出し側の責任です。

php
$request->hasHeader(string $name): bool

指定したヘッダーが存在する場合に true を返します。

php
$request->headers(): array

すべてのヘッダーを連想配列として返します。各キーは受信したままのヘッダー名(正規化なし)で、各値は生のヘッダー文字列です。

php
<?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

php
$request->cookie(string $name, ?string $default = null): ?string

単一の Cookie の値を返します。Cookie が存在しない場合は $default を返します。空で送られた Cookie(Cookie: a=)は '' として読み取られます。$default になるのは Cookie が存在しない場合だけです。

php
$request->cookies(): array

すべての Cookie を名前と値のペアの連想配列として返します。

デコード

Cookie はクエリパラメータとは異なる方法でデコードされます。これは PHP が $_COOKIE を構築するときの挙動に倣ったものです。値はパーセントデコードされますが、+ はリテラルの + のままで、名前は一切デコードされません。

php
// Cookie: token=a%20b; sep=a+b; %D0%BA=1 $request->cookie('token'); // "a b" — percent-escapes decoded $request->cookie('sep'); // "a+b" — unlike query(), "+" is not a space $request->cookie('%D0%BA'); // "1" — look up by the name as sent

名前はここでも送られたとおりに報告されます。PHP は Cookie を含むすべてのスーパーグローバルの名前の中の ' '、'.'、'[' を書き換えるため、Cookie: a.b=1 は $_COOKIE には a_b として入りますが、cookie('a.b') と cookies()['a.b'] はクライアントが送った名前を使います。

値はバイト文字列なので、署名付き Cookie や setcookie('sid', random_bytes(16)) は、設定されたときの正確なバイト列のままラウンドトリップします。

php
<?php $theme = $request->cookie('theme', 'light'); // "dark" or "light" $session = $request->cookie('session'); // null if absent $all = $request->cookies(); // ["theme" => "dark", ...]

生のボディ

php
$request->body(): string

生のリクエストボディのバイト列を返します。これは OxPHP における file_get_contents('php://input') に相当します。payload() とは異なり、body() はキャッシュされません。呼び出しのたびに基盤となるデータ構造にアクセスします。

php
$request->contentType(): ?string

Content-Type ヘッダーの値を返します。存在しない場合は null を返します。

php
<?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() は独立しています。同じリクエスト内で両方を呼び出せます。

ファイルアップロード

php
$request->file(string $name): ?UploadedFileInterface

指定したフィールド名のアップロードファイルを返します。フィールドが存在しない場合は null を返します。配列フィールド(name="photos[]")の場合は最初のファイルを返します。

php
$request->files(?string $name = null): array
呼び出し 戻り値
$request->files() すべてのアップロードファイルを UploadedFileInterface のフラットな配列として返す
$request->files('photos') photos フィールドのすべてのファイル(name="photos[]" に対応)

どちらも呼び出すたびに新しい UploadedFileInterface オブジェクトを構築します。file('avatar') を 2 回呼び出すと、同じアップロードファイルに対して 2 つのオブジェクトが返され、一方が type() で検出したタイプはもう一方には伝わりません。複数回必要な場合は、以下の例のように、取得したオブジェクトを保持しておいてください。

php
<?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())); } }

クライアント

php
$request->ip(): string

クライアントの IP アドレスを返します。TRUSTED_PROXIES が設定されていて、リクエストの接続元が信頼済みの集合に含まれる場合、これは X-Forwarded-For または RFC 7239 の Forwarded に含まれる、最も右側の信頼されていないアドレスになります。TRUSTED_PROXIES が cloudflare を指定していて、転送チェーンからリクエストが Cloudflare のエッジから到着したことがわかる場合は、代わりに CF-Connecting-IP のアドレスになります。そうでない場合は直接の接続元 IP であり、通常はエンドクライアントではなくロードバランサーの IP です。

生の X-Forwarded-For ヘッダーは高度なユースケースのために $request->header('X-Forwarded-For') で引き続き利用できますが、手動でパースして正しく処理できることはまれです(最も左か最も右か、CIDR による信頼チェックの欠如など)。代わりに TRUSTED_PROXIES を設定してください。信頼済みプロキシを参照してください。

タイミング

php
$request->startTime(bool $asFloat = false): int|float

このリクエストを受信した時刻の Unix タイムスタンプを返します。

呼び出し 戻り値
$request->startTime() 整数の秒数: 1711234567
$request->startTime(true) 秒未満の精度を持つ浮動小数点数: 1711234567.3412
php
<?php $elapsed = microtime(true) - $request->startTime(true); error_log(sprintf("Request took %.3fs so far", $elapsed));

属性

php
$request->attributes(): AttributesInterface

この Request オブジェクトが保持する、変更可能な属性コンテナを返します。属性を使うと、グローバル変数を使わずに、ミドルウェアからルートハンドラーや、同じオブジェクトを受け取るその他のコードへデータを渡せます。

php
<?php // In authentication middleware $request->attributes()->set('user', $authenticatedUser); // In the route handler $user = $request->attributes()->get('user');
コンテナはリクエストではなくオブジェクトに属する

oxphp_http_request() は呼び出すたびに新しい Request を返し、そこから取得するコンテナも新しい空のものです。ある呼び出しを通じて書き込んだ値は、別の呼び出しを通じては読み取れません。get() は代わりにデフォルト値を返し、警告もログも出ません。

php
<?php oxphp_http_request()->attributes()->set('tenant_id', $id); oxphp_http_request()->attributes()->get('tenant_id'); // null — a second Request, a second container

上の例の $request のようにオブジェクトを受け渡せば、それを保持するすべてのコードが同じコンテナを読み書きします。オブジェクトを渡したファイバーも同様です。ファイバーが自分で oxphp_http_request() を呼び出した場合は、他の呼び出し元と同じく独自のコンテナを受け取ります。PHP のファイバーは協調的(cooperative)であるため、2 つのファイバーが同時に 1 つのコンテナへアクセスすることはありません。

リクエスト間でのリセットは必要ありません。コンテナはそれを保持するオブジェクトとともに解放され、次のリクエストのハンドラーは oxphp_http_request() を呼び出して独自のコンテナを取得します。

セッション

php
$request->session(): ?SessionInterface

$_SESSION の読み取り専用ビューを返します。session_start() が呼び出されていない場合は null を返します。セッション管理(開始、保存、破棄、値の書き込み)には標準の PHP セッション関数を使用します。

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 はアクティブなセッションの読み取り専用ビューです。

php
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 は単一のアップロードファイルを表します。

php
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() マジックバイト検出(ext/fileinfo の mime_content_type())を用いて、ファイルの実際の内容から判定した MIME タイプ。タイプを判定できない場合は "application/octet-stream" を返します。最初の呼び出し時に検出され、このオブジェクトに保持されます(タイプの検出を参照)。
size() バイト単位のファイルサイズ
tmpPath() ディスク上の一時ファイルへのパス
error() UPLOAD_ERR_* 定数のいずれか
isValid() error() が UPLOAD_ERR_OK のとき true
moveTo(path) move_uploaded_file() を使ってファイルを $path に移動します。タイプの検出は行いません。ファイルが無効な場合や移動に失敗した場合は false を返します。移動後に type() が何を読み取るかはタイプの検出を参照してください。

アップロードファイルを使用する前に、必ず isValid() を確認してください。セキュリティに関わる判断を行う際は、clientType() ではなく type() を使用してください。

php
<?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');

タイプの検出

最初の type() 呼び出しはファイルを読み取り、その結果をオブジェクトに保持します。そのオブジェクトに対する以降の呼び出しは、ファイルを再び読み取ることなくその結果を返します。これには検出に失敗した後の "application/octet-stream" も含まれ、警告を例外に変換するエラーハンドラーの下で最初の呼び出しがスローした場合も同様です。以降の呼び出しは警告なしでその値を返します。別の file() や files() の呼び出しから得たオブジェクトは、それ自身の最初の呼び出しでファイルを改めて読み取ります。

mime_content_type() がない場合(ext/fileinfo が読み込まれていない、または関数が disable_functions に列挙されている)、ファイルを読み取るはずの呼び出しはすべて警告を発して "application/octet-stream" を返し、その値は保持されません。警告を例外に変換するエラーハンドラーの下ではスローします。OxPHP はこれについて、プロセスごとに 1 行を warn レベルで、サーバーログに、または error_log が設定されていればその出力先に記録します。警告自体は、他の PHP の警告と同じようにログに記録されます。

moveTo() はタイプを検出しません。移動前にそのオブジェクトで type() が呼び出されていなかった場合、移動が成功した後の最初の呼び出しは移動先のファイルを読み取ります。

  • ローカルパスの場合は移動されたファイルであり、移動によって配置された絶対パスで見つけられます。その後に作業ディレクトリが変わっても影響はありません。PHP がシャットダウン関数とデストラクタを実行する前に自ら行う変更も同様です。
  • ストリームラッパーの URL の場合は、そのラッパーを通じて改めて開かれ、そこから返されたものからタイプが検出されます。ラッパーが URL を読み取り用に開けない場合(警告付き)や、stat できない場合(たとえば compress.zlib://。PHP 8.4 では警告付き)、type() は "application/octet-stream" を返すか、警告を例外に変換するエラーハンドラーの下ではスローします。
  • data: URL はデコードされることがありません。move_uploaded_file() はそれをローカルのファイル名とみなしてリネームし、type() はそれを読み取ります。リネームに失敗した場合は URL へのコピーを行いますが、その URL は書き込みを受け付けません。通過するのは空のアップロードだけで、何も書き込まれず、type() はファイルを見つけられません。
  • 同じアップロードに対する別のオブジェクトで、移動前に type() が呼び出されていなかったもの(たとえば file() で再取得したもの)は、ファイルを見つけられません。mime_content_type() は存在しないファイルについて警告を発し、そのオブジェクトの type() は "application/octet-stream" を返すか、警告を例外に変換するエラーハンドラーの下ではスローします。
Tip

移動先がストリームラッパーである場合や、移動したファイルがタイプを問い合わせる前にさらに移動、削除、書き換えされる可能性がある場合は、moveTo() の前に type() を呼び出してください。オブジェクトはその呼び出しで検出したものを保持します。

AttributesInterface

AttributesInterface はリクエストオブジェクトの中で唯一変更可能な部分です。認証済みユーザー、解決されたルートパラメータ、ロケール、機能フラグなど、アプリケーション自身のコードが導き出し、同じ呼び出しチェーンの先で再び読み取るメタデータを格納するためのものです。そのデータを参照できるのは、それを保持する Request オブジェクトを受け取ったコードだけです。属性を参照してください。

php
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 名前空間の例外がスローされます。

php
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
<?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

bash
SUPERGLOBALS_ENABLED=true # default — full backward compatibility SUPERGLOBALS_ENABLED=false # the $_SERVER request keys and $_GET are not built

デフォルトでは、OxPHP は $_GET、$_POST、$_COOKIE、$_FILES、$_SERVER を通常どおり設定します。このモードでは、HTTP オブジェクト API がスーパーグローバルと並行して利用できます。

SUPERGLOBALS_ENABLED=false を設定すると、リクエストの内容を PHP に伝える処理が省略されます。CGI 変数と HTTP_* 変数は登録されず、クエリ文字列も PHP にパースさせるために渡されません。その効果は名前から想像されるよりも限定的です。リクエストボディと Cookie ヘッダーはどちらの場合も PHP に届き、PHP はこの設定に関係なく、それらから導かれる配列を構築します。

機能 SUPERGLOBALS_ENABLED=false のときの動作
oxphp_http_request() 利用可能。query() と payload() を含むすべてのメソッドが応答します
php://input 利用可能(スーパーグローバルではなくストリームであるため)
$_SESSION 利用可能(PHP のセッションモジュールが管理)
header(), headers_list() 利用可能(SAPI の出力関数)
session_start(), session_*() 利用可能(ネイティブの PHP 関数)
$_POST, $_FILES, $_COOKIE 通常どおり設定されます。 これらはボディと Cookie ヘッダーから構築され、この設定はそれらに影響しません
$_REQUEST 設定されます。PHP はいつもどおりマージします(スーパーグローバルを参照)。欠けるのは $_GET が提供するはずだった分だけです
$_GET 空
$_SERVER リクエスト中は REQUEST_TIME、REQUEST_TIME_FLOAT、argc、argv の 4 つのキーだけで、いずれもサーバーではなく PHP 自身が登録するものです。REQUEST_METHOD、REQUEST_URI、HTTP_* キー、プロセス環境は存在しません。ワーカーモードのブートストラップは異なります。 oxphp_worker() より上のコードはどのリクエストよりも前に実行され、この設定が有効であるかのように構築された $_SERVER(プロセス環境全体を含む)に加えて、プレースホルダーの REQUEST_URI である / を参照します

したがって、この設定はリクエストデータを PHP のグローバルから締め出すものではなく、フォームボディのパースを止めるものでもありません。省略されるのは、リクエストごとの $_SERVER の構築(CGI 変数と HTTP_* 変数、およびプロセス環境)と、PHP によるクエリ文字列の $_GET へのパースだけです。

REQUEST_URI によるルーティング

$_SERVER['REQUEST_URI'] をもとにルーティングするコードは、この設定のもとではそこに何も見つけられないため、代わりに oxphp_http_request()->path() を読み取る必要があります。ワーカーのエントリスクリプトでは、これはハンドラーに当てはまります。その上にあるブートストラップが参照するのは表で説明したプレースホルダーの / であり、これも実際のルートではありません。

実行時に現在の設定を確認するには oxphp_superglobals_enabled() を使用します。

php
<?php if (!oxphp_superglobals_enabled()) { $method = oxphp_http_request()->method(); } else { $method = $_SERVER['REQUEST_METHOD']; }

ワーカーモード

ワーカーモードでは、oxphp_http_request() はリクエストごとに 1 回ではなく、呼び出しのたびに新しい Request を構築します。下の例のように、ハンドラーの中で呼び出し、必要な場所へオブジェクトを渡してください。

worker.php
<?php // worker.php require __DIR__ . '/vendor/autoload.php'; $app = new MyApp\Application(); oxphp_worker(function () use ($app) { $request = oxphp_http_request(); $app->handle($request); });

オブジェクトを、それを構築したハンドラーより長く保持しないでください。リクエストをまたいで保持されたオブジェクトは無効化されず、古くなったことが報告されることもありません。method()、path()、header() などキャッシュを持たない読み取りメソッドは、呼び出した時点でワーカースレッドが処理しているリクエストのデータを返します。キャッシュされる値(query() の配列、パース済みの payload()、属性コンテナ)は、それぞれの読み取りメソッドが最初の呼び出し時に取り込んだ内容を保持します。現在のリクエスト中に初めて呼び出された読み取りメソッドは、このリクエストのデータを取り込みます。次のリクエストが始まっても、Request 上のものは何もクリアされません。リクエスト間のソフトリセットが対象とするものは、リセットされるものと維持されるものに記載されています。

IDE サポート

PhpStorm、VS Code、または LSP に対応した任意のエディタで補完と型チェックを利用するには、スタブパッケージをインストールします。

bash
composer require --dev oxphp/stubs

スタブパッケージは以下を提供します。

text
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
<?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
<?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
<?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]);

セッションを伴うワーカーモード

worker.php
<?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; session_write_close(); 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')) { session_write_close(); header('Location: /login'); return; } echo 'Welcome, ' . htmlspecialchars($session->get('user')); session_write_close(); } });
すべての終了経路でセッションを閉じる

すべての終了経路で session_write_close() を呼び出すと、書き込みと、セーブハンドラーがロックしたものの解放が、アプリケーションの選んだ時点で行われます。開いたまま残されたセッションも、ワーカーが抱えている他のどのリクエストもその中にいない限り、リクエストの終了時に書き込まれて閉じられます。別のリクエストが中にいるセッションは、ワーカーがそれを返却するまで開いたままで、セーブハンドラーはロックしたものを保持し続けます。リクエストがセッションの中にいるとは、そのリクエストがセッションを開いたか、セッションが開いている間に受け入れられたことを指し、session_start() を呼び出すかどうかは関係ありません。そのため、その期間に受け入れられた長寿命のストリームは、ストリームが続く限りセッションを開いたままにし、その間に受け入れられたリクエストには、セッションを開いたリクエストが戻った後でもそのセッションが引き渡されます。

自分で閉じても、リクエストの終了時に閉じられても、重なり合うリクエストは分離されません。ワーカーは実行中のリクエストが中断するたびに新しいリクエストを受け入れ、中断ポイントで開いたままのセッションは、その間に受け入れられたリクエストに引き渡されます。その時点より前に session_write_close() で閉じられたセッションは ID がインストールされたまま残り、その間に受け入れられたリクエストが自身の session_start() でそれを引き継ぎます。ワーカーモードを参照してください。

エージェント向けプロンプト

これらのプロンプトは、OxPHP ドキュメント MCP サーバー を接続したエージェントを前提としています — 必要なページはエージェントが自分で読み取ります。

prompt
私のコードをスーパーグローバルから OxPHP のリクエスト API へ移してください。OxPHP ドキュメント MCP サーバー経由で `php/request-api` ページを読んでください。 このリポジトリの中でリクエストデータを直接読んでいる箇所を洗い出し、そのうち書き方だけでなく挙動が変わるものについて、書き換え後のコードを見せてください。

関連項目

  • スーパーグローバル — OxPHP が $_SERVER、$_GET、$_POST、$_COOKIE、$_FILES をどのように設定するか
  • PHP 関数 — oxphp_http_request()、oxphp_superglobals_enabled()、その他すべての組み込み関数の完全なリファレンス
  • ワーカーモード — 永続的な PHP ワーカーとリクエストのライフサイクル
  • 設定リファレンス — SUPERGLOBALS_ENABLED とその他の環境変数