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
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全局变量,代码如下:

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接口存储起来。代码如下:

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()函数的时候。

你可能感兴趣的:(PHP,session原理)