ホームページ  >  記事  >  バックエンド開発  >  PHP と WEB サーバーの相互作用についての深い理解

PHP と WEB サーバーの相互作用についての深い理解

angryTom
angryTom転載
2019-08-21 17:55:052567ブラウズ

PHP と WEB サーバーの相互作用についての深い理解

PHP は Nginx、Apache などの特定の WEB サーバーで実行する必要があることは誰もが知っていますが、PHP はどのように起動し、サーバー内でどのように実行されるのでしょうか?交流する?

推奨チュートリアル: php ビデオ チュートリアル

##1.WEB サーバーは PHP インターフェイスを呼び出します

Apache サーバーを例として、サーバーがどのように PHP を起動し、PHP のメソッドを呼び出すかを見てみましょう。 Apache サーバーが起動して PHP を実行すると、通常は mod_php7 モジュール (php5.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 を開始するとき、実際には 2 つの読み込みが行われます) 1 つは静的ロード、もう 1 つは動的ロードです。今説明した mod_php5 モジュールのロード方法は静的ロードとして理解できます。これは、Apache を再起動する必要があることを意味します。サーバーは PHP をそれにロードできます。動的ロードでは再起動は必要ありません。 PHP 起動の目的を達成するには、シグナルを送信して PHP 固定モジュールをサーバーにロードするだけで済みます。ただし、動的ロードの前に、ロードされたモジュールをダイナミック リンク ライブラリにコンパイルし、それを構成する必要があります。サーバーの構成ファイル)。 PHP における Apache のモデル構造は上に示されていますが、Apache サーバー内の対応するモジュール構造は次のとおりです (ソース コードは 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 が定義されている方法を見ると、この 2 つの構造は非常に似ていると思うかもしれません。実際、このマクロは 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 は起動フックであり、次の場合に呼び出されます。ハンドラー フックはリクエスト フックであり、サーバー リクエストは呼び出しであり、これらのフックを通じて、Apache サーバーを介して PHP を開始できます。

この時点で、WEB サーバーがどのように PHP を起動し、PHP のメソッドを呼び出すのかをすでに理解しているはずです。次に、PHP がどのように WEB サーバー インターフェイスを呼び出すかを説明します。

2.PHP は WEB サーバー インターフェイスを呼び出します

この問題について説明する前に、SAPI とは何かを理解する必要があります。 SAPI は、実際にはサーバー抽象化層とサーバー抽象化層の間で守られる共通の取り決めであり、PHP がキャッシュのクリアなど、サーバー内のメソッドを呼び出す必要がある場合に、キャッシュをクリアする実装メソッドが実装されるため、容易に理解できます。サーバー内でこのメソッドを呼び出すと、PHP 層はそれをまったく認識しません。サーバー内でこのメソッドを呼び出すにはどうすればよいですか?どうすればよいですか?このとき、両当事者が合意を形成する必要があり、その後、サーバーは合意されたインターフェイスのセットを PHP に提供します。サーバー抽象化レイヤーとのこれらの共通の合意を SAPI インターフェイスと呼びます。

問題は、サーバー Apache に対しては SAPI のセットを提供できますが、次回別のサーバーまたは他の「サードパーティ」が登場した場合、それらにも 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
    //...
};

この構造には多くの変数があるため、1 つずつリストすることはしません。内部の変数について簡単に説明しましょう: SAPI の初期化時にスタートアップ関数が呼び出され、SAPI の初期化にシャットダウン関数が使用されます。 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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。