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的坑,是我本身踩坑,原谅我是个标题党

相关文章
相关标签/搜索