Laravelがセッションを作成してから保存するまでの流れ
はじめに
Laravelのセッションが作成されてから保存されるまでの過程を確認した時のメモです。
フレームワークの中を少し覗いてLaravelと仲良く(なった気に)なるシリーズです。
シリーズ
- Laravelがセッションを作成してから保存するまでの流れ
- Laravelのログイン/ログアウト時のセッションの流れ
- Laravelでリクエストが認証済みか判定する流れ
諸々
- 環境
- laravel 12
- 参考
ミドルウェアの確認
セッションの確認・判定・新規作成は主に\Illuminate\Session\Middleware\StartSession::classを起点に行われるため流れを見ていきます。
1.セッションマネージャーを作成
まずは__constructで、セッションマネージャーのインスタンスが作成されます。
2.handleメソッドが実行
handleメソッドが自動で実行されます。
3.セッションインスタンスを作成
まずはgetSessionメソッドの$this->manager->driver()でプロジェクトで指定したセッションドライバーのセッションインスタンスを作成しています。
driver()の補足
driver()メソッドは/Illuminate/Session/SessionManager.phpの継承元のIlluminate\Support\Managerのメソッドなので中身を確認。
$this->getDefaultDriver()で継承先のSessionManagerに移譲しているので確認。
getDefaultDriver()では開発者側で設定できるconfig/session.phpのdriverの設定の文字列を見ています。
Illuminate\Support\Managerの$driverメソッドにはドライバーの文字列がセットされ、最終的にdriver()メソッドは設定したドライバーのインスタンス(/Illuminate/Session/Store.php)を返します。
(再掲)
4.セッションインスタンスのidプロパティをセット
getSession()に戻ります。
前のセクションで取得したセッションインスタンスはIlluminate\Session\Store型です。
(Illuminate\Contracts\Session\Sessionインターフェースを実装しているため、getSession()の戻り値のアノテーションの記載はIlluminate\Contracts\Session\Sessionになっている)
$session変数はセッションインスタンスのため、StoreクラスのsetId()メソッドを実行して自身のセッションインスタンスのidプロパティにセッションIDをセットしている。
(再掲)
※Laravelのヘルパ関数であるtap関数は第一引数を返すので、そのままセッションインスタンスを返します。
setId()の補足
引数で取っている$request->cookies->get($session->getName())に値がある場合、つまりブラウザからCookieで送信されたセッションIDのキーに値がある場合はその値をセットします。
nullの場合だったとしてもgenerateSessionId()メソッドで、ここで40文字のセッションIDが作成されてセットされます。
現状の整理
続いてhandele()メソッド最後のreturn $this->handleStatefulRequest($request, $session, $next);でリクエストをステートフルになる工程を確認していきます。
以降の工程は基本的にはhandleStatefulRequestメソッド内で実行される処理です。
(再掲)
5.既存のセッションデータを取得する
$sessionのidプロパティにはクッキーで送られてきたセッションIDか新規作成したセッションIDがセットされていることがわかりました。
続いて、$this->startSession($request, $session)の処理ではドライバー(DBのsessionsテーブルなど)からセッションデータを取得し、セッションインスタンスのattributesプロパティに上書きしてセットしています。
readFromHandler()の補足[⭐️重要]
loadSession()メソッド内のreadFromHandler()メソッドのif ($data = $this->handler->read($this->getId()))のreadメソッドでは、sessionsテーブルのpayloadカラムの値を取得しattributesプロパティにセットしています。
ただし、有効期限切れ、もしくはsessionsテーブルに検索対象のセッションIDが見つからない場合は''空文字が返る。
(有効期限の判定基準はlast_activityカラムが「現在時刻 - セッション有効期間(分)」よりも古いかどうか)
start()の補足
- 取得して
attributesプロパティにセットされたセッションデータに_tokenというキーがなければ新しくトークンを生成(CSRF対策などセキュリティ)。 - セッションが開始されたことを示すフラグを
trueにする。
6.リクエストインスタンスにセッションデータがセットされる
5の工程のstartSession()の戻り値のセッションインスタンスを、Requestインスタンス(/Illuminate/Http/Request.php)のsessionプロパティにセットします。
7.不要なセッションを削除
$this->collectGarbage($session);で毎回ではなく一定確率で期限切れセッションを削除して掃除される。
8.現在のURLをセッションデータに保存
$this->storeCurrentUrl($request, $session);で実施されている。
リダイレクト後の戻り先などに利用されます。
9.リクエスト処理を実行
$nextで「次のミドルウェアやコントローラーの処理が実行」されます。
($responseにはリクエスト処理を終えた結果が入ってきます)
10.セッションを保存する
ここまでで、リクエストを受け取ってからセッションインスタンスを作成し、idをセット、既存のセッションデータがあればその値でセッションデータを上書き、現在のURLもセットし、リクエストの処理(Controller/Action)が実行までされてきましたが、まだDBに更新(または新規保存)していないので、この時点で$this->saveSession($request);の実行で保存する。
セッションID($this->getId())と既存のセッションデータのpayload($this->attributes)を引数に渡してwrite()メソッドを実行している。
write()の補足[⭐️重要]
以下がメソッドです。もう少し詳しく見ていきます。
getDefaultPayload()
$payloadはDBに保存する用の連想配列データです。
そのため後続の処理では$payloadのキーと値を、sessionsテーブルの構造に揃う形式にしていきます。
sessionsテーブルのカラム
| 説明 | カラム |
|---|---|
| セッションID | id |
| ユーザーID | user_id |
| IPアドレス | ip_address |
| ユーザーエージェント | user_agent |
| セッションデータ | payload |
| タイムスタンプ | last_activity |
$payload連想配列の中身
| キー | 呼び出し | 詳細 |
|---|---|---|
| id | 引数($sessionId) | $sessionId |
| user_id | $this->userId() | $this->container->make(Guard::class)->id() |
| ip_address | $this->ipAddress() | $this->container->make('request')->ip() |
| user_agent | $this->userAgent() | substr((string) $this->container->make('request')->header('User-Agent'), 0, 500) |
| payload | 引数($data) | base64_encode($data) |
| last_activity | $this->currentTime() | Carbon::now()->getTimestamp() |
※payloadはDB文脈だとセッションデータを入れるカラムを指しますが、ここのアプリのコードの文脈だとDBに保存する連想配列データというもっと大きな括りであることに注意。
(アプリのコードの文脈での$payloadのpayloadキーにセットする値の変数名は$dataであり、中身はStoreクラスのインスタンスの$this->attributesの値)
performUpdate() または performInsert()
すでにセッションが存在している場合は更新し、そうでない場合は新規作成する。
シリーズ
- Laravelがセッションを作成してから保存するまでの流れ
- Laravelのログイン/ログアウト時のセッションの流れ
- Laravelでリクエストが認証済みか判定する流れ
Discussion