溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

PHP與WEB服務(wù)器交互的案例分析

發(fā)布時(shí)間:2020-11-10 12:30:13 來源:億速云 閱讀:184 作者:小新 欄目:編程語言

這篇文章主要介紹了PHP與WEB服務(wù)器交互的案例分析,具有一定借鑒價(jià)值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。

  大家都知道,PHP需要在具體的WEB服務(wù)器中才能運(yùn)行,例如Nginx、Apache等,但是PHP是怎樣啟動(dòng),又是怎樣在服務(wù)器中運(yùn)行,然后兩者又是怎樣進(jìn)行交互的呢?

1.WEB服務(wù)器調(diào)用PHP接口

  以Apache服務(wù)器為例,我們看看該服務(wù)器是怎樣啟動(dòng)PHP,并調(diào)用PHP中的方法。Apache服務(wù)器啟動(dòng)并運(yùn)行PHP時(shí),一般是通過mod_php7模塊的形式集成(如果是php5.*版本,就是mod_php5模塊,模塊后綴名根據(jù)php版本而定),mod_php7的結(jié)構(gòu)如下(源碼路徑為php/sapi/apache2handler/mod_php7.c):

AP_MODULE_DECLARE_DATA module php7_module = {
    STANDARD20_MODULE_STUFF,/* 宏,包括版本,版本,模塊索引,模塊名,下個(gè)模塊指針等信息 */
    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 */
};

  當(dāng)Apache需要調(diào)用PHP中的方法時(shí),只需要將該請(qǐng)求通過mod_php7模塊傳達(dá)給PHP,PHP層處理完后將數(shù)據(jù)返回給Apache,整個(gè)過程就結(jié)束了(補(bǔ)充一下:Apache服務(wù)器啟動(dòng)PHP時(shí),其實(shí)有兩種加載方式,一種為靜態(tài)加載,一種為動(dòng)態(tài)加載,剛才討論的mod_php5模塊加載方式可以理解為靜態(tài)加載,也就是需要重新啟動(dòng)Apache服務(wù)器,才能將PHP加載進(jìn)去;動(dòng)態(tài)加載不需要重啟服務(wù)器,只需要通過發(fā)送信號(hào)的方式將PHP固定的模塊加載到服務(wù)器,以達(dá)到PHP啟動(dòng)的目的,但是在進(jìn)行動(dòng)態(tài)加載前,需要將加載模塊編譯成動(dòng)態(tài)鏈接庫(kù),然后將其配置到服務(wù)器的配置文件中)。上面已經(jīng)給出Apache在PHP中的model結(jié)構(gòu),下面給出Apache服務(wù)器中對(duì)應(yīng)的module結(jié)構(gòu),如下(該源代碼在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這個(gè)宏的定義方式,你可能就會(huì)覺得這兩個(gè)結(jié)構(gòu)體很像,其實(shí)這個(gè)宏定義了module_struct中的前8個(gè)參數(shù),定義如下:

#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定義了模塊的所有指令集合,具體定義內(nèi)容如下(代碼路徑為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個(gè)指令,每個(gè)指令的實(shí)現(xiàn)源碼也在apache_config.c文件中,最后就剩php7_module.php_ap2_register_hook了,它定義的內(nèi)容如下(代碼路徑為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函數(shù)包含4個(gè)鉤子和對(duì)應(yīng)的處理函數(shù),pre_config,pre_config、post_config和child_init是啟動(dòng)鉤子,它們是在服務(wù)器啟動(dòng)時(shí)調(diào)用,handler鉤子是請(qǐng)求掛鉤,它是在服務(wù)器請(qǐng)求是調(diào)用,通過這些鉤子,就可以通過Apache服務(wù)器啟動(dòng)PHP。

   將到這里,想必大家已經(jīng)知道WEB服務(wù)器是如何啟動(dòng)PHP,并調(diào)用PHP中的方法了哈,下面再給大家講講PHP是如何調(diào)用WEB服務(wù)器接口的。

2.PHP調(diào)用WEB服務(wù)器接口

  在講述這個(gè)問題前,我們需要了解一下什么是SAPI。SAPI其實(shí)是與服務(wù)器抽象層之間遵守的共同約定,可以這么簡(jiǎn)單理解,當(dāng)PHP需要調(diào)用服務(wù)器中的方法,例如清除緩存,但是清除緩存的實(shí)現(xiàn)方法是在服務(wù)器中實(shí)現(xiàn),PHP層根本就不知道怎么調(diào)用服務(wù)器中的該方法,怎么辦?這時(shí)雙方需要進(jìn)行約定,然后服務(wù)器提供一套約定后的接口給PHP,我們把這些與服務(wù)器抽象層之間遵守的共同約定稱為SAPI接口。

  問題來了,對(duì)于服務(wù)器Apache,我們可以提供一套SAPI,但是如果下次又來個(gè)其它的服務(wù)器,或者其它的“第三方”,那么我們是不是也要給他們提供一套單獨(dú)的SAPI呢?我們聰明的PHP開發(fā)者肯定想到了這一點(diǎn),即對(duì)所有的“第三方”提供一套通用的SAPI接口,但是你可以會(huì)問,如果新的“第三方”需要的接口,你的通用SAPI不支持,那怎么辦呢,我的理解是將新的功能添加到PHP的通用SAPI接口中,僅僅是個(gè)人見解哈,通用SAPI結(jié)構(gòu)如下(源碼路徑: php/main/SAPI.h):

struct _sapi_module_struct {
    char *name;         // 名字
    char *pretty_name;  // 更好理解的名字
    int (*startup)(struct _sapi_module_struct *sapi_module);    // 啟動(dòng)函數(shù)
    int (*shutdown)(struct _sapi_module_struct *sapi_module);   // 關(guān)閉函數(shù)
    int (*activate)(TSRMLS_D);           // 激活
    int (*deactivate)(TSRMLS_D);         // 停用
    void (*flush)(void *server_context); // flush
    char *(*read_cookies)(TSRMLS_D);     //read Cookies
    //...
};

  該結(jié)構(gòu)體變量較多,就不一一列舉,簡(jiǎn)要說明一下里面的變量:startup函數(shù)是當(dāng)SAPI初始化時(shí)會(huì)被調(diào)用,shutdown函數(shù)是用來釋放SAPI的數(shù)據(jù)結(jié)構(gòu)和內(nèi)存等,read_cookie 是在SAPI激活時(shí)被調(diào)用,然后將此函數(shù)獲取的值賦值給SG(request_info).cookie_data。那么對(duì)于PHP提供的通用SAPI,Apache服務(wù)器又是怎樣定制自己的接口呢?具體結(jié)構(gòu)如下(源碼路徑為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調(diào)用的“第三方”,該目錄結(jié)構(gòu)如下圖所示,目錄php/sapi/apache2handler中都是與PHP交互的接口,sapi_apache2.c是PHP與Apache約定的SAPI接口文件。

PHP與WEB服務(wù)器交互的案例分析

  看到這里,大家應(yīng)該基本清楚PHP層是怎樣調(diào)用服務(wù)器層的接口,為了鞏固上面的知識(shí),下面舉個(gè)栗子,即在Apache服務(wù)器環(huán)境下讀取cookie:

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

對(duì)于任意一個(gè)服務(wù)器在加載時(shí),我們都會(huì)指定sapi_module,Apache的sapi_module是apache2_sapi_module,它的read_cookies方法的是php_apache_sapi_read_cookies函數(shù),這樣就實(shí)現(xiàn)PHP層調(diào)用Apache的接口,是不是很簡(jiǎn)單呢:)

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享PHP與WEB服務(wù)器交互的案例分析內(nèi)容對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,遇到問題就找億速云,詳細(xì)的解決方法等著你來學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI