PHP Session原理分析

      Session是以擴展的形式嵌入到PHP內核的,因此咱們能夠把Session當成擴展來看待。通常擴展被載入到PHP時會調用擴展的MINIT函數,Session也不例外,當Session被載入到PHP內核時,MINIT函數將會被調用,Session的MINIT函數代碼以下:
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。
save_handler是存儲Sesison數據的接口,默認PHP提供了2種方式:files和user。files方式是以文件方式存儲,而user方式是使用用戶自定義接口來存儲。
若是開啓了session.auto_start,PHP便會調用php_session_start()函數自動開啓Session功能。

上面是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數組中。
當php_session_initialize()函數處理完畢後,咱們就能夠使用$_SESSION變量了。這就是爲何在PHP腳本中調用session_start()函數會生成一個$_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

相關文章
相關標籤/搜索