Laravel5.3之Session源碼解析(下)

說明:在中篇中學習了session的CRUD增刪改查操做,本篇主要學習關閉session的相關源碼。實際上,在Laravel5.3中關閉session主要包括兩個過程:保存當前URL到session介質中;在Response Header中存入cookie。其中,Laravel5.3把垃圾回收提早到了中間件的前置操做,中篇有聊到。OK,學習下關閉session的源碼吧先。php

開發環境:Laravel5.3 + PHP7laravel

關閉Session

首先看下\Illuminate\Session\Middleware\StartSession::class中間件源碼的handle()方法:ajax

public function handle($request, Closure $next)
    {
        ...
    
        $response = $next($request);

        // 檢查config/session.php中'driver'是否設置,這裏已經假設是redis做爲存儲介質
        if ($this->sessionConfigured()) {
            // 存儲當前URL
            $this->storeCurrentUrl($request, $session);
            // 往Response Header中添加cookie
            $this->addCookieToResponse($response, $session);
        }

        return $response;
    }
    
    protected function sessionConfigured()
    {
        return ! is_null(Arr::get($this->manager->getSessionConfig(), 'driver'));
    }

從源碼中可知關閉session作了兩件事:存儲當前URL;往Response Header中添加cookie。redis

OK,先看第一件事:瀏覽器

// \Illuminate\Session\Middleware\StartSession
    protected function storeCurrentUrl(Request $request, $session)
    {
        // 若是是GET,而且不是ajax,且route對象不能爲空
        if ($request->method() === 'GET' && $request->route() && ! $request->ajax()) {
            $session->setPreviousUrl($request->fullUrl());
        }
    }
    
    public function setPreviousUrl($url)
    {
        // 使用中篇聊到的put()方法更新式存儲$url,
        // 如sentry.app:8888/session,存入到redis中的'laravel:_previous.url'
        $this->put('_previous.url', $url);
    }

因此第一件事很簡單,OK,看下第二件事:cookie

protected function addCookieToResponse(Response $response, SessionInterface $session)
    {
        // No, we use redis as a session handler.
        if ($this->usingCookieSessions()) {
            $this->manager->driver()->save();
        }

        // Yes, use redis as the persistent store bucket.
        if ($this->sessionIsPersistent($config = $this->manager->getSessionConfig())) {
            $response->headers->setCookie(new Cookie(
                // 'laravel_session'
                $session->getName(),
                // Str::random(40)
                $session->getId(),
                // If it is not set to expire when the browser close. And after 60 minutes, the session will close.
                $this->getCookieExpirationDate(),
                // '/session'
                $config['path'],
                // 'session_domain'
                $config['domain'],
                // true
                Arr::get($config, 'secure', false),
                // true
                Arr::get($config, 'http_only', true)
            ));
        }
    }
    
    // 檢查是否是cookie存儲做爲handler,這裏是使用redis做爲handler
    protected function usingCookieSessions()
    {
        if (! $this->sessionConfigured()) {
            return false;
        }

        return $this->manager->driver()->getHandler() instanceof CookieSessionHandler;
    }
    
    // 檢查是否是永久存儲,array不是永久存儲,這裏使用redis是永久存儲
    protected function sessionIsPersistent(array $config = null)
    {
        $config = $config ?: $this->manager->getSessionConfig();

        return ! in_array($config['driver'], [null, 'array']);
    }

第二件事也很簡單,實例化Symfony\Component\HttpFoundation\Cookie,並存入到response header中。其中,實例化Cookie所須要的各個參數值爲:session

(1) $session->getName()app

// $session就是\Illuminate\Session\Store對象

// 在實例化Store對象時,傳入的name值是讀取的app['config']['session.cookie']
// 見 \Illuminate\Session\SessionManager::buildSession() line 178
'laravel_session' = $session->getName();

(2) $session->getId()dom

// 在實例化Store時,傳入的$id=null,則在Store構造函數中使用setId()設置$id值
//看下Store::setId()源碼就知道id是隨機生成的長度爲40的字符串
Str::random(40) = $session->getId();

    public function setId($id)
    {
        if (! $this->isValidId($id)) {
            $id = $this->generateSessionId();
        }

        $this->id = $id;
    }
    public function isValidId($id)
    {
        return is_string($id) && ctype_alnum($id) && strlen($id) === 40;
    }
    protected function generateSessionId()
    {
        return Str::random(40);
    }

(3) $this->getCookieExpirationDate()函數

// config/session.php中默認expire_on_close = false, lifetime = 60
    // 表示若是瀏覽器關閉session不過時,則保留60分鐘後再過時
    protected function getCookieExpirationDate()
    {
        $config = $this->manager->getSessionConfig();

        return $config['expire_on_close'] ? 0 : Carbon::now()->addMinutes($config['lifetime']);
    }

(4) $config['path']

// 默認是'/',這是設置'/session',等會看下響應頭
'/session' = $config['path']

(5) $config['domain']

// 這裏在config/session.php中設置成'session_domain',等會看下響應頭
'session_domain' = $config['domain']

(6) Arr::get($config, 'secure', false)

// 就默認值false
false = Arr::get($config, 'secure', false)

(7) Arr::get($config, 'http_only', true)

// 就默認值true
true = Arr::get($config, 'http_only', true)

這裏輸入路由sentry.app:8888/session(在本地環境配置你的路由)簡單輸出個字符串'session',主要看下響應頭是否是設置了配置的cookie值:

看下響應頭設置了'laravel_session' cookie,而且'path','domain'是剛剛在session.php中設置的'/session','session_domain'值。總之,Laravel關閉session的第二件事就是給Response Header添加'laravel_session' cookie。

經過對Laravel Session的源碼分析可看出Session共分爲三大步:啓動Session;操做Session;關閉Session。啓動Session包括Store實例化,從存儲介質中如redis讀取session數據,和垃圾回收;操做Session包括對Session的CRUD增刪改查操做;關閉Session包括存儲當前的URL和往Response Header添加Cookie。

總結:本小系列主要學習了Laravel Session的源碼,學習了Session的三大步。後續有好的技術再分享吧,到時見。

相關文章
相關標籤/搜索