php session 模块的源代码解析

image首先是扩展中的目录结构

首先来看几个重要的数据结构,第一个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
		}
	}
}
  1. session_start的流程可以概括为以下几个步骤
    1. 首先检查session全局配置参数的值use_cookie_only和use_trans_sid,两者分别表示sessionid在客户端只能通过cookie保存和只能通过url传递
    2. 检查PS(session_status)状态,若是已经启动了session,则会打印一条NOTICE日志,也就是我们常见的A session had already been started
    3. 若是session被禁用状态,则会检查save_handler,试图初始化save_handler
    4. 接下来就是最重要的获取session_id的值的流程了,概略的说就是根据客户端保存cookie的方式,一次遍历$_COOKIE、$_GET、$_POST、$_SERVER这些Hashtable获取key为PS(session_name)也就是一般为PHPSESSID的值
  2. …未完待续

转载于:https://my.oschina.net/yusaint/blog/142025

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值