往期精选(欢迎转发~~)

  • 如何看待程序员35岁职业危机?
  • Java全套学习资料(14W字),耗时半年整理
  • 我肝了三个月,为你写出了GO核心手册
  • 消息队列:从选型到原理,一文带你全部掌握
  • 肝了一个月的ETCD,从Raft原理到实践
  • 更多…

大家都知道,PHP需要在具体的WEB服务器中才能运行,例如Nginx、Apache等,但是PHP是怎样启动,又是怎样在服务器中运行,然后两者又是怎样进行交互的呢?
  
1.WEB服务器调用PHP接口
  以Apache服务器为例,我们看看该服务器是怎样启动PHP,并调用PHP中的方法。Apache服务器启动并运行PHP时,一般是通过mod_php7模块的形式集成(如果是php5.*版本,就是mod_php5模块,模块后缀名根据php版本而定),mod_php7的结构如下(源码路径为php/sapi/apache2handler/mod_php7.c):

AP_MODULE_DECLARE_DATA module php7_module = {STANDARD20_MODULE_STUFF,/* 宏,包括版本,版本,模块索引,模块名,下个模块指针等信息 */create_php_config,      /* create per-directory config structure */merge_php_config,       /* merge per-directory config structures */NULL,                   /* create per-server config structure */NULL,                   /* merge per-server config structures */php_dir_cmds,           /* 模块定义的所有指令 */php_ap2_register_hook   /* register hooks */
};

当Apache需要调用PHP中的方法时,只需要将该请求通过mod_php7模块传达给PHP,PHP层处理完后将数据返回给Apache,整个过程就结束了(补充一下:Apache服务器启动PHP时,其实有两种加载方式,一种为静态加载,一种为动态加载,刚才讨论的mod_php5模块加载方式可以理解为静态加载,也就是需要重新启动Apache服务器,才能将PHP加载进去;动态加载不需要重启服务器,只需要通过发送信号的方式将PHP固定的模块加载到服务器,以达到PHP启动的目的,但是在进行动态加载前,需要将加载模块编译成动态链接库,然后将其配置到服务器的配置文件中)。上面已经给出Apache在PHP中的model结构,下面给出Apache服务器中对应的module结构,如下(该源代码在Apache中,下同):

struct module_struct {int version;int minor_version;int module_index;const char *name;void *dynamic_load_handle;struct module_struct *next;unsigned long magic;void (*rewrite_args) (process_rec *process);void *(*create_dir_config) (apr_pool_t *p, char *dir);void *(*merge_dir_config) (apr_pool_t *p, void *base_conf, void *new_conf);void *(*create_server_config) (apr_pool_t *p, server_rec *s);void *(*merge_server_config) (apr_pool_t *p, void *base_conf, void *new_conf);const command_rec *cmds;void (*register_hooks) (apr_pool_t *p);
}

可以看得出php7_module和module_struct还是有很大不同,不过如果看到php7_module.STANDARD20_MODULE_STUFF这个宏的定义方式,你可能就会觉得这两个结构体很像,其实这个宏定义了module_struct中的前8个参数,定义如下:

#define STANDARD20_MODULE_STUFF MODULE_MAGIC_NUMBER_MAJOR, \MODULE_MAGIC_NUMBER_MINOR, \-1, \__FILE__, \NULL, \NULL, \MODULE_MAGIC_COOKIE, \NULL /* rewrite args spot */

然后php7_module.php_dir_cmds定义了模块的所有指令集合,具体定义内容如下(代码路径为php/sapi/apache2handler/apache_config.c):

const command_rec php_dir_cmds[] =
{AP_INIT_TAKE2("php_value", php_apache_value_handler, NULL, OR_OPTIONS, "PHP Value Modifier"),AP_INIT_TAKE2("php_flag", php_apache_flag_handler, NULL, OR_OPTIONS, "PHP Flag Modifier"),AP_INIT_TAKE2("php_admin_value", php_apache_admin_value_handler, NULL, ACCESS_CONF|RSRC_CONF, "PHP Value Modifier (Admin)"),AP_INIT_TAKE2("php_admin_flag", php_apache_admin_flag_handler, NULL, ACCESS_CONF|RSRC_CONF, "PHP Flag Modifier (Admin)"),AP_INIT_TAKE1("PHPINIDir", php_apache_phpini_set, NULL, RSRC_CONF, "Directory containing the php.ini file"),{NULL}
};

也就是说,PHP层只给Apache提供了上述5个指令,每个指令的实现源码也在apache_config.c文件中,最后就剩php7_module.php_ap2_register_hook了,它定义的内容如下(代码路径为php/sapi/apache2handler/mod_php7.c):

void php_ap2_register_hook(apr_pool_t *p)
{ap_hook_pre_config(php_pre_config, NULL, NULL, APR_HOOK_MIDDLE);ap_hook_post_config(php_apache_server_startup, NULL, NULL, APR_HOOK_MIDDLE);ap_hook_handler(php_handler, NULL, NULL, APR_HOOK_MIDDLE);
#ifdef ZEND_SIGNALSap_hook_child_init(zend_signal_init, NULL, NULL, APR_HOOK_MIDDLE);
#endifap_hook_child_init(php_apache_child_init, NULL, NULL, APR_HOOK_MIDDLE);
}

php7_module.php_ap2_register_hook函数包含4个钩子和对应的处理函数,pre_config,pre_config、post_config和child_init是启动钩子,它们是在服务器启动时调用,handler钩子是请求挂钩,它是在服务器请求是调用,通过这些钩子,就可以通过Apache服务器启动PHP。
   将到这里,想必大家已经知道WEB服务器是如何启动PHP,并调用PHP中的方法了哈,下面再给大家讲讲PHP是如何调用WEB服务器接口的。

2.PHP调用WEB服务器接口
  在讲述这个问题前,我们需要了解一下什么是SAPI。SAPI其实是与服务器抽象层之间遵守的共同约定,可以这么简单理解,当PHP需要调用服务器中的方法,例如清除缓存,但是清除缓存的实现方法是在服务器中实现,PHP层根本就不知道怎么调用服务器中的该方法,怎么办?这时双方需要进行约定,然后服务器提供一套约定后的接口给PHP,我们把这些与服务器抽象层之间遵守的共同约定称为SAPI接口。
  问题来了,对于服务器Apache,我们可以提供一套SAPI,但是如果下次又来个其它的服务器,或者其它的“第三方”,那么我们是不是也要给他们提供一套单独的SAPI呢?我们聪明的PHP开发者肯定想到了这一点,即对所有的“第三方”提供一套通用的SAPI接口,但是你可以会问,如果新的“第三方”需要的接口,你的通用SAPI不支持,那怎么办呢,我的理解是将新的功能添加到PHP的通用SAPI接口中,仅仅是个人见解哈,通用SAPI结构如下(源码路径: php/main/SAPI.h):

struct _sapi_module_struct {char *name;          // 名字char *pretty_name; // 更好理解的名字int (*startup)(struct _sapi_module_struct *sapi_module);  // 启动函数int (*shutdown)(struct _sapi_module_struct *sapi_module);    // 关闭函数int (*activate)(TSRMLS_D);            // 激活int (*deactivate)(TSRMLS_D);       // 停用void (*flush)(void *server_context); // flushchar *(*read_cookies)(TSRMLS_D);     //read Cookies//...
};

该结构体变量较多,就不一一列举,简要说明一下里面的变量:startup函数是当SAPI初始化时会被调用,shutdown函数是用来释放SAPI的数据结构和内存等,read_cookie 是在SAPI激活时被调用,然后将此函数获取的值赋值给SG(request_info).cookie_data。那么对于PHP提供的通用SAPI,Apache服务器又是怎样定制自己的接口呢?具体结构如下(源码路径为php/sapi/apache2handler/sapi_apache2.c):

static sapi_module_struct apache2_sapi_module = {"apache2handler","Apache 2.0 Handler",php_apache2_startup,         /* startup */php_module_shutdown_wrapper,   /* shutdown */NULL,                         /* activate */NULL,                         /* deactivate */php_apache_sapi_ub_write,       /* unbuffered write */php_apache_sapi_flush,            /* flush */php_apache_sapi_get_stat,        /* get uid */php_apache_sapi_getenv,            /* getenv */php_error,                      /* error handler */php_apache_sapi_header_handler,  /* header handler */php_apache_sapi_send_headers,   /* send headers handler */NULL,                         /* send header handler */php_apache_sapi_read_post,     /* read POST data */php_apache_sapi_read_cookies,   /* read Cookies */php_apache_sapi_register_variables,php_apache_sapi_log_message,       /* Log message */php_apache_sapi_get_request_time,  /* Request Time */NULL,                             /* Child Terminate */STANDARD_SAPI_MODULE_PROPERTIES
};

上述源码目录php/sapi/apache2handler/中,目录php/sapi下面放的都是通过SAPI调用的“第三方”,该目录结构如下图所示,目录php/sapi/apache2handler中都是与PHP交互的接口,sapi_apache2.c是PHP与Apache约定的SAPI接口文件。
  
 看到这里,大家应该基本清楚PHP层是怎样调用服务器层的接口,为了巩固上面的知识,下面举个栗子,即在Apache服务器环境下读取cookie:

SG(request_info).cookie_data = sapi_module.read_cookies(TSRMLS_C);

对于任意一个服务器在加载时,我们都会指定sapi_module,Apache的sapi_module是apache2_sapi_module,它的read_cookies方法的是php_apache_sapi_read_cookies函数,这样就实现PHP层调用Apache的接口,是不是很简单呢:)
  
3.后记
  这篇博文是我参考《深入理解PHP内核》一书总结的,参考的内容为第二章第二节“SAPI概述”,不过我感觉该书中这部分内容讲的有点绕,我重新编排了,然后提取了里面的重点,并加入个人见解,如果在该文中有哪些讲的不对的地方,希望能帮我指出来,大家共同提高哈,谢谢!

参考: http://www.php-internals.com/

深入理解PHP原理之PHP与WEB服务器交互相关推荐

  1. 流式传输原理(一) 之通过Web服务器访问音频和视频

    音频文件存储在Web服务器上,当用户要听该音频文件时: 用户的主机和Web服务器之间建立一个TCP连接,并且为该对象发送一个HTTP请求(请求元文件): Web服务器收到一个请求,在HTTP响应报文中 ...

  2. UE4与WEB服务器交互(json)

    http://www.uedev.org/forum.php?mod=viewthread&tid=114&extra=page%3D1 概述 制作游戏在很多情况下需要和WEB服务器进 ...

  3. 浏览器与WEB服务器交互

    问题:打开浏览器,在地址栏输入url到页面展现,整个过程发生了什么? 图示: 步骤: 1 用户输入网址,包括协议和域名. 2 浏览器先查找自身缓存有没有记录,没有的话再找操作系统缓存. 3 当浏览器在 ...

  4. java httpclient 采集_使用java HttpClient 与Web服务器交互 - elliott - 博客园

    下面程序想自动登陆系统: public static void main(String[] args) throws Exception{ HttpClient client = new HttpCl ...

  5. IIS Web 服务器/ASP.NET 运行原理基本知识概念整理

    前言: 记录 IIS 相关的笔记还是从公司笔试考核题开始的,问 Application Pool 与 AppDomain 的区别? 促使我对进程池进了知识的学习,所以记录一下学习的笔记. 我们知道现在 ...

  6. 易语言web服务器e2ee_用各种方法搭建自己的个人网页服务器

    笔者就是从搭建web服务器开始真正入坑搞电脑的,虽然说被坑的非常惨,但是还是挺有意思的.后来又搭建其他服务器(Minecraft.KMS),学习Python和搞linux也积累了不少经验. 感觉自己的 ...

  7. 通过WEB服务器来实现PHP多线程功能

    当然,对多线程有深入理解的人都知道通过WEB服务器实现的多线程只能模仿多线程的一些效果,并不是真正意义上的多线程. 但不管怎么样,它还是能满足我们的一些需要的,在需要类似多线程的功能方面还是可以采用这 ...

  8. 搭建web服务器需要哪些步骤?

    首先跟大家简单普及一下什么是web服务器? Web服务器也称为WWW(WORLD WIDE WEB)服务器,一般指网站服务器,是指驻留于因特网上某种类型计算机的程序.WEB服务器主要功能是提供网上信息 ...

  9. 嵌入式设备web服务器比较

    现在在嵌入式设备中所使用的web服务器主要有:boa.thttpd.mini_httpd.shttpd.lighttpd.goaheand.appweb和apache等. Boa 1.介绍 Boa诞生 ...

  10. 嵌入式web服务器预研报告(转)

    现在在嵌入式设备中所使用的web服务器主要有:boa.thttpd.mini_httpd.shttpd.lighttpd.goaheand.appweb和apache等. Boa 1.介绍 Boa诞生 ...

最新文章

  1. 在JS中最常看到切最容易迷惑的语法(转)
  2. go语言笔记——数组长度不可变,但是元素值是可变的!!!
  3. 基于快速GeoHash,如何实现海量商品与商圈的高效匹配?
  4. 方立勋_30天掌握JavaWeb_MySQL和表约束
  5. new JSONArray(ListMap).tostring()问题
  6. lucene之Field属性的解释
  7. Go如何对数组切片进行去重
  8. oracle join过滤数据,oracle join on 数据过滤问题
  9. 删除“ie8左侧收藏夹图标(黄星星)”及“恢复”的方法
  10. 《云网络:数字经济的连接》图书发布会将于7月16日在杭举行
  11. 如何快速构建一个企业revit族库(含插件)
  12. 【机器学习】二次规划
  13. 锐捷交换机-负载均衡方式配置说明
  14. 数据增强神器 SimpleCopyPaste 支持全流程
  15. 改了计算机名字后重启断网了,电脑断网重启就好了是什么回事
  16. 光吹牛可不行 无人驾驶何时才能实现真正无人? | 观察
  17. 随机生成名字的Android页面
  18. Axure RP8 进度条
  19. 从壹开始前后端分离【重要】║最全的部署方案 最丰富的错误分析
  20. ASK调制的matlab代码

热门文章

  1. win10计算机中删除桌面,win10 电脑桌面文件夹为什么删不掉该如何处理
  2. 第一阶段冲刺 博客评价
  3. 嵌入式硬件上电后,程序的运行过程剖析(CPU、FLASH、内存)
  4. 全美计算机科学与技术排名,卡耐基梅隆大学计算机科学专业排名第1(2020年USNEWS美国排名)...
  5. 获取汉字拼音首字母方法
  6. Ubuntu 14.04+cuda7.5+torch+tensorflow+remastersys
  7. Linux 磁盘满了如何扩容
  8. 手机指纹识别测试软件,指纹测算-指纹照相机 扫描识别指纹评分
  9. 蓝桥杯:国二选手经验贴 附蓝桥杯历年真题
  10. 黑客是怎样入侵你的网站的