跟厂长学PHP内核(四):生命周期之开始前的躁动

上一章我们对PHP的源码目录结构有了初步了解,本章我们继续从生命周期的维度对PHP进行剖析。

一、概览

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

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

在初步了解生命周期的五个阶段之后,我们先来讲述在进入模块初始化阶段(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_structmain/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);

该函数定义了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目录下开始的。

原文地址:https://www.cnblogs.com/enochzzg/p/9567318.html

时间: 2024-10-08 18:07:24

跟厂长学PHP内核(四):生命周期之开始前的躁动的相关文章

跟厂长学PHP7内核(五):系统分析生命周期

上篇文章讲述了模块初始化阶段之前的准备工作,本篇我来详细介绍PHP生命周期的五个阶段. 一.模块初始化阶段 我们先来看一下该阶段的每个函数的作用. 1.1.sapi_initialize_request_empty函数 // main/SAPI.c SAPI_API void sapi_initialize_empty_request(void) { SG(server_context) = NULL; SG(request_info).request_method = NULL; SG(req

从0系统学Android--4.2 Fragment 生命周期

从0系统学Android-- 本系列文章目录:更多精品文章分类 本系列持续更新中.... 初级阶段内容参考<第一行代码> 4.3 碎片的生命周期 碎片也有自己的生命周期,并且和 Activity 的生命周期还很像. 4.3.1 碎片的状态和回调 运行状态 当一个碎片可见,并且它所关联的活动正处在运行状态时,这个碎片也处于运行状态. 暂停状态 当一个 Activity 进入暂停状态时,与它相关联的可见碎片就会进入到暂停状态. 停止状态 当一个活动进入了停止状态时,与他关联的碎片就会进入到停止状态

从零开始学android&lt;ACtivity的生命周期.三十九.&gt;

意图是指两个UI主界面的转换,要想了解意图就必须学习ACtivity的生命周期 默认在UI界面显示的为运行为运行状态,而在后台的为onPause方法 主线: OnCreate()--> OnStart() --> OnResume() -->OnPause()-->OnStop ()-->OnDestroy() 副线: OnCreate()--> OnStart() --> OnResume() -->OnPause()-->OnStop ()--&g

跟厂长学PHP7内核(五):一步步分析生命周期之模块初始化阶段

上篇我们讲到了模块初始化阶段,并得知它是由php_module_startup函数来实现的.该阶段的主要作用是初始化变量.常量:注册各种函数,比如工具.词法.语法函数等:解析配置文件:加载扩展:当然最重要的是计算出PHP二进制程序的路径,现在让我们来研究一下该函数都做了哪些工作. 1.sapi_initialize_request_empty函数 // main/SAPI.c SAPI_API void sapi_initialize_empty_request(void) { SG(serve

跟厂长学PHP内核(二):源码分析的环境与工具

本文主要介绍分析源码的方式,其中包含环境的搭建.分析工具的安装以及源码调试的基本操作. 一.工具清单 PHP7.0.12 GDB CLion 二.源码下载及安装 $ wget http://php.net/distributions/php-7.0.12.tar.gz $ tar zxvf php-7.0.12.tar.gz $ cd php-7.0.12/ $ ./configure --prefix=/usr/local/php7 --enable-debug --enable-fpm $

跟厂长学PHP内核(三):源码目录结构

上篇文章我们已经介绍了源码分析工具的安装.配置以及调试方法,本文我们来讲述一下PHP源码的目录结构. 一.目录概览 以php-7.0.12为例,看过源码的同学们应该发现源码目录多达十多个,下面是每个目录的说明. 目录 说明 TSRM 线程相关安全的实现 Zend PHP解析器的核心实现 build linux下编译相关的目录 ext PHP的扩展 main PHP的主要代码 netware 网络目录,socket的定义与实现 pear PHP扩展及应用的代码仓库 sapi PHP的应用层接口 s

maven学习(四)maven的生命周期

官网:http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html maven有三套相互独立的生命周期,每个生命周期中又包含各个不同的阶段,这些阶段是有序的,执行某个阶段时,会将此生命周期此阶段前所有阶段都执行一遍.如:执行default周期的install会将validate到install都执行一遍. clean:清理项目 pre-clean 执行清理前需要完成的工作 clean 执行清理操作清理

数据库设计 Step by Step (2)——数据库生命周期

引言:数据库设计 Step by Step (1)得到这么多朋友的关注着实出乎了我的意外.这也坚定了我把这一系列的博文写好的决心.近来工作上的事务比较繁重,加之我期望这个系列的文章能尽可能的系统.完整,需要花很多时间整理.思考数据库设计的各种资料,所以文章的更新速度可能会慢一些,也希望大家能够谅解. 系列的第二讲我们将站在高处俯瞰一下数据库的生命周期,了解数据库设计的整体流程 数据库生命周期 大家对软件生命周期较为熟悉,数据库也有其生命周期,如下图所示. 图(1)数据库生命周期 数据库的生命周期

vue2.0-vue实例的生命周期

Vue把整个生命周期划分为创建.挂载.更新.销毁等阶段,每个阶段都会给一些"钩子"让我们来做一些我们想实现的动作.学习实例的生命周期,能帮助我们理解vue实例的运作机制,更好地合理利用各个钩子来完成我们的业务代码. 先看一下官网的生命周期图例: 如果觉的太复杂,那我们就从简单的开始学起. 这就是生命周期的8个阶段,其中我们将用到最多的钩子就是:created(成功创建)阶段. 下面我们就一 一来学习各个生命阶段: 1. beforeCreate(创建之前) 此阶段为实例初始化之后,此时