Laravel学习笔记之Session源码解析(中)
说明:在上篇中学习了session的启动过程,主要分为两步,一是session的实例化,即\Illuminate\Session\Store的实例化;二是从session存储介质redis中读取id = laravel_session*
的数据。Laravel5.3把session垃圾回收放在了启动过程中,尽管Laravel5.1是放在session关闭过程的,本篇聊下垃圾回收,这也是session第一步启动session的过程。session第二步就是操作session,包括对session数据的CRUD增删改查操作,本文也主要聊下相关操作源码。
开发环境:Laravel5.3 + PHP7
Session垃圾回收
首先看下session中间件的源码\Illuminate\Session\Middleware\StartSession::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()
返回的就是\Illuminate\Session\CacheBasedSessionHandler
实例,该handler就是负责从redis这个存储介质中CRUD数据,OK,看下该handler的gc()
源码:
public function gc($lifetime)
{
return true;
}
其实什么都没做。这是当然的,redis对于过期的key会自动清除,所以这里就让redis来负责垃圾回收过期数据。当然,对于database这种handler,可以看下它的垃圾回收\Illuminate\Session\DatabaseSessionHandler
:
public function gc($lifetime)
{
$this->getQuery()->where('last_activity', '<=', time() - $lifetime)->delete();
}
以数据库作为存储session的介质,垃圾回收就是从sessions表里删除掉对应字段。
操作Session
操作Session就是对从存储介质如redis中取出的数据进行CRUD增删改查操作,包括:数据读取;数据存储;数据删除;数据暂存。当然,在对session进行CRUD操作前,首先得获取session对象即\Illuminate\Session\Store
实例,有三种方法:通过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');
// Illuminate\Session\Store
public function get($name, $default = null)
{
return Arr::get($this->attributes, $name, $default);
}
// 'Store'是默认数据,读取key为'session:store'的数据,并删除key为'session'的数据
$value = Session::pull('session', 'Store');
// Illuminate\Session\Store
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提供了\Illuminate\Support\Arr
辅助类来操作数组,支持.
语法来操作数组,同时还提供了\Illuminate\Support\Str
辅助类来操作字符串。
总之,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学习笔记之Session源码解析(中)的更多相关文章
- Laravel学习笔记之Session源码解析(上)
说明:本文主要通过学习Laravel的session源码学习Laravel是如何设计session的,将自己的学习心得分享出来,希望对别人有所帮助.Laravel在web middleware中定义了 ...
- Laravel学习笔记之Session源码解析(下)
说明:在中篇中学习了session的CRUD增删改查操作,本篇主要学习关闭session的相关源码.实际上,在Laravel5.3中关闭session主要包括两个过程:保存当前URL到session介 ...
- JUC.Lock(锁机制)学习笔记[附详细源码解析]
锁机制学习笔记 目录: CAS的意义 锁的一些基本原理 ReentrantLock的相关代码结构 两个重要的状态 I.AQS的state(int类型,32位) II.Node的waitStatus 获 ...
- JUC.Condition学习笔记[附详细源码解析]
目录 Condition的概念 大体实现流程 I.初始化状态 II.await()操作 III.signal()操作 3个主要方法 Condition的数据结构 线程何时阻塞和释放 await()方法 ...
- memcached学习笔记——存储命令源码分析下篇
上一篇回顾:<memcached学习笔记——存储命令源码分析上篇>通过分析memcached的存储命令源码的过程,了解了memcached如何解析文本命令和mencached的内存管理机制 ...
- memcached学习笔记——存储命令源码分析上篇
原创文章,转载请标明,谢谢. 上一篇分析过memcached的连接模型,了解memcached是如何高效处理客户端连接,这一篇分析memcached源码中的process_update_command ...
- Hadoop学习笔记(10) ——搭建源码学习环境
Hadoop学习笔记(10) ——搭建源码学习环境 上一章中,我们对整个hadoop的目录及源码目录有了一个初步的了解,接下来计划深入学习一下这头神象作品了.但是看代码用什么,难不成gedit?,单步 ...
- Laravel开发:Laravel核心——Ioc服务容器源码解析(服务器解析)
make解析 服务容器对对象的自动解析是服务容器的核心功能,make 函数.build 函数是实例化对象重要的核心,先大致看一下代码: public function make($abstract) ...
- [Golang学习笔记] 03 库源码文件
库源码文件:不能被直接运行的源码文件,它仅用于存放程序实体,这些程序实体可以被其他代码使用. 代码包声明的基本规则: 1. 同目录下的源码文件的代码包声明语句要一致.也就是说,它们要同属于一个代码包( ...
随机推荐
- [POI2013]Łuk triumfalny
[POI2013]Łuk triumfalny 题目大意: 一棵\(n(n\le3\times10^5)\)个结点的树,一开始\(1\)号结点为黑色.\(A\)与\(B\)进行游戏,每次\(B\)能选 ...
- 如何用dat批处理文件关闭某端口对应程序-Windows自动化命令
如何用dat批处理文件关闭某端口对应程序? 网上找到的大部分都是手动操作,第一步先查出端口,第二步在根据上一步查到的端口手动去关闭进程.但我的需求不是这样的,我需要全自动处理.用于 dubbo 服务进 ...
- java并发基础(三)--- 任务执行
第6章开始是第二部分,讲解结构化并发应用程序,大多数并发应用程序都是围绕“任务执行”构造的,任务通常是一些抽象的且离散的工作单元. 一.线程池 大多数服务器应用程序都提供了一种自然的任务边界:以独立的 ...
- Jedis使用总结【pipeline】【分布式的id生成器】【分布式锁【watch】【multi】】【redis分布式】(转)
前段时间细节的了解了Jedis的使用,Jedis是redis的java版本的客户端实现.本文做个总结,主要分享如下内容: [pipeline][分布式的id生成器][分布式锁[watch][multi ...
- (转载)Spring 注解@Component,@Service,@Controller,@Repository
Spring 2.5 中除了提供 @Component 注释外,还定义了几个拥有特殊语义的注释,它们分别是:@Repository.@Service 和 @Controller.在目前的 Spring ...
- 使用CefSharp在.Net程序中嵌入Chrome浏览器(十)——独立文件夹部署
CefSharp本身携带了一大堆文件,这些文件默认直接释放在exe文件底下,这种方式本身没有什么问题,但多了一大堆文件后不是很好看.本文这里就介绍一个方法,使得可以将CEF相关的文件部署到独立的文件夹 ...
- BoundingBoxUV与BoundingBoxXYZ
start UIApplication app = commandData.Application; Document doc = app.ActiveUIDocument.Document; ); ...
- [Winform]Media Player com组件应用中遇到的问题
摘要 最近一个项目中,需要用到在客户端全屏循环播放视频,当时考虑使用开源的播放器,但控制起来不方便,然后考虑既然都是windows系统,那么可以考虑使用微软自带的Media Player播放器.所以在 ...
- Ubuntu上的Hadoop安装教程
Install Hadoop 2.2.0 on Ubuntu Linux 13.04 (Single-Node Cluster) This tutorial explains how to insta ...
- Windows Phone本地数据库(SQLCE):11、使用LINQ查询数据库(翻译) (转)
这是“windows phone mango本地数据库(sqlce)”系列短片文章的第十一篇. 为了让你开始在Windows Phone Mango中使用数据库,这一系列短片文章将覆盖所有你需要知道的 ...