上一章我们对PHP的源码目录结构有了初步了解,本章我们继续从生命周期的维度对PHP进行剖析。
一、概览生命周期是什么呢?你可以把它看作执行过程,PHP的生命周期也就是它从开始执行到结束执行的过程。
PHP生命周期有五个阶段,分别为模块初始化阶段、请求初始化阶段、执行阶段、请求关闭阶段、模块关闭阶段。只是不同SAPI模式下,请求的情况略有不同,比如FastCGI下只经历了一次模块初始化阶段,接下来所有请求只经历请求初始化、执行脚本、请求关闭阶段。
在初步了解生命周期的五个阶段之后,我们先来讲述在进入模块初始化阶段(php_module_startup)之前PHP所做的工作(本文继续以PHP7.0.12版本的CLI模式)。好了,我们现在开始。
二、源码分析 2.1、sapi_module_structcli模式下的入口文件是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中的定义:
//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我们继续往下看,在经过一系列变量的初始化后,于1302行又调用了sapi_startup函数。
//sapi/cli/php_cli.c sapi_startup(sapi_module);