首先来看几个重要的数据结构,第一个ps_module_struct代表着PHP中session中要实现的几个处理session的函数指针,分别的作用是open、close、read、write、destory等功能
typedef struct ps_module_struct { const char *s_name; 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;
第二个数据结构,全局的session变量,可以通过PS(var_name)来访问,其中部分变量代表着php.ini配置文件中的设置的参数,如save_path、session_name、use_cookie、use_http_cookie等
typedef struct _php_ps_globals { char *save_path; char *session_name; char *id; char *extern_referer_chk; char *entropy_file; char *cache_limiter; long entropy_length; long cookie_lifetime; char *cookie_path; char *cookie_domain; zend_bool cookie_secure; zend_bool cookie_httponly; ps_module *mod; void *mod_data; php_session_status session_status; long gc_probability; long gc_divisor; long gc_maxlifetime; 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; zend_bool auto_start; zend_bool use_cookies; zend_bool use_only_cookies; 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; #if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH) php_hash_ops *hash_ops; #endif long hash_bits_per_character; int send_cookie; int define_sid; zend_bool invalid_session_id; /* allows the driver to report about an invalid session id and request id regeneration */ } php_ps_globals;
来看session模块启动过程,
static PHP_INI_MH(OnUpdateSaveHandler) /* {{{ */函数首先会根据php.ini文件中的save_handler配置,初始化PS全局变量中的mod,php默认的两个saveHandler可以为file和user
static ps_module *ps_modules[MAX_MODULES + 1] = { ps_files_ptr, ps_user_ptr };我们在php代码中通过ini_set设置session.save_handler既可以通过这个函数更新PS(mod)变量
static PHP_INI_MH(OnUpdateSaveHandler) /* {{{ */ { ps_module *tmp; SESSION_CHECK_ACTIVE_STATE; tmp = _php_find_ps_module(new_value TSRMLS_CC); if (PG(modules_activated) && !tmp) { int err_type; if (stage == ZEND_INI_STAGE_RUNTIME) { err_type = E_WARNING; } else { err_type = E_ERROR; } /* Do not output error when restoring ini options. */ if (stage != ZEND_INI_STAGE_DEACTIVATE) { php_error_docref(NULL TSRMLS_CC, err_type, "Cannot find save handler '%s'", new_value); } return FAILURE; } PS(mod) = tmp; return SUCCESS; }
……看重要的session_start函数的逻辑,N长,概略的说下自己的理解,不一定对,请轻拍~
PHPAPI void php_session_start(TSRMLS_D) /* {{{ */ { zval **ppid; zval **data; char *p, *value; int nrand; int lensess; if (PS(use_only_cookies)) { PS(apply_trans_sid) = 0; } else { PS(apply_trans_sid) = PS(use_trans_sid); } switch (PS(session_status)) { case php_session_active: php_error(E_NOTICE, "A session had already been started - ignoring session_start()"); return; break; case php_session_disabled: value = zend_ini_string("session.save_handler", sizeof("session.save_handler"), 0); if (!PS(mod) && value) { PS(mod) = _php_find_ps_module(value TSRMLS_CC); if (!PS(mod)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot find save handler '%s' - session startup failed", value); return; } } value = zend_ini_string("session.serialize_handler", sizeof("session.serialize_handler"), 0); if (!PS(serializer) && value) { PS(serializer) = _php_find_ps_serializer(value TSRMLS_CC); if (!PS(serializer)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot find serialization handler '%s' - session startup failed", value); return; } } PS(session_status) = php_session_none; /* fallthrough */ default: case php_session_none: PS(define_sid) = 1; PS(send_cookie) = 1; } lensess = strlen(PS(session_name)); /* Cookies are preferred, because initially * cookie and get variables will be available. */ if (!PS(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; } if (!PS(use_only_cookies) && !PS(id) && zend_hash_find(&EG(symbol_table), "_GET", sizeof("_GET"), (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(send_cookie) = 0; } if (!PS(use_only_cookies) && !PS(id) && zend_hash_find(&EG(symbol_table), "_POST", sizeof("_POST"), (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(send_cookie) = 0; } } /* Check the REQUEST_URI symbol for a string of the form * '<session-name>=<session-id>' to allow URLs of the form * http://yoursite/<session-name>=<session-id>/script.php */ if (!PS(use_only_cookies) && !PS(id) && PG(http_globals)[TRACK_VARS_SERVER] && zend_hash_find(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), "REQUEST_URI", sizeof("REQUEST_URI"), (void **) &data) == SUCCESS && Z_TYPE_PP(data) == IS_STRING && (p = strstr(Z_STRVAL_PP(data), PS(session_name))) && p[lensess] == '=' ) { char *q; p += lensess + 1; if ((q = strpbrk(p, "/?\\"))) { PS(id) = estrndup(p, q - p); PS(send_cookie) = 0; } } /* Check whether the current request was referred to by * an external site which invalidates the previously found id. */ if (PS(id) && PS(extern_referer_chk)[0] != '\0' && PG(http_globals)[TRACK_VARS_SERVER] && zend_hash_find(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER]), "HTTP_REFERER", sizeof("HTTP_REFERER"), (void **) &data) == SUCCESS && Z_TYPE_PP(data) == IS_STRING && Z_STRLEN_PP(data) != 0 && strstr(Z_STRVAL_PP(data), PS(extern_referer_chk)) == NULL ) { efree(PS(id)); PS(id) = NULL; PS(send_cookie) = 1; if (PS(use_trans_sid) && !PS(use_only_cookies)) { PS(apply_trans_sid) = 1; } } php_session_initialize(TSRMLS_C); if (!PS(use_cookies) && PS(send_cookie)) { if (PS(use_trans_sid) && !PS(use_only_cookies)) { 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); #ifdef SESSION_DEBUG if (nrdels != -1) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "purged %d expired session objects", nrdels); } #endif } } }