浅析 Laravel Session 组件的工作流程
前言
写框架的时候发现自己的session功能不是很完善,所有希望从laravel中学习一些精华!
当然Session的用处想必不用我在这里叙述了
Session组件
首先,我们需要知道
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组件服务提供者
服务提供者注册的过程为什么会调用
register
和boot
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实际还是
SessionManager
中driver
函数返回的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 协议》,转载必须注明作者和本文链接