Laravelがセッションを作成してから保存するまでの流れ
はじめに
Laravelのセッションが作成されてから保存されるまでの過程を確認した時のメモです。
(忘れたときに見返す用なのでざっくりです)
シリーズ
- Laravelがセッションを作成してから保存するまでの流れ
- Laravelのログイン/ログアウト時のセッションの流れ
- Laravelでリクエストが認証済みか判定する流れ
諸々
- 環境
- laravel 12
- 参考
ミドルウェアの確認
セッションの確認・判定・新規作成は主に\Illuminate\Session\Middleware\StartSession::class
を起点に行われるため流れを見ていきます。
1.セッションマネージャーを作成
まずは__construct
で、セッションマネージャーのインスタンスが作成されます。
handle
メソッドが実行
2.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
)を返します。
(再掲)
idプロパティ
をセット
4.セッションインスタンスの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