php逻辑分析,PHP内核分析之生命周期(三)

一、概览

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

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

二、源码分析

2.1、sapi_module_struct

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

先来看一下sapi_module_struct在main/SAPI.h中的定义://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 */

......

}

FPM中://sapi/fpm/fpm/fpm_main.c

static sapi_module_struct cgi_sapi_module = {

"fpm-fcgi",                     /* name */

"FPM/FastCGI",                  /* pretty name */

......

}

在litespeed中也有相同定义://sapi/litespeed/lsapi_main.c

static sapi_module_struct lsapi_sapi_module =

{

"litespeed",

"LiteSpeed V6.10",

......

}

2.2、sapi_startup

我们继续往下看,在经过一系列变量的初始化后,于1297行又调用了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://sapi/cli/php_cli.c

static sapi_module_struct cli_sapi_module = {

"cli",                          /* name */

"Command Line Interface",       /* pretty name */

php_cli_startup,                /* 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_struct也就是模块初始化阶段开始,才执行到了main目录,这意味着PHP的生命周期的第一个阶段是从main目录下开始的。

版权声明:本文由PHP面试资料网发布,如需转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值