PHP_RINIT_FUNCTION(session) { php_rinit_session_globals(TSRMLS_C); if (PS(mod) == NULL) { char *value; value = zend_ini_string("session.save_handler", sizeof("session.save_handler"), 0); if (value) { /* 查找save_handler */ PS(mod) = _php_find_ps_module(value TSRMLS_CC); } if (!PS(mod)) { PS(session_status) = php_session_disabled; return SUCCESS; } } if (PS(auto_start)) { /* 是否開啓了session.auto_start功能 */ php_session_start(TSRMLS_C); } return SUCCESS; }Session的MINIT函數作的事情很少,主要是獲取處理Session的save_handler(存儲Session數據的接口)和判斷是否開啓了session.auto_start。
上面是Session模塊被載入到PHP時所作的事情,而要在PHP腳本中使用Session功能,就必須先調用session_start()函數(在沒有開啓session.auto_start的狀況下)。如:php
<?php session_start(); /* 開啓Session功能 */ $_SESSION["name"] = "Jack"; /* 設置Session數據 */ ?>
session_start()函數會調用PHP內核的php_session_start()函數來開啓Session功能。php_session_start()函數作的事情主要有:1)從cookie中獲取Session ID。2)根據Session ID調用save_handler的read接口來讀取Session的數據。3)註冊$_SESSION全局變量。php_session_start()函數代碼以下:數組
PHPAPI void php_session_start(TSRMLS_D) { ...... if (!PS(id)) { /* 經過$_COOKIE獲取Session ID */ if (PS(use_cookies) && zend_hash_find(&EG(symbol_table), "_COOKIE", sizeof("_COOKIE"), (void **) &data) == SUCCESS && Z_TYPE_PP(data) == IS_ARRAY && zend_hash_find(Z_ARRVAL_PP(data), PS(session_name), lensess + 1, (void **) &ppid) == SUCCESS) { PPID2SID; PS(apply_trans_sid) = 0; PS(send_cookie) = 0; PS(define_sid) = 0; } } ...... php_session_initialize(TSRMLS_C); /* 註冊$_SESSION全局變量 */ if (!PS(use_cookies) && PS(send_cookie)) { if (PS(use_trans_sid)) PS(apply_trans_sid) = 1; PS(send_cookie) = 0; } php_session_reset_id(TSRMLS_C); PS(session_status) = php_session_active; php_session_cache_limiter(TSRMLS_C); if (PS(mod_data) && PS(gc_probability) > 0) { int nrdels = -1; nrand = (int) ((float) PS(gc_divisor) * php_combined_lcg(TSRMLS_C)); if (nrand < PS(gc_probability)) { PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &nrdels TSRMLS_CC); } } }
在上面的代碼中,咱們要注意php_session_initialize()這個函數,這個函數主要是調用save_handler的read接口讀取Session數據和註冊$_SESSION全局變量,代碼以下:cookie
static void php_session_initialize(TSRMLS_D) { ...... if (PS(mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name) TSRMLS_CC) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to initialize storage module: %s (path: %s)", PS(mod)->s_name, PS(save_path)); return; } /* 從客戶端處讀取不到Session ID時,生成新的Session ID */ if (!PS(id)) PS(id) = PS(mod)->s_create_sid(&PS(mod_data), NULL TSRMLS_CC); /* 註冊$_SESSION和$_HTTP_SESSION_VARS全局變量 */ php_session_track_init(TSRMLS_C); if (PS(mod)->s_read(&PS(mod_data), PS(id), &val, &vallen TSRMLS_CC) == SUCCESS) { php_session_decode(val, vallen TSRMLS_CC); efree(val); } }從上面的代碼能夠看出,php_session_initialize()函數首先調用save_handler的open接口打開存儲上下文。而後調用php_session_track_init()函數註冊$_SESSION和$_HTTP_SESSION_VARS全局變量,$_SESSION和$_HTTP_SESSION_VARS會被註冊爲同一個數組。接着調用save_handler的read接口讀取Session數據,若是是使用files方式存儲的話,就從文件中讀取Session數據,讀取完畢後會把讀到的數據寫入到$_SESSION數組中。
你可能會問$_SESSION變量的值是怎麼被存儲的的呢?是的,前面咱們只是解釋了PHP怎麼讀取Session數據,可是它是怎麼存儲的呢?答案就是:當一個請求完畢的時候,PHP會調用php_session_save_current_state()函數存儲$_SESSION變量的值。php_session_save_current_state()函數所作的事情是調用php_session_encode()函數把$_SESSION變量的值序列化,而後調用save_handler的write接口存儲起來。代碼以下:session
static void php_session_save_current_state(TSRMLS_D) { int ret = FAILURE; IF_SESSION_VARS() { ...... if (PS(mod_data)) { char *val; int vallen; /* 序列化$_SESSION變量的值 */ val = php_session_encode(&vallen TSRMLS_CC); if (val) { /* 存儲Session數據 */ ret = PS(mod)->s_write(&PS(mod_data), PS(id), val, vallen TSRMLS_CC); efree(val); } else { ret = PS(mod)->s_write(&PS(mod_data), PS(id), "", 0 TSRMLS_CC); } } ...... if (PS(mod_data)) PS(mod)->s_close(&PS(mod_data) TSRMLS_CC); }
至此,Session的原理基本分析完畢。不過還有個問題,就是Session ID怎麼來的?由於第一次訪問頁面的時候不可能存在Session ID,因此PHP會在用戶沒有Session ID的時候調用php_session_create_id()函數自動生成一個惟一的Session ID,而後把這個Session ID經過cookie發送給用戶。這個動做也是發生在調用session_start()函數的時候。app