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接口文件。
看到这里,大家应该基本清楚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 サイトの他の関連記事を参照してください。