前言
寫框架的時候發現自己的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 協議》,轉載必須註明作者和本文連結