這兩天遇到了一個很奇怪的問題,更新session
,session
的值不變。通過一番追查,終於找到問題,並搞明白了原理。寫這篇博客記錄下。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,當你將redis
中id
的值改爲2時,在訪問,發現返回的仍是1,並且以後的訪問也都是1。這裏說明一下session
使用的是redis
。緩存
看到這樣神奇的結果,百思不得其解。因而打開Xdebug
,開始調試。通過屢次調試,發如今執行完
\Illuminate\Session\Middleware\StartSession
這個Middleware
後,session
裏面的值就變回1了,在以前都是2。而後想到會不會咱們的Middleware
在StartSession
以前執行形成的,將咱們的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
中有些內容是必須在最開始的,能夠考慮分紅兩個Middleware
。app
雖然解決了問題,但仍是不知道其原理到底是怎樣的,帶着這樣的疑問我繼續查看源碼,最終找到了相應的內容。框架
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的坑,是我本身踩坑,原諒我是個標題黨