session()到底是做什么的
session是啥?
首先,我大致的知道,session是一次瀏覽器和服務(wù)器的交互的會(huì)話,會(huì)話是啥呢?就是我問候你好嗎?你回恩很好。就是一次會(huì)話,那么對(duì)話完成后,這次會(huì)話就結(jié)束了,還有我也知道,我們可以將一個(gè)變量存入全部的$_SESSION['name']中,這樣php的各個(gè)頁面和邏輯都能訪問到,所以很輕松的用來判斷是否登陸。
這是我之前理解的session,當(dāng)然也是對(duì)的,只是解釋的太膚淺,理解的太表面了,面試官如果聽到這樣的答案其實(shí)是不太滿意的。我參考了其他的很多資料,徹底理解清楚session。
在說session是啥之前,我們先來說說為什么會(huì)出現(xiàn)session會(huì)話,它出現(xiàn)的機(jī)理是什么?我們知道,我們用瀏覽器打開一個(gè)網(wǎng)頁,用到的是HTTP協(xié)議,學(xué)過計(jì)算機(jī)的應(yīng)該都知道這個(gè)協(xié)議,它是無狀態(tài)的,什么是無狀態(tài)呢?就是說這一次請(qǐng)求和上一次請(qǐng)求是沒有任何關(guān)系的,互不認(rèn)識(shí)的,沒有關(guān)聯(lián)的。但是這種無狀態(tài)的的好處是快速。
所以就會(huì)帶來一個(gè)問題就是,我希望幾個(gè)請(qǐng)求的頁面要有關(guān)聯(lián),比如:我在www.a.com/login.php里面登陸了,我在www.a.com/index.php 也希望是登陸狀態(tài),但是,這是2個(gè)不同的頁面,也就是2個(gè)不同的HTTP請(qǐng)求,這2個(gè)HTTP請(qǐng)求是無狀態(tài)的,也就是無關(guān)聯(lián)的,所以無法單純的在index.php中讀取到它在login.php中已經(jīng)登陸了!
那咋搞呢?我不可能這2個(gè)頁面我都去登陸一遍吧?;蛘哂帽糠椒ㄟ@2個(gè)頁面都去查詢數(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)用戶用瀏覽器訪問時(shí),php就可以從這個(gè)域名的任意頁面讀取cookie中的信息。所以就很好的解決了我在www.a.com/login.php頁面登陸了,我也可以在www.a.com/index.php獲取到這個(gè)登陸信息了。同時(shí)又不用反復(fù)去查詢數(shù)據(jù)庫(kù)。
雖然這種方案很不錯(cuò),也很快速方便,但是由于cookie 是存在用戶端,而且它本身存儲(chǔ)的尺寸大小也有限,最關(guān)鍵是用戶可以是可見的,并可以隨意的修改,很不安全。那如何又要安全,又可以方便的全局讀取信息呢?于是,這個(gè)時(shí)候,一種新的存儲(chǔ)會(huì)話機(jī)制:session 誕生了。
我擦,終于把session是怎么誕生的給圓清楚了,不容易?。。?!
好,session 誕生了,從上面的描述來講,它就是在一次會(huì)話中解決2次HTTP的請(qǐng)求的關(guān)聯(lián),讓它們產(chǎn)生聯(lián)系,讓2兩個(gè)頁面都能讀取到找個(gè)這個(gè)全局的session信息。session信息存在于服務(wù)器端,所以也就很好的解決了安全問題。
session的運(yùn)行機(jī)制和是怎么保存的?
既然,它也是一種服務(wù)區(qū)存儲(chǔ)數(shù)據(jù)的方式,肯定也是存在服務(wù)器的某個(gè)地方了。確實(shí),它存在服務(wù)器的/tmp 目錄下,這一點(diǎn)我們接下來慢慢講。
我們先說下它的運(yùn)行機(jī)制,是怎么分配的。我們主要用PHP中session的機(jī)制,其實(shí)各種語言都差不多。
如果這個(gè)時(shí)候,我們需要用到session,那我們第一步怎么辦呢?第一步是開啟session:
session_start();
這是個(gè)無任何返回值的函數(shù),既不會(huì)報(bào)錯(cuò),也不會(huì)成功。它的作用是開啟session,并隨機(jī)生成一個(gè)唯一的32位的session_id,類似于這樣:
4c83638b3b0dbf65583181c2f89168ec
session的全部機(jī)制也是基于這個(gè)session_id,它用來區(qū)分哪幾次請(qǐng)求是一個(gè)人發(fā)出的。為什么要這樣呢?因?yàn)镠TTP是無狀態(tài)無關(guān)聯(lián)的,一個(gè)頁面可能會(huì)被成百上千人訪問,而且每個(gè)人的用戶名是不一樣的,那么服務(wù)器如何區(qū)分這次是小王訪問的,那次是小名訪問的呢?所以就有了找個(gè)唯一的session_id 來綁定一個(gè)用戶。一個(gè)用戶在一次會(huì)話上就是一個(gè)session_id,這樣成千上萬的人訪問,服務(wù)器也能區(qū)分到底是誰在訪問了。
我們做個(gè)試驗(yàn),看看,是不是這樣的:
我們?cè)趐hp.iyangyi.com 域名下的a.php 頁面中,輸入如下代碼:
session_start(); echo "SID: ".SID."
"; echo "session_id(): ".session_id()."
"; echo "COOKIE: ".$_COOKIE["PHPSESSID"];
我們?cè)L問一下a.php頁面,看能輸出什么?
我們看到居然還有一個(gè)警告。我們先一個(gè)一個(gè)的看。首先SID這個(gè)常量,我們沒有給它賦值,它居然能有輸出,其次session_id()這個(gè)系統(tǒng)方法是輸出本次生成的session_id。最后$_COOKIE['PHPSESSIID'] 沒有值,這個(gè)我們接下來說。
好,我們?cè)俅嗡⑿逻@個(gè)頁面,我們能看到什么?
奇怪的事情發(fā)生了。SID 沒有值了,$_COOKIE['PHPSESSID']中有值了。而且,2次刷新,session_id 都是一樣
的:bjvlo4p38cfqkr1hr7pe924ts3,實(shí)際情況下,只要不關(guān)閉網(wǎng)頁,怎么刷新都是一樣:
既然我們看到COOKIE中有值了,我們,打開firebug開看到底是什么:
而且這個(gè)PHPSESSID的過期時(shí)間是會(huì)話,什么意思呢?就是瀏覽器只要不關(guān)就一直不存,瀏覽器一關(guān)就過期,消失了。
好,我們關(guān)掉瀏覽器,重新打開a.php頁面,看看有沒有什么變化:
你看,是不是又回到當(dāng)初第一次打開時(shí)候的樣子。
OK,解惑的時(shí)候到了:
每次我們?cè)L問一個(gè)頁面,如果有開啟session,也就是有session_start() 時(shí),就會(huì)自動(dòng)生成一個(gè)session_id 來標(biāo)注是這次會(huì)話的唯一ID,同時(shí)也會(huì)自動(dòng)往cookie里寫入一個(gè)名字為PHPSESSID的變量,它的值正是session_id,當(dāng)這次會(huì)話沒結(jié)束,再次訪問的時(shí)候,服務(wù)器會(huì)去讀取這個(gè)PHPSESSID的cookie是否有值有沒過期,如果能夠讀取到,則繼續(xù)用這個(gè)session_id,如果沒有,就會(huì)新生成一個(gè)session_id,同時(shí)生成PHPSESSID這個(gè)cookie。由于默認(rèn)生成的這個(gè)PHPSESSID cookie是會(huì)話,也就是說關(guān)閉瀏覽器就會(huì)過期掉,所以,下次重新瀏覽時(shí),會(huì)重新生成一個(gè)session_id。
好,這個(gè)是session_id,就用來標(biāo)識(shí)綁定一個(gè)用戶的,既然session_id生成了。那么當(dāng)我們往session里面寫入數(shù)據(jù),是如何保存的,答案是保存在服務(wù)器的臨時(shí)目錄里,根據(jù)php.ini的配置,我機(jī)子上的這個(gè)session是存在D:\wamp\tmp 目錄里的。我們先說是存在這個(gè)目錄下,然后待會(huì)將如何修改。
那么它是怎么存的呢?
同樣也是用到session_id。session_id是32位的,服務(wù)器會(huì)用 sess_前綴 + session_id 的形式存在這個(gè)臨時(shí)目錄下,比如上面這個(gè)例子:
所以,每一次生成的session_id都會(huì)生成一個(gè)這樣的文件,用來保存這次會(huì)話的session信息。
我們往session里寫入些數(shù)據(jù),來看看session是怎么往這個(gè)文件里寫數(shù)據(jù)的,我們同樣在a.php頁面繼續(xù)加上寫入session的語句:
<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>
然后,我刷新頁面,由于我并沒有關(guān)閉頁面,就這是說這次會(huì)話還沒結(jié)束,那么肯定還會(huì)是同樣的session_id : bjvlo4p38cfqkr1hr7pe924ts3
然后,我們 用編輯器打開它的存儲(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出來。當(dāng)我們往$_SESSION全局變量里寫數(shù)據(jù)時(shí),它會(huì)自動(dòng)往這個(gè)文件里寫入。讀取session的時(shí)候,也會(huì)根據(jù)session_id 找到這個(gè)文件,然后讀取需要的session變量。
這個(gè)sess文件不會(huì)隨著客戶端的PHPSESSID過期,也一起過期掉,它會(huì)一直存在,出息GC掃描到它過期或者使用session_destroy()函數(shù)摧毀,我們?cè)谙旅嬷v到session·回收的時(shí)候會(huì)說到。
我們大致總結(jié)下:
HTTP請(qǐng)求一個(gè)頁面后,如果用到開啟session,會(huì)去讀cookie中的PHPSESSID是否有,如果沒有,則會(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文件,來獲取對(duì)應(yīng)的數(shù)據(jù)。由于默認(rèn)的PHPSESSID是臨時(shí)的會(huì)話,在瀏覽器關(guān)閉后,會(huì)消失,所以,我們重新訪問的時(shí)候,會(huì)新生成session_id和sess_這個(gè)文件。
好。session生成和保存將清楚了。我們?cè)賮砜辞懊嫣岬降膸讉€(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ù)用來返回當(dāng)前會(huì)話的session_id,它會(huì)去讀取cookie中的name,也就是PHPSESSID值。