学习使用的PHP是7.2.6

我们在源码安装好PHP之后查看当前的目录:

其中SAPI就是我们这篇文章所要学习的内容。那么什么是SAPI呢?

SAPI全称为Server Application Programming Interface,服务器应用编程接口

我们都知道PHP是一个脚本解释器,提供脚本解析与执行,我们可以在不同环境中应用这个解析器,例如web环境中,命令行中,嵌入其他应用中,面对着这么多不同的环境,PHP提供了一个SAPI层以适配不同的应用环境,SAPI是整个PHP架构最外层的一部分,主要负责PHP的初始化工作,如下是鸟哥给出的PHP架构图,大家可以点此链接到鸟哥的文章

在学习之前,我们先大致了解PHP的生命周期,他大概被划分为以下几个阶段:

如上因为不同的SAPI会有差异,例如CLI下每次执行脚本都会走一遍完整的流程(不会有请求关闭到请求开始的那条线),而在FastCgi模式下则会在启动时执行一次模块初始化,然后在各个请求只经历请求初始化,执行脚本,请求关闭。在SAPI关闭时经历模块关闭阶段。

下面以cli模式来学习SAPI机制:

cli SAPI的main函数在/sapi/cli/php_cli.c中,执行时首先解析命令行参数,然后初始化sapi_module_struct,这是一个结构体,记录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
};

其中sapi_module_struct定义在main/SAPI.h中,代码如下:

struct _sapi_module_struct {  char *name; // 应用层名称char *pretty_name; // 应用层更易读的名字int (*startup)(struct _sapi_module_struct *sapi_module); // 当一个应用要调用php的时候,这个模块启动的时候会调用的函数int (*shutdown)(struct _sapi_module_struct *sapi_module); // 当一个应用要调用php的时候,这个模块结束的时候会调用的函数int (*activate)(void); // 在处理每个request的时候,激活需要调用的函数int (*deactivate)(void); // 在处理完每个request的时候,收尾时候要调用的函数size_t (*ub_write)(const char *str, size_t str_length); // 这个函数告诉php如何输出数据void (*flush)(void *server_context); // 提供给php的刷新缓存的函数指针zend_stat_t *(*get_stat)(void); // 用来判断要执行文件的权限,来判断是否有执行权限char *(*getenv)(char *name, size_t name_len); // 获取环境变量的方法void (*sapi_error)(int type, const char *error_msg, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3); // 错误处理方法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); // 发送所有的headervoid (*send_header)(sapi_header_struct *sapi_header, void *server_context); // 单独发送某一个headersize_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, int syslog_type_int); // 输出错误信息函数double (*get_request_time)(void); // 获取请求时间的函数void (*terminate_process)(void);  // TODO: 调用exit的时候调用的方法char *php_ini_path_override;  // PHP的ini文件被复写了所复写的地址void (*default_post_reader)(void); // 这里和前面的read_post有个差别,read_post负责如何获取POST数据,而这里的函数负责如何解析POST数据void (*treat_data)(int arg, char *str, zval *destArray); // 对数据进行处理,比如进行安全过滤等。 default_post_reader/tread_data/input_filter是三个能对输入进行过滤和处理的函数char *executable_location; // 执行的地理位置int php_ini_ignore; // 是否不使用任何ini配置文件,比如php -n 就将这个位置设置为1int php_ini_ignore_cwd; // 不在当前路径寻找php.iniint (*get_fd)(int *fd); // 获取执行文件的fdint (*force_http_10)(void); // 强制使用http1.0int (*get_target_uid)(uid_t *); // 获取执行程序的uidint (*get_target_gid)(gid_t *); // 获取执行程序的gidunsigned 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); // 默认的ini配置int phpinfo_as_text; // 是否打印phpinfo信息char *ini_entries; // 有没有附带的ini配置,比如使用php -d date.timezone=America/Adak,可以在命令行中设置时区const zend_function_entry *additional_functions; // 每个SAPI模块特有的一些函数注册,比如cli的cli_get_process_titleunsigned int (*input_filter_init)(void); // TODO:
};

一个PHP命令行应用调用时,经历了以上步骤之后,就会进入module startup阶段

 /* startup after we get the above ini override se we get things right */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;}

而cli_sapi_module变量定义的startup函数是php_cli_startup(),这个函数代码十分简单,直接调用php_module_startup,代码如下:

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

而php_module_startup()函数定义在main/main.c中,由于代码量太大就不帖出来;

可以看到上面代码调用了php_module_startup函数,他定义在main/main.c中,主要功能是:

1、激活SAPI

2、启动php输出:php_output_startup

3、初始化垃圾回收器:gc_globals_ctor,分配zend_gc_globals

4、启动zend引擎,zend_startup()

5、注册PHP定义的常量

6、解析php.ini

7、注册用于获取$_GET,$_POST,$_COOKIE,$_SERVER,$_ENV,$_REQUEST,$FILES变量的handleer

8、注册静态编译的扩展

9、注册动态加载的扩展

在完成了module startup之后就会进入请求初始化阶段:

       zend_first_try {
#ifndef PHP_CLI_WIN32_NO_CONSOLEif (sapi_module == &cli_sapi_module) {
#endifexit_status = do_cli(argc, argv);
#ifndef PHP_CLI_WIN32_NO_CONSOLE} else {exit_status = do_cli_server(argc, argv);}
#endif} zend_end_try();、

在这里do_cli()函数将完成请求的处理,它会在一开始对使用到的命令行参数进行解析,如果是一些查询之类的请求,例如(-v,-m),则不会经历PHP的请求生命周期,当脚本执行完成之后就会退出do_cli()回到main中进入module_shutdown阶段

学习的比较浅,有什么不对的地方不吝请教,这样才会有进步^^

参考链接:

《php7内核剖析》

http://www.laruence.com/2008/08/12/180.html

https://www.cnblogs.com/yjf512/p/6084963.html

PHP学习之SAPI相关推荐

  1. c语言编程判断谁说谎,c语言训练题:关于张三李四王五说谎的问题(此处用javascript实现)...

    (第一篇博文) 今天在一个交流群里见他们无聊,然后找到之前收藏的一些c语言题目放出去想让他们做,结果反倒是自己不会做,于是花了很多时间去想. 原题:张三说李四在说谎,李四说王五在说谎,王五说张三和李四 ...

  2. PHP内核学习(一)SAPI

    一.源码结构 学习PHP-src之前,我准备了一份源文件: GitHub下载->https://github.com/helingfeng/php-src 1. root根目录下,包含项目的说明 ...

  3. php7.0 cli,PHP-7.1 源代码学习:php-cli 启动流程

    前言 php cli main 函数 configure & make 默认构建目标为 php-cli,相关代码在 sapi/cli 目录下,php_cli.c 文件中能够找到 main(入口 ...

  4. SAPI ++微信SaaS平台源码v1.8.7.1

    介绍: – 接入微信开放平台(SaaS)应用一键授权发布– 完整的微信公众号.小程序帐号授权体系– 腾讯云市场应用的一键购买.续费等(2020年3月24日新增)– 用户应用的购买.授权.充值.消费账单 ...

  5. 学习笔记-B/S - Exploits

    B/S - Exploits 免责声明 本文档仅供学习和研究使用,请勿使用文中的技术源码用于非法用途,任何人造成的任何负面影响,与本人无关. 大纲 各类论坛/CMS框架 AEM 74CMS dedeC ...

  6. 渗透学习-文件上传篇-基础知识部分(持续更新中)

    提示:仅供进行学习使用,请勿做出非法的行为.如若由任何违法行为,将依据法律法规进行严惩!!! 文章目录 前言 一.文件上传漏洞简要阐述 文件上传是什么? 为什么会产生文件上传漏洞?及其可能一哪些危害? ...

  7. PHP学习记录【php数据类型】

    在PHP中,一共支持八种原始数据类型:他们是四种标量类型:boolean(布尔型),integer(整形),float(浮点型)和string(字符串型):两种复合数据类型:array(数组)和obj ...

  8. 每周工作总结-记录总结自己遇到问题及学习内容,及时分析,找到不足,让自己不断进步

    2018-10-29   2018-11-02 总结: 周一:1.git reset  和 git revert 在使用时区别,避免自己在开发中,未在指定分支开发提交代码,并推到远程,导致再合并分支后 ...

  9. python学习之recognition的多人人脸识别

    此次介绍的基于电脑摄像头的人脸识别,在Ubuntu16.04环境下,还没有测试成功,主要是环境的搭建的问题,还没有解决! 文章目录 1.实现背景 2.实现设计 2.1.需求设计 2.2.代码设计 3. ...

最新文章

  1. 谷歌上海研发院院长带队出走创业!VC热捧,塞钱都得跑得快
  2. 关于Reporting Service中的Report builder的几个疑问,高手来解答下
  3. 微服务统计,分析,图表,监控一体化的HttpReports项目在.Net Core 中的使用
  4. 异步广度优先搜索算法
  5. 深入学习SpringMVC以及学习总结
  6. [NOIP2002]矩形覆盖
  7. Azure 中国篇之网络服务—(2)Azure虚拟机使用公网ip(PIP)
  8. Leetcode: 2. Add Two Numbers
  9. 导入项目jsp文件报错,但打开相应的文件并没有错误解决办法
  10. linux新建 txt文件,通过Linux终端快速创建文本文件的3种方法
  11. clientHeight、offsetHeight、scrollHeight、scrollTop的区别以及上拉加载的实现
  12. 用Python写游戏脚本原来这么简单
  13. 东芝服务器报错误代码维修,东芝复印机维修故障代码
  14. html编写菜鸟教程首页页面
  15. STM32的IAP在线升级
  16. 西瓜书——支持向量机
  17. 从双钻模型看产品规划
  18. 破解IntelliJ IDEA
  19. 手把手带你开发一个批量下载资源的谷歌浏览器扩展
  20. xmanager连接linux端口,怎么使用xmanager连接linux

热门文章

  1. 二叉树的左视图-java
  2. 戴尔服务器安装ESXI
  3. 戴尔微型计算机3048,戴尔5460一体机拆解,戴尔3048一体机
  4. Allegro使用技巧(2)----Allegro 覆铜显示与隐藏
  5. 开发常用镜像站 - 阿里云镜像站
  6. sqlserver转mysql_数据库 SQLServer转MySQL数据库
  7. “progtam received signal SIGABRT,Aborted.“错误
  8. 使用fiddler实现手机抓包
  9. APP怎样通过免填邀请码,提高App邀请效率?
  10. Oracle数据库操作:将有顿号的一条数据拆分为多行