Laravel 的 Session机制简介
2018-05-28
后端
前些天,为了解答一个问题,就去研究了Laravel的源码,讲讲我的收获:
这个是问题源:http://segmentfault.com/q/1010000003776645?_ea=365137 本文讲的
,它实际上是调用
FileHandler
`是如何读取,我们知道在这里读取了cookie中存放的SessionId就够了。 接下去看怎么加载数据
我们看
on
义的各种
到此为止,
ion`共享的目的也可以达到~
这个是问题源:http://segmentfault.com/q/1010000003776645?_ea=365137 本文讲的
Laravel
全部使用5.1
版本。 我们首先看Laravel是如何创建Session组件的。首先我们可以看见在Kernel.php
中注册了StartSession
这个类(这里不去讨论Laravel的DI和IoC),看下这个类是如何使用的。 Session Start
我们在这个类中的handle
方法看到如下代码 publicfunctionhandle($request, Closure $next){$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() ) {$session = $this->startSession($request);$request->setSession($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->collectGarbage($session);$this->addCookieToResponse($response, $session);
}return$response;
}
OK,一个典型的过滤器,在这个StartSession
中获取Session的方法是getSession
。 Get Session
它使用了Laravel
注入的SessionManager
这个类的完整限定名是:IlluminateSessionSessionManager
/**
* Get the session implementation from the manager.
* * @param IlluminateHttpRequest $request
* @return IlluminateSessionSessionInterface
*/publicfunctiongetSession(Request $request){$session = $this->manager->driver();$session->setId($request->cookies->get($session->getName()));return$session;
}
我们在这个函数中看到,它调用了SessionManager
的drive
方法,drive
方法是SessionManager
的父类Manager
中定义的,看到它的实现是这样的 /** * Get a driver instance.
*
* @param string $driver
* @return mixed
*/publicfunctiondriver($driver = null){$driver = $driver ?: $this->getDefaultDriver(); // If the given driver has not been created before, we will create the instances// here and cache it so we can return it next time very quickly. If there is// already a driver created by this name, we'll just return that instance.if ( !isset($this->drivers[ $driver ]) ) {$this->drivers[ $driver ] = $this->createDriver($driver);
}return$this->drivers[ $driver ];
}/**
* Create a new driver instance.
*
* @param string $driver * @return mixed *
* * @throws InvalidArgumentException
*/protectedfunctioncreateDriver($driver){$method = 'create' . ucfirst($driver) . '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);
} elseif ( method_exists($this, $method) ) {return$this->$method();
}thrownew InvalidArgumentException("Driver [$driver] not supported.");
}
也就是调用getDefaultDriver
方法 /** * Get the default session driver name. * * @return string */publicfunctiongetDefaultDriver(){return$this->app['config']['session.driver'];
}
这里就是我们在app/config/session.php
中定义的driver
字段,获取这个字段后,我们可以从createDriver
的源码看到,它实际上是调用
createXXXXDriver
的方式获取到driver
的。 OK,我们看下最简单的File
,也就是createFileDriver
/** * Create an instance of the file session driver.
* * @return IlluminateSessionStore
* */protectedfunctioncreateFileDriver(){return$this->createNativeDriver();
}/** * Create an instance of the file session driver.
* * @return IlluminateSessionStore
*/protectedfunctioncreateNativeDriver(){$path = $this->app['config']['session.files'];return$this->buildSession(new FileSessionHandler($this->app['files'], $path));
}/** * Build the session instance.
* * @param SessionHandlerInterface $handler * @return IlluminateSessionStore
*/protectedfunctionbuildSession($handler){if ( $this->app['config']['session.encrypt'] ) {returnnew EncryptedStore($this->app['config']['session.cookie'], $handler, $this->app['encrypter']);
} else {returnnew Store($this->app['config']['session.cookie'], $handler);
}
}
这个Store
就是我们Session
的实体了,它的具体读写调用使用抽象的Handler
进行,也就是说如果是读文件,就`newFileHandler
如果是Redis就是
new RedisHandler实现
read和
write`即可。 Session ID
回到StartSession
这个类,我们再看getSession
这个方法 $session->setId($request->cookies->get($session->getName()));
了解Web的人都应该知道,session是根据cookie中存的key来区分不同的会话的,所以sessionId
尤为重要,这里我们先不关心`Cookie`是如何读取,我们知道在这里读取了cookie中存放的SessionId就够了。 接下去看怎么加载数据
Data
依然是StartSession
这个类, 我们看
startSession
这个方法,下一句调用的就是$session->start();
,这句话是干嘛呢?经过前面的分析,我们知道`$session
已经是
Store对象了,那么它的
start`方法如下: publicfunctionstart(){$this->loadSession();if ( !$this->has('_token') ) {$this->regenerateToken();
}return$this->started = true;
}/** * Load the session data from the handler. * * @return void */protectedfunctionloadSession(){$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() ]);
}
}/** * Read the session data from the handler. * * @return array */protectedfunctionreadFromHandler(){$data = $this->handler->read($this->getId());if ( $data ) {$data = @unserialize($this->prepareForUnserialize($data));if ( $data !== false&& $data !== null&& is_array($data) ) {return$data;
}
}return [];
}
依次调用了loadSession
和readFromHandler
好,可以看到从handler
中调用了read
方法,这个方法就是从我们之前定义的各种
handler
中读取数据了,它是个抽象方法,在子类中具体实现,然后返回给Store(存入到Store
中的attributes
数组中去了,到此为止,
Session
的初始化方法都结束了, publicfunctionget($name, $default = null){return Arr::get($this->attributes, $name, $default);
}
这个函数去读取Session
中的数据了。 Summary
OK,Session整个初始化的过程总结下:StartSession
#handle
-> StartSession
#startSession
-> StartSession
#getSession
-> SessionManager
#getDefaultDriver
->SessionManager
#createXXXHandler
-> Store
-> Store
#setId
->Store
#startSession
Laravel
巧妙的使用了面向对象的接口
方式,为我们提供了各种各样不同的存储方式,一旦我们了解了存储方式和加密规则,让不同的web容器进行`Session`共享的目的也可以达到~