Laravel5.5源码详解 -- Session的启动分析
来源:互联网 发布:雅米网络兼职靠谱吗 编辑:程序博客网 时间:2024/05/31 19:26
Laravel5.5源码详解 – Session的启动分析
Session的整个过程包括三个主要流程(laravel默认的sesssion名称都是laravel_session),
(1)启动session,
(2)操作session,对数据进行CRUD增删改查操作,
(3)关闭session。
Session启动之后的操作,和数据库的操作类似。这里不打算讲解。这里只关注启动过程,其一是因为session本身,这里相对比较难理解,其二是因为,CSRF等都是在session的启动过程中传入进去的,所以有必要了解。
首先看一下调用关系,session作为中间件由pipeline引入,我们从session的handle函数开始,这个函数在
\Illuminate\Session\Middleware\StartSession::class。
public function handle(
{
$this->sessionHandled = true;
// If a session driver has been configured, we will need to start the session here // so that the data is ready for an application. Note that the Laravel sessions // do not make use of PHP "native" sessions in any way since they are crappy. if ($this->sessionConfigured()) { $request->setLaravelSession( $session = $this->startSession($request) ); $this->collectGarbage($session); } $response = $next($request); // Again, if the session has been configured we will need to close out the session // so that the attributes may be persisted to some storage medium. We will also // add the session identifier cookie to the application response headers now. if ($this->sessionConfigured()) { $this->storeCurrentUrl($request, $session); $this->addCookieToResponse($response, $session); } return $response;}
其中Handle调用 startSession
再调用 getSession & setRequestOnHandler
。我们一步一步来,慢慢剥开这些外衣,
protected function startSession(Request $request){ return tap($this->getSession($request), function ($session) use ($request) { //下面这句没细查,先跳过。我调试了下,没发现有干任何有意义的事情,可能是采用redis等时才需要, //这里配置的是file。与其相关的接口是SessionHandlerInterface,这个一般是php对session接口设计的, //如memcached, redis等。 $session->setRequestOnHandler($request); //关键是后面这句 $session->start(); });}
startSession()包括两步,首先是获取session的实例,也就是\Illuminate\Session\Store,主要步骤是
第一步的源码:获取session实例\Illuminate\Session\Store
在SessionStart.php里,
public function getSession(Request $request){ return tap($this->manager->driver(), function ($session) use ($request) { $session->setId($request->cookies->get($session->getName())); });}
这个$this->manager->driver()是这么个东东?
Store {#325 ▼id: "4qbYEQItvRNIJKSfAeRO0rGT1S4JGk3bAuedRFEJ"name: "laravel_session"attributes: []handler: FileSessionHandler {#326 ▼ #files: Filesystem {#101} #path: "D:\wamp64\www\laravel\larablog\storage\framework/sessions" #minutes: "120" } started: false}
说明白点,$this->manager->driver()就相当于StartSession->SessionManager->Store。也就是session实例本尊。这个manager,就是SessionManger类。
那么laravel的sessionManager是什么时候配置store的呢?
本质上,是调用了父类Illuminate\Support\Manager里面的driver()函数,该函数又调用Illuminate\Session\SessionManager中的getDefaultDriver()函数。我们先看getDefaultDriver()函数
public function getDefaultDriver()
{
return $this->app[‘config’][‘session.driver’];
}
这个app[‘config’]的来源,后面还会详细讲解,这理先了解结果。总之,就是把/bootstrap/cache/config.php 里面的配置项session.driver拉出来。比如我的配置项是这样的,
'session' => array ('driver' => 'file','lifetime' => '120','expire_on_close' => false,'encrypt' => false,'files' => 'D:\\wamp64\\www\\laravel\\larablog\\storage\\framework/sessions', …此处省略若干行 )
再看driver()函数。整体上,driver()函数是下面这个样子,其中的createDriver后面还会讲到,这个又和buildStore有关。
public function driver($driver = null){ //得到/bootstrap/cache/config.php 里面的配置项 $driver = $driver ?: $this->getDefaultDriver(); //如果这个store不存在的话,就创建一个,store实例就是在这创建的。 if (! isset($this->drivers[$driver])) { $this->drivers[$driver] = $this->createDriver($driver); } return $this->drivers[$driver];}
最后
Store {#326 ▼id: "cp0yC4HOkgwAq2h9FfGeLF98RM3IgscXkENFRFnp"name: "laravel_session"attributes: []handler: FileSessionHandler {#327 ▼ #files: Filesystem {#101} #path: "D:\wamp64\www\laravel\larablog\storage\framework/sessions" #minutes: "120" }started: false}
说明一下,这个id没有什么实质意义,每次刷新都会不同。
那么我们明白,startSession 首先运行的就是getSession(),其中得到的是store,也就是$session。
再注意getSession()中这一小行,$session->setId($request->cookies->get($session->getName()));
$request->cookies是在request创建时就传递进去的,是一个数组,如下,
cookies: ParameterBag {#21 ▼ #parameters: array:2 [▼ "XSRF-TOKEN" => "eyJpdiI6IllOSStvZlMwNEk0T084eWJVWFZ5VVE9PSI▶" "laravel_session" => "eyJpdiI6IlRNSCtGcmlkeEEybGc5TzUzdXRyR ▶" ] }
这里要说一句,就是读者不必去关注XSRF-TOKEN的具体值,因为调试时,每次都会不一样,在实际运行时,只有一个。
接上面,$session->getName()
就是那个名称”laravel_session”,经过$session->setId()处理之后,会得到这么一个store,
Store {#325 ▼id: "0z8Sd3lsnJYIkTBILQeamqh80floYcF0InWwdgm2"name: "laravel_session"attributes: []handler: FileSessionHandler {#326 ▼ #files: Filesystem {#101} #path: "D:\wamp64\www\laravel\larablog\storage\framework/sessions" #minutes: "120" }started: false}
还是前面提到的那个store,也是getSession的返回值。
第二步,读取session数据
startSession在得到$session(也就是store)之后,接下来会启动下面这个start,现在来看Session->start()
public function start(){ //最重要的一条语句,实质是读取保存的session数据 $this->loadSession(); //如果没有CSRF_TOKEN,就创建一个 if (! $this->has('_token')) { $this->regenerateToken(); } //session启动完毕(就是取到了需要的数据而已),返回 return $this->started = true;}protected function loadSession(){ $this->attributes = array_merge($this->attributes, $this->readFromHandler());}protected function readFromHandler(){ //read读到session中的数据 if ($data = $this->handler->read($this->getId())) { //反序列化 -- 从已存储的session数据中创建 PHP 的值。 $data = @unserialize($this->prepareForUnserialize($data)); //确定读到的数据是非空数组。 if ($data !== false && ! is_null($data) && is_array($data)) { return $data; } } return [];}
在上面的函数中,readFromHandler里用到的这个handler是指向下面这个我们用来存储session数据的具体位置,
FileSessionHandler {#326 ▼files: Filesystem {#101}path: "D:\wamp64\www\laravel\larablog\storage\framework/sessions"minutes: "120"}
必须特别说明的是,这里我用的是laravel默认的file,也就是文件来存储session。所以,handler也就是FileSessionHandler。如果你的配置不一样,那么handler就会不一样。
继续深入readFromhandler之前,还要了解以下两点:store的创建和参数传递,及FileSessionHandler。
Store是什么时候被创建的?如何传递参数。
现在回过头来看前面提到的createDriver了,前面已经提到过,该函数被Illuminate\Support\Manager里面的driver()函数调用。其原型是这样子的,
protected function createDriver($driver) { // We'll check to see if a creator method exists for the given driver. If not we // will check for a custom driver creator, which allows developers to create // drivers using their own customized driver creator Closure to create it. if (isset($this->customCreators[$driver])) { return $this->callCustomCreator($driver); } else { $method = 'create'.Str::studly($driver).'Driver'; if (method_exists($this, $method)) { return $this->$method(); } } throw new InvalidArgumentException("Driver [$driver] not supported."); }
这里面的$method,如果你打印出来的话,正是”createFileDriver”。所以,最终创建store的命令,也就是下面要提到的createFileDriver。这也是因为我们使用的是file作为store配置的结果,所以看这个
protected function createFileDriver(){ return $this->createNativeDriver();}protected function createNativeDriver(){ $lifetime = $this->app['config']['session.lifetime']; return $this->buildSession(new FileSessionHandler( $this->app['files'], $this->app['config']['session.files'], $lifetime ));}protected function buildSession($handler){ if ($this->app['config']['session.encrypt']) { return $this->buildEncryptedSession($handler); } else { //默认的情况下是不加密的session,所以执行下面这句, return new Store($this->app['config']['session.cookie'], $handler); }}
最后这里createFileDriver调用了createNativeDriver,其中又用new createNativeDriver创建了这个handler作为参数,传递给buildSession ,buildSession再用这个handler创建了store。最重要的,是要了解new Store($this->app['config']['session.cookie'], $handler)
这一句,Store也正是在这里被创建的。
了解FileSessionHandler
前面已经特别强调,那个反复提到的handler是FileSessionHandler,他实现了SessionHandlerInterface这个PHP接口。
回想起前面提到的readFromHandler这个函数,其中最关键的就是获取session数据这一句:$data = $this->handler->read($this->getId())
,这个read,也正是调用了FileSessionHandler里的read函数
public function read($sessionId){ if ($this->files->exists($path = $this->path.'/'.$sessionId)) { if (filemtime($path) >= Carbon::now()->subMinutes($this->minutes)->getTimestamp()) { return $this->files->get($path, true); } } return '';}
把那个$path打印出来会是下面这个样子,这也是数据存储的文件名(是不是觉得名字好怪?)。对照看一下,
"D:\wamp64\www\laravel\larablog\storage\framework/sessions/0z8Sd3lsnJYIkTBILQeamqh80floYcF0InWwdgm2"
该read函数最后返回的$this->files->get($path, true)
正是文件里的内容,其中包括那个大名鼎鼎的CSRF-TOKEN。
a:4:{s:6:"_token";s:40:"zalGw4lffAeW6alsQKFBxb54QE9o3hIpEb0kK3Sd";s:9:"_previous";a:1:{s:3:"url";s:33:"http://localhost:8000/admin/login";}s:6:"_flash";a:2:{ s:3:"old";a:0:{}s:3:"new";a:0:{} }s:22:"PHPDEBUGBAR_STACK_DATA";a:0:{}}
这个数据再经过前面提到的readFromhandler的反序列化unserialize操作,会是下面这个样子
array:4 [▼ "_token" => "zalGw4lffAeW6alsQKFBxb54QE9o3hIpEb0kK3Sd" "_previous" => array:1 [▼ "url" => "http://localhost:8000/admin/login" ] "_flash" => array:2 [▼ "old" => [] "new" => [] ] "PHPDEBUGBAR_STACK_DATA" => []]
到这里,极为值得一提的是那个_token,你是不是在程序调试CSRF_TOKEN时经常碰到TokenMismatchException的问题?到这里查一下,说不定有意想不以的收获!
另外,关于这个CSRF-TOKEN,我们看到有两个地方,一个是在这里直接引入,如果这里没有,就会在Session->start()中用regenerateToken()创建。
至此,startSession完全运行完毕。
但还有些疑问,比如session是什么时候配置的?
在前面讲到的handle 函数中,在startSession之前,有这么一句配置函数
if ($this->sessionConfigured()) 。。。
这个sessionConfigured是这样的,
protected function sessionConfigured(){ return ! is_null($this->manager->getSessionConfig()['driver'] ?? null);}
该函数再调用sessionManager的getSessionConfig()来寻找配置,
直接dump($this)把sessionManager打印出来,看看这到底是个什么东西?(代码很长,所有有省略)
SessionManager {#193 ▼ app: Application {#2 ▶} customCreators: [] drivers: array:1 [▼ file" => Store {#490 ▼ #id: "8bfH3MIt3iY5BiNUae14sCRd6QT1ScY2QwHdbBSE" #name: "laravel_session" #attributes: [] #handler: FileSessionHandler {#586 ▼ #files: Filesystem {#101} #path: "D:\wamp64\www\laravel\larablog\storage\framework/sessions" #minutes: "120" } #started: false } ]}
在sessionManger中加入调试代码,
public function getSessionConfig(){ dump($this); dd(); return $this->app['config']['session'];}
得到下面的结果,
$this就是SessionManager,
SessionManager {#193 ▼ app: Application {#2 ▼ #basePath: "D:\wamp64\www\laravel\larablog" … } customCreators: [] drivers: [] }
$this->app[‘config’]是下面这个东西,
Repository {#24 ▼ items: array:14 [▼ "app" => array:13 [▶] "auth" => array:4 [▶] "broadcasting" => array:2 [▶] "cache" => array:3 [▶] "database" => array:4 [▶] "debugbar" => array:13 [▶] "filesystems" => array:3 [▶] "mail" => array:9 [▶] "queue" => array:3 [▶] "scout" => array:5 [▶] "services" => array:4 [▶] "session" => array:15 [▶] "view" => array:2 [▶] "trustedproxy" => array:2 [▶] ]}
要明白那个$this->app[‘config’]是如何得到Repository的,看下面:config 配置文件的加载。
插曲:config 配置文件的加载
config 配置文件由类 \Illuminate\Foundation\Bootstrap\LoadConfiguration::class 完成:
public function bootstrap(Application $app)
{ $items = []; if (file_exists($cached = $app->getCachedConfigPath())) { //注意,这个$cached返回的是路径, //"D:\wamp64\www\laravel\larablog\bootstrap/cache/config.php" $items = require $cached; //items就相当于/bootstrap/cache/config.php里面的所有配置项 $loadedFromCache = true; } //下面,new Repository($items)创建一个仓库,并把所有配置项作为参数传递进去, //然后绑定到$app->instances数组中的config上,说明config已经实例化。 $app->instance('config', $config = new Repository($items)); //此时,如果打印出$app,就会得到后面的那些内容,可以明显看到, //instances数组中添加了’config’ (已经刻意展开了该项) if (! isset($loadedFromCache)) { $this->loadConfigurationFiles($app, $config); } $app->detectEnvironment(function () use ($config) { return $config->get('app.env', 'production'); }); date_default_timezone_set($config->get('app.timezone', 'UTC')); mb_internal_encoding('UTF-8');}
dd(#app)得到的结果,
Application {#2 ▼basePath: "D:\wamp64\www\laravel\larablog" …instances: array:17 [▼…"config" => Repository {#24 ▼ #items: array:14 [▼ "app" => array:13 [▶] "auth" => array:4 [▶] "broadcasting" => array:2 [▶] "cache" => array:3 [▶] "database" => array:4 [▶] "debugbar" => array:13 [▶] "filesystems" => array:3 [▶] "mail" => array:9 [▶] "queue" => array:3 [▶] "scout" => array:5 [▶] "services" => array:4 [▶] "session" => array:15 [▶] "view" => array:2 [▶] "trustedproxy" => array:2 [▶] ] …}
这个配置,也就是前面的getDefaultDriver()中拉出来用的配置。
当然,有兴趣的朋友可以继续研究loadConfigurationFiles。
至此,我们已经 全面了解这个session是如何启动的了。
- Laravel5.5源码详解 -- Session的启动分析
- Laravel5.5源码详解 -- 数据库的启动与连接过程
- Laravel5.5源码详解 -- 中间件MiddleWare分析
- Laravel5.5源码详解 -- Config 配置文件的加载
- laravel5.1启动详解
- Laravel5.x启动过程分析
- laravel5.2session的使用
- Laravel5.5源码详解 -- 一次查询的详细执行:从Auth-Login-web中间件到数据库查询结果的全过程
- laravel5.2中session的使用
- Laravel5.5的MVC
- Master启动的源码详解
- Laravel5之Session
- Laravel5中的Session
- Mesos源码分析(5): Mesos Master的启动之四
- memcached源码分析-----memcached启动参数详解以及关键配置的默认值
- netty源码分析之-服务端启动核心源码分析(5)
- U-Boot的启动过程源码分析
- mapreduce源码分析之TaskTracker的启动
- PHP中文件上传的功能模块实现
- 如何配置Sublime Text的LaTeXTools(Sublime Text + MikText + LaTeXTools + Sumatra PDF)
- 39. Combination Sum
- 【开发日记】马桶识别之数据收集,通过Python抓取天猫评论图片
- Python 从入门到实践 7-8 课后习题
- Laravel5.5源码详解 -- Session的启动分析
- AVL树(平衡树)
- 常见的进制转换
- vmware workstation 14 黑屏
- 895B
- Spring2.2——实例化Bean的3种方式
- Matlab代码-遍历文件夹下所有指定格式的图像
- CF900C:Remove Extra One(思维)
- oozie使用常见的bug解决