sapi_module_struct 研究(一)

今日研究了PHP中的sapi_module_struct结构体。众所周知,PHP_CLI中先include了SAPI.h。先列出这个著名的stuct。 琢磨虽一无是用,但人生本有何用?

struct _sapi_module_struct {
	char *name;
	char *pretty_name;

	int (*startup)(struct _sapi_module_struct *sapi_module);
	int (*shutdown)(struct _sapi_module_struct *sapi_module);

	int (*activate)(TSRMLS_D);
	int (*deactivate)(TSRMLS_D);

	int (*ub_write)(const char *str, unsigned int str_length TSRMLS_DC);
	void (*flush)(void *server_context);
	struct stat *(*get_stat)(TSRMLS_D);
	char *(*getenv)(char *name, size_t name_len TSRMLS_DC);

	void (*sapi_error)(int type, const char *error_msg, ...);

	int (*header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC);
	int (*send_headers)(sapi_headers_struct *sapi_headers TSRMLS_DC);
	void (*send_header)(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC);

	int (*read_post)(char *buffer, uint count_bytes TSRMLS_DC);
	char *(*read_cookies)(TSRMLS_D);

	void (*register_server_variables)(zval *track_vars_array TSRMLS_DC);
	void (*log_message)(char *message TSRMLS_DC);
	double (*get_request_time)(TSRMLS_D);
	void (*terminate_process)(TSRMLS_D);

	char *php_ini_path_override;

	void (*block_interruptions)(void);
	void (*unblock_interruptions)(void);

	void (*default_post_reader)(TSRMLS_D);
	void (*treat_data)(int arg, char *str, zval *destArray TSRMLS_DC);
	char *executable_location;

	int php_ini_ignore;
	int php_ini_ignore_cwd; /* don't look for php.ini in the current directory */

	int (*get_fd)(int *fd TSRMLS_DC);

	int (*force_http_10)(TSRMLS_D);

	int (*get_target_uid)(uid_t * TSRMLS_DC);
	int (*get_target_gid)(gid_t * TSRMLS_DC);

	unsigned int (*input_filter)(int arg, char *var, char **val, unsigned int val_len, unsigned int *new_val_len TSRMLS_DC);
	
	void (*ini_defaults)(HashTable *configuration_hash);
	int phpinfo_as_text;

	char *ini_entries;
	const zend_function_entry *additional_functions;
	unsigned int (*input_filter_init)(TSRMLS_D);
};
上面结构体给出了sapi_module_struct定义。在CLI作为SAPI入口的内核的启动过程中,至少创建了这样的一个实例:

static sapi_module_struct cli_sapi_module = {
	"cli",							/* name */
	"Command Line Interface",    	/* pretty name */

	php_cli_startup,				/* startup */
	php_module_shutdown_wrapper,	/* shutdown */

	NULL,							/* activate */
	sapi_cli_deactivate,			/* deactivate */

	sapi_cli_ub_write,		    	/* unbuffered write */
	sapi_cli_flush,				    /* flush */
	NULL,							/* get uid */
	NULL,							/* getenv */

	php_error,						/* error handler */

	sapi_cli_header_handler,		/* header handler */
	sapi_cli_send_headers,			/* send headers handler */
	sapi_cli_send_header,			/* send header handler */

	NULL,				            /* read POST data */
	sapi_cli_read_cookies,          /* read Cookies */

	sapi_cli_register_variables,	/* register server variables */
	sapi_cli_log_message,			/* Log message */
	NULL,							/* Get request time */
	NULL,							/* Child terminate */
	
	STANDARD_SAPI_MODULE_PROPERTIES
};

这个cli_sapi_module在main函数中将地址交给了sapi_module指针变量:

	sapi_module_struct *sapi_module = &cli_sapi_module;

因而,在CLI主函数内,这个sapi_module指针被送入php_module_startup等上层进行启动。在这里进入了启动过程:

if (sapi_module->startup(sapi_module) == FAILURE) {
		/* there is no way to see if we must call zend_ini_deactivate()
		 * since we cannot check if EG(ini_directives) has been initialised
		 * because the executor's constructor does not set initialize it.
		 * Apart from that there seems no need for zend_ini_deactivate() yet.
		 * So we goto out_err.*/
		exit_status = 1;
		goto out;
	}
由于是指针,访问成员函数使用了 "->"。

然而在PHP_API/ZEND_API中,均使用了sapi_module.ub_write()访问成员函数,在命名中为什么要使用相同的变量名呢?容易搞晕。如下例:

/* {{{ int php_output_write_unbuffered(const char *str, size_t len TSRMLS_DC)
 * Unbuffered write */
PHPAPI int php_output_write_unbuffered(const char *str, size_t len TSRMLS_DC)
{
#if PHP_DEBUG
	if (len > UINT_MAX) {
		php_error(E_WARNING, "Attempt to output more than UINT_MAX bytes at once; "
				"output will be truncated %lu => %lu",
				(unsigned long) len, (unsigned long) (len % UINT_MAX));
	}
#endif
	if (OG(flags) & PHP_OUTPUT_DISABLED) {
		return 0;
	}
	if (OG(flags) & PHP_OUTPUT_ACTIVATED) {
		return sapi_module.ub_write(str, len TSRMLS_CC);
	}
	return php_output_direct(str, len);
}


这是因为,在SAPI.C文件中,定义了sapi_module这样一个全局变量:

/* True globals (no need for thread safety) */
SAPI_API sapi_module_struct sapi_module;

这个变量在SAPI.h中被extern:

BEGIN_EXTERN_C()
extern SAPI_API sapi_module_struct <strong>sapi_module</strong>;  /* true global */
END_EXTERN_C()


定义说明它不是指针类型,它在何处拥有了cli_sapi_module的成员呢?我猜估计在php_module_startup里,果然是:

/* {{{ php_module_startup
 */
int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_modules, uint num_additional_modules)
{
	zend_utility_functions zuf;
	zend_utility_values zuv;
	int retval = SUCCESS, module_number=0;	/* for REGISTER_INI_ENTRIES() */
	char *php_os;
	zend_module_entry *module;
#ifdef ZTS
	zend_executor_globals *executor_globals;
	void ***tsrm_ls;
	php_core_globals *core_globals;
#endif

#if defined(PHP_WIN32) || (defined(NETWARE) && defined(USE_WINSOCK))
	WORD wVersionRequested = MAKEWORD(2, 0);
	WSADATA wsaData;
#endif
#ifdef PHP_WIN32
	php_os = "WINNT";
#if _MSC_VER >= 1400
	old_invalid_parameter_handler =
		_set_invalid_parameter_handler(dummy_invalid_parameter_handler);
	if (old_invalid_parameter_handler != NULL) {
		_set_invalid_parameter_handler(old_invalid_parameter_handler);
	}

	/* Disable the message box for assertions.*/
	_CrtSetReportMode(_CRT_ASSERT, 0);
#endif
#else
	php_os=PHP_OS;
#endif

#ifdef ZTS
	tsrm_ls = ts_resource(0);
#endif

#ifdef PHP_WIN32
	php_win32_init_rng_lock();
#endif

	module_shutdown = 0;
	module_startup = 1;
	sapi_initialize_empty_request(TSRMLS_C);
	sapi_activate(TSRMLS_C);

	if (module_initialized) {
		return SUCCESS;
	}

	sapi_module = *sf;

	php_output_startup();
        // ......//
}


自此以后,只要include "sapi.h"即可访问SAPI定义的函数。通过这种设计,成功的将不同SAPI的sapi_module送给同一个上层全局变量。使得上层handler不用关心底层的具体实现。
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值