セッション

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_pathsavePath と書きます)と 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 関数を使いますが、残念ながらホスティングによってはその使用を禁じています。あなたのホスティングがそうなら、その関数を使えるようにしてもらうか、少なくともサーバーをきちんと設定してもらえるよう相談してみてください。

バージョン: 4.x