Laravel Session 遇到的坑

這兩天遇到了一個很奇怪的問題,更新sessionsession的值不變。通過一番追查,終於找到問題,並搞明白了原理。寫這篇博客記錄下。php

框架版本

Laravel 5.4web

問題

先來描述下問題,我在咱們項目基礎的Middleware中,加入session操做,存入了一個值,再在Controller中取出使用,大體代碼以下:redis

// Middleware
public function handle($request, Closure $next)
{
    $id = Redis::get('id');
    session(['id' => $id]);
    return $next($request);
}

// Controller
public function index()
{
    $id = session('id');
    return ['id' => $id];
}

假設reids中的id是1,這一次訪問index這個action,返回的是1,當你將redisid的值改爲2時,在訪問,發現返回的仍是1,並且以後的訪問也都是1。這裏說明一下session使用的是redis緩存

解決問題

看到這樣神奇的結果,百思不得其解。因而打開Xdebug,開始調試。通過屢次調試,發如今執行完
\Illuminate\Session\Middleware\StartSession這個Middleware後,session裏面的值就變回1了,在以前都是2。而後想到會不會咱們的MiddlewareStartSession以前執行形成的,將咱們的Middleware移到StartSession以後,發現果真能夠了,app/Http/Kernel.php中的代碼以下:session

protected $middlewareGroups = [
    'web' => [
        \Illuminate\Cookie\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \App\Http\Middleware\OurMiddleware::class,
    ]
];

其中的OurMiddleware是咱們本身寫的Middleware,以前是放在最上面的,$next($request)以前的代碼的執行順序是從上到下的,若是OurMiddleware中有些內容是必須在最開始的,能夠考慮分紅兩個Middlewareapp

理解原理

雖然解決了問題,但仍是不知道其原理到底是怎樣的,帶着這樣的疑問我繼續查看源碼,最終找到了相應的內容。框架

  • session不是實時落地的,也就是說當你調用session(['id' => $id])時,id並無被真正存入redis中,而是緩存在 \Illuminate\Session\Store單例的attributes屬性中,能夠查看其put方法,代碼以下:
    ```php
    public function put($key, $value = null)
    {
    if (! is_array($key)) {
    $key = [$key => $value];
    }this

    foreach ($key as $arrayKey => $arrayValue) {
          Arr::set($this->attributes, $arrayKey, $arrayValue);
      }

    }
    ```debug

  • \Illuminate\Session\Middleware\StartSession在執行時,回自動加載redis中已經實例化的數據,並覆蓋\Illuminate\Session\Store單例中的attributes屬性,因此這就致使咱們一直取到的都是redis中的session數據。加載覆蓋的代碼以下:調試

    protected function loadSession()
    {
        $this->attributes = array_merge($this->attributes, $this->readFromHandler());
    }
    protected function readFromHandler()
    {
        if ($data = $this->handler->read($this->getId())) {
            $data = @unserialize($this->prepareForUnserialize($data));
    
            if ($data !== false && ! is_null($data) && is_array($data)) {
                return $data;
            }
        }
    
        return [];
    }

其中的readFromHandler方法就是獲取redis中的session數據。

後記

其實這不是Laravel session的坑,是我本身踩坑,原諒我是個標題黨

相關文章
相關標籤/搜索