執行php腳本,session_start()會從php.ini中讀取配置項,將生成的惟一值sessionID保存文件放到配置項中的保存路徑和地點。並經過HTTP協議返回響應消息頭setCookie
Cookie名=Cookie值發送給客戶端。
客戶端接受到Set-Cookie,將cookie值寫入cookie文件
在接下來的訪問中,客戶端會攜帶cookie訪問瀏覽器,瀏覽器接受到cookie值,生成http請求頭將包含的COOKIE發送給Php,php識別cookie值,從保存路徑中找對應的文件。
找到文件以後,檢驗是否在有效期內,在有效期內就讀取文件,再也不有效期內就清空文件
vendor/laravel/framework/src/Illuminate/Contracts定義都是接口類(契約)。向開發者提供了統一的接口類訪問Session數據
看一下session接口定義的是什麼?
<?php namespace Illuminate\Contracts\Session; interface Session { public function getName();//得到session名字 public function getId();//得到當前sessionID public function setId($id);//修改sessionID public function start();//啓動session,從配置文件中讀取數據 public function save();//將session數據保存 public function all();//得到全部的session public function exists($key);//檢查session名稱是否存在 public function has($key);//檢查session名稱是否存在和不爲空 public function get($key, $default = null);//經過session名稱獲取session值 public function put($key, $value = null);//將session名稱和session值存入session文件或數據庫 public function token(); //得到csrf token的值 public function remove($key);//根據session名稱刪除session信息 public function forget($keys);//根據session名稱刪除session信息 public function flush();//清空全部的session內容 public function migrate($destroy = false); //爲session會話建立一個新的session會話 public function isStarted();//肯定會話是否啓動 public function previousUrl();//從會話中獲取session的url public function setPreviousUrl($url);//設置previous的url存入session public function getHandler();//得到session處理實例 public function handlerNeedsRequest();//肯定會話程序是否須要請求 public function setRequestOnHandler($request);//在處理請求實例上設置請求 }
實現接口類的具體實現類只有一個,也被稱爲驅動器
vendor/laravel/framework/src/Illuminate/Session/Store.php
<?php namespace Illuminate\Session; use Closure; use stdClass; use Illuminate\Support\Arr; use Illuminate\Support\Str; use SessionHandlerInterface; use Illuminate\Contracts\Session\Session;
class Store implements Session { protected $id;//session id protected $name;//session name protected $attributes = [];//session protected $handler;//會話處理程序實現,使用了sessionHandlerInterface接口 protected $started = false;//會話存儲處理狀態
public function __construct($name, SessionHandlerInterface $handler, $id = null)//建立一個新的session實例 { $this->setId($id); $this->name = $name; $this->handler = $handler; //setId //$this->id = $this->isValidId($id) ? $id : $this->generateSessionId(); //一、 $this->isValidId($id)? //return is_string($id) && ctype_alnum($id) && strlen($id) === 40; //is_string($id)檢查sessionid是否是字符串,ctype_alnum檢查sessionid全部的字符全是字母和數字,是爲true,否爲false;strlen檢查字符串長度是否等於40 //二、$this->generateSessionId();? //return Str::random(40); 這個使用Str門面類生成長度爲40的惟一值 //handler使用的是php預留接口類SessionnHandlerInterface,這個接口是爲了將session存儲到數據庫中,(難怪在laravel查半天都沒查到)。該類的回調方法是在php內部調用。 }
來源於vendorlaravelframeworksrcIlluminateSupportStr.php
laravel實現生成session惟一值算法函數,重點是使用了random_bytes函數和base64_encode函數
public static function random($length = 16) { $string = ''; while (($len = strlen($string)) < $length) {//當它的長度小於$length時 $size = $length - $len;//長度差 $bytes = random_bytes($size);//根據size生成加密安全的僞隨機字節字符串 $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);//base64_encode對其進行編碼,目的時爲了使二進制數據能夠經過非純8比特的傳輸層傳輸,經過str_replace函數去掉字符串的/+=3種符號,在用substr截取$size大小的字符串。 } return $string; }
來源於php手冊中的關於SessionHandlerInterface接口的定義
SessionHandlerInterface { /* 方法 */ abstract public close ( void ) : bool abstract public destroy ( string $session_id ) : bool abstract public gc ( int $maxlifetime ) : int abstract public open ( string $save_path , string $session_name ) : bool abstract public read ( string $session_id ) : string abstract public write ( string $session_id , string $session_data ) :bool }
看看都有那些類實現了SessionHandler,在編程軟件中全局搜索implements SessionHandlerInterface//CacheBasedSessionHandler 使用的實例化對象是
Illuminate/Contracts/Cache/Repository
'cache.store' => [IlluminateCacheRepository::class,
IlluminateContractsCacheRepository::class],php//CookieSessionHandler
//DatabaseSessionHandler
//FileSessionHandler
//NullSessionHandlerlaravel
//根據session id讀取數據 public function start() { $this->loadSession(); //驗證csrf_token if (! $this->has('_token')) { $this->regenerateToken(); } return $this->started = true; }
protected function loadSession() { //array_merge合併兩個數組 //readFromHandler() 從驅動器中根據session id獲得session信息 $this->attributes = array_merge($this->attributes, $this->readFromHandler()); }
protected function readFromHandler() { if ($data = $this->handler->read($this->getId())) { //@阻止警告輸出 //unserialize反序列化讀出來的session的id信息 $data = @unserialize($this->prepareForUnserialize($data)); //若是數組不是錯誤,不爲空,是數組就返回數據 if ($data !== false && ! is_null($data) && is_array($data)) { return $data; } } return []; }
//返回數據 protected function prepareForUnserialize($data) { return $data; }
public function save() { $this->ageFlashData(); $this->handler->write($this->getId(), $this->prepareForStorage( serialize($this->attributes) )); $this->started = false; }
protected function prepareForStorage($data) { return $data; }
//使會話閃存存數據老化 public function ageFlashData() { $this->forget($this->get('_flash.old', [])); //其餘文件定義的forget方法 > public static function forget(&$array, $keys) > { > $original = &$array; > > $keys = (array) $keys; > > if (count($keys) === 0) { > return; > } > > foreach ($keys as $key) { > // if the exact key exists in the top-level, remove it > if (static::exists($array, $key)) { > unset($array[$key]); > > continue; > } > > $parts = explode('.', $key); > > // clean up before each pass > $array = &$original; > > while (count($parts) > 1) { > $part = array_shift($parts); > > if (isset($array[$part]) && is_array($array[$part])) { > $array = &$array[$part]; > } else { > continue 2; > } > } > > unset($array[array_shift($parts)]); > } > } $this->put('_flash.old', $this->get('_flash.new', [])); $this->put('_flash.new', []); }
public function all() { return $this->attributes; }
public function exists($key) { $placeholder = new stdClass; return ! collect(is_array($key) ? $key : func_get_args())->contains(function ($key) use ($placeholder) { return $this->get($key, $placeholder) === $placeholder; }); }
//檢查是否存在Key值 public function has($key) { return ! collect(is_array($key) ? $key : func_get_args())->contains(function ($key) { return is_null($this->get($key)); }); }
//獲取session信息 public function get($key, $default = null) { return Arr::get($this->attributes, $key, $default); }
//刪除,相似pop public function pull($key, $default = null) { return Arr::pull($this->attributes, $key, $default); }
public function hasOldInput($key = null) { $old = $this->getOldInput($key); return is_null($key) ? count($old) > 0 : ! is_null($old); }
public function getOldInput($key = null, $default = null) { return Arr::get($this->get('_old_input', []), $key, $default); }
public function replace(array $attributes) { $this->put($attributes); }
//session永久保存,在不過時範圍內 public function put($key, $value = null) { if (! is_array($key)) { $key = [$key => $value]; } foreach ($key as $arrayKey => $arrayValue) { Arr::set($this->attributes, $arrayKey, $arrayValue); } }
public function remember($key, Closure $callback) { if (! is_null($value = $this->get($key))) { return $value; } return tap($callback(), function ($value) use ($key) { $this->put($key, $value); }); }
//相似於push public function push($key, $value) { $array = $this->get($key, []); $array[] = $value; $this->put($key, $array); }
public function increment($key, $amount = 1) { $this->put($key, $value = $this->get($key, 0) + $amount); return $value; }
public function decrement($key, $amount = 1) { return $this->increment($key, $amount * -1); }
//快閃保存,只保存兩次請求 public function flash(string $key, $value = true) { $this->put($key, $value); $this->push('_flash.new', $key); $this->removeFromOldFlashData([$key]); }
public function now($key, $value) { $this->put($key, $value); $this->push('_flash.old', $key); }
public function reflash() { $this->mergeNewFlashes($this->get('_flash.old', [])); $this->put('_flash.old', []); }
//刷新快閃數據時間,保持到下次請求 public function keep($keys = null) { $this->mergeNewFlashes($keys = is_array($keys) ? $keys : func_get_args()); $this->removeFromOldFlashData($keys); }
protected function mergeNewFlashes(array $keys) { $values = array_unique(array_merge($this->get('_flash.new', []), $keys)); $this->put('_flash.new', $values); }
protected function removeFromOldFlashData(array $keys) { $this->put('_flash.old', array_diff($this->get('_flash.old', []), $keys)); }
public function flashInput(array $value) { $this->flash('_old_input', $value); }
public function remove($key) { return Arr::pull($this->attributes, $key); }
public function forget($keys) { Arr::forget($this->attributes, $keys); }
//每次flush將數組置空 public function flush() { $this->attributes = []; }
public function invalidate() { $this->flush(); return $this->migrate(true); }
public function regenerate($destroy = false) { return tap($this->migrate($destroy), function () { $this->regenerateToken(); }); }
//給session生成一個新的sessionID public function migrate($destroy = false) { //若是 if ($destroy) { $this->handler->destroy($this->getId()); } $this->setExists(false); $this->setId($this->generateSessionId()); return true; } //是否啓動 public function isStarted() { return $this->started; } //獲取session名 public function getName() { return $this->name; } //設置session 名 public function setName($name) { $this->name = $name; } //獲取session id public function getId() { return $this->id; } //若是sessionid有效,就返回有效id,若是失效就生成session id public function setId($id) { $this->id = $this->isValidId($id) ? $id : $this->generateSessionId(); } //檢查session id public function isValidId($id) { return is_string($id) && ctype_alnum($id) && strlen($id) === 40; } //獲取sessionID 惟一的40長度值 protected function generateSessionId() { return Str::random(40); } //若是適用,在處理程序上設置會話的存在性 public function setExists($value) { if ($this->handler instanceof ExistenceAwareInterface) { $this->handler->setExists($value); } } //獲取token值 public function token() { return $this->get('_token'); } //生成惟一值40,存入_token public function regenerateToken() { $this->put('_token', Str::random(40)); } //獲取當前url public function previousUrl() { return $this->get('_previous.url'); } //存儲當前url public function setPreviousUrl($url) { $this->put('_previous.url', $url); } //獲取當前使用的驅動器 public function getHandler() { return $this->handler; } //返回驅動器實例是否使CokieSessionHandler public function handlerNeedsRequest() { return $this->handler instanceof CookieSessionHandler; } //若是驅動器是CookieSessionHandler,那麼執行setRequest方法 public function setRequestOnHandler($request) { if ($this->handlerNeedsRequest()) { $this->handler->setRequest($request); } } }