セッション
HTTP は状態を持たないプロトコルですが、ほとんどのアプリケーションはリクエストをまたいで状態を保つ必要があります。買い物かごの中身などです。まさにそのためにセッションを使います。ここでは次のことをお見せします。
- セッションの使い方
- 名前の衝突を防ぐ方法
- 有効期限の決め方
セッションを使うと、それぞれの利用者はセッション ID と呼ばれる一意の識別子を受け取り、それがクッキーで運ばれます。これはセッションのデータへの鍵の役目を果たします。ブラウザ側に保存されるクッキーと違って、セッションのデータはサーバー側に保存されます。
セッションは設定で設定します。とりわけ有効期限の時間の選び方が大事です。
セッションの管理は Nette\Http\Sessionオブジェクトが受け持ちます。これは
dependency
injectionで渡してもらえます。プレゼンターでは $session = $this->getSession()
を呼ぶだけです。
セッションを始める
既定では、Nette
はデータを読み書きしはじめた瞬間にセッションを自動的に始めます。セッションを手で始めるには
$session->start() を使います。
PHP はセッションを始めるときに、キャッシュに影響する HTTP のヘッダー(session_cache_limiterをご覧ください)と、場合によってはセッション ID のクッキーを送ります。ですからセッションは、ブラウザへ何かを出力する前に必ず始めなければなりません。そうしないと例外が投げられます。ですからページを描いているあいだにセッションを使うと分かっているなら、たとえばプレゼンターの中で、前もって手で始めてください。
開発モードでは Tracy がセッションを始めます。Tracy Bar でリダイレクトと AJAX のリクエストの帯を表示するのにセッションを使うからです。
区画
素の PHP では、セッションのデータの保管場所は大域変数 $_SESSION
で触れる配列として作られています。困るのは、アプリケーションがふつう多くの独立した部分から成っていて、そのすべてがひとつの配列しか使えないなら、遅かれ早かれ名前の衝突が起きることです。
Nette Framework はこの問題を、空間全体を区画(Nette\Http\SessionSectionのオブジェクト)に分けることで解決します。それぞれの部分は一意の名前を持つ自分の区画を使うので、衝突は起きません。
区画はセッションから取り出します。
$section = $session->getSection('unique name');
プレゼンターでは getSession() にパラメータを渡すだけです。
// $this はプレゼンターです
$section = $this->getSession('unique name');
区画があるかどうかは $session->hasSection('unique name')
メソッドで調べられます。存在するすべての区画の名前の一覧は
$session->getSectionNames() が返します。
区画そのものを扱うのは、set()、get()、remove()
のメソッドでとても簡単です。
// 変数を書き込みます
$section->set('userName', 'john');
// 変数を読み出します。なければ null を返します
echo $section->get('userName');
// 変数を取り除きます
$section->remove('userName');
区画のすべての変数を得るには foreach のループを使えます。
foreach ($section as $key => $val) {
echo "$key = $val";
}
有効期限の決め方
有効期限は区画ごとに、さらには変数ごとに決められます。利用者のログインを 20 分で切らしつつ、買い物かごの中身は覚えておくことができます。
// この区画は 20 分で切れます
$section->setExpiration('20 minutes');
変数ごとに有効期限を決めるには、set() メソッドの第 3 パラメータを使います。
// 変数 'flash' は 30 秒で切れます
$section->set('flash', $message, '30 seconds');
セッション全体の有効期限(セッションの設定をご覧ください)は、区画や変数ごとに決めた時間と同じか、それより長くなければならないことを忘れないでください。
前に決めた有効期限を取り消すには removeExpiration()
メソッドを使います。特定の変数の有効期限を消すには、その名前を渡します。removeExpiration('flash')
のようにです。区画全体をすぐに取り除くには remove() メソッドを使います。
$onStart、$onBeforeWrite のイベント
Nette\Http\Session オブジェクトにはイベント
$onStart と $onBeforeWrite
があるので、セッションが始まったあとや、ディスクに書き込まれて終わる前に呼ばれるコールバックを足せます。
$session->onBeforeWrite[] = function () {
// セッションにデータを書き込みます
$this->section->set('basket', $this->basket);
};
セッションの管理
セッションを管理する Nette\Http\Session クラスのメソッドの一覧です。
start(): void
セッションを始めます。
isStarted(): bool
セッションは始まっていますか。
close(): void
セッションを終わらせます。セッションはスクリプトの実行の終わりに自動的に終わります。
destroy(): void
セッションを終わらせて消します。
exists(): bool
HTTP のリクエストにセッション ID のクッキーが入っていますか。
regenerateId(): void
新しい無作為なセッション ID を生成します。データはそのまま残ります。
getId(): string
セッション ID を返します。
設定
セッションは設定で設定します。DI コンテナを使わないアプリケーションを書いているなら、設定にはこれらのメソッドを使います。セッションを始める前に呼ばなければなりません。
setName (string $name): static
セッション ID を運ぶクッキーの名前を設定します。標準の名前は PHPSESSID
です。同じウェブサイトでいくつかの違うアプリケーションを動かしているときに役立ちます。
getName(): string
セッション ID を運ぶクッキーの名前を返します。
setOptions (array $options): static
セッションを設定します。PHP のすべてのセッションのディレクティブ(camelCase
の形で。たとえば session.save_path は savePath と書きます)と readAndCloseを設定できます。
setExpiration (?string $expire): static
セッションが切れるまでの無操作の時間を設定します。
setCookieParameters (string $path, ?string $domain=null, ?bool $secure=null, SameSite|string|null $samesite=null): static
クッキーのパラメータを設定します。パラメータの既定値は設定で変えられます。
setSavePath (string $path): static
セッションのファイルを保存するディレクトリを設定します。
setHandler (\SessionHandlerInterface $handler): static
独自のハンドラを設定します。PHP のドキュメントをご覧ください。
安全第一
サーバーは、リクエストに同じセッション ID が伴っている限り、同じ利用者とやり取りしていると考えます。安全のしくみの仕事は、それが本当にそのとおりであり、識別子が盗まれたりすり替えられたりしないようにすることです。
ですから Nette Framework は PHP のディレクティブを正しく設定し、セッション ID をクッキーだけで運び、JavaScript から触れないようにし、URL の中の識別子は無視します。さらに利用者のログインのような大事な瞬間には、新しいセッション ID を生成します。
PHP の設定には ini_set
関数を使いますが、残念ながらホスティングによってはその使用を禁じています。あなたのホスティングがそうなら、その関数を使えるようにしてもらうか、少なくともサーバーをきちんと設定してもらえるよう相談してみてください。