php lifecycle - yaokun123/php-wiki GitHub Wiki

php内核分析-生命周期

一、概览

生命周期是什么呢?你可以把它看作执行过程,PHP的生命周期也就是它从开始执行到结束执行的过程。

PHP生命周期有五个阶段,分别为模块初始化阶段请求初始化阶段执行阶段请求关闭阶段模块关闭阶段。只是不同SAPI模式下,请求的情况略有不同,比如FastCGI下只经历了一次模块初始化阶段和一次模块关闭阶段,接下来所有请求只经历请求初始化、执行脚本、请求关闭3个阶段。

在初步了解生命周期的五个阶段之后,我们先来讲述在进入模块初始化阶段(php_module_startup)之前PHP所做的工作(本文继续以PHP7.0.12版本的CLI模式)。好了,我们现在开始。

二、源码分析

2.1、sapi_module_struct

cli模式下的入口文件是sapi/cli/php_cli.c,打开该文件,定位到主函数main,有没有觉得1180行出现的结构体sapi_module_struct很眼熟?这就是上篇文章SAPI的介绍中提到到结构体,它是扩展PHP对外服务的关键。

//sapi/cli/php_cli.c
sapi_module_struct *sapi_module = &cli_sapi_module;

先来看一下sapi_module_struct在main/SAPI.h中的定义:(typedef struct _sapi_module_struct sapi_module_struct;)

//main/SAPI.h

struct _sapi_module_struct {
	char *name;                                                   //名字,如cli、fpm等
	char *pretty_name;                                            //更容易理解的名字

	int (*startup)(struct _sapi_module_struct *sapi_module);     //模块启动时调用的函数(函数指针)
	int (*shutdown)(struct _sapi_module_struct *sapi_module);    //模块结束时调用的函数

	int (*activate)(void);                                       //处理request时需要调用的函数
	int (*deactivate)(void);                                     //处理完request要调用的函数

	size_t (*ub_write)(const char *str, size_t str_length);      //用于输出数据
	void (*flush)(void *server_context);                         //刷新缓存
	zend_stat_t *(*get_stat)(void);                              //判断对执行的文件是否有执行权限
	char *(*getenv)(char *name, size_t name_len);                //获取函数变量的函数指针

	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);    //调用header()时被调用的函数
	int (*send_headers)(sapi_headers_struct *sapi_headers);      //发送全部header的函数指针
	void (*send_header)(sapi_header_struct *sapi_header, void *server_context);    //发送某一个header的函数指针

	size_t (*read_post)(char *buffer, size_t count_bytes);       //获取HTTP POST中数据的函数指针
	char *(*read_cookies)(void);                                 //获取COOKIE

	void (*register_server_variables)(zval *track_vars_array);   //从$_SERVER中获取变量的函数指针
	void (*log_message)(char *message);                          //输出错误信息函数指针
	double (*get_request_time)(void);                            //获取请求时间的函数指针
	void (*terminate_process)(void);                             //调用exit退出时的函数指针

	char *php_ini_path_override;                                 //PHP的ini文件被复写的地址

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

	void (*default_post_reader)(void);                           //负责解析POST数据
	void (*treat_data)(int arg, char *str, zval *destArray);     //对数据进行处理
	char *executable_location;                                   //执行的地理位置

	int php_ini_ignore;                                          //是否不使用任何ini配置文件
	int php_ini_ignore_cwd;                                      //忽略当前路径的php.ini
	int (*get_fd)(int *fd);                                      //获取执行文件的fd的函数指针

	int (*force_http_10)(void);                                  //强制使用http1.0版本的函数指针

	int (*get_target_uid)(uid_t *);                              //获取执行程序的uid函数指针
	int (*get_target_gid)(gid_t *);                              //获取执行程序的gid函数指针

	unsigned int (*input_filter)(int arg, char *var, char **val, size_t val_len, size_t *new_val_len);    //对输入进行过滤的函数指针,比如将输入参数填充到自动全局变量$_GET、$_POST、$_COOKIE中

	void (*ini_defaults)(HashTable *configuration_hash);         
	int phpinfo_as_text;                                         //是否输出phpinfo信息 //默认的ini配置的函数指针,把ini配置信息在HashTable中

	char *ini_entries;                                           //执行时附带的ini配置,可以使php -d设置
	const zend_function_entry *additional_functions;             //每个SAPI模块特有的一些函数注册,比如cli的cli_get_process_title
	unsigned int (*input_filter_init)(void);                     
};

SAPI下的每一个模式都实现了该结构体,比如在CLI中:

//sapi/cli/php_cli.c


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
};

FPM中:

//sapi/fpm/fpm/fpm_main.c

static sapi_module_struct cgi_sapi_module = {
	"fpm-fcgi",					/* name */
	"FPM/FastCGI",					/* pretty name */

	php_cgi_startup,				/* startup */
	php_module_shutdown_wrapper,	                /* shutdown */

	sapi_cgi_activate,				/* activate */
	sapi_cgi_deactivate,			        /* deactivate */

	sapi_cgibin_ub_write,			        /* unbuffered write */
	sapi_cgibin_flush,				/* flush */
	NULL,						/* get uid */
	sapi_cgibin_getenv,				/* getenv */

	php_error,					/* error handler */

	NULL,						/* header handler */
	sapi_cgi_send_headers,			        /* send headers handler */
	NULL,						/* send header handler */

	sapi_cgi_read_post,				/* read POST data */
	sapi_cgi_read_cookies,			        /* read Cookies */

	sapi_cgi_register_variables,	                /* register server variables */
	sapi_cgi_log_message,			        /* Log message */
	NULL,						/* Get request time */
	NULL,						/* Child terminate */

	STANDARD_SAPI_MODULE_PROPERTIES
};

在litespeed中也有相同定义:

//sapi/litespeed/lsapi_main.c
static sapi_module_struct lsapi_sapi_module =
{
    "litespeed",
    "LiteSpeed V6.10",
    ......
}

2.2、sapi_startup

我们继续往下看,在经过一系列变量的初始化后,于1302行又调用了sapi_startup函数。

//sapi/cli/php_cli.c

sapi_startup(sapi_module);

该函数定义了sapi_globals_struct,也就是我们常说的SG宏,它的主要作用是保存请求的基本信息,比如服务器信息、header、编码等。

//main/SAPI.h

typedef struct _sapi_globals_struct {
	void *server_context;
	sapi_request_info request_info;
	sapi_headers_struct sapi_headers;
	int64_t read_post_bytes;
	unsigned char post_read;
	unsigned char headers_sent;
	zend_stat_t global_stat;
	char *default_mimetype;
	char *default_charset;
	HashTable *rfc1867_uploaded_files;
	zend_long post_max_size;
	int options;
	zend_bool sapi_started;
	double global_request_time;
	HashTable known_post_content_types;
	zval callback_func;
	zend_fcall_info_cache fci_cache;
} sapi_globals_struct;

2.3、sapi_module->startup

们继续往下看,sapi_module调用了startup函数:

//sapi/cli/php_cli.c

if (sapi_module->startup(sapi_module) == FAILURE) {
	exit_status = 1;
	goto out;
}

然后又调用了CLI在sapi_module_struct中定义的startup对应的钩子函数php_cli_startup:

继续跟进,php_cli_startup函数中又调用了php_module_startup函数:

//sapi/cli/php_cli.c

static int php_cli_startup(sapi_module_struct *sapi_module) /* {{{ */
{
	if (php_module_startup(sapi_module, NULL, 0)==FAILURE) {
		return FAILURE;
	}
	return SUCCESS;
}

2.4、php_module_startup

是不是很眼熟,这不就是模块初始化阶段的函数嘛!原来执行了这么久才到我们的关键点,模块初始化阶段内容比较多,我们通过下一章进行详细剖析。

//main/main.c

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;
......
}

注意:我们可以看出来,在PHP五大生命周期开始之前一直都是在sapi目录中执行的,而从php_module_startup也就是模块初始化阶段开始,才执行到了main目录,这意味着PHP的生命周期的第一个阶段是从main目录下开始的。

出处:https://www.cnblogs.com/pingyeaa/p/9567318.html