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 を投げます。