すべての始まり: SAPI インターフェイス
SAPI (サーバー アプリケーション プログラミング インターフェイス) は、PC と同様に、PC インターフェイス仕様を満たしている限り、どのオペレーティング システムがインストールされているかに関係なく、特定の PHP アプリケーション用のプログラミング インターフェイスを指します。 PC 上では通常どおり動作します。PHP スクリプトは、Web サーバー経由、コマンド ラインから直接、または他のプログラムに埋め込むなど、さまざまな方法で実行できます。
通常、PHP スクリプトをテストするには、Apache や Nginx などの Web サーバーを使用するか、コマンドラインで PHP インタープリター プログラムを通じてスクリプトを実行します。 スクリプトが実行されると、Web サーバーが応答し、ブラウザーは応答情報を表示するか、コマンド ラインの標準出力に内容を表示します。
PHP インタープリターがどこにあるかを気にすることはほとんどありません。 Web サーバーとコマンドライン プログラムを介したスクリプトの実行は非常に異なって見えますが、ワークフローは実際には同じです。コマンド ライン パラメーターは、PHP インタープリターによって実行されるスクリプトに渡されます。これは、URL を介して PHP ページをリクエストするのと同じです。スクリプト実行後は応答結果が返されますが、ターミナル上にはコマンドラインからの応答結果が表示されます。
スクリプトの実行は、SAPI インターフェースの実装から始まります。たとえば、Apache の mod_php SAPI 実装は、Apache から取得した一部の情報を初期化し、コンテンツを出力するときにそのコンテンツを Apache に返す必要があるというだけです。
次のセクションでは、いくつかの一般的な SAPI 実装について詳しく説明します。
開始と終了
PHP が実行を開始すると、リクエストを処理する前の開始フェーズとリクエスト後の終了フェーズの 2 つの主なフェーズを経ます。 開始フェーズには 2 つのプロセスがあります。最初のプロセスはモジュール初期化フェーズ (MINIT) です。このプロセスは、SAPI ライフ サイクル全体 (Apache の開始後のライフ サイクル全体、または Apache の実行プロセス全体など) で 1 回だけ実行されます。コマンドラインプログラム)。 2 番目のプロセスはモジュール アクティベーション フェーズ (RINIT) で、リクエスト フェーズで発生します。たとえば、URL を通じてページがリクエストされた場合、各リクエストの前にモジュール アクティベーション (RINIT リクエストの開始) が実行されます。 たとえば、PHP がいくつかの拡張モジュールを登録すると、MINIT ステージ中にすべてのモジュールの MINIT 関数がコールバックされます。 モジュールはこの段階で、定数の登録、モジュールで使用されるクラスの定義などの初期化作業を実行できます。 モジュールが実装されると、これらのコールバック関数は次のマクロを通じて実装できます:
<span>PHP_MINIT_FUNCTION(myphpextension) { </span><span>//</span><span> 注册常量或者类等初始化操作</span><span>return</span><span> SUCCESS; }</span>
リクエストが到着した後、PHP は、保存するシンボル テーブルを含む実行環境の作成など、スクリプトを実行するための基本環境を初期化します。 PHP 実行中の変数名と値の内容、および現在のすべての関数、クラス、その他の情報のシンボル テーブル。次に、PHP はすべてのモジュールの RINIT 関数を呼び出します。この段階で、モジュールの RINIT 関数は、MINIT コールバック関数と似ています:
<span>PHP_RINIT_FUNCTION(myphpextension) { </span><span>//</span><span> 例如记录请求开始时间 </span><span>//</span><span> 随后在请求结束的时候记录结束时间。这样我们就能够记录下处理请求所花费的时间了</span><span>return</span><span> SUCCESS; }</span>
リクエストが処理された後、一般的なスクリプト PHP は、最後まで実行されるか、exit() または die() 関数を呼び出すと、終了フェーズに入ります。開始フェーズに対応して、終了フェーズも 2 つのステージに分かれています。1 つはリクエストの完了後にモジュールを非アクティブ化するもの (RSHUTDOWN、RINIT に対応)、もう 1 つは SAPI ライフサイクルの終了時にモジュールを閉じるものです ( Web サーバーが終了するか、コマンド ライン スクリプトが実行されて終了します) (MSHUTDOWN、MINIT に対応)。
<span>PHP_RSHUTDOWN_FUNCTION(myphpextension) { </span><span>//</span><span> 例如记录请求结束时间,并把相应的信息写入到日至文件中。</span><span>return</span><span> SUCCESS; }</span>
シングルプロセス SAPI ライフサイクル
CLI/CGI モード PHP はシングルプロセス SAPI モードに属します。このタイプのリクエストは、リクエストを一度処理した後に閉じられます。つまり、次のリンクのみを経由します。 開始 - リクエストの開始 - リクエストのクローズ - 終了 SAPI インターフェイスの実装はライフ サイクルを完了します。以下の図に示すように:
単一プロセス SAPI ライフサイクル
上の図は非常にシンプルで理解しやすいです。ただ、PHP はさまざまな段階の間でも多くの作業を実行します。以下にいくつかの追加があります:
スタートアップ
各モジュールのモジュール初期化を呼び出す前に、以下を含む初期化プロセスがあります:
ここで初期化されたグローバル変数は、ほとんどの場合次のようになります。 zuf (zend_utility_functions) の設定などの一部を除いて、NULL に設定します。 zuf.printf_function = php_printf を例にとると、ここでの php_printf は zend_startup 関数の zend_printf にグローバル関数ポインターとして割り当てられ、zend_printf 関数は通常は次のように使用されます。たとえば、プログラムの呼び出し履歴を表示する debug_print_backtrace は、関連情報を出力するために使用されます。
ここでの定数は、PHP 独自の定数の一部です。これらの定数は、PHP_VERSION などのプログラム内にハードコーディングされるか、PEAR_EXTENSION_DIR などの構成ヘッダー ファイルに書き込まれます。 w32.h ファイル内にあります。
前面提到的zend_startup函数的作用就是初始化Zend引擎,这里的初始化操作包括内存管理初始化、 全局使用的函数指针初始化(如前面所说的zend_printf等),对PHP源文件进行词法分析、语法分析、 中间代码执行的函数指针的赋值,初始化若干HashTable(比如函数表,常量表等等),为ini文件解析做准备, 为PHP源文件解析做准备,注册内置函数(如strlen、define等),注册标准常量(如E_ALL、TRUE、NULL等)、注册GLOBALS全局变量等。
php_init_config函数的作用是读取php.ini文件,设置配置参数,加载zend扩展并注册PHP扩展函数。此函数分为如下几步: 初始化参数配置表,调用当前模式下的ini初始化配置,比如CLI模式下,会做如下初始化:
INI_DEFAULT(<span>"</span><span>report_zend_debug</span><span>"</span>, <span>"</span><span>0</span><span>"</span><span>); INI_DEFAULT(</span><span>"</span><span>display_errors</span><span>"</span>, <span>"</span><span>1</span><span>"</span>);
不过在其它模式下却没有这样的初始化操作。接下来会的各种操作都是查找ini文件:
php_startup_auto_globals函数会初始化在用户空间所使用频率很高的一些全局变量,如:$_GET、$_POST、$_FILES等。 这里只是初始化,所调用的zend_register_auto_global函数也只是将这些变量名添加到CG(auto_globals)这个变量表。
php_startup_sapi_content_types函数用来初始化SAPI对于不同类型内容的处理函数, 这里的处理函数包括POST数据默认处理函数、默认数据处理函数等。
php_register_internal_extensions_func函数用来注册静态构建的模块,也就是默认加载的模块, 我们可以将其认为内置模块。在PHP5.3.0版本中内置的模块包括PHP标准扩展模块(/ext/standard/目录, 这里是我们用的最频繁的函数,比如字符串函数,数学函数,数组操作函数等等),日历扩展模块、FTP扩展模块、 session扩展模块等。这些内置模块并不是一成不变的,在不同的PHP模板中,由于不同时间的需求或其它影响因素会导致这些默认加载的模块会变化, 比如从代码中我们就可以看到mysql、xml等扩展模块曾经或将来会作为内置模块出现。
模块初始化会执行两个操作: 1. 将这些模块注册到已注册模块列表(module_registry),如果注册的模块已经注册过了,PHP会报Module XXX already loaded的错误。 1. 将每个模块中包含的函数注册到函数表( CG(function_table) ),如果函数无法添加,则会报 Unable to register functions, unable to load。
在注册了静态构建的模块后,PHP会注册附加的模块,不同的模式下可以加载不同的模块集,比如在CLI模式下是没有这些附加的模块的。
在内置模块和附加模块后,接下来是注册通过共享对象(比如DLL)和php.ini文件灵活配置的扩展。
在所有的模块都注册后,PHP会马上执行模块初始化操作(zend_startup_modules)。 它的整个过程就是依次遍历每个模块,调用每个模块的模块初始化函数, 也就是在本小节前面所说的用宏PHP_MINIT_FUNCTION包含的内容。
php_disable_functions函数用来禁用PHP的一些函数。这些被禁用的函数来自PHP的配置文件的disable_functions变量。 其禁用的过程是调用zend_disable_function函数将指定的函数名从CG(function_table)函数表中删除。
php_disable_classes函数用来禁用PHP的一些类。这些被禁用的类来自PHP的配置文件的disable_classes变量。 其禁用的过程是调用zend_disable_class函数将指定的类名从CG(class_table)类表中删除。
ACTIVATION
ファイル関連のコンテンツを処理した後、PHP は php_request_startup を呼び出してリクエストの初期化操作を実行します。 リクエストの初期化操作は、図に示されている各モジュールのリクエスト初期化関数の呼び出しに加えて、その他の多くの作業も実行します。
gc_reset 関数を使用します。ガベージ コレクション メカニズムをリセットします。もちろん、これは PHP5.3 以降でのみ利用可能です。
init_compiler 関数は、コンパイルプロセス中にオペコードが配置される配列をクリアしたり、コンパイルに使用されるデータ構造を準備したりするなど、コンパイラーを初期化するために使用されます。
init_executor関数は中間コード実行プロセスの初期化に使用されます。 コンパイル プロセス中、関数リスト、クラス リストなどがコンパイル時にグローバル変数に保存され、実行プロセスの準備時に、これらのリストは次のように実行されるグローバル変数に割り当てられます。 function_table) ; 中間コードは PHP の実行仮想スタックで実行され、これらのスタックは初期化中に一緒に初期化されます。 スタックに加えて、変数を格納するシンボルテーブル(EG(symbol_table))は50要素のハッシュテーブルに初期化され、オブジェクトを格納するEG(objects_store)は1024要素で初期化されます。上記の変数の一部に加えて、PHP の実行環境にはエラー処理、例外処理などもあり、これらはすべてここで初期化されます。 php.ini で設定された zend_extensions も調べられ、ここで activate 関数が呼び出されます。
sapi_activate 関数は、SG (sapi_headers) と SG (request_info) を初期化し、HTTP リクエスト メソッドの内容を設定するために使用されます。たとえば、リクエスト メソッドが HEAD の場合、SG (request_info).headers_only を設定します。 =1 ; この関数の最も重要な操作は、要求されたデータを処理することであり、最終的に sapi_module.default_post_reader を呼び出します。 sapi_module.default_post_reader は、前のモジュールの初期化で php_startup_sapi_content_types 関数を通じて登録されました。デフォルトの処理関数は、main/php_content_types.c ファイルの php_default_post_reader 関数です。 この関数は、POST の生データを $HTTP_RAW_POST_DATA 変数に書き込みます。
投稿データを処理した後、PHP は sapi_module.read_cookies を通じて Cookie 値を読み取ります。CLI モードでは、この関数の実装は sapi_cli_read_cookies ですが、現在のモードの場合、関数本体には NULL が 1 つだけ返されます。 activate 関数が設定されている場合は、この関数を実行して SAPI をアクティブにします。CLI モードでは、この関数ポインタは NULL に設定されます。
環境の初期化$_COOKIE を例にとると、php_default_treat_data 関数は区切り文字に基づいてすべての Cookie を分割し、対応する変数に割り当てます。
モジュールリクエストの初期化Run
php_execute_script 関数には、PHP スクリプトを実行するプロセス全体が含まれています。
PHP ファイルを解析して実行する必要がある場合、実行前ファイル、現在実行する必要があるメイン ファイル、実行後ファイルを含む 3 つのファイルを実行する必要がある場合があります。 2 つの非現行ファイルは、auto_prepend_file パラメーターと auto_append_file パラメーターを使用して php.ini ファイルに設定できます。 これら 2 つのパラメータが空に設定されている場合、対応する実行可能ファイルは無効になります。
解析して実行する必要があるファイルの場合、字句解析、構文解析、中間コード生成操作が zend_compile_file (compile_file 関数) によって実行され、このファイルのすべての中間コードが返されます。 解析されたファイルが有効な中間コードを生成した場合は、zend_execute (実行関数) を呼び出して中間コードを実行します。 実行中に例外が発生し、ユーザーがこれらの例外の処理を定義している場合、これらの例外処理関数が呼び出されます。 すべての操作が処理された後、PHP は EG (return_value_ptr_ptr) を通じて結果を返します。
DEACTIVATION
PHP がリクエストを閉じるプロセスは、いくつかの終了操作のセットであり、このセットは php_request_shutdown 関数内に存在します。 このコレクションには次のものが含まれます:
マルチスレッド SAPI ライフ サイクル
マルチスレッド モードは、マルチプロセスの特定のプロセスに似ていますが、次の点が異なります。プロセス全体のライフサイクル中、リクエストの開始からリクエストの終了までのプロセスが並行して繰り返されます
マルチスレッドSAPIライフサイクル
抜粋: http://www.php-internals.com/book /?p=chapt02/02-01- php-life-cycle-and-zend-engine
上記では、PHP カーネルと PHP のライフサイクルの学習について、内容の側面も含めて紹介しています。PHP チュートリアルに興味のある友人に役立つことを願っています。