PHP源码阅读笔记三十五:PHP中的SESSION实现之多种存储方式

PHP源码阅读笔记三十五:PHP中的SESSION实现之多种存储方式
源码版本:php5.3.1
环境:VS2008

在php.ini中,可以看到配置项session.save_handler = files
默认情况下,php.ini 中设置的 SESSION 保存方式是 files(session.save_handler = files),即使用读写文件的方式保存 SESSION 数据,而 SESSION 文件保存的目录由 session.save_path 指定,文件名以 sess_ 为前缀,后跟 SESSION ID,如:sess_sf26st2tfamqbarvnkfo2aftf2。文件中的数据即是序列化之后的 SESSION 数据了。如果访问量大,可能产生的 SESSION 文件会比较多,这时可以设置分级目录进行 SESSION 文件的保存,效率会提高很多,设置方法为:session.save_path=”N;/save_path”,N 为分级的级数,save_path 为开始目录。当写入 SESSION 数据的时候,PHP 会获取到客户端的 SESSION_ID,然后根据这个 SESSION ID 到指定的 SESSION 文件保存目录中找到相应的 SESSION 文件,不存在则创建之,最后将数据序列化之后写入文件。读取 SESSION 数据是也是类似的操作流程,对读出来的数据需要进行解序列化,生成相应的 SESSION 变量。如果需要对session文件设置mode,设置方法为:session.save_path = “N;MODE;/path”

同样我们可以使用session_set_save_handler函数设置session的处理方法。
bool session_set_save_handler ( callback $open , callback $close , callback $read , callback $write , callback $destroy , callback $gc )
那么此时可能需要思考一些问题,对于save_handler的设置是如何实现的?在内部存在怎样的结构存储这些处理方法?不同的存储方式之间是如何替换的?如果以一个扩展的方法添加,则需要实现哪些方法?这些方法在哪里添加到系统中的?

【结构】
/ext/session/php_session.h文件

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#define PS_OPEN_ARGS void **mod_data, const char *save_path, const char *session_name TSRMLS_DC
#define PS_CLOSE_ARGS void **mod_data TSRMLS_DC
#define PS_READ_ARGS void **mod_data, const char *key, char **val, int *vallen TSRMLS_DC
#define PS_WRITE_ARGS void **mod_data, const char *key, const char *val, const int vallen TSRMLS_DC
#define PS_DESTROY_ARGS void **mod_data, const char *key TSRMLS_DC
#define PS_GC_ARGS void **mod_data, int maxlifetime, int *nrdels TSRMLS_DC
#define PS_CREATE_SID_ARGS void **mod_data, int *newlen TSRMLS_DC
 
/* default create id function */
PHPAPI char *php_session_create_id(PS_CREATE_SID_ARGS);
 
typedef struct ps_module_struct {
	const char *s_name;	// session存储方式的名字 如默认的files
	int (*s_open)(PS_OPEN_ARGS);
	int (*s_close)(PS_CLOSE_ARGS);
	int (*s_read)(PS_READ_ARGS);
	int (*s_write)(PS_WRITE_ARGS);
	int (*s_destroy)(PS_DESTROY_ARGS);
	int (*s_gc)(PS_GC_ARGS);
	char *(*s_create_sid)(PS_CREATE_SID_ARGS);
} ps_module;

第43到52行 定义ps_module,用于存放整个php_session结构,通过方法名应该可以很容易的识别出其作用意图。
【以扩展方式支持session】
如果一个扩展模块需要支持session,则在其PHP_MINIT_FUNCTION方法中调用php_session_register_module方法。
如在/ext/sqlite/sqlite.c 1395行,其调用了php_session_register_module(ps_sqlite_ptr);,说明在此扩展模块初始化时已经将其作为一个session的存储方式加载到内存中了。并且在此扩展中有sess_sqlite.c文件专门处理关于session中的应该定义的各方法,其分别以宏PS_OPEN_FUNC、PS_CLOSE_FUNC、PS_READ_FUNC、PS_WRITE_FUNC、PS_DESTROY_FUNC、PS_GC_FUNC、PS_CREATE_SID_FUNC等给出。这些宏的定义如下:

57
58
59
60
61
62
63
#define PS_OPEN_FUNC(x) 	int ps_open_##x(PS_OPEN_ARGS)
#define PS_CLOSE_FUNC(x) 	int ps_close_##x(PS_CLOSE_ARGS)
#define PS_READ_FUNC(x) 	int ps_read_##x(PS_READ_ARGS)
#define PS_WRITE_FUNC(x) 	int ps_write_##x(PS_WRITE_ARGS)
#define PS_DESTROY_FUNC(x) 	int ps_delete_##x(PS_DESTROY_ARGS)
#define PS_GC_FUNC(x) 		int ps_gc_##x(PS_GC_ARGS)
#define PS_CREATE_SID_FUNC(x)	char *ps_create_sid_##x(PS_CREATE_SID_ARGS)

php_session_register_module方法的实现在/ext/session/session.c 1021行开始。如下:

1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
#define MAX_MODULES 10
#define PREDEFINED_MODULES 2
 
static ps_module *ps_modules[MAX_MODULES + 1] = {
	ps_files_ptr,
	ps_user_ptr
};
 
PHPAPI int php_session_register_module(ps_module *ptr) /* {{{ */
{
	int ret = -1;
	int i;
 
	for (i = 0; i < MAX_MODULES; i++) {
		if (!ps_modules[i]) {
			ps_modules[i] = ptr;
			ret = 0;
			break;
		}
	}
	return ret;
}
/* }}} */

从上面的代码我们可能看到:PHP中的存储方式只能有10种,如果需要更多的方式,则需要修改MAX_MODULES的值,重新编译PHP。
存储方式的添加顺序与对应的扩展模块加载顺序有关。

【关于session_set_save_handler】
此函数用于将session的存储方式设置为用户自定义的函数,此函数的各参数对应ps_module结构的各个部分,并且会是新session.save_handler配置为user
在将用户定义的函数设置为session的存储方式时会提前判断这些用户函数是否可用。如果存在一个函数不可用,则警告并退出,表示设置失败。

【关于各方法的调用】
对于各存储方式定义的函数以类似于PS(mod)->s_open的方式调用。
对于用户自定义的方法,通过PS(mod_user_names)保存函数名,在mod_user.c中以类似于retval = ps_call_handler(PSF(open), 2, args TSRMLS_CC);的方式调用。其中#define PSF(a) PS(mod_user_names).name.ps_##a

到此处理我们可以回答之前的问题。
1、save_handler的设置是如何实现的?
在php.ini中设置session.save_handler,在请求初始化时(PHP_RINIT_FUNCTION)查找对应的ps_module,将其赋值到全局变量PS(mod),后续都是通过PS(mod)来处理session的存储操作,如果用户使用了session_set_save_handler方法设置存储方式,则此时的存储方式为user,通过user的模块方法中转调用用户指定的方法
2、在内部存在怎样的结构存储这些处理方法?
此问题可以参见上面关于结构的说明,ps_module
3、不同的存储方式之间是如何替换的?
可以在php.ini中设置session.save_handler修改,或者使用session_set_save_handler使用用户自定义的函数
4、如果以一个扩展的方法添加,则需要实现哪些方法?
需要实现PS_OPEN_FUNC、PS_CLOSE_FUNC、PS_READ_FUNC、PS_WRITE_FUNC、PS_DESTROY_FUNC、PS_GC_FUNC、PS_CREATE_SID_FUNC等宏,这些分别对应ps_module的各个方法
5、这些方法在哪里添加到系统中的?
通过php_session_register_module函数在模块初始化时加载

PHP源码阅读笔记三十五:PHP中的SESSION实现之多种存储方式》上有1条评论

  1. Pingback引用通告: PHP中的SESSION实现之多种存储方式 | 超越起点 追随自由

发表评论

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


*

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