HTTP リクエスト

Nette は HTTP のリクエストを、分かりやすい API を持つオブジェクトに包み、あわせて清めのフィルタも用意します。

HTTP のリクエストは Nette\Http\Requestオブジェクトが表します。Nette を使っているなら、このオブジェクトはフレームワークが自動的に作るので、dependency injectionで渡してもらえます。プレゼンターでは $this->getHttpRequest() メソッドを呼ぶだけです。Nette Framework の外で作業しているなら、RequestFactoryでこのオブジェクトを作れます。

Nette の大きな利点は、オブジェクトを作るときにすべての入力のパラメータ(GET、POST、COOKIE)と URL を自動的に清め、制御文字と正しくない UTF-8 の並びを取り除くことです。そのあとはそのデータを安全に扱えます。清められたデータはそのあとプレゼンターやフォームで使われます。

インストールと要件

Nette\Http\Request

このオブジェクトは変更できません。セッターはなく、いわゆる wither である withUrl() だけがあります。これはオブジェクトを変えずに、値を変えた新しいインスタンスを返します。

withUrl (Nette\Http\UrlScript $url): Nette\Http\Request

URL を変えた複製を返します。

getUrl(): Nette\Http\UrlScript

リクエストの URL を UrlScriptオブジェクトとして返します。

$url = $httpRequest->getUrl();
echo $url; // https://nette.org/en/documentation?action=edit
echo $url->getHost(); // nette.org

注意: ブラウザはフラグメントをサーバーへ送らないので、$url->getFragment() は空の文字列を返します。

getQuery (?string $key=null): string|array|null

GET のリクエストのパラメータを返します。

$all = $httpRequest->getQuery();    // URL のすべてのパラメータの配列
$id = $httpRequest->getQuery('id'); // GET のパラメータ 'id' を返します(なければ null)

getPost (?string $key=null): string|array|null

POST のリクエストのパラメータを返します。

$all = $httpRequest->getPost();     // すべての POST のパラメータの配列
$id = $httpRequest->getPost('id');  // POST のパラメータ 'id' を返します(なければ null)

getFile (string|string[] $key): ?Nette\Http\FileUpload

アップロードNette\Http\FileUploadオブジェクトとして返します。

$file = $httpRequest->getFile('avatar');
if ($file?->hasFile()) { // ファイルはアップロードされましたか
	$file->getUntrustedName(); // 利用者が送ったファイル名
	$file->getSanitizedName(); // 危険な文字を取り除いた名前
}

入れ子の構造にアクセスするには、キーの配列を渡します。

// <input type="file" name="my-form[details][avatar]">
$file = $request->getFile(['my-form', 'details', 'avatar']);

外から来るデータは信じられず、したがってファイルの構造にも頼れないので、このやり方はたとえば失敗しかねない $request->getFiles()['my-form']['details']['avatar'] より安全です。

getFiles(): array

すべてのアップロードを、葉が Nette\Http\FileUploadオブジェクトになる整えられた構造の木として返します。

$files = $httpRequest->getFiles();

getCookie (string $key): ?string

クッキーを返します。なければ null を返します。

$sessId = $httpRequest->getCookie('sess_id');

getCookies(): array

すべてのクッキーを返します。

$cookies = $httpRequest->getCookies();

getMethod(): string

そのリクエストで使われた HTTP のメソッドを返します。

$httpRequest->getMethod(); // GET, POST, HEAD, PUT

isMethod (string $method)bool

そのリクエストで使われた HTTP のメソッドを調べます。パラメータは大文字と小文字を区別しません。

if ($httpRequest->isMethod('GET')) // ...

getHeader (string $header): ?string

HTTP のヘッダーを返します。なければ null を返します。パラメータは大文字と小文字を区別しません。

$userAgent = $httpRequest->getHeader('User-Agent');

getHeaders(): array<string, string>

すべての HTTP のヘッダーを連想配列として返します。キーは小文字にそろえられます。

$headers = $httpRequest->getHeaders();
echo $headers['content-type'];

isSecured(): bool

接続は暗号化されていますか(HTTPS)。正しく働くにはプロキシの設定が要ることがあります。

isSameSite(): bool

リクエストは同じサイトから来ましたか。バージョン 3.4 からは、より力のある isFrom()に取って代わられました。

isFrom (FetchSite|array $site, FetchDest|array|null $dest=null, ?bool $user=null)bool

リクエストがどこから来て、ブラウザがそれをどう行ったかを教えてくれます。もとにするのは Sec-Fetch-* のヘッダー(いわゆる Fetch Metadata)で、これはブラウザ自身が設定するもので、被害者のブラウザで動くページには偽ることも取り除くこともできません。Nette はこれを内部で使い、フォームとシグナルをクロスサイトリクエストフォージェリ(CSRF)から自動的に守っています。API のエンドポイントや破壊的なリンクのような、あなた自身の機微な操作を守りたいときに役立ちます。

このメソッドが true を返すのは、リクエストがあなたの渡した条件をすべて満たすときだけです。第 1 パラメータ $site は、リクエストを起こしたページとあなたのサイトの関係(Sec-Fetch-Site のヘッダー)を表します。ひとつの値も、次の FetchSite の値の並びも受け取ります。

  • FetchSite::SameOrigin – まったく同じオリジンから(スキーム、ホスト、ポート)
  • FetchSite::SameSite – 同じサイトから。サブドメインは違ってもかまいません
  • FetchSite::CrossSite – よそのサイトから
  • FetchSite::None – 利用者が直接起こしました。たとえば URL を打ち込んだか、ブックマークを開きました
// リクエストは私たち自身のページから来ましたか
if (!$httpRequest->isFrom([FetchSite::SameOrigin, FetchSite::SameSite])) {
	// その操作を止めます
}

省略できる $dest パラメータ(Sec-Fetch-Dest のヘッダー)は、ブラウザがどんな種類の資源を取りに来たかを伝えます。たとえば最上位の移動なら FetchDest::Document、JavaScript から行われたリクエストなら FetchDest::Empty です。省略できる $user パラメータ(Sec-Fetch-User のヘッダー)は、その移動がリンクのクリックやフォームの送信のような本物の利用者の操作で起きたかを示します。それを求めるなら true を渡します。

ある操作が自分のページからだけ、しかも本物の利用者の操作でだけたどり着けることを確かめるなら、次のようになります。

if (!$httpRequest->isFrom(FetchSite::SameOrigin, FetchDest::Document, user: true)) {
	$this->error();
}

古いブラウザ(16.4 より前の Safari)は Sec-Fetch-* のヘッダーを送りません。それらには Nette が SameSite=Strict のクッキーで代わりを務めますが、これはリクエストが別サイトからでないことしか証明できません。さらに $dest$user を求める確認はこの方法では確かめられず、そうしたブラウザでは false を返します。それが厳しすぎるなら $site だけを調べてください。

isAjax(): bool

AJAX のリクエストですか。

getRemoteAddress(): ?string

利用者の IP アドレスを返します。正しく働くにはプロキシの設定が要ることがあります。

getRemoteHost(): ?string

非推奨で、いつも null を返します。逆引き DNS の問い合わせは遅く、あてにならないものでした。ホスト名が必要なら、getRemoteAddress()から自分で引いてください。

getBasicCredentials(): ?array

HTTP Basic 認証の資格情報を返します。

[$user, $password] = $httpRequest->getBasicCredentials();

getRawBody(): ?string

HTTP のリクエストの本文を返します。

$body = $httpRequest->getRawBody();

getOrigin(): ?UrlImmutable

リクエストが来たオリジンを返します。オリジンはスキーム(プロトコル)、ホスト名、ポートから成ります。たとえば https://example.com:8080 です。origin のヘッダーがないか 'null' に設定されている場合は null を返します。

$origin = $httpRequest->getOrigin();
echo $origin; // https://example.com:8080
echo $origin?->getHost(); // example.com

ブラウザが Origin のヘッダーを送るのは次の場合です。

  • 別オリジンのリクエスト(別のドメインへの AJAX の呼び出し)
  • POST、PUT、DELETE などの変更を伴うリクエスト
  • Fetch API を使って行われたリクエスト

ブラウザが Origin のヘッダーを送らないのは次の場合です。

  • 同じドメインへのふつうの GET のリクエスト(同一オリジンの移動)
  • アドレス欄に URL を打ち込む直接の移動
  • ブラウザ以外のクライアントからのリクエスト

Referer のヘッダーと違って、Origin にはスキーム、ホスト、ポートだけが入り、URL のパス全体は入りません。おかげで利用者の私生活を守りつつ、安全の確認に向いています。Origin のヘッダーは主に CORS(Cross-Origin Resource Sharing)の検証に使われます。

detectLanguage (array $langs): ?string

言語を見分けます。$langs パラメータにアプリケーションが対応している言語の配列を渡すと、訪問者のブラウザが好むものを返します。魔法ではなく、Accept-Language のヘッダーを使っているだけです。合うものがなければ null を返します。

// ブラウザはたとえば Accept-Language: cs,en-us;q=0.8,en;q=0.5,sl;q=0.3 を送ります

$langs = ['hu', 'pl', 'en']; // アプリケーションが対応している言語
echo $httpRequest->detectLanguage($langs); // en

RequestFactory

Nette\Http\RequestFactoryクラスは、今の HTTP のリクエストを表す Nette\Http\Request のインスタンスを作るのに使います。(Nette を使っているなら、HTTP のリクエストのオブジェクトはフレームワークが自動的に作ります。)

$factory = new Nette\Http\RequestFactory;
$httpRequest = $factory->fromGlobals();

fromGlobals() メソッドは、今の PHP の大域変数($_GET$_POST$_COOKIE$_FILES$_SERVER)をもとにリクエストのオブジェクトを作ります。オブジェクトを作るときにすべての入力のパラメータ(GET、POST、COOKIE)と URL から、制御文字と正しくない UTF-8 の並びを自動的に取り除くので、そのあとそのデータを扱うときに安全です。

RequestFactory は fromGlobals() を呼ぶ前に設定できます。

  • $factory->setBinary() メソッドを使うと、入力のパラメータから制御文字と正しくない UTF-8 の並びを自動的に取り除く働きを止められます。
  • $factory->setProxy(...) メソッドでプロキシのサーバーの IP アドレスを指定します。これは利用者の IP アドレスを正しく判別するのに必要です。
  • $factory->setForceHttps() .{data-version:3.3.4} メソッドは、サーバーの環境に関わらずリクエストのスキームを HTTPS にします。

RequestFactory では、URL のリクエストの一部を自動的に変えるフィルタも定義できます。これらのフィルタは、たとえばさまざまなウェブサイトのコメントの仕組みの誤った実装によって差し込まれたかもしれない、望まない文字を URL から取り除きます。

// パスから空白を取り除きます
$requestFactory->urlFilters['path']['%20'] = '';

// URI の終わりからドット、コンマ、閉じかっこを取り除きます
$requestFactory->urlFilters['url']['[.,)]$'] = '';

// パスから二重のスラッシュを取り除きます(既定のフィルタ)
$requestFactory->urlFilters['path']['/{2,}'] = '/';

最初のキー 'path''url' は、そのフィルタを URL のどの部分に当てるかを決めます。2 つめのキーは探すための正規表現で、値は見つかった文の代わりに使われる置き換えです。

アップロードされたファイル

Nette\Http\Request::getFiles() メソッドは、すべてのアップロードを、葉が Nette\Http\FileUploadオブジェクトになる整えられた構造の配列として返します。これらは <input type=file> のフォームの要素で送られたデータを包みます。

その構造は HTML の要素の名前の付け方を映します。もっとも単純な場合、ひとつの名前の付いたフォームの要素が次のように送られます。

<input type="file" name="avatar">

この場合、$request->getFiles() は次の配列を返します。

[
	'avatar' => /* FileUpload のインスタンス */
]

FileUpload オブジェクトは、利用者がファイルをアップロードしなかった場合やアップロードが失敗した場合にも作られます。ファイルが送られたなら hasFile() メソッドが true を返します。

$request->getFile('avatar')?->hasFile();

要素の名前に配列の書き方を使った場合は、

<input type="file" name="my-form[details][avatar]">

返される木は次のようになります。

[
	'my-form' => [
		'details' => [
			'avatar' => /* FileUpload のインスタンス */
		],
	],
]

ファイルの配列も作れます。

<input type="file" name="my-form[details][avatars][]" multiple>

その場合の構造は次のようになります。

[
	'my-form' => [
		'details' => [
			'avatars' => [
				0 => /* FileUpload のインスタンス */,
				1 => /* FileUpload のインスタンス */,
				2 => /* FileUpload のインスタンス */,
			],
		],
	],
]

入れ子の配列の添字 1 にアクセスするいちばんよい方法は次のとおりです。

$file = $request->getFile(['my-form', 'details', 'avatars', 1]);
if ($file instanceof Nette\Http\FileUpload) {
	// ...
}

外から来るデータは信じられず、したがってファイルの構造にも頼れないので、このやり方はたとえば失敗しかねない $request->getFiles()['my-form']['details']['avatars'][1] より安全です。

FileUpload のメソッドの一覧

hasFile(): bool

利用者がファイルをアップロードしたなら true を返します。

isOk(): bool

ファイルのアップロードが成功したなら true を返します。

getError(): int

アップロードされたファイルにまつわるエラーのコードを返します。これは UPLOAD_ERR_XXXの定数のどれかです。アップロードが成功したなら UPLOAD_ERR_OK を返します。

move (string $dest)

アップロードされたファイルを新しい場所へ移します。移し先のファイルがすでにあれば上書きされます。

$file->move('/path/to/files/name.ext');

getContents(): ?string

アップロードされたファイルの中身を返します。アップロードが成功しなかった場合は null を返します。

getContentType(): ?string

アップロードされたファイルの MIME の内容の型を、その署名から見分けます。アップロードが成功しなかったか、見分けに失敗した場合は null を返します。

PHP の fileinfo 拡張が要ります。

getUntrustedName(): string

ブラウザが送ってきたもとのファイル名を返します。

このメソッドが返す値を信じないでください。クライアントは、あなたのアプリケーションを壊したり乗っ取ったりするつもりで、悪意のあるファイル名を送っているかもしれません。

getSanitizedName(): string

清められたファイル名を返します。ASCII の文字 [a-zA-Z0-9.-] だけを含みます。名前にそうした文字が入っていなければ 'unknown' を返します。ファイルが JPEG、PNG、GIF、WebP、AVIF の画像なら、正しい拡張子も付けて返します。

PHP の fileinfo 拡張が要ります。

getSuggestedExtension(): ?string

見分けられた MIME の型に対応する、ふさわしいファイルの拡張子(ドットなし)を返します。

PHP の fileinfo 拡張が要ります。

getUntrustedFullPath(): string

ディレクトリのアップロードのときにブラウザが送ってきたもとのファイルのパスを返します。完全なパスが得られるのは PHP 8.1 以降だけです。それより前のバージョンでは、このメソッドはもとのファイル名を返します。

このメソッドが返す値を信じないでください。クライアントは、あなたのアプリケーションを壊したり乗っ取ったりするつもりで、悪意のあるファイル名を送っているかもしれません。

getSize(): int

アップロードされたファイルの大きさを返します。アップロードが成功しなかった場合は 0 を返します。

getTemporaryFile(): string

アップロードされたファイルの一時的な置き場所へのパスを返します。アップロードが成功しなかった場合は '' を返します。

__toString(): string

アップロードされたファイルの一時的な置き場所へのパスを返します。おかげで FileUpload オブジェクトをそのまま文字列として使えます。

isImage(): bool

アップロードされたファイルが JPEG、PNG、GIF、WebP、AVIF の画像なら true を返します。判別はその署名をもとに行われ、ファイル全体の健全さは確かめません。画像が壊れているかどうかは、たとえば読み込んでみることで判断できます。

PHP の fileinfo 拡張が要ります。

getImageSize(): ?array

アップロードされた画像の寸法の組 [幅, 高さ] を返します。アップロードが成功しなかったか、正しい画像でない場合は null を返します。

toImage(): Nette\Utils\Image

画像を Imageオブジェクトとして読み込みます。アップロードが成功しなかったか、正しい画像でない場合は Nette\Utils\ImageException を投げます。

バージョン: 4.x