这两天遇到了一个很奇怪的问题,更新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的坑,是我本身踩坑,原谅我是个标题党