php内核探索

引自:http://www.nowamagic.net/librarys/veda/detail/1285

SAPI:Server Application Programming Interface 服务器端应用编程端口。研究过PHP架构的同学应该知道这个东东的重要性,它提供了一个接口,使得PHP可以和其他应用进行交互数据。 本文不会详细介绍每个PHP的SAPI,只是针对最简单的CGI SAPI,来说明SAPI的机制。

我们先来看看PHP的架构图:

SAPI指的是PHP具体应用的编程接口, 就像PC一样,无论安装哪些操作系统,只要满足了PC的接口规范都可以在PC上正常运行, PHP脚本要执行有很多种方式,通过Web服务器,或者直接在命令行下,也可以嵌入在其他程序中。

通常,我们使用Apache或者Nginx这类Web服务器来测试PHP脚本,或者在命令行下通过PHP解释器程序来执行。 脚本执行完后,Web服务器应答,浏览器显示应答信息,或者在命令行标准输出上显示内容。

我们很少关心PHP解释器在哪里。虽然通过Web服务器和命令行程序执行脚本看起来很不一样, 实际上它们的工作流程是一样的。命令行参数传递给PHP解释器要执行的脚本, 相当于通过url请求一个PHP页面。脚本执行完成后返回响应结果,只不过命令行的响应结果是显示在终端上。

脚本执行的开始都是以SAPI接口实现开始的。只是不同的SAPI接口实现会完成他们特定的工作, 例如Apache的mod_php SAPI实现需要初始化从Apache获取的一些信息,在输出内容是将内容返回给Apache, 其他的SAPI实现也类似。

SAPI提供了一个和外部通信的接口, 对于PHP5.2,默认提供了很多种SAPI, 常见的给apache的mod_php5,CGI,给IIS的ISAPI,还有Shell的CLI,本文就从CGI SAPI入手 ,介绍SAPI的机制。 虽然CGI简单,但是不用担心,它包含了绝大部分内容,足以让你深刻理解SAPI的工作原理。

要定义个SAPI,首先要定义个sapi_module_struct, 查看 PHP-SRC/sapi/cgi/cgi_main.c:

01*/
02static sapi_module_struct cgi_sapi_module = {
03#if PHP_FASTCGI
04    "cgi-fcgi",                     /* name */
05    "CGI/FastCGI",                  /* pretty name */
06#else
07    "cgi",                          /* name */
08    "CGI",                          /* pretty name */
09#endif
10  
11    php_cgi_startup,                /* startup */
12    php_module_shutdown_wrapper,    /* shutdown */
13  
14    NULL,                           /* activate */
15    sapi_cgi_deactivate,            /* deactivate */
16  
17    sapi_cgibin_ub_write,           /* unbuffered write */
18    sapi_cgibin_flush,              /* flush */
19    NULL,                           /* get uid */
20    sapi_cgibin_getenv,             /* getenv */
21  
22    php_error,                      /* error handler */
23  
24    NULL,                           /* header handler */
25    sapi_cgi_send_headers,          /* send headers handler */
26    NULL,                           /* send header handler */
27  
28    sapi_cgi_read_post,             /* read POST data */
29    sapi_cgi_read_cookies,          /* read Cookies */
30  
31    sapi_cgi_register_variables,    /* register server variables */
32    sapi_cgi_log_message,           /* Log message */
33    NULL,                           /* Get request time */
34  
35    STANDARD_SAPI_MODULE_PROPERTIES
36};

这个结构,包含了一些常量,比如name, 这个会在我们调用php_info()的时候被使用。一些初始化,收尾函数,以及一些函数指针,用来告诉Zend,如何获取,和输出数据。

1. php_cgi_startup, 当一个应用要调用PHP的时候,这个函数会被调用,对于CGI来说,它只是简单的调用了PHP的初始化函数:

1static int php_cgi_startup(sapi_module_struct *sapi_module)
2{
3    if (php_module_startup(sapi_module, NULL, 0) == FAILURE) {
4        return FAILURE;
5    }
6    return SUCCESS;
7}

2. php_module_shutdown_wrapper , 一个对PHP关闭函数的简单包装。只是简单的调用php_module_shutdown;

3. PHP会在每个request的时候,处理一些初始化,资源分配的事务。这部分就是activate字段要定义的,从上面的结构我们可以看出,对于CGI来说,它并没有提供初始化处理句柄。对于mod_php来说,那就不同了,他要在apache的pool中注册资源析构函数, 申请空间, 初始化环境变量,等等。

4. sapi_cgi_deactivate, 这个是对应与activate的函数,顾名思义,它会提供一个handler, 用来处理收尾工作,对于CGI来说,他只是简单的刷新缓冲区,用以保证用户在Zend关闭前得到所有的输出数据:

01static int sapi_cgi_deactivate(TSRMLS_D)
02{
03    /* flush only when SAPI was started. The reasons are:
04        1. SAPI Deactivate is called from two places: module init and request shutdown
05        2. When the first call occurs and the request is not set up, flush fails on
06            FastCGI.
07    */
08    if (SG(sapi_started)) {
09        sapi_cgibin_flush(SG(server_context));
10    }
11    return SUCCESS;
12}

5. sapi_cgibin_ub_write, 这个hanlder告诉了Zend,如何输出数据,对于mod_php来说,这个函数提供了一个向response数据写的接口,而对于CGI来说,只是简单的写到stdout:

01static inline size_t sapi_cgibin_single_write(const char *str, uint str_length TSRMLS_DC)
02{
03#ifdef PHP_WRITE_STDOUT
04    long ret;
05#else
06    size_t ret;
07#endif
08  
09#if PHP_FASTCGI
10    if (fcgi_is_fastcgi()) {
11        fcgi_request *request = (fcgi_request*) SG(server_context);
12        long ret = fcgi_write(request, FCGI_STDOUT, str, str_length);
13        if (ret <= 0) {
14            return 0;
15        }
16        return ret;
17    }
18#endif
19#ifdef PHP_WRITE_STDOUT
20    ret = write(STDOUT_FILENO, str, str_length);
21    if (ret <= 0) return 0;
22    return ret;
23#else
24    ret = fwrite(str, 1, MIN(str_length, 16384), stdout);
25    return ret;
26#endif
27}
28  
29static int sapi_cgibin_ub_write(const char *str, uint str_length TSRMLS_DC)
30{
31    const char *ptr = str;
32    uint remaining = str_length;
33    size_t ret;
34  
35    while (remaining > 0) {
36        ret = sapi_cgibin_single_write(ptr, remaining TSRMLS_CC);
37        if (!ret) {
38            php_handle_aborted_connection();
39            return str_length - remaining;
40        }
41        ptr += ret;
42        remaining -= ret;
43    }
44  
45    return str_length;
46}

把真正的写的逻辑剥离出来,就是为了简单实现兼容fastcgi的写方式。

6. sapi_cgibin_flush, 这个是提供给zend的刷新缓存的函数句柄,对于CGI来说,只是简单的调用系统提供的fflush;

7.NULL, 这部分用来让Zend可以验证一个要执行脚本文件的state,从而判断文件是否据有执行权限等等,CGI没有提供。

8. sapi_cgibin_getenv, 为Zend提供了一个根据name来查找环境变量的接口,对于mod_php5来说,当我们在脚本中调用getenv的时候,就会间接的调用这个句柄。而对于CGI来说,因为他的运行机制和CLI很类似,直接调用父级是Shell, 所以,只是简单的调用了系统提供的genenv:

01static char *sapi_cgibin_getenv(char *name, size_t name_len TSRMLS_DC)
02{
03#if PHP_FASTCGI
04    /* when php is started by mod_fastcgi, no regular environment
05       is provided to PHP.  It is always sent to PHP at the start
06       of a request.  So we have to do our own lookup to get env
07       vars.  This could probably be faster somehow.  */
08    if (fcgi_is_fastcgi()) {
09        fcgi_request *request = (fcgi_request*) SG(server_context);
10        return fcgi_getenv(request, name, name_len);
11    }
12#endif
13    /*  if cgi, or fastcgi and not found in fcgi env
14        check the regular environment */
15    return getenv(name);
16}

9. php_error, 错误处理函数, 到这里,说几句题外话,上次看到php maillist 提到的使得PHP的错误处理机制完全OO化, 也就是,改写这个函数句柄,使得每当有错误发生的时候,都throw一个异常。而CGI只是简单的调用了PHP提供的错误处理函数。

10. 这个函数会在我们调用PHP的header()函数的时候被调用,对于CGI来说,不提供。

11. sapi_cgi_send_headers, 这个函数会在要真正发送header的时候被调用,一般来说,就是当有任何的输出要发送之前:

01static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
02{
03    char buf[SAPI_CGI_MAX_HEADER_LENGTH];
04    sapi_header_struct *h;
05    zend_llist_position pos;
06  
07    if (SG(request_info).no_headers == 1) {
08        return  SAPI_HEADER_SENT_SUCCESSFULLY;
09    }
10  
11    if (cgi_nph || SG(sapi_headers).http_response_code != 200)
12    {
13        int len;
14  
15        if (rfc2616_headers && SG(sapi_headers).http_status_line) {
16            len = snprintf(buf, SAPI_CGI_MAX_HEADER_LENGTH,
17                           "%s\r\n", SG(sapi_headers).http_status_line);
18  
19            if (len > SAPI_CGI_MAX_HEADER_LENGTH) {
20                len = SAPI_CGI_MAX_HEADER_LENGTH;
21            }
22  
23        else {
24            len = sprintf(buf, "Status: %d\r\n", SG(sapi_headers).http_response_code);
25        }
26  
27        PHPWRITE_H(buf, len);
28    }
29  
30    h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
31    while (h) {
32        /* prevent CRLFCRLF */
33        if (h->header_len) {
34            PHPWRITE_H(h->header, h->header_len);
35            PHPWRITE_H("\r\n", 2);
36        }
37        h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
38    }
39    PHPWRITE_H("\r\n", 2);
40  
41    return SAPI_HEADER_SENT_SUCCESSFULLY;
42   }

12. NULL, 这个用来单独发送每一个header, CGI没有提供

13. sapi_cgi_read_post, 这个句柄指明了如何获取POST的数据,如果做过CGI编程的话,我们就知道CGI是从stdin中读取POST DATA的:

01static int sapi_cgi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
02{
03    uint read_bytes=0, tmp_read_bytes;
04#if PHP_FASTCGI
05    char *pos = buffer;
06#endif
07  
08    count_bytes = MIN(count_bytes, (uint) SG(request_info).content_length - SG(read_post_bytes));
09    while (read_bytes < count_bytes) {
10#if PHP_FASTCGI
11        if (fcgi_is_fastcgi()) {
12            fcgi_request *request = (fcgi_request*) SG(server_context);
13            tmp_read_bytes = fcgi_read(request, pos, count_bytes - read_bytes);
14            pos += tmp_read_bytes;
15        else {
16            tmp_read_bytes = read(0, buffer + read_bytes, count_bytes - read_bytes);
17        }
18#else
19        tmp_read_bytes = read(0, buffer + read_bytes, count_bytes - read_bytes);
20#endif
21  
22        if (tmp_read_bytes <= 0) {
23            break;
24        }
25        read_bytes += tmp_read_bytes;
26    }
27    return read_bytes;
28}

14. sapi_cgi_read_cookies, 这个和上面的函数一样,只不过是去获取cookie值:

1static char *sapi_cgi_read_cookies(TSRMLS_D)
2{
3    return sapi_cgibin_getenv((char *) "HTTP_COOKIE",sizeof("HTTP_COOKIE")-1 TSRMLS_CC);
4}

15. sapi_cgi_register_variables, 这个函数给了一个接口,用以给$_SERVER变量中添加变量,对于CGI来说,注册了一个PHP_SELF,这样我们就可以在脚本中访问$_SERVER['PHP_SELF']来获取本次的request_uri:

1static void sapi_cgi_register_variables(zval *track_vars_array TSRMLS_DC)
2{
3    /* In CGI mode, we consider the environment to be a part of the server
4     * variables
5     */
6    php_import_environment_variables(track_vars_array TSRMLS_CC);
7    /* Build the special-case PHP_SELF variable for the CGI version */
8    php_register_variable("PHP_SELF", (SG(request_info).request_uri ? SG(request_info).request_uri : ""), track_vars_array TSRMLS_CC);
9}

16. sapi_cgi_log_message ,用来输出错误信息,对于CGI来说,只是简单的输出到stderr:

01static void sapi_cgi_log_message(char *message)
02{
03#if PHP_FASTCGI
04    if (fcgi_is_fastcgi() && fcgi_logging) {
05        fcgi_request *request;
06        TSRMLS_FETCH();
07  
08        request = (fcgi_request*) SG(server_context);
09        if (request) {
10            int len = strlen(message);
11            char *buf = malloc(len+2);
12  
13            memcpy(buf, message, len);
14            memcpy(buf + len, "\n"sizeof("\n"));
15            fcgi_write(request, FCGI_STDERR, buf, len+1);
16            free(buf);
17        else {
18            fprintf(stderr, "%s\n", message);
19        }
20        /* ignore return code */
21    else
22#endif /* PHP_FASTCGI */
23    fprintf(stderr, "%s\n", message);
24}

经过分析,我们已经了解了一个SAPI是如何实现的了, 分析过CGI以后,我们也就可以想象mod_php, embed等SAPI的实现机制。

延伸阅读

此文章所在专题列表如下:

  1. PHP内核探索:从SAPI接口开始
  2. PHP内核探索:一次请求的开始与结束
  3. PHP内核探索:一次请求生命周期
  4. PHP内核探索:单进程SAPI生命周期
  5. PHP内核探索:多进程/线程的SAPI生命周期
  6. PHP内核探索:Zend引擎
  7. PHP内核探索:再次探讨SAPI
  8. PHP内核探索:Apache模块介绍
  9. PHP内核探索:通过mod_php5支持PHP
  10. PHP内核探索:Apache运行与钩子函数
  11. PHP内核探索:嵌入式PHP
  12. PHP内核探索:PHP的FastCGI
  13. PHP内核探索:如何执行PHP脚本
  14. PHP内核探索:PHP脚本的执行细节
  15. PHP内核探索:操作码OpCode
  16. PHP内核探索:PHP里的opcode
  17. PHP内核探索:解释器的执行过程
  18. PHP内核探索:变量概述
  19. PHP内核探索:变量存储与类型
  20. PHP内核探索:PHP中的哈希表
  21. PHP内核探索:理解Zend里的哈希表
  22. PHP内核探索:PHP哈希算法设计
  23. PHP内核探索:翻译一篇HashTables文章
  24. PHP内核探索:哈希碰撞攻击是什么?
  25. PHP内核探索:常量的实现
  26. PHP内核探索:变量的存储
  27. PHP内核探索:变量的类型
  28. PHP内核探索:变量的值操作
  29. PHP内核探索:变量的创建
  30. PHP内核探索:预定义变量
  31. PHP内核探索:变量的检索
  32. PHP内核探索:变量的类型转换
  33. PHP内核探索:弱类型变量的实现
  34. PHP内核探索:静态变量的实现
  35. PHP内核探索:变量类型提示
  36. PHP内核探索:变量的生命周期
  37. PHP内核探索:变量赋值与销毁
  38. PHP内核探索:变量作用域
  39. PHP内核探索:诡异的变量名
  40. PHP内核探索:变量的value和type存储
  41. PHP内核探索:全局变量Global
  42. PHP内核探索:变量类型的转换
  43. PHP内核探索:内存管理开篇
  44. PHP内核探索:Zend内存管理器
  45. PHP内核探索:PHP的内存管理
  46. PHP内核探索:内存的申请与销毁
  47. PHP内核探索:引用计数与写时复制
  48. PHP内核探索:PHP5.3的垃圾回收机制
  49. PHP内核探索:内存管理中的cache
  50. PHP内核探索:写时复制COW机制
  51. PHP内核探索:数组与链表
  52. PHP内核探索:使用哈希表API
  53. PHP内核探索:数组操作
  54. PHP内核探索:数组源码分析
  55. PHP内核探索:函数的分类
  56. PHP内核探索:函数的内部结构
  57. PHP内核探索:函数结构转换
  58. PHP内核探索:定义函数的过程
  59. PHP内核探索:函数的参数
  60. PHP内核探索:zend_parse_parameters函数
  61. PHP内核探索:函数返回值
  62. PHP内核探索:形参return value
  63. PHP内核探索:函数调用与执行
  64. PHP内核探索:引用与函数执行
  65. PHP内核探索:匿名函数及闭包
  66. PHP内核探索:面向对象开篇
  67. PHP内核探索:类的结构和实现
  68. PHP内核探索:类的成员变量
  69. PHP内核探索:类的成员方法
  70. PHP内核探索:类的原型zend_class_entry
  71. PHP内核探索:类的定义
  72. PHP内核探索:访问控制
  73. PHP内核探索:继承,多态与抽象类
  74. PHP内核探索:魔术函数与延迟绑定
  75. PHP内核探索:保留类与特殊类
  76. PHP内核探索:对象
  77. PHP内核探索:创建对象实例
  78. PHP内核探索:对象属性读写
  79. PHP内核探索:命名空间
  80. PHP内核探索:定义接口
  81. PHP内核探索:继承与实现接口
  82. PHP内核探索:资源resource类型
  83. PHP内核探索:Zend虚拟机
  84. PHP内核探索:虚拟机的词法解析
  85. PHP内核探索:虚拟机的语法分析
  86. PHP内核探索:中间代码opcode的执行
  87. PHP内核探索:代码的加密与解密
  88. PHP内核探索:zend_execute的具体执行过程
  89. PHP内核探索:变量的引用与计数规则
  90. PHP内核探索:新垃圾回收机制说明

转载于:https://www.cnblogs.com/lppblogs/archive/2013/02/21/2920111.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/274353.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

hp-ux锁定用户密码_UX设计101:用户研究-入门需要了解的一切

hp-ux锁定用户密码这是什么&#xff1f; (What is this?) This session is part of a learning curriculum that I designed to incrementally skill up and empower a team of Designers and Researchers whose skillset and ways of working needed to evolve to keep up wi…

extjs6 引入ux_关于UX以及如何摆脱UX的6种常见误解

extjs6 引入uxDo you ever browse social media, internet, or talk to colleagues and hear them say something UX related you disagree with so much that you just want to lecture them on the spot?您是否曾经浏览过社交媒体&#xff0c;互联网或与同事交谈&#xff0c…

Cocos2D-HTML5开源2D游戏引擎

http://www.programmer.com.cn/12198/ Cocos2D-HTML5是基于HTML5规范集的Cocos2D引擎的分支&#xff0c;于2012年5月发布。Cocos2D-HTML5的作者林顺将在本文中介绍Cocos2D-HTML5的框架、API、跨平台能力以及强大的性能。Cocos2D-HTML5是Cocos2D系列引擎随着互联网技术演进而产生…

illustrator下载_Illustrator笔工具练习

illustrator下载Adobe Illustrator is a fantastic vector creation tool and you can create a lot of things without ever using the Pen Tool. However, if you want to use Illustrator at its full potential, I personally believe that you need to master and become …

怎么更好练习数位板_如何设计更好的仪表板

怎么更好练习数位板重点 (Top highlight)Dashboard noun \ˈdash-ˌbȯrd\ A screen on the front of a usually horse-drawn vehicle to intercept water, mud, or snow.仪表盘 名词\ ˈdash-ˌbȯrd \\通常在马拉的车辆前部的屏幕&#xff0c;用来拦截水&#xff0c;泥或雪。…

学习正则表达式

deerchao的blog Be and aware of who you are. 正则表达式30分钟入门教程 来园子之前写的一篇正则表达式教程&#xff0c;部分翻译自codeproject的The 30 Minute Regex Tutorial。 由于评论里有过长的URL,所以本页排版比较混乱,推荐你到原处查看,看完了如果有问题,再到这里来提…

人物肖像速写_去哪儿? 优步肖像之旅

人物肖像速写In early 2018, the Uber brand team started a rebranding exercise, exploring a fresh take on what it means to be a global transportation and technology company. A new logo was developed in tandem with a bespoke sans-serif typeface called Uber Mo…

hp-ux锁定用户密码_我们如何简化925移动应用程序的用户入门— UX案例研究

hp-ux锁定用户密码Prologue: While this is fundamentally a showcase of our process in the hopes of helping others, it’s also a story about the realism of limitations when working with clients and how we ultimately were able to deliver a product the client w…

微信公众号无需二次登录_您无需两次解决问题-您需要一个设计系统

微信公众号无需二次登录重点 (Top highlight)The design system concept can be differently defined according to each person’s background. Designers may say that a design system is a style guide while developers may say it is UI standards, or specs, or even as…

视觉工程师面试指南_选择正确视觉效果的终极指南

视觉工程师面试指南When it comes to effective data visualization, the very first and also the most critical step is to select the right graph/visual for the data that you want to present. With a wide range of visualization software that is available offerin…

问题反馈模板_使用此模板可获得更好,更有价值的UX反馈

问题反馈模板Feedback is an important part of UX design. To improve the work you do you need to be able to give and receive feedback. Receiving valuable feedback is for a very large part up to you.反馈是UX设计的重要组成部分。 为了改进您的工作&#xff0c;您需…

iofd:文件描述符_文字很重要:谈论设计时18个有意义的描述符

iofd:文件描述符As designers, many of us think we’re just visual creatures. But creating visuals is only half of the job. The other half is verbal communication — actually talking about design. Whether we’re showcasing our own work, giving or receiving c…

保护程序猿滴眼睛-----修改VS 2008 编辑器颜色 (修改 chrome浏览器的背景色)

前几天更改了 chrome 的背景色后&#xff0c;虽然有些地方看起来不和谐&#xff0c;想百度的首页&#xff0c;显示出了大快的图片区域&#xff0c;但是&#xff0c;整体感觉这个颜色设置真的对眼睛有一定保护作用。。。 所以&#xff0c;再顺便修改一下 经常用的 vs2008 编辑器…

数据可视化 信息可视化_可视化哲学的黎明

数据可视化 信息可视化Note: this is the foreword of the book Data Visualization in Society (Amsterdam University Press, 2020)注意&#xff1a;这是《 社会中的数据可视化 》一书的前言 (阿姆斯特丹大学出版社&#xff0c;2020年) Geographer John Pickles once wrote …

HTTP 错误 404.2 - Not Found 由于 Web 服务器上的“ISAPI 和 CGI 限制”列表设置,无法提供您请求的页面...

详细错误&#xff1a;HTTP 错误 404.2 - Not Found. 由于 Web 服务器上的“ISAPI 和 CGI 限制”列表设置&#xff0c;无法提供您请求的页面. 出现环境&#xff1a;win7 IIS7.0 解决办法&#xff1a;IIS的根节点->右侧“ISAPI和CGI限制”->把禁止的DotNet版本项设置为允许…

重口味动漫_每种口味的图标样式

重口味动漫The icons are, without a doubt, one of the most used graphic elements today in the interface design of digital products. And to make this statement with some degree of certainty, we do not even need a very robust statistical analysis. Just rememb…

从头开始vue创建项目_我正在以设计师的身份开始一个被动的收入项目。 从头开始。...

从头开始vue创建项目Do you ever read an article on Medium (or elsewhere) about passive income, side projects and big money making blogs? When I read such an article it looks like it is easy to do yourself if you just put in the work. To see if that is the …

Exaple2_1(显示转换)

public class Example2_1{ public static void main(String arg[]){ char ca; System.out.println(""c"unicode:"(int)c); System.out.println(":"); for(int i(int)c;i<c25;i){ System.out.println(""(char)i); } }}转载于…

英国文化影响管理风格_文化如何影响用户体验

英国文化影响管理风格重点 (Top highlight)The Internet makes the world a smaller place. You can make money or gain users outside of your demographic with a digital product or service easier than a physical business. But, is selling the exact same design of t…

element ui 空格_空格是您的UI朋友。 大量使用它。

element ui 空格Originally published at marcandrew.me on July 30th, 2020.最初于 2020 年7月30日 在 marcandrew.me 上 发布 。 Ah good old White Space. One of the simplest things to add to your designs to improve both your UIs, and user experience. Let me shar…