session()到底是做什么的
session是啥?
首先淑翼,我大致的知道,session是一次瀏覽器和服務(wù)器的交互的會(huì)話品追,會(huì)話是啥呢玄括?就是我問(wèn)候你好嗎?你回恩很好肉瓦。就是一次會(huì)話遭京,那么對(duì)話完成后,這次會(huì)話就結(jié)束了泞莉,還有我也知道哪雕,我們可以將一個(gè)變量存入全部的$_SESSION['name']中,這樣php的各個(gè)頁(yè)面和邏輯都能訪問(wèn)到鲫趁,所以很輕松的用來(lái)判斷是否登陸斯嚎。
這是我之前理解的session,當(dāng)然也是對(duì)的,只是解釋的太膚淺堡僻,理解的太表面了糠惫,面試官如果聽(tīng)到這樣的答案其實(shí)是不太滿意的。我參考了其他的很多資料钉疫,徹底理解清楚session硼讽。
在說(shuō)session是啥之前,我們先來(lái)說(shuō)說(shuō)為什么會(huì)出現(xiàn)session會(huì)話牲阁,它出現(xiàn)的機(jī)理是什么固阁?我們知道,我們用瀏覽器打開(kāi)一個(gè)網(wǎng)頁(yè)城菊,用到的是HTTP協(xié)議您炉,學(xué)過(guò)計(jì)算機(jī)的應(yīng)該都知道這個(gè)協(xié)議,它是無(wú)狀態(tài)的役电,什么是無(wú)狀態(tài)呢赚爵?就是說(shuō)這一次請(qǐng)求和上一次請(qǐng)求是沒(méi)有任何關(guān)系的,互不認(rèn)識(shí)的法瑟,沒(méi)有關(guān)聯(lián)的冀膝。但是這種無(wú)狀態(tài)的的好處是快速。
所以就會(huì)帶來(lái)一個(gè)問(wèn)題就是霎挟,我希望幾個(gè)請(qǐng)求的頁(yè)面要有關(guān)聯(lián)窝剖,比如:我在www.a.com/login.php里面登陸了,我在www.a.com/index.php 也希望是登陸狀態(tài)酥夭,但是赐纱,這是2個(gè)不同的頁(yè)面,也就是2個(gè)不同的HTTP請(qǐng)求熬北,這2個(gè)HTTP請(qǐng)求是無(wú)狀態(tài)的疙描,也就是無(wú)關(guān)聯(lián)的,所以無(wú)法單純的在index.php中讀取到它在login.php中已經(jīng)登陸了讶隐!
那咋搞呢起胰?我不可能這2個(gè)頁(yè)面我都去登陸一遍吧∥籽樱或者用笨方法這2個(gè)頁(yè)面都去查詢數(shù)據(jù)庫(kù)效五,如果有登陸狀態(tài),就判斷是登陸的了炉峰。這種查詢數(shù)據(jù)庫(kù)的方案雖然可行畏妖,但是每次都要去查詢數(shù)據(jù)庫(kù)不是個(gè)事,會(huì)造成數(shù)據(jù)庫(kù)的壓力疼阔。
所以正是這種訴求戒劫,這個(gè)時(shí)候半夷,一個(gè)新的客戶端存儲(chǔ)數(shù)據(jù)方式出現(xiàn)了:cookie。cookie是把少量的信息存儲(chǔ)在用戶自己的電腦上谱仪,它在一個(gè)域名下是一個(gè)全局的玻熙,只要設(shè)置它的存儲(chǔ)路徑在域名www.a.com下 ,那么當(dāng)用戶用瀏覽器訪問(wèn)時(shí)疯攒,php就可以從這個(gè)域名的任意頁(yè)面讀取cookie中的信息嗦随。所以就很好的解決了我在www.a.com/login.php頁(yè)面登陸了,我也可以在www.a.com/index.php獲取到這個(gè)登陸信息了敬尺。同時(shí)又不用反復(fù)去查詢數(shù)據(jù)庫(kù)枚尼。
雖然這種方案很不錯(cuò),也很快速方便砂吞,但是由于cookie 是存在用戶端署恍,而且它本身存儲(chǔ)的尺寸大小也有限,最關(guān)鍵是用戶可以是可見(jiàn)的蜻直,并可以隨意的修改盯质,很不安全。那如何又要安全,又可以方便的全局讀取信息呢?于是龄毡,這個(gè)時(shí)候,一種新的存儲(chǔ)會(huì)話機(jī)制:session 誕生了王悍。
我擦,終于把session是怎么誕生的給圓清楚了餐曼,不容易把勾ⅰ!T雌集惋!
好,session 誕生了瓶佳,從上面的描述來(lái)講芋膘,它就是在一次會(huì)話中解決2次HTTP的請(qǐng)求的關(guān)聯(lián),讓它們產(chǎn)生聯(lián)系霸饲,讓2兩個(gè)頁(yè)面都能讀取到找個(gè)這個(gè)全局的session信息。session信息存在于服務(wù)器端臂拓,所以也就很好的解決了安全問(wèn)題厚脉。
session的運(yùn)行機(jī)制和是怎么保存的?
既然胶惰,它也是一種服務(wù)區(qū)存儲(chǔ)數(shù)據(jù)的方式傻工,肯定也是存在服務(wù)器的某個(gè)地方了。確實(shí),它存在服務(wù)器的/tmp 目錄下中捆,這一點(diǎn)我們接下來(lái)慢慢講鸯匹。
我們先說(shuō)下它的運(yùn)行機(jī)制,是怎么分配的泄伪。我們主要用PHP中session的機(jī)制殴蓬,其實(shí)各種語(yǔ)言都差不多。
如果這個(gè)時(shí)候蟋滴,我們需要用到session染厅,那我們第一步怎么辦呢?第一步是開(kāi)啟session:
session_start();
這是個(gè)無(wú)任何返回值的函數(shù)津函,既不會(huì)報(bào)錯(cuò)肖粮,也不會(huì)成功。它的作用是開(kāi)啟session尔苦,并隨機(jī)生成一個(gè)唯一的32位的session_id涩馆,類似于這樣:
4c83638b3b0dbf65583181c2f89168ec
session的全部機(jī)制也是基于這個(gè)session_id,它用來(lái)區(qū)分哪幾次請(qǐng)求是一個(gè)人發(fā)出的允坚。為什么要這樣呢魂那?因?yàn)镠TTP是無(wú)狀態(tài)無(wú)關(guān)聯(lián)的,一個(gè)頁(yè)面可能會(huì)被成百上千人訪問(wèn)屋讶,而且每個(gè)人的用戶名是不一樣的冰寻,那么服務(wù)器如何區(qū)分這次是小王訪問(wèn)的,那次是小名訪問(wèn)的呢皿渗?所以就有了找個(gè)唯一的session_id 來(lái)綁定一個(gè)用戶斩芭。一個(gè)用戶在一次會(huì)話上就是一個(gè)session_id,這樣成千上萬(wàn)的人訪問(wèn)乐疆,服務(wù)器也能區(qū)分到底是誰(shuí)在訪問(wèn)了划乖。
我們做個(gè)試驗(yàn),看看挤土,是不是這樣的:
我們?cè)趐hp.iyangyi.com 域名下的a.php 頁(yè)面中琴庵,輸入如下代碼:
session_start(); echo "SID: ".SID."
"; echo "session_id(): ".session_id()."
"; echo "COOKIE: ".$_COOKIE["PHPSESSID"];
我們?cè)L問(wèn)一下a.php頁(yè)面,看能輸出什么仰美?
我們看到居然還有一個(gè)警告迷殿。我們先一個(gè)一個(gè)的看。首先SID這個(gè)常量咖杂,我們沒(méi)有給它賦值庆寺,它居然能有輸出,其次session_id()這個(gè)系統(tǒng)方法是輸出本次生成的session_id诉字。最后$_COOKIE['PHPSESSIID'] 沒(méi)有值懦尝,這個(gè)我們接下來(lái)說(shuō)知纷。
好,我們?cè)俅嗡⑿逻@個(gè)頁(yè)面陵霉,我們能看到什么琅轧?
奇怪的事情發(fā)生了。SID 沒(méi)有值了踊挠,$_COOKIE['PHPSESSID']中有值了乍桂。而且,2次刷新止毕,session_id 都是一樣
的:bjvlo4p38cfqkr1hr7pe924ts3模蜡,實(shí)際情況下,只要不關(guān)閉網(wǎng)頁(yè)扁凛,怎么刷新都是一樣:
既然我們看到COOKIE中有值了忍疾,我們,打開(kāi)firebug開(kāi)看到底是什么:
而且這個(gè)PHPSESSID的過(guò)期時(shí)間是會(huì)話谨朝,什么意思呢卤妒?就是瀏覽器只要不關(guān)就一直不存,瀏覽器一關(guān)就過(guò)期字币,消失了则披。
好,我們關(guān)掉瀏覽器洗出,重新打開(kāi)a.php頁(yè)面士复,看看有沒(méi)有什么變化:
你看翩活,是不是又回到當(dāng)初第一次打開(kāi)時(shí)候的樣子阱洪。
OK,解惑的時(shí)候到了:
每次我們?cè)L問(wèn)一個(gè)頁(yè)面菠镇,如果有開(kāi)啟session冗荸,也就是有session_start() 時(shí),就會(huì)自動(dòng)生成一個(gè)session_id 來(lái)標(biāo)注是這次會(huì)話的唯一ID利耍,同時(shí)也會(huì)自動(dòng)往cookie里寫入一個(gè)名字為PHPSESSID的變量蚌本,它的值正是session_id,當(dāng)這次會(huì)話沒(méi)結(jié)束隘梨,再次訪問(wèn)的時(shí)候程癌,服務(wù)器會(huì)去讀取這個(gè)PHPSESSID的cookie是否有值有沒(méi)過(guò)期,如果能夠讀取到轴猎,則繼續(xù)用這個(gè)session_id席楚,如果沒(méi)有,就會(huì)新生成一個(gè)session_id税稼,同時(shí)生成PHPSESSID這個(gè)cookie烦秩。由于默認(rèn)生成的這個(gè)PHPSESSID cookie是會(huì)話,也就是說(shuō)關(guān)閉瀏覽器就會(huì)過(guò)期掉郎仆,所以只祠,下次重新瀏覽時(shí),會(huì)重新生成一個(gè)session_id扰肌。
好抛寝,這個(gè)是session_id,就用來(lái)標(biāo)識(shí)綁定一個(gè)用戶的曙旭,既然session_id生成了盗舰。那么當(dāng)我們往session里面寫入數(shù)據(jù),是如何保存的桂躏,答案是保存在服務(wù)器的臨時(shí)目錄里钻趋,根據(jù)php.ini的配置,我機(jī)子上的這個(gè)session是存在D:\wamp\tmp 目錄里的剂习。我們先說(shuō)是存在這個(gè)目錄下蛮位,然后待會(huì)將如何修改。
那么它是怎么存的呢鳞绕?
同樣也是用到session_id失仁。session_id是32位的,服務(wù)器會(huì)用 sess_前綴 + session_id 的形式存在這個(gè)臨時(shí)目錄下们何,比如上面這個(gè)例子:
所以萄焦,每一次生成的session_id都會(huì)生成一個(gè)這樣的文件,用來(lái)保存這次會(huì)話的session信息冤竹。
我們往session里寫入些數(shù)據(jù)拂封,來(lái)看看session是怎么往這個(gè)文件里寫數(shù)據(jù)的,我們同樣在a.php頁(yè)面繼續(xù)加上寫入session的語(yǔ)句:
<pre style="font-family:Menlo, Monaco, Consolas, 'Courier New', monospace;font-size:14px;line-height:1.42857;color:rgb(51,51,51);border:1px solid rgb(204,204,204);">$_SESSION['hello'] = 123;</pre>
<pre style="font-family:Menlo, Monaco, Consolas, 'Courier New', monospace;font-size:14px;line-height:1.42857;color:rgb(51,51,51);border:1px solid rgb(204,204,204);">$_SESSION['word'] = 456;</pre>
然后贴见,我刷新頁(yè)面烘苹,由于我并沒(méi)有關(guān)閉頁(yè)面,就這是說(shuō)這次會(huì)話還沒(méi)結(jié)束片部,那么肯定還會(huì)是同樣的session_id : bjvlo4p38cfqkr1hr7pe924ts3
然后镣衡,我們 用編輯器打開(kāi)它的存儲(chǔ)文件sess_bgg20mcl86drbt3j08jg5h5h17這個(gè)文件,看看里面是啥?
<pre style="font-size:14px;line-height:22px;border:1px solid rgb(204,204,204);">hello|i:123;word|i:456;
</pre>
是序列化的數(shù)據(jù)档悠,我們?nèi)庋垡材茏x出來(lái)廊鸥。當(dāng)我們往$_SESSION全局變量里寫數(shù)據(jù)時(shí),它會(huì)自動(dòng)往這個(gè)文件里寫入辖所。讀取session的時(shí)候惰说,也會(huì)根據(jù)session_id 找到這個(gè)文件,然后讀取需要的session變量缘回。
這個(gè)sess文件不會(huì)隨著客戶端的PHPSESSID過(guò)期吆视,也一起過(guò)期掉典挑,它會(huì)一直存在,出息GC掃描到它過(guò)期或者使用session_destroy()函數(shù)摧毀啦吧,我們?cè)谙旅嬷v到session·回收的時(shí)候會(huì)說(shuō)到您觉。
我們大致總結(jié)下:
HTTP請(qǐng)求一個(gè)頁(yè)面后,如果用到開(kāi)啟session授滓,會(huì)去讀cookie中的PHPSESSID是否有琳水,如果沒(méi)有,則會(huì)新生成一個(gè)session_id般堆,先存入cookie中的PHPSESSID中在孝,再生成一個(gè)sess_前綴文件。當(dāng)有寫入$SESSION的時(shí)候淮摔,就會(huì)往sess文件里序列化寫入數(shù)據(jù)私沮。當(dāng)讀取的session變量的時(shí)候,先會(huì)讀取cookie中的PHPSESSID噩咪,獲得session_id顾彰,然后再去找這個(gè)sess_sessionid文件,來(lái)獲取對(duì)應(yīng)的數(shù)據(jù)胃碾。由于默認(rèn)的PHPSESSID是臨時(shí)的會(huì)話涨享,在瀏覽器關(guān)閉后,會(huì)消失仆百,所以厕隧,我們重新訪問(wèn)的時(shí)候,會(huì)新生成session_id和sess_這個(gè)文件俄周。
好吁讨。session生成和保存將清楚了。我們?cè)賮?lái)看前面提到的幾個(gè)變量:
<pre style="font-family:Menlo, Monaco, Consolas, 'Courier New', monospace;font-size:14px;line-height:1.42857;color:rgb(51,51,51);border:1px solid rgb(204,204,204);">echo "SID: ".SID."
";
echo "session_id(): ".session_id()."
";
echo "COOKIE: ".$_COOKIE["PHPSESSID"];
</pre>
SID 是一個(gè)系統(tǒng)常量峦朗,SID包含著會(huì)話名以及會(huì)話 ID 的常量建丧,格式為 "name=ID",或者如果會(huì)話 ID 已經(jīng)在適cookie 中設(shè)定時(shí)則為空字符串波势,第一次顯示的時(shí)候輸出的是SID的值翎朱,當(dāng)你刷新的時(shí)候,因?yàn)橐呀?jīng)在cookie中存在尺铣,所以顯示的是一個(gè)空字符串拴曲。
session_id() 函數(shù)用來(lái)返回當(dāng)前會(huì)話的session_id,它會(huì)去讀取cookie中的name凛忿,也就是PHPSESSID值澈灼。