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

时间:2022-12-18 13:07:24

说明:本文主要通过学习Laravel的session源码学习Laravel是如何设计session的,将自己的学习心得分享出来,希望对别人有所帮助。Laravel在web middleware中定义了session中间件\Illuminate\Session\Middleware\StartSession::class,并通过该中间件来设计session,这个中间件的主要工作分为三步:(1)启动session,通过session handler从一些存储介质如redis中读取session值;(2)操作session,对session数据CRUD增删改查操作;(3)关闭session,把session_id写入到response header中,默认是laravel_session

开发环境:Laravel5.3 + PHP7

启动Session

首先看下\Illuminate\Session\Middleware\StartSession::class中间件源码中handle()方法:

    public function handle($request, Closure $next)
{
// 前置操作
$this->sessionHandled = true; if ($this->sessionConfigured()) {
// Start session.
/**
* @var \Illuminate\Session\Store $session
*/
$session = $this->startSession($request); $request->setSession($session); $this->collectGarbage($session);
} $response = $next($request); // 后置操作
if ($this->sessionConfigured()) {
$this->storeCurrentUrl($request, $session); $this->addCookieToResponse($response, $session);
} return $response;
}

Laravel学习笔记之Middleware源码解析这篇文章中知道,该中间件有前置操作和后置操作。看下sessionConfigured()的源码:

    /**
* Determine if a session driver has been configured.
*
* @return bool
*/
protected function sessionConfigured()
{
// 检查session.php中driver选项是否设置
return ! is_null(Arr::get($this->manager->getSessionConfig(), 'driver'));
} // \Illuminate\Session\SessionManager
/**
* Get the session configuration.
*
* @return array
*/
public function getSessionConfig()
{
return $this->app['config']['session'];
}

首先中间件检查session.php中driver选项是否设置,这里假设设置为经常使用的redis作为session的存储介质,并且需要在database.php中设置下redis的链接,本地需要装好redis,通过redis-cli命令查看redis是否已经安装好。OK,然后中间件使用startSession()方法来启动session:

    protected function startSession(Request $request)
{
/**
* @var \Illuminate\Session\Store $session
*/
$session = $this->getSession($request); // 获取session实例,Laravel使用Store类来管理session $session->setRequestOnHandler($request); // Load the session data from the store repository by the handler.
$session->start(); return $session;
} public function getSession(Request $request)
{
/**
* Get the session store instance via the driver.
*
* @var \Illuminate\Session\Store $session
*/
$session = $this->manager->driver(); /**
* $session->getName() === 'laravel_session' === config('session.cookie')
*/
$session->setId($request->cookies->get($session->getName())); return $session;
}

startSession()主要分为两步:获取session实例\Illuminate\Session\Store,主要步骤是$session = $this->manager->driver();通过该实例从存储介质中读取该次请求所需要的session数据,主要步骤是$session->start()。首先看下第一步的源码:

    // \Illuminate\Support\Manager
public function driver($driver = null)
{
// $driver = 'redis'
$driver = $driver ?: $this->getDefaultDriver(); if (! isset($this->drivers[$driver])) {
$this->drivers[$driver] = $this->createDriver($driver);
} return $this->drivers[$driver];
} protected function createDriver($driver)
{
$method = 'create'.Str::studly($driver).'Driver'; if (isset($this->customCreators[$driver])) {
return $this->callCustomCreator($driver);
} elseif (method_exists($this, $method)) { // 判断\Illuminate\Session\SessionManager中是否存在createRedisDriver()方法
// 存在,call这个createRedisDriver()方法
return $this->$method();
} throw new InvalidArgumentException("Driver [$driver] not supported.");
} // \Illuminate\Session\SessionManager
public function getDefaultDriver()
{
// 返回 'redis'
return $this->app['config']['session.driver'];
}

从以上源码中很容易知道,选择的driver是redis,最后还是要调用\Illuminate\Session\SessionManager中的createRedisDriver()方法:

    protected function createRedisDriver()
{
/**
* @var \Illuminate\Session\CacheBasedSessionHandler $handler
*/
$handler = $this->createCacheHandler('redis'); // 设置redis连接
$handler->getCache()->getStore()->setConnection($this->app['config']['session.connection']); return $this->buildSession($handler);
} protected function createCacheHandler($driver)
{
// $store = 'redis'
$store = $this->app['config']->get('session.store') ?: $driver; $minutes = $this->app['config']['session.lifetime']; // $this->app['cache']->store($store)返回\Illuminate\Cache\Repository实例
return new CacheBasedSessionHandler(clone $this->app['cache']->store($store), $minutes);
} // Illuminate\Session\CacheBasedSessionHandler
/**
* Get the underlying cache repository.
*
* @return \Illuminate\Contracts\Cache\Repository|\Illuminate\Cache\Repository
*/
public function getCache()
{
return $this->cache;
} // \Illuminate\Cache\Repository
/**
* Get the cache store implementation.
*
* @return \Illuminate\Contracts\Cache\Store|RedisStore
*/
public function getStore()
{
return $this->store;
} // \Illuminate\Cache\RedisStore
/**
* Set the connection name to be used.
*
* @param string $connection
* @return void
*/
public function setConnection($connection)
{
$this->connection = $connection;
}

从以上源码知道获取到\Illuminate\Session\CacheBasedSessionHandler这个handler后,就可以buildSession()了:

    protected function buildSession($handler)
{
// 设置加密的则返回EncryptedStore实例,这里假设没有加密
if ($this->app['config']['session.encrypt']) {
return new EncryptedStore(
$this->app['config']['session.cookie'], $handler, $this->app['encrypter']
);
} else {
// 默认$this->app['config']['session.cookie'] === 'laravel_session'
return new Store($this->app['config']['session.cookie'], $handler);
}
}

从源码中可看出session实例就是\Illuminate\Session\Store实例,并且构造Store类还需要一个重要的部件handler,构造好了session实例后,就可以通过这个handler来从session存储的介质中如redis获取session数据了,这里设置的session driver是redis,所以handler就会是\Illuminate\Session\CacheBasedSessionHandler。总的来说,现在已经构造好了session实例即\Illuminate\Session\Store

然后第二步就是$session->start()从存储介质中加载session数据:

    public function start()
{
// 从存储介质中加载session数据
$this->loadSession(); // session存储介质中没有'_token'这个key就生成一个
if (! $this->has('_token')) {
$this->regenerateToken();
} return $this->started = true;
}

关键是loadSession()的源码:

    // Illuminate/Session/Store
protected function loadSession()
{
// 从redis中读取key为'laravel_session'的数据后存入session实例即Store的$attributes属性中
$this->attributes = array_merge($this->attributes, $this->readFromHandler()); foreach (array_merge($this->bags, [$this->metaBag]) as $bag) {
/**
* @var \Symfony\Component\HttpFoundation\Session\Storage\MetadataBag $bag
*/
$this->initializeLocalBag($bag); $bag->initialize($this->bagData[$bag->getStorageKey()]);
}
} protected function readFromHandler()
{
// 主要是这句,通过handler从存储介质redis中读取session数据
// $this->getId() === 'laravel_session'
$data = $this->handler->read($this->getId()); if ($data) {
$data = @unserialize($this->prepareForUnserialize($data)); if ($data !== false && ! is_null($data) && is_array($data)) {
return $data;
}
} return [];
}

这里的handler是\Illuminate\Session\CacheBasedSessionHandler,看下该handler的read()源码:

    // $sessionId === 'laravel_session'
public function read($sessionId)
{
// 这里的cache是Illuminate\Cache\Repository
return $this->cache->get($sessionId, '');
} // Illuminate\Cache\Repository
public function get($key, $default = null)
{
if (is_array($key)) {
return $this->many($key);
} // 这里的store是Illuminate\Cache\RedisStore
$value = $this->store->get($this->itemKey($key)); if (is_null($value)) {
$this->fireCacheEvent('missed', [$key]); $value = value($default);
} else {
$this->fireCacheEvent('hit', [$key, $value]);
} return $value;
} // Illuminate\Cache\RedisStore
public function get($key)
{
if (! is_null($value = $this->connection()->get($this->prefix.$key))) {
return $this->unserialize($value);
}
}

通过以上代码,很容易了解从redis存储介质中加载key为'laravel_session'的数据,最后还是调用了RedisStore::get($key, $default)方法。

但不管咋样,通过handle()第一步$session = $this->startSession($request);就得到了session实例即Store,该步骤中主要做了两步:一是Store实例化;二是从redis中读取key为'laravel_session'的数据。

然后就是$this->collectGarbage($session)做了垃圾回收。中篇再聊。

总结:本文主要学习了session机制的启动工作中第一步session的实例化,主要包括两步骤:Store的实例化;从redis中读取key为laravel_session的数据。中篇再聊下session垃圾回收,和session的增删改查操作,到时见。

Laravel学习笔记之Session源码解析(上)的更多相关文章

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

    说明:在上篇中学习了session的启动过程,主要分为两步,一是session的实例化,即\Illuminate\Session\Store的实例化:二是从session存储介质redis中读取id ...

  2. Laravel学习笔记之Session源码解析(下)

    说明:在中篇中学习了session的CRUD增删改查操作,本篇主要学习关闭session的相关源码.实际上,在Laravel5.3中关闭session主要包括两个过程:保存当前URL到session介 ...

  3. JUC.Lock(锁机制)学习笔记[附详细源码解析]

    锁机制学习笔记 目录: CAS的意义 锁的一些基本原理 ReentrantLock的相关代码结构 两个重要的状态 I.AQS的state(int类型,32位) II.Node的waitStatus 获 ...

  4. JUC.Condition学习笔记[附详细源码解析]

    目录 Condition的概念 大体实现流程 I.初始化状态 II.await()操作 III.signal()操作 3个主要方法 Condition的数据结构 线程何时阻塞和释放 await()方法 ...

  5. memcached学习笔记——存储命令源码分析下篇

    上一篇回顾:<memcached学习笔记——存储命令源码分析上篇>通过分析memcached的存储命令源码的过程,了解了memcached如何解析文本命令和mencached的内存管理机制 ...

  6. memcached学习笔记——存储命令源码分析上篇

    原创文章,转载请标明,谢谢. 上一篇分析过memcached的连接模型,了解memcached是如何高效处理客户端连接,这一篇分析memcached源码中的process_update_command ...

  7. Hadoop学习笔记&lpar;10&rpar; ——搭建源码学习环境

    Hadoop学习笔记(10) ——搭建源码学习环境 上一章中,我们对整个hadoop的目录及源码目录有了一个初步的了解,接下来计划深入学习一下这头神象作品了.但是看代码用什么,难不成gedit?,单步 ...

  8. Laravel学习笔记(三)--在CentOS上配置Laravel

    在Laravel框架上开发了几天,不得不说,确实比较优雅,处理问题逻辑比较清楚.     今天打算在CentOS 7上配置一个Laravel,之前都是在本机上开发,打算实际配置一下.     1)系统 ...

  9. Laravel开发:Laravel核心——Ioc服务容器源码解析(服务器解析)

    make解析 服务容器对对象的自动解析是服务容器的核心功能,make 函数.build 函数是实例化对象重要的核心,先大致看一下代码: public function make($abstract) ...

随机推荐

  1. ABP框架 - 缓存

    文档目录 本节内容: 简介 ICacheManager ICache ITypedCache 配置 实体缓存 EntityCache 是如何工作 Redis 缓存集成 简介 ABP提供了一个缓存接口, ...

  2. 【积累篇:他山之石,把玉攻】解决XP 系统 &period;Net Framework 4安装时出现严重错误 &lpar;0x80070643&rpar;

    第一步: 1.开始——运行——输入cmd——回车——在打开的窗口中输入net stop WuAuServ 2.开始——运行——输入%windir% 3.在打开的 的窗口中有个文件夹叫SoftwareD ...

  3. BZOJ 1072&colon; &lbrack;SCOI2007&rsqb;排列perm 状态压缩DP

    1072: [SCOI2007]排列perm Description 给一个数字串s和正整数d, 统计s有多少种不同的排列能被d整除(可以有前导0).例如123434有90种排列能被2整除,其中末位为 ...

  4. Maven与Eclipse使用中遇到的问题解决之道

    在使用Maven以及Eclipse的Maven插件时,我和同事遇到了一下几个问题,本着知其然知其所以然的学习精神,总结如下: Unrecognised tag 问题 由于我使用本地代理仓库,所以set ...

  5. VC&plus;&plus;开发AutoCAD 2018&sol;objectARX 用向导新建项目无法新建的问题

    话说笔者最近想用新机子上装的AutoCAD ObjectARX 2018来进行二次开发,兴致勃勃安装了ARX API和向导, 然后打开VS2015,新建项目,无法新建. 折腾了一下,还是没有解决,后面 ...

  6. unresolved external symbol boost&colon;&colon;throw&lowbar;exception

    使用boost库,VS生成的时候一直报错, error LNK2019: 无法解析的外部符号 "void __cdecl boost::throw_exception(class std:: ...

  7. django 数据模型中 null&equals;True 和 blank&equals;True 有什么区别

    null: If True, Django will store empty values as NULL in the database. Default is False. 如果为True,空值将 ...

  8. 双向认证SSL原理

    http://m.blog.chinaunix.net/uid-540802-id-3170984.html 文中首先解释了加密解密的一些基础知识和概念,然后通过一个加密通信过程的例子说明了加密算法的 ...

  9. Linux内核设计第八周学习总结 理解进程调度时机跟踪分析进程调度与进程切换的过程

    陈巧然 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.视频内容 Linux ...

  10. hibench学习

    hibench包含几个hadoop的负载 micro benchmarksSort:使用hadoop randomtextwriter生成数据,并对数据进行排序. Wordcount:统计输入数据中每 ...