PHP5 Session 使用詳解(一)


http協議是WEB服務器與客戶端(瀏覽器)相互通信的協議,它是一種無狀態協議。所謂無 狀態,指的是不會維護http請求數據,http請求是獨立的,不持久的。而越來越複雜的WEB應用,需要保存一些用戶狀態信息。這時候,Session 這種方案應需而生。PHP從4.1開始支持Session管理。

Session!它是很抽象的一個概念。我們不妨先從與它幾個息息相關的有跡可尋的小切入點入手,然後逐漸地認識瞭解Session!

一、Session存儲

首先,我們爲什麼需要Session,就是因爲我們需要:存儲各個用戶的狀態數據。那麼試問,如果由你來設計解決這個需求的方案,那麼也許你會設置這樣一個數據表用與存儲各個用戶的狀態信息

uid created data max_age
94c55770fdf044a7 1270802787 jtUsername=admin 14400
2c37df64277e4409 1270822787 jtUsername=Joe;jtBooks=8; 14400

uid:用戶唯一標識符,區分其它用戶, created :記錄產生時間,data: 存放與用戶相關的數據, max_age :記錄的有效時間

同樣地,PHP設計管理Session方案也大致如此,它分別包含了以下信息:

1.session id

用戶session唯一標識符,隨機生成的一串字符串,具有唯一性,隨機性。主要用於區分其它用戶的session數據。用戶第一次訪問Web頁面的時候,PHP的session初始化函數調用會分配給當前來訪用戶一個唯一的ID,也稱之爲session_id。

2.session data

我們把需要通過session保存的用戶狀態信息,稱爲用戶session數據,也稱爲session數據。一般是在當前session生命週期,相應用的 $_SESSION數據。

3.session file

PHP 默認將session數據存放在一個文件裏。我們把存放session數據的文件稱爲session文件。它由特殊的php.ini設置 session.save_path指定session文件的存放路徑,CentOS5.3操作系統,PHP5.1默認存放在 /var/lib/php/session 目錄中。用戶session文件的名稱,就是以sess_爲前綴,以session_id爲結尾命名,比如session id爲vp8lfqnskjvsiilcp1c4l484d3,那麼session文件名就是 sess_vp8lfqnskjvsiilcp1c4l484d3。

4.session lifetime

我們把初始化session開始,直到註銷session這段期間,稱爲session生命週期,這樣有助於我們理解session管理函數。

由 此,我們可見:當每個用戶訪問Web, PHP的session初始化函數都會給當前來訪用戶分配一個唯一的session ID。並且在session生命週期結束的時候,將用戶在此週期產生的session數據持久到session文件中。用戶再次訪問的時 候,session初始化函數,又會從session文件中讀取session數據,開始新的session生命週期。

二、與session存儲相關php.ini設置

1.session.save_handler = file

用於讀取/回寫session數據的方式,默認是files。它會讓PHP的session管理函數使用指定的文本文件存儲session數據。

2.session.save_path = "/var/lib/php/session"

指定保存session文件的目錄,可以指定到別的目錄,但是指定目錄必須要有httpd守護進程屬主(比如apache或www等)寫權限,否則無法回存session數據。當指定目錄不存在時,php session環境初始化函數是不會幫你創建指定目錄的,所以需要你手工建立指定目錄。

它還可以寫成這樣session.save_path = "N;/path" 其中N是整數。這樣使得不是所有的session文件都保存在同一個目錄中,而是分散在不同目錄。這對於服務器處理大量session文件是很有幫助的。(注:目錄需要自己手工創建)

3.session.auto_start = 0

如果啓用該選項,用戶的每次請求都會初始化session。我們推薦不啓用該設置,最好通過session_start()顯示地初始化session。

三、Session同步數據

一旦調用了session_start()初始化session,就意味着開始了一個session生命週期。也就是宣佈了,可以使用相關函數操作$_SESSION來管理session數據。這個session生命週期產生的數據並沒有 實時地寫入session文件,而是通過$_SESSION變量寄存在內存中。那麼,寄存在內存的數據什麼時候會寫入到session文件?這也是我們這 一小節的主要測試內容。

在進行測試之前,先讓我們介紹幾個影響session數據的PHP函數、或事件:

1.session_start()

函數session_start會初始化session,也標識着session生命週期的開始。要使用session,必須初始化一個session環境。有點類似於OOP概念中調用構造函數構創建對象實例一樣。

session初始化操作,聲明一個全局數組$_SESSION,映射寄存在內存的session數據。如果session文件已經存在,並且保存有session數 據,session_start()則會讀取session數據,填入$_SESSION中,開始一個新的session生命週期。

2.$_SESSION

它是一個全局變量,類型是Array,映射了session生命週期的session數據,寄存在內存中。在session初始化的時候,從session文件中讀取數據,填入該變量中。在session生命週期結束時,將$_SESSION數據寫回session文件。

3. session_register()

在 session生命週期內,使用全局變量名稱將注全局變量註冊到當前session中。所謂註冊,就是將變量填入$_SESSION中,值爲NULL。它 不會對session文件進行任何IO操作,只是影響$_SESSION變量。注意,它的正確寫法是 session_register('varname'),而不是session_register($varname)。

4.session_unregister()

與session_register操作正好相反,即在session生命週期,從當前session註銷指定變量。同樣隻影響$_SESSION,並不進行任何IO操作。

5.session_unset()

在 session生命週期,從當前session中註銷全部session數據,讓$_SESSION成爲一個空數組。它與 unset($_SESSION)的區別在於:unset直接刪除$_SESSION變量,釋放內存資源;另一個區別在 於:session_unset()僅在session生命週期能夠操作$_SESSION數組,而unset()則在整個頁面(page)生命週期都能操作$_SESSION數組。session_unset()同樣不進行任何IO操作,隻影響$_SESSION數組。

6.session_destroy()

如果說session_start()初始化一個session的話,而它則註銷一個session。意味着session生命週期結束了。在 session生命週期結束後,session_register, session_unset, session_unregister都將不能操作$_SESSION數組,而$_SESSION數組依然可以被unset()等函數操作。這 時,session意味着是未定義的,而$_SESSION依然是一個全局變量,他們脫離了關映射關係。

通過session_destroy()註銷session,除了結束session生命週期外,它還會刪除sesion文件,但不會影響當前$_SESSION變量。即它會產生一個IO操作。

7.session_regenerate_id()

調 用它,會給當前用戶重新分配一個新的session id。並且在結束當前頁面生命週期的時候,將當前session數據寫入session文件。前提是,調用此函數之前,當前session生命週期沒有被 終止(參考第9點)。它會產生一個IO操作,創建一個新的session文件,創建新的session文件的是在session結束之前,而不是調用此函 數就立即創建新的session文件。

8.session_commit()

session_commit()函數是 session_write_close()函數的別名。它會結束當前session的生命週期,並且將session數據立即強制寫入session文 件。不推薦通過session_commit()來手工寫入session數據,因爲PHP會在頁面生命週期結束的時候,自動結束當前沒有終止的 session生命週期。它會產生一個IO寫操作。

9.end session

結束session,默認是在頁面生命週期結束的之 前,PHP會自動結束當前沒有終止的session。但是還可以通過session_commit()與session_destroy()二個函數提前結束session。不管是哪種方式,結束session都會產生IO操作,分別不一樣。默認情況,產生一個IO寫操作,將當前session數據寫回 session文件。session_commit()則是調用該函數那刻,產生一個IO寫操作,將session數據寫回session文件。而session_destroy()不一樣在於,它不會將數據寫回session文件,而是直接刪除當前session文件。有趣的是,不管是 session_commit(),還是session_destroy()都不會清空$_SESSION數組,更不會刪除$_SESSION數組,只是所有session_*函數不能再操作session數據,因爲當前的session生命週期終止了,即不能操作一個未定義對象。

爲了驗證以上陳述,我們可以做以下測試

任務一:觀察session初始化與默認結束session的時候,產生的IO操作  

1
2
3
4
5
6
7
8
9
<?php
//@file test_session_2.php
session_start();
$pg_uuid 'ac606826-9620-490b-b850-ea9dbce6cfd5';
//註冊全局變量pg_uuid到session,但$_SESSION['pg_uuid']值爲NULL,隻影響$_SESSION
session_register('pg_uuid');
var_dump($_SESSION);
fopen(__FILE__"r");
?>
[root@localhost ~]# strace -p `cat /var/run/httpd.pid`
Process 21819 attached - interrupt to quit
...
st_mode=S_IFREG|0644, st_size=72, ...}) = 0
open("/var/www/html/test_session.php", O_RDONLY) = 17
fstat64(17, {st_mode=S_IFREG|0644, st_size=72, ...}) = 0
lseek(17, 0, SEEK_CUR)                  = 0
read(17, "<p;?php
//@file test_session.php
s"..., 8192) = 72
read(17, "", 8192)                      = 0
read(17, "", 8192)                      = 0
close(17)                               = 0
gettimeofday({1270906664, 11602}, NULL) = 0
open("/var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5", O_RDWR|O_CREAT, 0600) = 17
flock(17, LOCK_EX)                      = 0
fcntl64(17, F_SETFD, FD_CLOEXEC)        = 0
fstat64(17, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
time(NULL)                              = 1270906664
open("/var/www/html/test_session.php", O_RDONLY) = 18
fstat64(18, {st_mode=S_IFREG|0644, st_size=72, ...}) = 0
lseek(18, 0, SEEK_CUR)                  = 0
close(18)                               = 0
chdir("/var/lib/php/session") = 0
pwrite64(17, "", 0, 0) = 0
close(17) = 0
setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={0, 0}}, NULL) = 0
writev(16, [{"HTTP/1.1 200 OK
Date: Sat, 10 A"..., 385}], 1) = 385
write(12, "192.168.0.98 - - [10/Apr/2010:21"..., 207) = 207
shutdown(16, 1 /* send */)              = 0
epoll_wait(15,

藍色加粗,通過系統內核函數open調用打開session文件,這是由 session_start()產生的調用,注意這裏並沒有產生讀文件操作。紅色部分,將一個空字符串寫入session文件。由此可見session初 始化在頁面生命週期開始之時,手工調用session_start可以初始化session文件,而在頁面生命週期結束之時,會自動地註銷 session,結束當前session生命週期,同時在此週期產生的session數據寫回session文件,我們把這種方式結束的session, 稱爲session默認結束。

任務2.觀察session_register()查看它是否會產生磁盤操作,還是隻操作$_SESSION。

1
2
3
4
5
6
7
8
<?php
//@file test_session_2.php
session_start();
$pg_uuid 'ac606826-9620-490b-b850-ea9dbce6cfd5';
session_register('pg_uuid');    //註冊全局變量pg_uuid到session,但值爲NULL,隻影響$_SESSION
var_dump($_SESSION);
fopen(__FILE__"r");
?>
[root@localhost ~]# strace -p `cat /var/run/httpd.pid`
Process 21819 attached - interrupt to quit
...
open("/var/www/html/test_session_2.php", O_RDONLY) = 17
fstat64(17, {st_mode=S_IFREG|0644, st_size=148, ...}) = 0
lseek(17, 0, SEEK_CUR)                  = 0
read(17, "<?php
session_start();
$pg_uuid "..., 8192) = 148
read(17, "", 8192)                      = 0
read(17, "", 8192)                      = 0
close(17)                               = 0
open("/var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5", O_RDWR|O_CREAT, 0600) = 17
flock(17, LOCK_EX)                      = 0
fcntl64(17, F_SETFD, FD_CLOEXEC)        = 0
fstat64(17, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
time(NULL)                              = 1270907613
open("/var/www/html/test_session_2.php", O_RDONLY) = 18
fstat64(18, {st_mode=S_IFREG|0644, st_size=148, ...}) = 0
lseek(18, 0, SEEK_CUR)                  = 0
close(18)                               = 0
chdir("/var/lib/php/session")           = 0
pwrite64(17, "pg_uuid|N;", 10, 0)       = 10
close(17)                               = 0
setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={0, 0}}, NULL) = 0
writev(16, [{"HTTP/1.1 200 OK
Date: Sat, 10 A"..., 328}, {"array(1) {
  ["pg_uuid"]=>
  NUL"..., 36}], 2) = 364
write(12, "192.168.0.98 - - [10/Apr/2010:21"..., 210) = 210
shutdown(16, 1 /* send */)              = 0
epoll_wait(15,

通過上面的觀察,藍色部分還是由session初始化(session_start)產生,注意這裏依然沒讀文件操作,這是因爲session文件爲空。 紅色部分,依然是默認結束session產生的文件寫操作(pwrite)。由此,我們可以知道session_register()不會對 session文件操作,即不會把$_SESSION中的數據寫回session文件,它沒有產生任何IO操作。而只在session生命週期是影響當 前$_SESSION變量,即$_SESSION[‘pg_uuid’] = NULL。所以,推薦使用$_SESSION['pg_uuid'] = $pg_uuid;

任務3.觀察session_destroy()與session_unset()的區別

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php
session_start();
echo "<br/>---1--<br/>";
$pg_uid = 1;
//$_SESSION['pg_uid'];      //該行會報一個Notice消息,即沒有初始化該變量
$_SESSION['pg_name'] = 'boys';  //填入到$_SESSION變量,但不立即寫入session文件,值爲boys
$pg_sex = 1;
$pg_theme 'default';
session_register('pg_sex'); //填入到$_SESSION變量,但不立即寫入session文件,值爲NULL
session_register('pg_theme');   //填入到$_SESSION變量,但不立即寫入session文件,值爲NULL
var_dump($_SESSION);
 
//--
echo "<br/>---2--<br/>";
unset($_SESSION['pg_theme']);   //從$_SESSION清除該元素,不立即同步到session文件
unset($_SESSION['pg_name']);    //從$_SESSION清除該元素,不立即同步到session文件
session_unregister('pg_sex');   //從$_SESSION清除該元素,不立即同步到session文件
session_unregister('pg_uid');   //從$_SESSION清除該元素,不立即同步到session文件
var_dump($_SESSION);
 
echo "<br/>---3--<br/>";
$_SESSION['pg_members'] = 5;    //填入$_SESSION數組,但不立即同步到session文件,值爲5
$pg_boy = 6;
session_register('pg_boy'); //填入$_SESSION數組,但不立即同步到session文件,值爲NULL
session_unset($_SESSION);   //清空$_SESSION
var_dump($_SESSION);
 
echo "<br/>---4--<br/>";
$_SESSION['pg_boss'] = 3;   //填入$_SESSION數組,但不立即同步到session文件,值爲3
$pg_girls = 6;
session_register('pg_girls');   //填入$_session數組,但不立即同步到session文件,值爲NULL
session_destroy();      //註銷session_destroy
var_dump($_SESSION);
 
echo "<br/>---5---<br/>";
session_unregister('pg_boss');  //pg_boss不會被清除,還爲NULL
session_unset();        //不會清空$_SESSION數組,因爲session已被session_destroy註銷
var_dump($_SESSION);
 
fopen(__FILE__"r");
 
//@這裏是頁面析構的時候-- 本應該將$_SESSION數據同步到session文件, 真的嗎???
//@事實,沒有發生任何IO操作,即沒有將$_SESSION數據回寫,怎麼回事???
//@因爲被session_destroy()消毀了session...
?>

程序輸出:

---1--
array(3) { ["pg_name"]=> string(4) "boys" ["pg_sex"]=> NULL ["pg_theme"]=> NULL }
---2--
array(0) { }
---3--
array(0) { }
---4--
array(2) { ["pg_boss"]=> int(3) ["pg_girls"]=> NULL }
---5---
array(2) { ["pg_boss"]=> int(3) ["pg_girls"]=> NULL }
 
[root@localhost ~]# strace -p `cat /var/run/httpd.pid`
Process 21819 attached - interrupt to quit
...
open("/var/www/html/test_session_3.php", O_RDONLY) = 17
fstat64(17, {st_mode=S_IFREG|0644, st_size=706, ...}) = 0
lseek(17, 0, SEEK_CUR)                  = 0
read(17, "<?php
session_start();
echo "<br"..., 8192) = 706
read(17, "", 8192)                      = 0
read(17, "", 8192)                      = 0
close(17)                               = 0
open("/var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5", O_RDWR|O_CREAT, 0600) = 17
flock(17, LOCK_EX)                      = 0
fcntl64(17, F_SETFD, FD_CLOEXEC)        = 0
fstat64(17, {st_mode=S_IFREG|0600, st_size=10, ...}) = 0
pread64(17, "pg_uuid|N;", 10, 0) = 10
close(17)                               = 0
unlink("/var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5") = 0
time(NULL)                              = 1270910665
open("/var/www/html/test_session_3.php", O_RDONLY) = 17
fstat64(17, {st_mode=S_IFREG|0644, st_size=706, ...}) = 0
lseek(17, 0, SEEK_CUR)                  = 0
close(17)                               = 0
chdir("/var/lib/php/session")           = 0
setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={0, 0}}, NULL) = 0
...
write(12, "192.168.0.98 - - [10/Apr/2010:22"..., 211) = 211
shutdown(16, 1 /* send */)              = 0

藍色部分是我們熟悉的session初始化的時候產生的open系統內核調用。綠色部分,是一個IO讀操作,因爲上一次訪問頁面的時候,產生了 session數據,所以這一次會將上次的session填入$_SESSION中。紅色部分,可以看出,這裏調用unlink刪除session文件, 而且後面(頁面生命週期結束時),一直沒有看到前兩例看到的任何與session文件有關的IO寫操作,即沒有將$_SESSION中的數據寫回 session文件。我們也沒有在session.save_path找到相應的session文件。

[root@localhost html]# ls /var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5
ls: /var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5: No such file or directory

注:雖然刪除了session文件,但用戶再次訪問web的時候,並不會給用戶重新分配一個新的session id,而是依然用該session id,並且會重新創建文件名相同的session文件,即sess_SESSION-ID。

任務4:測試並觀察session_regenerate_id行爲,以及$_SESSION的變化

1
2
3
4
5
6
7
8
<?php
session_start();
$_SESSION['pfid'] = 123;
var_dump($_SESSION);
session_regenerate_id();
var_dump($_SESSION);
fopen(__FILE__"r");
?>
[root@localhost ~]# strace -p `cat /var/run/httpd.pid`
Process 22641 attached - interrupt to quit
...
open("/var/www/html/test_session_4.php", O_RDONLY) = 17
fstat64(17, {st_mode=S_IFREG|0644, st_size=141, ...}) = 0
lseek(17, 0, SEEK_CUR)                  = 0
read(17, "<?php
session_start();
$_SESSION"..., 8192) = 141
read(17, "", 8192)                      = 0
read(17, "", 8192)                      = 0
close(17)                               = 0
open("/var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5", O_RDWR|O_CREAT, 0600) = 17
flock(17, LOCK_EX)                      = 0
fcntl64(17, F_SETFD, FD_CLOEXEC)        = 0
fstat64(17, {st_mode=S_IFREG|0600, st_size=11, ...}) = 0
pread64(17, "pfid|i:123;", 11, 0)       = 11
gettimeofday({1270915896, 122016}, NULL) = 0
time(NULL)                              = 1270915896
open("/var/www/html/test_session_4.php", O_RDONLY) = 18
fstat64(18, {st_mode=S_IFREG|0644, st_size=141, ...}) = 0
lseek(18, 0, SEEK_CUR)                  = 0
close(18)                               = 0
chdir("/var/lib/php/session")           = 0
close(17)                               = 0
open("/var/lib/php/session/sess_qoa6knu9fg77un8le99o1vk1c7", O_RDWR|O_CREAT, 0600) = 17
flock(17, LOCK_EX)                      = 0
fcntl64(17, F_SETFD, FD_CLOEXEC)        = 0
pwrite64(17, "pfid|i:123;", 11, 0)      = 11
close(17)                               = 0
setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={0, 0}}, NULL) = 0
writev(16, [{"HTTP/1.1 200 OK
Date: Sat, 10 A"..., 386}, {"array(1) {
  ["pfid"]=>
  int(12"..., 75}], 2) = 461
write(12, "192.168.0.98 - - [11/Apr/2010:00"..., 210) = 210
shutdown(16, 1 /* send */)              = 0

請注意觀察,藍色部分, session初始化打開的session文件是sess_4j38nv7l1fq1bj6n80l6g9cum5,這也驗證了上一例的陳述。而綠色加粗 部分,而這次打開的是新session文件sess_qoa6knu9fg77un8le99o1vk1c7,並且將session數據寫入到了新的 session文件中。值得注意的是,打開新文件的時候,是發生在fopen(__FILE__,  'r')之後,在回寫session數據到session文件之前open的新session文件。可 見,session_regenerate_id()會給當前用戶重新分配一個新的session id,並將當前session數據寫入新的session文件,另外不會刪除舊的session文件。通過文件列表,也驗證了,正是如此

[root@localhost html]# ls -lt /var/lib/php/session/
-rw------- 1 apache apache 11 Apr 11 00:11 sess_qoa6knu9fg77un8le99o1vk1c7
-rw------- 1 apache apache 11 Apr 11 00:08 sess_4j38nv7l1fq1bj6n80l6g9cum5
-rw------- 1 apache apache  0 Apr 10 17:37 sess_ktj0giniplqf51nravl1fsga72

任務5.:測試並觀察session_commit()是否會像session_destroy註銷session,什麼時候進行IO寫操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
session_start();
echo "-----<br />---";
var_dump($SESSION);
 
echo "<br />-----<br />---";
$SESSION['pfguest'] = 'guest';
session_commit();
var_dump($SESSION);
 
echo "<br />-----<br />---";
session_unset();
var_dump($SESSION);
 
fopen(__FILE__'r');
?>
程序輸出如下:
-----1---array(1) { ["pfid"]=> int(123) }
-----2---array(2) { ["pfid"]=> int(123) ["pfguest"]=> string(5) "guest" }
-----3---array(2) { ["pfid"]=> int(123) ["pfguest"]=> string(5) "guest" }

[root@localhost ~]# strace -p `cat /var/run/httpd.pid`
Process 22641 attached - interrupt to quit
...
open("/var/www/html/test_session_5.php", O_RDONLY) = 17
fstat64(17, {st_mode=S_IFREG|0644, st_size=246, ...}) = 0
lseek(17, 0, SEEK_CUR)                  = 0
read(17, "<?php
session_start();
echo "---"..., 8192) = 246
read(17, "", 8192)                      = 0
read(17, "", 8192)                      = 0
close(17)                               = 0
open("/var/lib/php/session/sess_qoa6knu9fg77un8le99o1vk1c7", O_RDWR|O_CREAT, 0600) = 17
flock(17, LOCK_EX)                      = 0
fcntl64(17, F_SETFD, FD_CLOEXEC)        = 0
fstat64(17, {st_mode=S_IFREG|0600, st_size=11, ...}) = 0
pread64(17, "pfid|i:123;", 11, 0)       = 11
pwrite64(17, "pfid|i:123;pfguest|s:5:"guest";", 31, 0) = 31
close(17)                               = 0
time(NULL)                              = 1270927098
open("/var/www/html/test_session_5.php", O_RDONLY) = 17
fstat64(17, {st_mode=S_IFREG|0644, st_size=246, ...}) = 0
lseek(17, 0, SEEK_CUR)                  = 0
close(17)                               = 0
chdir("/var/lib/php/session")           = 0
setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={0, 0}}, NULL) = 0
writev(16, [{"HTTP/1.1 200 OK
Date: Sat, 10 A"..., 329}, {"-----1---array(1) {
  ["pfid"]=>"..., 222}], 2) = 551
write(12, "192.168.0.98 - - [11/Apr/2010:03"..., 211) = 211
shutdown(16, 1 /* send */)              = 0

觀察測試結果,藍色部分照舊是session初始化的時候產生的系統內核open調用,接着綠色部分是一個IO讀操作,即讀取session文件中的數 據,由第一個var_dump($_SESSION)輸出。隨後,往session加入新的一條已定義了的session記錄,並且通過 session_commit()將記錄寫回去。紅色部分就是由session_commit產生的一次IO寫操作。之 後,session_unset()並沒有生效,同時,我們也沒有在頁面生命週期結束的時候看到任何與session文件有關的IO寫操作。這也正說明 了,session_commit()調用的當下,就會將session數據寫回session文件,並且會像session_destroy一樣註銷 session,但與session_destroy不同的時,session_commit不會刪除session文件,而且會將當前的session 數據寫回session文件。我們可以查看,調用session_commit之後,session文件還是依然存在的

[root@localhost html]# ls -lt /var/lib/php/session
-rw------- 1 apache apache 31 Apr 11 03:18 sess_qoa6knu9fg77un8le99o1vk1c7
-rw------- 1 apache apache 11 Apr 11 00:08 sess_4j38nv7l1fq1bj6n80l6g9cum5
…

總結

1. 用戶註銷web應用系統,最好的調用方式依次是 session_unset();  session_destroy();  unset($_SESSION);

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
function user_sigout() {
  global $user;
  sys_event_register('user_sigout'$user);
 
  session_unset();          //清空session
  session_destroy();            //刪除session文件
  if (isset($_SESSION)) {
    unset($_SESSIONI);  //註銷$_SESSION
  }
  return TRUE;
}
?>

2. 儘量將鍵與值填入$_SESSION,而不推薦使用session_register()。同樣,儘量使用unset($_SESSION[‘var’]),而不使用session_unregister()。

3. 對於可能產生大量session的WEB應用,推薦使用的session.save_path的格式是session.save_path=”N:/path”。注意:這些目錄需要手工創建,並且有httpd守護進程屬主寫權限。這樣做可以獲得更好的性能

4. 如果調用了session_regenerate_id()給用戶分配了新的session id。該函數並不會主動刪除舊的session文件,需要定時清理舊的session文件,這樣更優化。

5. 儘量不要使用session_commit()提交sessioin數據,因爲它同時會結束當前session,PHP默認會在頁面生命週期的時候提交session數據到session文件


摘自:http://www.cnblogs.com/52php/p/5658197.html

http協議是WEB服務器與客戶端(瀏覽器)相互通信的協議,它是一種無狀態協議。所謂無 狀態,指的是不會維護http請求數據,http請求是獨立的,不持久的。而越來越複雜的WEB應用,需要保存一些用戶狀態信息。這時候,Session 這種方案應需而生。PHP從4.1開始支持Session管理。

Session!它是很抽象的一個概念。我們不妨先從與它幾個息息相關的有跡可尋的小切入點入手,然後逐漸地認識瞭解Session!

一、Session存儲

首先,我們爲什麼需要Session,就是因爲我們需要:存儲各個用戶的狀態數據。那麼試問,如果由你來設計解決這個需求的方案,那麼也許你會設置這樣一個數據表用與存儲各個用戶的狀態信息

uid created data max_age
94c55770fdf044a7 1270802787 jtUsername=admin 14400
2c37df64277e4409 1270822787 jtUsername=Joe;jtBooks=8; 14400

uid:用戶唯一標識符,區分其它用戶, created :記錄產生時間,data: 存放與用戶相關的數據, max_age :記錄的有效時間

同樣地,PHP設計管理Session方案也大致如此,它分別包含了以下信息:

1.session id

用戶session唯一標識符,隨機生成的一串字符串,具有唯一性,隨機性。主要用於區分其它用戶的session數據。用戶第一次訪問Web頁面的時候,PHP的session初始化函數調用會分配給當前來訪用戶一個唯一的ID,也稱之爲session_id。

2.session data

我們把需要通過session保存的用戶狀態信息,稱爲用戶session數據,也稱爲session數據。一般是在當前session生命週期,相應用的 $_SESSION數據。

3.session file

PHP 默認將session數據存放在一個文件裏。我們把存放session數據的文件稱爲session文件。它由特殊的php.ini設置 session.save_path指定session文件的存放路徑,CentOS5.3操作系統,PHP5.1默認存放在 /var/lib/php/session 目錄中。用戶session文件的名稱,就是以sess_爲前綴,以session_id爲結尾命名,比如session id爲vp8lfqnskjvsiilcp1c4l484d3,那麼session文件名就是 sess_vp8lfqnskjvsiilcp1c4l484d3。

4.session lifetime

我們把初始化session開始,直到註銷session這段期間,稱爲session生命週期,這樣有助於我們理解session管理函數。

由 此,我們可見:當每個用戶訪問Web, PHP的session初始化函數都會給當前來訪用戶分配一個唯一的session ID。並且在session生命週期結束的時候,將用戶在此週期產生的session數據持久到session文件中。用戶再次訪問的時 候,session初始化函數,又會從session文件中讀取session數據,開始新的session生命週期。

二、與session存儲相關php.ini設置

1.session.save_handler = file

用於讀取/回寫session數據的方式,默認是files。它會讓PHP的session管理函數使用指定的文本文件存儲session數據。

2.session.save_path = "/var/lib/php/session"

指定保存session文件的目錄,可以指定到別的目錄,但是指定目錄必須要有httpd守護進程屬主(比如apache或www等)寫權限,否則無法回存session數據。當指定目錄不存在時,php session環境初始化函數是不會幫你創建指定目錄的,所以需要你手工建立指定目錄。

它還可以寫成這樣session.save_path = "N;/path" 其中N是整數。這樣使得不是所有的session文件都保存在同一個目錄中,而是分散在不同目錄。這對於服務器處理大量session文件是很有幫助的。(注:目錄需要自己手工創建)

3.session.auto_start = 0

如果啓用該選項,用戶的每次請求都會初始化session。我們推薦不啓用該設置,最好通過session_start()顯示地初始化session。

三、Session同步數據

一旦調用了session_start()初始化session,就意味着開始了一個session生命週期。也就是宣佈了,可以使用相關函數操作$_SESSION來管理session數據。這個session生命週期產生的數據並沒有 實時地寫入session文件,而是通過$_SESSION變量寄存在內存中。那麼,寄存在內存的數據什麼時候會寫入到session文件?這也是我們這 一小節的主要測試內容。

在進行測試之前,先讓我們介紹幾個影響session數據的PHP函數、或事件:

1.session_start()

函數session_start會初始化session,也標識着session生命週期的開始。要使用session,必須初始化一個session環境。有點類似於OOP概念中調用構造函數構創建對象實例一樣。

session初始化操作,聲明一個全局數組$_SESSION,映射寄存在內存的session數據。如果session文件已經存在,並且保存有session數 據,session_start()則會讀取session數據,填入$_SESSION中,開始一個新的session生命週期。

2.$_SESSION

它是一個全局變量,類型是Array,映射了session生命週期的session數據,寄存在內存中。在session初始化的時候,從session文件中讀取數據,填入該變量中。在session生命週期結束時,將$_SESSION數據寫回session文件。

3. session_register()

在 session生命週期內,使用全局變量名稱將注全局變量註冊到當前session中。所謂註冊,就是將變量填入$_SESSION中,值爲NULL。它 不會對session文件進行任何IO操作,只是影響$_SESSION變量。注意,它的正確寫法是 session_register('varname'),而不是session_register($varname)。

4.session_unregister()

與session_register操作正好相反,即在session生命週期,從當前session註銷指定變量。同樣隻影響$_SESSION,並不進行任何IO操作。

5.session_unset()

在 session生命週期,從當前session中註銷全部session數據,讓$_SESSION成爲一個空數組。它與 unset($_SESSION)的區別在於:unset直接刪除$_SESSION變量,釋放內存資源;另一個區別在 於:session_unset()僅在session生命週期能夠操作$_SESSION數組,而unset()則在整個頁面(page)生命週期都能操作$_SESSION數組。session_unset()同樣不進行任何IO操作,隻影響$_SESSION數組。

6.session_destroy()

如果說session_start()初始化一個session的話,而它則註銷一個session。意味着session生命週期結束了。在 session生命週期結束後,session_register, session_unset, session_unregister都將不能操作$_SESSION數組,而$_SESSION數組依然可以被unset()等函數操作。這 時,session意味着是未定義的,而$_SESSION依然是一個全局變量,他們脫離了關映射關係。

通過session_destroy()註銷session,除了結束session生命週期外,它還會刪除sesion文件,但不會影響當前$_SESSION變量。即它會產生一個IO操作。

7.session_regenerate_id()

調 用它,會給當前用戶重新分配一個新的session id。並且在結束當前頁面生命週期的時候,將當前session數據寫入session文件。前提是,調用此函數之前,當前session生命週期沒有被 終止(參考第9點)。它會產生一個IO操作,創建一個新的session文件,創建新的session文件的是在session結束之前,而不是調用此函 數就立即創建新的session文件。

8.session_commit()

session_commit()函數是 session_write_close()函數的別名。它會結束當前session的生命週期,並且將session數據立即強制寫入session文 件。不推薦通過session_commit()來手工寫入session數據,因爲PHP會在頁面生命週期結束的時候,自動結束當前沒有終止的 session生命週期。它會產生一個IO寫操作。

9.end session

結束session,默認是在頁面生命週期結束的之 前,PHP會自動結束當前沒有終止的session。但是還可以通過session_commit()與session_destroy()二個函數提前結束session。不管是哪種方式,結束session都會產生IO操作,分別不一樣。默認情況,產生一個IO寫操作,將當前session數據寫回 session文件。session_commit()則是調用該函數那刻,產生一個IO寫操作,將session數據寫回session文件。而session_destroy()不一樣在於,它不會將數據寫回session文件,而是直接刪除當前session文件。有趣的是,不管是 session_commit(),還是session_destroy()都不會清空$_SESSION數組,更不會刪除$_SESSION數組,只是所有session_*函數不能再操作session數據,因爲當前的session生命週期終止了,即不能操作一個未定義對象。

爲了驗證以上陳述,我們可以做以下測試

任務一:觀察session初始化與默認結束session的時候,產生的IO操作  

1
2
3
4
5
6
7
8
9
<?php
//@file test_session_2.php
session_start();
$pg_uuid 'ac606826-9620-490b-b850-ea9dbce6cfd5';
//註冊全局變量pg_uuid到session,但$_SESSION['pg_uuid']值爲NULL,隻影響$_SESSION
session_register('pg_uuid');
var_dump($_SESSION);
fopen(__FILE__"r");
?>
[root@localhost ~]# strace -p `cat /var/run/httpd.pid`
Process 21819 attached - interrupt to quit
...
st_mode=S_IFREG|0644, st_size=72, ...}) = 0
open("/var/www/html/test_session.php", O_RDONLY) = 17
fstat64(17, {st_mode=S_IFREG|0644, st_size=72, ...}) = 0
lseek(17, 0, SEEK_CUR)                  = 0
read(17, "<p;?php
//@file test_session.php
s"..., 8192) = 72
read(17, "", 8192)                      = 0
read(17, "", 8192)                      = 0
close(17)                               = 0
gettimeofday({1270906664, 11602}, NULL) = 0
open("/var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5", O_RDWR|O_CREAT, 0600) = 17
flock(17, LOCK_EX)                      = 0
fcntl64(17, F_SETFD, FD_CLOEXEC)        = 0
fstat64(17, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
time(NULL)                              = 1270906664
open("/var/www/html/test_session.php", O_RDONLY) = 18
fstat64(18, {st_mode=S_IFREG|0644, st_size=72, ...}) = 0
lseek(18, 0, SEEK_CUR)                  = 0
close(18)                               = 0
chdir("/var/lib/php/session") = 0
pwrite64(17, "", 0, 0) = 0
close(17) = 0
setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={0, 0}}, NULL) = 0
writev(16, [{"HTTP/1.1 200 OK
Date: Sat, 10 A"..., 385}], 1) = 385
write(12, "192.168.0.98 - - [10/Apr/2010:21"..., 207) = 207
shutdown(16, 1 /* send */)              = 0
epoll_wait(15,

藍色加粗,通過系統內核函數open調用打開session文件,這是由 session_start()產生的調用,注意這裏並沒有產生讀文件操作。紅色部分,將一個空字符串寫入session文件。由此可見session初 始化在頁面生命週期開始之時,手工調用session_start可以初始化session文件,而在頁面生命週期結束之時,會自動地註銷 session,結束當前session生命週期,同時在此週期產生的session數據寫回session文件,我們把這種方式結束的session, 稱爲session默認結束。

任務2.觀察session_register()查看它是否會產生磁盤操作,還是隻操作$_SESSION。

1
2
3
4
5
6
7
8
<?php
//@file test_session_2.php
session_start();
$pg_uuid 'ac606826-9620-490b-b850-ea9dbce6cfd5';
session_register('pg_uuid');    //註冊全局變量pg_uuid到session,但值爲NULL,隻影響$_SESSION
var_dump($_SESSION);
fopen(__FILE__"r");
?>
[root@localhost ~]# strace -p `cat /var/run/httpd.pid`
Process 21819 attached - interrupt to quit
...
open("/var/www/html/test_session_2.php", O_RDONLY) = 17
fstat64(17, {st_mode=S_IFREG|0644, st_size=148, ...}) = 0
lseek(17, 0, SEEK_CUR)                  = 0
read(17, "<?php
session_start();
$pg_uuid "..., 8192) = 148
read(17, "", 8192)                      = 0
read(17, "", 8192)                      = 0
close(17)                               = 0
open("/var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5", O_RDWR|O_CREAT, 0600) = 17
flock(17, LOCK_EX)                      = 0
fcntl64(17, F_SETFD, FD_CLOEXEC)        = 0
fstat64(17, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
time(NULL)                              = 1270907613
open("/var/www/html/test_session_2.php", O_RDONLY) = 18
fstat64(18, {st_mode=S_IFREG|0644, st_size=148, ...}) = 0
lseek(18, 0, SEEK_CUR)                  = 0
close(18)                               = 0
chdir("/var/lib/php/session")           = 0
pwrite64(17, "pg_uuid|N;", 10, 0)       = 10
close(17)                               = 0
setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={0, 0}}, NULL) = 0
writev(16, [{"HTTP/1.1 200 OK
Date: Sat, 10 A"..., 328}, {"array(1) {
  ["pg_uuid"]=>
  NUL"..., 36}], 2) = 364
write(12, "192.168.0.98 - - [10/Apr/2010:21"..., 210) = 210
shutdown(16, 1 /* send */)              = 0
epoll_wait(15,

通過上面的觀察,藍色部分還是由session初始化(session_start)產生,注意這裏依然沒讀文件操作,這是因爲session文件爲空。 紅色部分,依然是默認結束session產生的文件寫操作(pwrite)。由此,我們可以知道session_register()不會對 session文件操作,即不會把$_SESSION中的數據寫回session文件,它沒有產生任何IO操作。而只在session生命週期是影響當 前$_SESSION變量,即$_SESSION[‘pg_uuid’] = NULL。所以,推薦使用$_SESSION['pg_uuid'] = $pg_uuid;

任務3.觀察session_destroy()與session_unset()的區別

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php
session_start();
echo "<br/>---1--<br/>";
$pg_uid = 1;
//$_SESSION['pg_uid'];      //該行會報一個Notice消息,即沒有初始化該變量
$_SESSION['pg_name'] = 'boys';  //填入到$_SESSION變量,但不立即寫入session文件,值爲boys
$pg_sex = 1;
$pg_theme 'default';
session_register('pg_sex'); //填入到$_SESSION變量,但不立即寫入session文件,值爲NULL
session_register('pg_theme');   //填入到$_SESSION變量,但不立即寫入session文件,值爲NULL
var_dump($_SESSION);
 
//--
echo "<br/>---2--<br/>";
unset($_SESSION['pg_theme']);   //從$_SESSION清除該元素,不立即同步到session文件
unset($_SESSION['pg_name']);    //從$_SESSION清除該元素,不立即同步到session文件
session_unregister('pg_sex');   //從$_SESSION清除該元素,不立即同步到session文件
session_unregister('pg_uid');   //從$_SESSION清除該元素,不立即同步到session文件
var_dump($_SESSION);
 
echo "<br/>---3--<br/>";
$_SESSION['pg_members'] = 5;    //填入$_SESSION數組,但不立即同步到session文件,值爲5
$pg_boy = 6;
session_register('pg_boy'); //填入$_SESSION數組,但不立即同步到session文件,值爲NULL
session_unset($_SESSION);   //清空$_SESSION
var_dump($_SESSION);
 
echo "<br/>---4--<br/>";
$_SESSION['pg_boss'] = 3;   //填入$_SESSION數組,但不立即同步到session文件,值爲3
$pg_girls = 6;
session_register('pg_girls');   //填入$_session數組,但不立即同步到session文件,值爲NULL
session_destroy();      //註銷session_destroy
var_dump($_SESSION);
 
echo "<br/>---5---<br/>";
session_unregister('pg_boss');  //pg_boss不會被清除,還爲NULL
session_unset();        //不會清空$_SESSION數組,因爲session已被session_destroy註銷
var_dump($_SESSION);
 
fopen(__FILE__"r");
 
//@這裏是頁面析構的時候-- 本應該將$_SESSION數據同步到session文件, 真的嗎???
//@事實,沒有發生任何IO操作,即沒有將$_SESSION數據回寫,怎麼回事???
//@因爲被session_destroy()消毀了session...
?>

程序輸出:

---1--
array(3) { ["pg_name"]=> string(4) "boys" ["pg_sex"]=> NULL ["pg_theme"]=> NULL }
---2--
array(0) { }
---3--
array(0) { }
---4--
array(2) { ["pg_boss"]=> int(3) ["pg_girls"]=> NULL }
---5---
array(2) { ["pg_boss"]=> int(3) ["pg_girls"]=> NULL }
 
[root@localhost ~]# strace -p `cat /var/run/httpd.pid`
Process 21819 attached - interrupt to quit
...
open("/var/www/html/test_session_3.php", O_RDONLY) = 17
fstat64(17, {st_mode=S_IFREG|0644, st_size=706, ...}) = 0
lseek(17, 0, SEEK_CUR)                  = 0
read(17, "<?php
session_start();
echo "<br"..., 8192) = 706
read(17, "", 8192)                      = 0
read(17, "", 8192)                      = 0
close(17)                               = 0
open("/var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5", O_RDWR|O_CREAT, 0600) = 17
flock(17, LOCK_EX)                      = 0
fcntl64(17, F_SETFD, FD_CLOEXEC)        = 0
fstat64(17, {st_mode=S_IFREG|0600, st_size=10, ...}) = 0
pread64(17, "pg_uuid|N;", 10, 0) = 10
close(17)                               = 0
unlink("/var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5") = 0
time(NULL)                              = 1270910665
open("/var/www/html/test_session_3.php", O_RDONLY) = 17
fstat64(17, {st_mode=S_IFREG|0644, st_size=706, ...}) = 0
lseek(17, 0, SEEK_CUR)                  = 0
close(17)                               = 0
chdir("/var/lib/php/session")           = 0
setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={0, 0}}, NULL) = 0
...
write(12, "192.168.0.98 - - [10/Apr/2010:22"..., 211) = 211
shutdown(16, 1 /* send */)              = 0

藍色部分是我們熟悉的session初始化的時候產生的open系統內核調用。綠色部分,是一個IO讀操作,因爲上一次訪問頁面的時候,產生了 session數據,所以這一次會將上次的session填入$_SESSION中。紅色部分,可以看出,這裏調用unlink刪除session文件, 而且後面(頁面生命週期結束時),一直沒有看到前兩例看到的任何與session文件有關的IO寫操作,即沒有將$_SESSION中的數據寫回 session文件。我們也沒有在session.save_path找到相應的session文件。

[root@localhost html]# ls /var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5
ls: /var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5: No such file or directory

注:雖然刪除了session文件,但用戶再次訪問web的時候,並不會給用戶重新分配一個新的session id,而是依然用該session id,並且會重新創建文件名相同的session文件,即sess_SESSION-ID。

任務4:測試並觀察session_regenerate_id行爲,以及$_SESSION的變化

1
2
3
4
5
6
7
8
<?php
session_start();
$_SESSION['pfid'] = 123;
var_dump($_SESSION);
session_regenerate_id();
var_dump($_SESSION);
fopen(__FILE__"r");
?>
[root@localhost ~]# strace -p `cat /var/run/httpd.pid`
Process 22641 attached - interrupt to quit
...
open("/var/www/html/test_session_4.php", O_RDONLY) = 17
fstat64(17, {st_mode=S_IFREG|0644, st_size=141, ...}) = 0
lseek(17, 0, SEEK_CUR)                  = 0
read(17, "<?php
session_start();
$_SESSION"..., 8192) = 141
read(17, "", 8192)                      = 0
read(17, "", 8192)                      = 0
close(17)                               = 0
open("/var/lib/php/session/sess_4j38nv7l1fq1bj6n80l6g9cum5", O_RDWR|O_CREAT, 0600) = 17
flock(17, LOCK_EX)                      = 0
fcntl64(17, F_SETFD, FD_CLOEXEC)        = 0
fstat64(17, {st_mode=S_IFREG|0600, st_size=11, ...}) = 0
pread64(17, "pfid|i:123;", 11, 0)       = 11
gettimeofday({1270915896, 122016}, NULL) = 0
time(NULL)                              = 1270915896
open("/var/www/html/test_session_4.php", O_RDONLY) = 18
fstat64(18, {st_mode=S_IFREG|0644, st_size=141, ...}) = 0
lseek(18, 0, SEEK_CUR)                  = 0
close(18)                               = 0
chdir("/var/lib/php/session")           = 0
close(17)                               = 0
open("/var/lib/php/session/sess_qoa6knu9fg77un8le99o1vk1c7", O_RDWR|O_CREAT, 0600) = 17
flock(17, LOCK_EX)                      = 0
fcntl64(17, F_SETFD, FD_CLOEXEC)        = 0
pwrite64(17, "pfid|i:123;", 11, 0)      = 11
close(17)                               = 0
setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={0, 0}}, NULL) = 0
writev(16, [{"HTTP/1.1 200 OK
Date: Sat, 10 A"..., 386}, {"array(1) {
  ["pfid"]=>
  int(12"..., 75}], 2) = 461
write(12, "192.168.0.98 - - [11/Apr/2010:00"..., 210) = 210
shutdown(16, 1 /* send */)              = 0

請注意觀察,藍色部分, session初始化打開的session文件是sess_4j38nv7l1fq1bj6n80l6g9cum5,這也驗證了上一例的陳述。而綠色加粗 部分,而這次打開的是新session文件sess_qoa6knu9fg77un8le99o1vk1c7,並且將session數據寫入到了新的 session文件中。值得注意的是,打開新文件的時候,是發生在fopen(__FILE__,  'r')之後,在回寫session數據到session文件之前open的新session文件。可 見,session_regenerate_id()會給當前用戶重新分配一個新的session id,並將當前session數據寫入新的session文件,另外不會刪除舊的session文件。通過文件列表,也驗證了,正是如此

[root@localhost html]# ls -lt /var/lib/php/session/
-rw------- 1 apache apache 11 Apr 11 00:11 sess_qoa6knu9fg77un8le99o1vk1c7
-rw------- 1 apache apache 11 Apr 11 00:08 sess_4j38nv7l1fq1bj6n80l6g9cum5
-rw------- 1 apache apache  0 Apr 10 17:37 sess_ktj0giniplqf51nravl1fsga72

任務5.:測試並觀察session_commit()是否會像session_destroy註銷session,什麼時候進行IO寫操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
session_start();
echo "-----<br />---";
var_dump($SESSION);
 
echo "<br />-----<br />---";
$SESSION['pfguest'] = 'guest';
session_commit();
var_dump($SESSION);
 
echo "<br />-----<br />---";
session_unset();
var_dump($SESSION);
 
fopen(__FILE__'r');
?>
程序輸出如下:
-----1---array(1) { ["pfid"]=> int(123) }
-----2---array(2) { ["pfid"]=> int(123) ["pfguest"]=> string(5) "guest" }
-----3---array(2) { ["pfid"]=> int(123) ["pfguest"]=> string(5) "guest" }

[root@localhost ~]# strace -p `cat /var/run/httpd.pid`
Process 22641 attached - interrupt to quit
...
open("/var/www/html/test_session_5.php", O_RDONLY) = 17
fstat64(17, {st_mode=S_IFREG|0644, st_size=246, ...}) = 0
lseek(17, 0, SEEK_CUR)                  = 0
read(17, "<?php
session_start();
echo "---"..., 8192) = 246
read(17, "", 8192)                      = 0
read(17, "", 8192)                      = 0
close(17)                               = 0
open("/var/lib/php/session/sess_qoa6knu9fg77un8le99o1vk1c7", O_RDWR|O_CREAT, 0600) = 17
flock(17, LOCK_EX)                      = 0
fcntl64(17, F_SETFD, FD_CLOEXEC)        = 0
fstat64(17, {st_mode=S_IFREG|0600, st_size=11, ...}) = 0
pread64(17, "pfid|i:123;", 11, 0)       = 11
pwrite64(17, "pfid|i:123;pfguest|s:5:"guest";", 31, 0) = 31
close(17)                               = 0
time(NULL)                              = 1270927098
open("/var/www/html/test_session_5.php", O_RDONLY) = 17
fstat64(17, {st_mode=S_IFREG|0644, st_size=246, ...}) = 0
lseek(17, 0, SEEK_CUR)                  = 0
close(17)                               = 0
chdir("/var/lib/php/session")           = 0
setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={0, 0}}, NULL) = 0
writev(16, [{"HTTP/1.1 200 OK
Date: Sat, 10 A"..., 329}, {"-----1---array(1) {
  ["pfid"]=>"..., 222}], 2) = 551
write(12, "192.168.0.98 - - [11/Apr/2010:03"..., 211) = 211
shutdown(16, 1 /* send */)              = 0

觀察測試結果,藍色部分照舊是session初始化的時候產生的系統內核open調用,接着綠色部分是一個IO讀操作,即讀取session文件中的數 據,由第一個var_dump($_SESSION)輸出。隨後,往session加入新的一條已定義了的session記錄,並且通過 session_commit()將記錄寫回去。紅色部分就是由session_commit產生的一次IO寫操作。之 後,session_unset()並沒有生效,同時,我們也沒有在頁面生命週期結束的時候看到任何與session文件有關的IO寫操作。這也正說明 了,session_commit()調用的當下,就會將session數據寫回session文件,並且會像session_destroy一樣註銷 session,但與session_destroy不同的時,session_commit不會刪除session文件,而且會將當前的session 數據寫回session文件。我們可以查看,調用session_commit之後,session文件還是依然存在的

[root@localhost html]# ls -lt /var/lib/php/session
-rw------- 1 apache apache 31 Apr 11 03:18 sess_qoa6knu9fg77un8le99o1vk1c7
-rw------- 1 apache apache 11 Apr 11 00:08 sess_4j38nv7l1fq1bj6n80l6g9cum5
…

總結

1. 用戶註銷web應用系統,最好的調用方式依次是 session_unset();  session_destroy();  unset($_SESSION);

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
function user_sigout() {
  global $user;
  sys_event_register('user_sigout'$user);
 
  session_unset();          //清空session
  session_destroy();            //刪除session文件
  if (isset($_SESSION)) {
    unset($_SESSIONI);  //註銷$_SESSION
  }
  return TRUE;
}
?>

2. 儘量將鍵與值填入$_SESSION,而不推薦使用session_register()。同樣,儘量使用unset($_SESSION[‘var’]),而不使用session_unregister()。

3. 對於可能產生大量session的WEB應用,推薦使用的session.save_path的格式是session.save_path=”N:/path”。注意:這些目錄需要手工創建,並且有httpd守護進程屬主寫權限。這樣做可以獲得更好的性能

4. 如果調用了session_regenerate_id()給用戶分配了新的session id。該函數並不會主動刪除舊的session文件,需要定時清理舊的session文件,這樣更優化。

5. 儘量不要使用session_commit()提交sessioin數據,因爲它同時會結束當前session,PHP默認會在頁面生命週期的時候提交session數據到session文件


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章