摘要:然后中間件使用方法來啟動獲取實例,使用類來管理主要分為兩步獲取實例,主要步驟是通過該實例從存儲介質中讀取該次請求所需要的數據,主要步驟是。
說明:本文主要通過學習Laravel的session源碼學習Laravel是如何設計session的,將自己的學習心得分享出來,希望對別人有所幫助。Laravel在web middleware中定義了session中間件IlluminateSessionMiddlewareStartSession::class,并通過該中間件來設計session,這個中間件的主要工作分為三步:(1)啟動session,通過session handler從一些存儲介質如redis中讀取session值;(2)操作session,對session數據CRUD增刪改查操作;(3)關閉session,把session_id寫入到response header中,默認是laravel_session。
開發環境:Laravel5.3 + PHP7
啟動Session首先看下IlluminateSessionMiddlewareStartSession::class中間件源碼中handle()方法:
public function handle($request, Closure $next) { // 前置操作 $this->sessionHandled = true; if ($this->sessionConfigured()) { // Start session. /** * @var IlluminateSessionStore $session */ $session = $this->startSession($request); $request->setSession($session); $this->collectGarbage($session); } $response = $next($request); // 后置操作 if ($this->sessionConfigured()) { $this->storeCurrentUrl($request, $session); $this->addCookieToResponse($response, $session); } return $response; }
從Laravel學習筆記之Middleware源碼解析這篇文章中知道,該中間件有前置操作和后置操作。看下sessionConfigured()的源碼:
/** * Determine if a session driver has been configured. * * @return bool */ protected function sessionConfigured() { // 檢查session.php中driver選項是否設置 return ! is_null(Arr::get($this->manager->getSessionConfig(), "driver")); } // IlluminateSessionSessionManager /** * Get the session configuration. * * @return array */ public function getSessionConfig() { return $this->app["config"]["session"]; }
首先中間件檢查session.php中driver選項是否設置,這里假設設置為經常使用的redis作為session的存儲介質,并且需要在database.php中設置下redis的鏈接,本地需要裝好redis,通過redis-cli命令查看redis是否已經安裝好。OK,然后中間件使用startSession()方法來啟動session:
protected function startSession(Request $request) { /** * @var IlluminateSessionStore $session */ $session = $this->getSession($request); // 獲取session實例,Laravel使用Store類來管理session $session->setRequestOnHandler($request); // Load the session data from the store repository by the handler. $session->start(); return $session; } public function getSession(Request $request) { /** * Get the session store instance via the driver. * * @var IlluminateSessionStore $session */ $session = $this->manager->driver(); /** * $session->getName() === "laravel_session" === config("session.cookie") */ $session->setId($request->cookies->get($session->getName())); return $session; }
startSession()主要分為兩步:獲取session實例IlluminateSessionStore,主要步驟是$session = $this->manager->driver();通過該實例從存儲介質中讀取該次請求所需要的session數據,主要步驟是$session->start()。首先看下第一步的源碼:
// IlluminateSupportManager public function driver($driver = null) { // $driver = "redis" $driver = $driver ?: $this->getDefaultDriver(); if (! isset($this->drivers[$driver])) { $this->drivers[$driver] = $this->createDriver($driver); } return $this->drivers[$driver]; } protected function createDriver($driver) { $method = "create".Str::studly($driver)."Driver"; if (isset($this->customCreators[$driver])) { return $this->callCustomCreator($driver); } elseif (method_exists($this, $method)) { // 判斷IlluminateSessionSessionManager中是否存在createRedisDriver()方法 // 存在,call這個createRedisDriver()方法 return $this->$method(); } throw new InvalidArgumentException("Driver [$driver] not supported."); } // IlluminateSessionSessionManager public function getDefaultDriver() { // 返回 "redis" return $this->app["config"]["session.driver"]; }
從以上源碼中很容易知道,選擇的driver是redis,最后還是要調用IlluminateSessionSessionManager中的createRedisDriver()方法:
protected function createRedisDriver() { /** * @var IlluminateSessionCacheBasedSessionHandler $handler */ $handler = $this->createCacheHandler("redis"); // 設置redis連接 $handler->getCache()->getStore()->setConnection($this->app["config"]["session.connection"]); return $this->buildSession($handler); } protected function createCacheHandler($driver) { // $store = "redis" $store = $this->app["config"]->get("session.store") ?: $driver; $minutes = $this->app["config"]["session.lifetime"]; // $this->app["cache"]->store($store)返回IlluminateCacheRepository實例 return new CacheBasedSessionHandler(clone $this->app["cache"]->store($store), $minutes); } // IlluminateSessionCacheBasedSessionHandler /** * Get the underlying cache repository. * * @return IlluminateContractsCacheRepository|IlluminateCacheRepository */ public function getCache() { return $this->cache; } // IlluminateCacheRepository /** * Get the cache store implementation. * * @return IlluminateContractsCacheStore|RedisStore */ public function getStore() { return $this->store; } // IlluminateCacheRedisStore /** * Set the connection name to be used. * * @param string $connection * @return void */ public function setConnection($connection) { $this->connection = $connection; }
從以上源碼知道獲取到IlluminateSessionCacheBasedSessionHandler這個handler后,就可以buildSession()了:
protected function buildSession($handler) { // 設置加密的則返回EncryptedStore實例,這里假設沒有加密 if ($this->app["config"]["session.encrypt"]) { return new EncryptedStore( $this->app["config"]["session.cookie"], $handler, $this->app["encrypter"] ); } else { // 默認$this->app["config"]["session.cookie"] === "laravel_session" return new Store($this->app["config"]["session.cookie"], $handler); } }
從源碼中可看出session實例就是IlluminateSessionStore實例,并且構造Store類還需要一個重要的部件handler,構造好了session實例后,就可以通過這個handler來從session存儲的介質中如redis獲取session數據了,這里設置的session driver是redis,所以handler就會是IlluminateSessionCacheBasedSessionHandler。總的來說,現在已經構造好了session實例即IlluminateSessionStore。
然后第二步就是$session->start()從存儲介質中加載session數據:
public function start() { // 從存儲介質中加載session數據 $this->loadSession(); // session存儲介質中沒有"_token"這個key就生成一個 if (! $this->has("_token")) { $this->regenerateToken(); } return $this->started = true; }
關鍵是loadSession()的源碼:
// Illuminate/Session/Store protected function loadSession() { // 從redis中讀取key為"laravel_session"的數據后存入session實例即Store的$attributes屬性中 $this->attributes = array_merge($this->attributes, $this->readFromHandler()); foreach (array_merge($this->bags, [$this->metaBag]) as $bag) { /** * @var SymfonyComponentHttpFoundationSessionStorageMetadataBag $bag */ $this->initializeLocalBag($bag); $bag->initialize($this->bagData[$bag->getStorageKey()]); } } protected function readFromHandler() { // 主要是這句,通過handler從存儲介質redis中讀取session數據 // $this->getId() === "laravel_session" $data = $this->handler->read($this->getId()); if ($data) { $data = @unserialize($this->prepareForUnserialize($data)); if ($data !== false && ! is_null($data) && is_array($data)) { return $data; } } return []; }
這里的handler是IlluminateSessionCacheBasedSessionHandler,看下該handler的read()源碼:
// $sessionId === "laravel_session" public function read($sessionId) { // 這里的cache是IlluminateCacheRepository return $this->cache->get($sessionId, ""); } // IlluminateCacheRepository public function get($key, $default = null) { if (is_array($key)) { return $this->many($key); } // 這里的store是IlluminateCacheRedisStore $value = $this->store->get($this->itemKey($key)); if (is_null($value)) { $this->fireCacheEvent("missed", [$key]); $value = value($default); } else { $this->fireCacheEvent("hit", [$key, $value]); } return $value; } // IlluminateCacheRedisStore public function get($key) { if (! is_null($value = $this->connection()->get($this->prefix.$key))) { return $this->unserialize($value); } }
通過以上代碼,很容易了解從redis存儲介質中加載key為"laravel_session"的數據,最后還是調用了RedisStore::get($key, $default)方法。
但不管咋樣,通過handle()第一步$session = $this->startSession($request);就得到了session實例即Store,該步驟中主要做了兩步:一是Store實例化;二是從redis中讀取key為"laravel_session"的數據。
然后就是$this->collectGarbage($session)做了垃圾回收。中篇再聊。
總結:本文主要學習了session機制的啟動工作中第一步session的實例化,主要包括兩步驟:Store的實例化;從redis中讀取key為laravel_session的數據。中篇再聊下session垃圾回收,和session的增刪改查操作,到時見。
歡迎關注Laravel-China。
RightCapital招聘Laravel DevOps
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/30519.html
摘要:說明在上篇中學習了的啟動過程,主要分為兩步,一是的實例化,即的實例化二是從存儲介質中讀取的數據。第二步就是操作,包括對數據的增刪改查操作,本文也主要聊下相關操作源碼。下篇再學習下關閉,到時見。 說明:在上篇中學習了session的啟動過程,主要分為兩步,一是session的實例化,即IlluminateSessionStore的實例化;二是從session存儲介質redis中讀取id ...
摘要:實際上,在中關閉主要包括兩個過程保存當前到介質中在中存入。,學習下關閉的源碼吧先。總之,關閉的第二件事就是給添加。通過對的源碼分析可看出共分為三大步啟動操作關閉。總結本小系列主要學習了的源碼,學習了的三大步。 說明:在中篇中學習了session的CRUD增刪改查操作,本篇主要學習關閉session的相關源碼。實際上,在Laravel5.3中關閉session主要包括兩個過程:保存當前U...
摘要:總結本文主要學習了啟動時做的七步準備工作環境檢測配置加載日志配置異常處理注冊注冊啟動。 說明:Laravel在把Request通過管道Pipeline送入中間件Middleware和路由Router之前,還做了程序的啟動Bootstrap工作,本文主要學習相關源碼,看看Laravel啟動程序做了哪些具體工作,并將個人的研究心得分享出來,希望對別人有所幫助。Laravel在入口index...
摘要:說明本文主要學習容器的實例化過程,主要包括等四個過程。看下的源碼如果是數組,抽取別名并且注冊到中,上文已經討論實際上就是的。 說明:本文主要學習Laravel容器的實例化過程,主要包括Register Base Bindings, Register Base Service Providers , Register Core Container Aliases and Set the ...
摘要:實際上的綁定主要有三種方式且只是一種的,這些已經在學習筆記之實例化源碼解析聊過,其實現方法并不復雜。從以上源碼發現的反射是個很好用的技術,這里給出個,看下能干些啥打印結果太長了,就不粘貼了。 說明:本文主要學習Laravel中Container的源碼,主要學習Container的綁定和解析過程,和解析過程中的依賴解決。分享自己的研究心得,希望對別人有所幫助。實際上Container的綁...
閱讀 474·2021-10-09 09:57
閱讀 477·2019-08-29 18:39
閱讀 818·2019-08-29 12:27
閱讀 3032·2019-08-26 11:38
閱讀 2672·2019-08-26 11:37
閱讀 1298·2019-08-26 10:59
閱讀 1385·2019-08-26 10:58
閱讀 995·2019-08-26 10:48