浅析 Laravel Session 组件的工作流程

前言

写框架的时候发现自己的session功能不是很完善,所有希望从laravel中学习一些精华!
当然Session的用处想必不用我在这里叙述了

Session组件

t59wG6JfgQ.png!large

首先,我们需要知道Session组件主要由一下几个部分组成,我将按照一下顺序逐步解析Session组件

服务提供者(把组件注册到容器等)
    Illuminate\Session\SessionServiceProvider
中间件
    Illuminate\Session\Middleware\StartSession.php
管理类
    Illuminate\Session\SessionManager
核心类
    Illuminate\Session\Store
    Illuminate\Session\EncryptedStore(加密继承Store类)
处理类(实现SessionHandlerInterface接口)
    Illuminate\Session\NullSessionHandler
    Illuminate\Session\FileSessionHandler
    Illuminate\Session\DataBaseSessionHandler
    Illuminate\Session\CacheBasedSessionHandler
    Illuminate\Session\CookieSessionHandler
模板(stub这里就不叙述了)
    Console\stubs\database.stub

SessionServiceProvider Session组件服务提供者

服务提供者注册的过程为什么会调用registerboot

public function register($provider, $options = [], $force = false)
{
  if (($registered = $this->getProvider($provider)) && ! $force) {
  return $registered;
 }
 if (is_string($provider)) {
  $provider = $this->resolveProvider($provider);
 }
 if (method_exists($provider, 'register')) {
 //调用register函数
  $provider->register();
 }
  $this->markAsRegistered($provider);

 if ($this->booted) {
 //调用boot函数
  $this->bootProvider($provider);
 }
  return $provider;
}

看完Application中处理provider的函数之后,我们再回到SessionServiceProvider

public function register()
{
    //注册实例'session'=>new SessionManager($app)
  $this->registerSessionManager();
    //注册session.store 实际上是调用SessionManager成员方法driver()
  $this->registerSessionDriver();
    //将StartSession::class注入容器
  $this->app->singleton(StartSession::class);
}

此时我们再进入SessionManager类的driver()函数中一探究竟
创建driver并存入数组且返回
返回的是核心类Store但是内部的handler是根据配置文件从容器等获取的

public function driver($driver = null)
{
 //参数为空则获取默认file
 $driver = $driver ?: $this->getDefaultDriver();
 //若本地无加入的driver则创建driver
 if (! isset($this->drivers[$driver])) {
      $this->drivers[$driver] = $this->createDriver($driver);
 }ch
 //返回创建的driver 以session.store为键返回创建的driver为值存入容器中
  return $this->drivers[$driver];
}

让我们逐步打开driver()方法


public function getDefaultDriver()
{
//返回配置文件中的session.driver的session
  return $this->app['config']['session.driver'];
}
protected function createDriver($driver)
{
    //一般为空
     if (isset($this->customCreators[$driver])) {
         //调用customCreators[$driver]的方法生成driver
          return $this->callCustomCreator($driver);
     } else {
         //组合字符串
          $method = 'create'.Str::studly($driver).'Driver';
     if (method_exists($this, $method)) {
         //调用方法创建driver
          return $this->$method();
     }
 }
 throw new InvalidArgumentException("Driver [$driver] not supported.");
}

若此时设置为redis 此时我们进入createRedisDriver方法

//创建redis驱动
protected function createRedisDriver()
{
    //创建CacheBasedSessionHandler对象
  $handler = $this->createCacheHandler('redis');
    //设置链接
  $handler->getCache()->getStore()->setConnection(
  $this->app['config']['session.connection']
 );
 /创建构建Store并且返回
  return $this->buildSession($handler);
}
//创建缓存处理者
protected function createCacheHandler($driver)
{
    //若配置文件中有就用配置文件中,无就用传入的$driver
  $store = $this->app['config']->get('session.store') ?: $driver;
    //实例化某SessionHandler
  return new CacheBasedSessionHandler(
  //这里返回的是Illuminate\Cache\Repository对象内部store为RedisStore
  clone $this->app['cache']->store($store),
  //redis有效时间
  $this->app['config']['session.lifetime']
 );
}
//CacheManager的store方法 返回缓存中的驱动
public function store($name = null)
{
  $name = $name ?: $this->getDefaultDriver();
    //返回并存入数组
  return $this->stores[$name] = $this->get($name);
}

构建Session核心类(Store,EncryptedStore)对象的过程

protected function buildSession($handler)
{

 if ($this->app['config']['session.encrypt']) {

      return $this->buildEncryptedSession($handler);
 }
      return new Store($this->app['config']['session.cookie'], $handler);
}
//加密Store
return new EncryptedStore(
  $this->app['config']['session.cookie'], $handler, $this->app['encrypter']
);
//不加密Store
return new Store($this->app['config']['session.cookie'], $handler);

以上driver()方法就打开完毕了,返回的对象存入数组和容器中
Store,EncryptedStore实例化过程

public function __construct($name, SessionHandlerInterface $handler, $id = null)
{
    //生成sessionId
  $this->setId($id);
    //设置session的cookie名称
  $this->name = $name;
    //设置处理者
  $this->handler = $handler;
}
public function __construct($name, SessionHandlerInterface $handler, EncrypterContract $encrypter, $id = null)
{
  $this->encrypter = $encrypter;
  parent::__construct($name, $handler, $id);
}

中间件引导Session过程

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

 if ($this->sessionConfigured()) {
     //将session保存到request中
     $request->setLaravelSession(
         //返回session内容
        $session = $this->startSession($request)
     );
     //清理session中的垃圾
    $this->collectGarbage($session);
 }
     //调用下一层中间件/控制器
  $response = $next($request);

 if ($this->sessionConfigured()) {
     //存储当前url
      $this->storeCurrentUrl($request, $session);
    //把cookie加入到response中
      $this->addCookieToResponse($response, $session);
 }
  return $response;
}

这里session实际还是SessionManagerdriver函数返回的Store
这里直接从SessionManager$this->drivers获取即可

     $request->setLaravelSession(
        $session = $this->startSession($request)
     );

加密的实现

主要通过子类重写父类的准备存储和准备取出方法

//存储的时候加密
    protected function prepareForStorage($data)
//反序列化的时候解密
    protected function prepareForUnserialize($data)

Session门脸

class Session extends Facade
{
      protected static function getFacadeAccessor()
     {  
         //从容器中取出的是SessionManager
        //至于为什么自行去看Facade的 __callStatic魔术方法
         return 'session';
     }
 }
 //用Session门脸执行set("1","2");

Session::set("1","2");

//都通过__call
abstract class Manager{

    ...

    public function __call($method, $parameters)
    {
        //调用Store中的函数执行
      return $this->driver()->$method(...$parameters);
    }
}

//也就是执行Store中的成员方法set

看到这里相比也学习到了laravel组件开发模式的益处和其中多种设计模式的使用使得框架的扩展性和稳定性增强

如果让你构建一个框架并实现Session的功能你会怎么做呢?

欢迎大家一起讨论

本作品采用《CC 协议》,转载必须注明作者和本文链接
学习,冲冲冲~
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!