首頁  >  文章  >  後端開發  >  深入理解PHP與WEB伺服器交互

深入理解PHP與WEB伺服器交互

angryTom
angryTom轉載
2019-08-21 17:55:052547瀏覽

深入理解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層只也就是說,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接口文件。 

20160821163330607 (1).jpg

  看到这里,大家应该基本清楚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伺服器交互的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除

相關文章

看更多