资讯专栏INFORMATION COLUMN

Laravel学习笔记之Session源码解析(中)

longshengwang / 2571人阅读

摘要:说明在上篇中学习了的启动过程,主要分为两步,一是的实例化,即的实例化二是从存储介质中读取的数据。第二步就是操作,包括对数据的增删改查操作,本文也主要聊下相关操作源码。下篇再学习下关闭,到时见。

说明:在上篇中学习了session的启动过程,主要分为两步,一是session的实例化,即IlluminateSessionStore的实例化;二是从session存储介质redis中读取id = laravel_session*的数据。Laravel5.3把session垃圾回收放在了启动过程中,尽管Laravel5.1是放在session关闭过程的,本篇聊下垃圾回收,这也是session第一步启动session的过程。session第二步就是操作session,包括对session数据的CRUD增删改查操作,本文也主要聊下相关操作源码。

开发环境:Laravel5.3 + PHP7

Session垃圾回收

首先看下session中间件的源码IlluminateSessionMiddlewareStartSession::class

    public function handle($request, Closure $next)
    {
        $this->sessionHandled = true;

        if ($this->sessionConfigured()) {
            $session = $this->startSession($request);

            // 把session对象存储到Request中
            // 所以可以在控制器Controller中使用Request实例获取session对象:$request->session()
            $request->setSession($session);

            $this->collectGarbage($session);
        }   
        
        ...
        
     }  
     
     protected function collectGarbage(SessionInterface $session)
    {
        // 读取config/session.php中的配置
        $config = $this->manager->getSessionConfig();

        if ($this->configHitsLottery($config)) {
            // CacheBasedSessionHandler::gc(60) 60 minutes
            $session->getHandler()->gc($this->getSessionLifetimeInSeconds());
        }
    } 
    
    protected function configHitsLottery(array $config)
    {
        // session.php中"lottery"默认配置是[2, 100],这里就是取概率2/100 = 2%
        // 也就是100次请求有2次会触发过期session的垃圾回收
        return random_int(1, $config["lottery"][1]) <= $config["lottery"][0];
    } 

这里假设session的存储介质是常用的redis,则$session->getHandler()返回的就是IlluminateSessionCacheBasedSessionHandler实例,该handler就是负责从redis这个存储介质中CRUD数据,OK,看下该handler的gc()源码:

    public function gc($lifetime)
    {
        return true;
    }

其实什么都没做。这是当然的,redis对于过期的key会自动清除,所以这里就让redis来负责垃圾回收过期数据。当然,对于database这种handler,可以看下它的垃圾回收IlluminateSessionDatabaseSessionHandler:

    public function gc($lifetime)
    {
        $this->getQuery()->where("last_activity", "<=", time() - $lifetime)->delete();
    }

以数据库作为存储session的介质,垃圾回收就是从sessions表里删除掉对应字段。

操作Session

操作Session就是对从存储介质如redis中取出的数据进行CRUD增删改查操作,包括:数据读取;数据存储;数据删除;数据暂存。当然,在对session进行CRUD操作前,首先得获取session对象即IlluminateSessionStore实例,有三种方法:通过Request实例;通过Session Facade方法;通过helper函数session(),代码如下:

    // 因为在中间件StartSesstion前置操作中有把session实例存入到$request中,$request->setSession($session);
    $session = $request->session(); 
    // 通过Session Facade直接获取到$session对象,并进行CRUD操作
    Session::put("session", "Store"); 
    // 通过helper函数来获取session实例,实际上是通过app("session")从Container中解析出名为"session"的服务即Store实例
    $session = session()->driver(); 

    function session($key = null, $default = null)
    {
        if (is_null($key)) {
            return app("session");
        }

        if (is_array($key)) {
            return app("session")->put($key);
        }

        return app("session")->get($key, $default);
    }
session数据读取

session数据读取方法包括:

    // "Store"是默认数据,读取key为"session:store"的数据
    $value = Session::get("session.store", "Store"); 
    // IlluminateSessionStore
    public function get($name, $default = null)
    {
        return Arr::get($this->attributes, $name, $default);
    }
    
    // "Store"是默认数据,读取key为"session:store"的数据,并删除key为"session"的数据
    $value = Session::pull("session", "Store"); 
    // IlluminateSessionStore
    public function pull($key, $default = null)
    {
        return Arr::pull($this->attributes, $key, $default);
    }
    
    // 返回所有数据
    $value = Session:all();
    public function all()
    {
        return $this->attributes;
    }

在Session启动过程中,就包含了把session数据从存储介质如redis中取出来,并存放在Store的$attributes属性中,可看Store::loadSession()源代码:

    protected function loadSession()
    {
        $this->attributes = array_merge($this->attributes, $this->readFromHandler());

        foreach (array_merge($this->bags, [$this->metaBag]) as $bag) {
            $this->initializeLocalBag($bag);

            $bag->initialize($this->bagData[$bag->getStorageKey()]);
        }
    }

所以,使用Arr类的一些数组操作函数从Store的$attributes属性中读取session数据。Laravel提供了IlluminateSupportArr辅助类来操作数组,支持.语法来操作数组,同时还提供了IlluminateSupportStr辅助类来操作字符串。

总之,Laravel提供了三种方法来读取session数据:

Session::get();
Session::pull();
Session::all();
session数据存储

session数据存储方法包括:

    // "更新式存储",即如果redis中有"session.store"数据,就使用"Store"来update旧数据
    Session::put("session.store", "Store");
    public function put($key, $value = null)
    {
        if (! is_array($key)) {
            $key = [$key => $value];
        }

        foreach ($key as $arrayKey => $arrayValue) {
            $this->set($arrayKey, $arrayValue);
        }
    }
    
    // "压入式存储",即如果redis中有"session.store"数据,就使用"Store"和旧数据如"StoreOld"作为新数组数据
    // 这时"session.store"新数据是["StoreOld", "Store"];
    Session::push("session.store", "Store");
    public function push($key, $value)
    {
        $array = $this->get($key, []);

        $array[] = $value;

        $this->put($key, $array);
    }

总之,Laravel提供了两种方法来存储数据:

Session::put("session.store", "Store");
Session::push("session.store", "StoreNew");
session数据删除

session数据删除方法包括:

    // 删除key为"session.store"的数据
    Session::forget("session.store");
    public function forget($keys)
    {
        Arr::forget($this->attributes, $keys);
    }
    
    // 清空所有数据,$attributes为空
    Session::flush();
    public function flush()
    {
        $this->clear();
    }
    public function clear()
    {
        $this->attributes = [];

        foreach ($this->bags as $bag) {
            $bag->clear();
        }
    }

总之,Laravel提供了两种方法来删除数据:

Session::forget("session.store");
Session::flush();
session数据暂存

数据暂存是把session中的数据保留到下一次请求中,下一次请求结束后则删除数据,数据暂存方法包括:

    // 把"session.store"数据刷到"_flash.new",等待下一次请求使用,然后再删除
    Session::flash("session.store", "Store");
    public function flash($key, $value)
    {
        // 更新式存储"session.store" => "Store"
        $this->put($key, $value);

        // 压入式存储"_flash.new" => ["session.store", XXX]
        $this->push("_flash.new", $key);

        // 删除"session.store"这个value值
        $this->removeFromOldFlashData([$key]);
    }
    protected function removeFromOldFlashData(array $keys)
    {
        // 把"_flash.old"数组中不包含"session.store"的结果存储到"_flash.old"中
        // 即删除"session.store"这个value值
        $this->put("_flash.old", array_diff($this->get("_flash.old", []), $keys));
    }
    
    // 把所有本次需要删除的数据全部刷到"_flash.new"中,等待下一次请求使用,然后再删除
    Session::reflash();
    public function reflash()
    {
        $this->mergeNewFlashes($this->get("_flash.old", []));
        
        $this->put("_flash.old", []);
    }
    protected function mergeNewFlashes(array $keys)
    {
        // 把"_flash.old"中的value值合并到"_flash.new"中
        $values = array_unique(array_merge($this->get("_flash.new", []), $keys));

        $this->put("_flash.new", $values);
    }
    
    // 把要删除的"session.store"重新激活,刷到"_flash.new"中,等待下一次使用
    Session::keep(["session.store" => "Store"]);
    public function keep($keys = null)
    {
        $keys = is_array($keys) ? $keys : func_get_args();
        
        // 把"session.store"刷到"_flash.new"中
        $this->mergeNewFlashes($keys);
        // 同时,把"session.store"从"_flash.old"中删除
        $this->removeFromOldFlashData($keys);
    }

总之,就是把本次请求要删除的数据放在"_flash.old",留到下一次请求中使用的就把它刷到"_flash.new"中。Laravel提供了三种方法来暂存数据:

Session::flash();
Session::reflash();
Session::keep();

总结:本文主要学习下Laravel的session的垃圾回收和CRUD增删改查操作。下篇再学习下关闭session,到时见。

欢迎关注Laravel-China。

RightCapital招聘Laravel DevOps

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/21942.html

相关文章

  • Laravel学习笔记Session源码解析(上)

    摘要:然后中间件使用方法来启动获取实例,使用类来管理主要分为两步获取实例,主要步骤是通过该实例从存储介质中读取该次请求所需要的数据,主要步骤是。 说明:本文主要通过学习Laravel的session源码学习Laravel是如何设计session的,将自己的学习心得分享出来,希望对别人有所帮助。Laravel在web middleware中定义了session中间件IlluminateSess...

    NervosNetwork 评论0 收藏0
  • Laravel学习笔记Session源码解析(下)

    摘要:实际上,在中关闭主要包括两个过程保存当前到介质中在中存入。,学习下关闭的源码吧先。总之,关闭的第二件事就是给添加。通过对的源码分析可看出共分为三大步启动操作关闭。总结本小系列主要学习了的源码,学习了的三大步。 说明:在中篇中学习了session的CRUD增删改查操作,本篇主要学习关闭session的相关源码。实际上,在Laravel5.3中关闭session主要包括两个过程:保存当前U...

    Awbeci 评论0 收藏0
  • Laravel学习笔记bootstrap源码解析

    摘要:总结本文主要学习了启动时做的七步准备工作环境检测配置加载日志配置异常处理注册注册启动。 说明:Laravel在把Request通过管道Pipeline送入中间件Middleware和路由Router之前,还做了程序的启动Bootstrap工作,本文主要学习相关源码,看看Laravel启动程序做了哪些具体工作,并将个人的研究心得分享出来,希望对别人有所帮助。Laravel在入口index...

    xiaoxiaozi 评论0 收藏0
  • Laravel学习笔记IoC Container实例化源码解析

    摘要:说明本文主要学习容器的实例化过程,主要包括等四个过程。看下的源码如果是数组,抽取别名并且注册到中,上文已经讨论实际上就是的。 说明:本文主要学习Laravel容器的实例化过程,主要包括Register Base Bindings, Register Base Service Providers , Register Core Container Aliases and Set the ...

    ningwang 评论0 收藏0
  • Laravel学习笔记Container源码解析

    摘要:实际上的绑定主要有三种方式且只是一种的,这些已经在学习笔记之实例化源码解析聊过,其实现方法并不复杂。从以上源码发现的反射是个很好用的技术,这里给出个,看下能干些啥打印结果太长了,就不粘贴了。 说明:本文主要学习Laravel中Container的源码,主要学习Container的绑定和解析过程,和解析过程中的依赖解决。分享自己的研究心得,希望对别人有所帮助。实际上Container的绑...

    huayeluoliuhen 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<