PHP源码阅读笔记三十六:PHP中的SESSION实现之常规操作

PHP源码阅读笔记三十六:PHP中的SESSION实现之常规操作
源码版本:php5.3.1
环境:VS2008
本笔记包括PHP中SESSION用到的全局变量,session_id的生成算法,初始化及session的清除操作
【全局变量】
/ext/session/php_session.h 文件

98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
typedef struct _php_ps_globals {
	char *save_path;	//	保存路径 
	char *session_name;	//	session名称 默认为PHPSESSID 这个在cookie中会看到
	char *id;		//	session ID
	char *extern_referer_chk;	//	请求头中的"Referer"字段不包含此处指定的字符串则会话ID将被视为无效
	char *entropy_file;	// 指定这里建立 session id
	char *cache_limiter;	// 可以设为{nocache,private,public},以决定 HTTP 的缓存问题
	long entropy_length;	//	 从文件中读取多少字节 
	long cookie_lifetime;	//	为按秒记的cookie的保存时间
	char *cookie_path;	//	cookie的有效路径
	char *cookie_domain;	//	cookie的有效域 
	zend_bool  cookie_secure;	//	是否仅仅通过安全连接(https)发送cookie。
	zend_bool  cookie_httponly;	//是否在cookie中添加httpOnly标志(仅允许HTTP协议访问)
	ps_module *mod;	//	session的处理方式
	void *mod_data;
	php_session_status session_status;	//	session的状态
	long gc_probability;	//	在每次 session 初始化的时候开始的可能性。 
	long gc_divisor;	// 收集概率计算公式:gc_probability/gc_divisor
	long gc_maxlifetime;	// 在这里数字所指的秒数后,保存的数据将被视为´碎片(garbage)´并由gc 进程清理掉。 
	int module_number;	//	存储方式编号
	long cache_expire;	//	过期时间,单位为分钟 
	union {
		zval *names[6];
		struct {
			zval *ps_open;
			zval *ps_close;
			zval *ps_read;
			zval *ps_write;
			zval *ps_destroy;
			zval *ps_gc;
		} name;
	} mod_user_names;	//	用户自定义函数 你懂的
	zend_bool bug_compat; /* Whether to behave like PHP 4.2 and earlier */
	zend_bool bug_compat_warn; /* Whether to warn about it */
	const struct ps_serializer_struct *serializer;	//	序列化处理函数
	zval *http_session_vars;	//	存放session变量
	zend_bool auto_start;	//	是否自动开始 默认为0
	zend_bool use_cookies;	//	是否使用cookies 
	zend_bool use_only_cookies;	//	是否仅仅使用cookie在客户端保存会话ID
	zend_bool use_trans_sid;	/* contains the INI value of whether to use trans-sid */
	zend_bool apply_trans_sid;	/* whether or not to enable trans-sid for the current request */
 
	long hash_func;	//	生成SID的散列算法。SHA-1的安全性更高一些
#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
	php_hash_ops *hash_ops;
#endif
	long hash_bits_per_character;	//指定在SID字符串中的每个字符内保存多少bit,
	int send_cookie;	//	是否发送cookie头
	int define_sid;		//	session常量id
	zend_bool invalid_session_id;	/* allows the driver to report about an invalid session id and request id regeneration */
} php_ps_globals;

【初始化】
在PHP_MINIT_FUNCTION方法中,注册全局变量_SESSION,将session_status设置为php_session_none,注册ini实体
[PHP_RINIT_FUNCTION方法]

2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
 
static 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) {
			PS(mod) = _php_find_ps_module(value TSRMLS_CC);
		}
	}
 
	if (PS(serializer) == NULL) {
		char *value;
 
		value = zend_ini_string("session.serialize_handler", sizeof("session.serialize_handler"), 0);
		if (value) {
			PS(serializer) = _php_find_ps_serializer(value TSRMLS_CC);
		}
	}
 
	if (PS(mod) == NULL || PS(serializer) == NULL) {
		/* current status is unusable */
		PS(session_status) = php_session_disabled;
		return SUCCESS;
	}
 
	if (PS(auto_start)) {
		php_session_start(TSRMLS_C);
	}
 
	return SUCCESS;
}
/* }}} */

第2011行 调用php_rinit_session_globals函数初始化一些变量,其代码吓:

79
80
81
82
83
84
85
86
87
88
/* Dispatched by RINIT and by php_session_destroy */
static inline void php_rinit_session_globals(TSRMLS_D) /* {{{ */
{
	PS(id) = NULL;
	PS(session_status) = php_session_none;
	PS(mod_data) = NULL;
	/* Do NOT init PS(mod_user_names) here! */
	PS(http_session_vars) = NULL;
}
/* }}} */

PHP_RINIT_FUNCTION第2102~2109行 如果PS(mod)存在,则跳过,否则根据session.save_handle初始化PS(mod);
第2111~2118行 如果PS(serializer)存在,则跳过,否则根据session.serialize_handler,通过_php_find_ps_serializer函数初始化PS(serializer);
第2120~2124行 处理session不可用状态
第2126~2129行 根据PS(auto_start)(即php.ini中的session.auto_start)来判断是否自动开启session

【session id的默认生成算法】
session.c 350行开始定义的php_session_create_id实现了session_id的默认生成算法
从其实现看,session id的内容和REMOTE_ADDR、当前时间和一个随机数有关
通过PS(hash_func)获取生成session id的函数,此默认为0,即MD5算法,以MD5为例,此时会初始化MD5算法(PHP_MD5Init),把REMOTE_ADDR及当前时间组成的字符串添加(PHP_MD5Update)到算法的加密内容中
然后,判断PS(entropy_length) (从文件中读取多少字节)是否大于0,如果大于0,则需要从PS(entropy_file) (指定建立 session id的文件)中读取PS(entropy_length)长的字符串,添加(PHP_MD5Update)到MD5算法的加密内容中
最后执行PHP_MD5Final操作,生成session id

【清除过期session】
在php.ini文件中对于session的配置项有三个是与过期session的清除有关的,它们是session.gc_probability、session.gc_divisor和session.gc_maxlifetime。其中session.gc_probability默认为1,session.gc_divisor默认为100,session.gc_maxlifetime默认为1440秒
如下所示代码在session.c 1469行,为php_session_start函数的部分实现。

1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
	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);
#ifdef SESSION_DEBUG
			if (nrdels != -1) {
				php_error_docref(NULL TSRMLS_CC, E_NOTICE, "purged %d expired session objects", nrdels);
			}
#endif
		}
	}

以上代码段在php_session_start函数中,表示每次启动session时都会执行,只是它会依据 PS(gc_probability)和 PS(gc_divisor)以及一个生成的随机数判断是否执行垃圾清除操作。默认其概率为1/100
php_combined_lcg会生成一个(0,1)的随机数
PS(mod)->s_gc是根据选择的存储方式调用其定义的垃圾收集方法
以files存储方式为例,PS_GC_FUNC(files)函数调用ps_files_cleanup_dir遍历指定的目录,查找文件名为”sess_”的文件(这点在mod_files.c的51行定义#define FILE_PREFIX “sess_”),通过判断当前时间与最后修改时间之差是否大于maxlifetime(即在php.ini中配置的session.gc_maxlifetime文件)来决定是否删除文件。

PHP源码阅读笔记三十六:PHP中的SESSION实现之常规操作》上有1条评论

  1. Pingback引用通告: Thinking In LAMP Blog » Blog Archive » PHP每月通讯(2011年1月)

发表评论

电子邮件地址不会被公开。 必填项已用*标注


*

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>