深入了解PHP与WEB办事器交互
大家都知道,PHP需要在详细的WEB效劳器中才能运转,例如Nginx、Apache等,但是PHP是怎样启动,又是怎样在效劳器中运转,然后两者又是怎样停止交互的呢?
引荐教程: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_SIGNALS ap_hook_child_init(zend_signal_init, NULL, NULL, APR_HOOK_MIDDLE); #endif ap_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); // flush char *(*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概述”,不外我感受该书中这部分内容讲的有点绕,我从新编排了,然后提取了里面的重点,并参加个人见解,假如在该文中是什么讲的不合错误的地方,但愿能帮我指出来,大家共同提高哈,感谢!
原文地址:https://blog.csdn.net/lml200701158/article/details/52267573
以上就是深入懂得PHP与WEB效劳器交互的具体内容,更多请关注百分百源码网其它相关文章!