laravel session 仕組み

Laravel sessionの仕組みを追う

SessionServiceProvider.phpでSessionの仕組みがLaravelで使えるようになる。
ここでは、
1. SessionManagerの登録
2. SessionDriverの登録
3. StartSession::classのDI登録
を行う。

```SessionServiceProvider.php
    public function register()
    {
        $this->registerSessionManager();

        $this->registerSessionDriver();

        $this->app->singleton(StartSession::class, function () {
            return new StartSession($this->app->make(SessionManager::class), function () {
                return $this->app->make(CacheFactory::class);
            });
        });
    }
```

## registerSessionManager
sessionをキーにSessionManagerを登録する。

```
    protected function registerSessionManager()
    {
        $this->app->singleton('session', function ($app) {
            return new SessionManager($app);
        });
    }
```

## registerSessionDriver
session.storeでセッションのドライバーが返るようにする。

```php

    protected function registerSessionDriver()
    {
        $this->app->singleton('session.store', function ($app) {
            // First, we will create the session manager which is responsible for the
            // creation of the various session drivers when they are needed by the
            // application instance, and will resolve them on a lazy load basis.
            return $app->make('session')->driver();
        });
    }
```

## StartSession
StartSessionはミドルウェアでKernel.phpの$middlewareGroupsで指定されている。
StartSessionでセッション処理の起動を行う

```php

    protected $middlewareGroups = [
        'web' => [
            \Illuminate\Session\Middleware\StartSession::class,
            略...
        ],
```

handle関数では以下を行っている。
1. session設定されているか確認し、設定されていないなら次のミドルウェアに処理を進める
2. sessionドライバーを取得
3. ブロックの有無に合わせてセッション管理

```php

    public function handle($request, Closure $next)
    {
        if (! $this->sessionConfigured()) {
            return $next($request);
        }

        $session = $this->getSession($request);

        if ($this->manager->shouldBlock() ||
            ($request->route() instanceof Route && $request->route()->locksFor())) {
            return $this->handleRequestWhileBlocking($request, $session, $next);
        } else {
            return $this->handleStatefulRequest($request, $session, $next);
        }
    }
```

### getSession
getSessionではsessionIdをセットしたドライバーを返す。
`$this->manager->driver()`ではSessionManagerインスタンスのdriver関数を実行する。
driver関数ではconfigのsession.phpのdriverで指定したdriverインスタンスを返す。

```php

    public function getSession(Request $request)
    {
        // $session->getName()はconfigのsession.cookieの値。それを元にcookie値を取得する。
        return tap($this->manager->driver(), function ($session) use ($request) {
            $session->setId($request->cookies->get($session->getName()));
        });
    }
```

driverとしてどのインスタンスが返されるかはSessionManagerをみると分かる。
ドライバー作成時にcreateOOOODriver()関数が実行される。OOOはドライバー名。
SessionManagerには↓のような関数があり、具体的なインスタンスが分かる。

```php

    protected function createFileDriver()
    {
        return $this->createNativeDriver();
    }
```

createNativeDriverではbuildSession関数で暗号するしないの設定に合わせてstoreインスタンスを返します。
ドライバーごとにhandlerインスタンスを作成し、storeインスタンスに渡す。

```php
    protected function createNativeDriver()
    {
        $lifetime = $this->config->get('session.lifetime');

        return $this->buildSession(new FileSessionHandler(
            $this->container->make('files'), $this->config->get('session.files'), $lifetime
        ));
    }
```


## handleStatefulRequest
handleStatefulRequest関数では以下を行う
1. セッションを開始
2. 一定の確率でゴミ掃除
3. 次のmiddlewareを実行
4. 必要ならsessionに現在のurlを設定
5. cookieにsessionを登録

```php

    protected function handleStatefulRequest(Request $request, $session, Closure $next)
    {
        // If a session driver has been configured, we will need to start the session here
        // so that the data is ready for an application. Note that the Laravel sessions
        // do not make use of PHP "native" sessions in any way since they are crappy.
        $request->setLaravelSession(
            $this->startSession($request, $session)
        );

        $this->collectGarbage($session);

        $response = $next($request);

        $this->storeCurrentUrl($request, $session);

        $this->addCookieToResponse($response, $session);

        // Again, if the session has been configured we will need to close out the session
        // so that the attributes may be persisted to some storage medium. We will also
        // add the session identifier cookie to the application response headers now.
        $this->saveSession($request);

        return $response;
    }
```


### startSession
setRequestOnHandlerで必要な場合はハンドラーにリクエストをセットする。(現状CookieSessionHandlerのみ)
start関数では以下を行う
1. sessionをload
2. _tokenがあればtokenを再作成

```php
    protected function startSession(Request $request, $session)
    {
        return tap($session, function ($session) use ($request) {
            $session->setRequestOnHandler($request);

            $session->start();
        });
    }

    public function start()
    {
        $this->loadSession();

        if (! $this->has('_token')) {
            $this->regenerateToken();
        }

        return $this->started = true;
    }
```

### addCookieToResponse
cookieにsessionIdを登録する

```php

    protected function addCookieToResponse(Response $response, Session $session)
    {
        if ($this->sessionIsPersistent($config = $this->manager->getSessionConfig())) {
            $response->headers->setCookie(new Cookie(
                $session->getName(), $session->getId(), $this->getCookieExpirationDate(),
                $config['path'], $config['domain'], $config['secure'] ?? false,
                $config['http_only'] ?? true, false, $config['same_site'] ?? null
            ));
        }
    }
```

試しにLaravelページのcookieをみるとsessionIdが登録されているのが分かる。
![スクリーンショット 2021-01-18 1.38.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278474/962e20f2-a1b1-2f1d-a813-f85383acbb13.png)