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函数在模块初始化时加载