PHP を使用する生徒は、php.ini 設定が SAPI ライフサイクル全体を通じて有効になることを知っています。 PHP スクリプトの実行中に、ini 構成を手動で変更しても、その変更は有効になりません。現時点で Apache または nginx を再起動できない場合は、php コードで ini_set インターフェイスを明示的に呼び出すことしかできません。 ini_set は、PHP が提供する動的に設定を変更する機能ですが、ini_set を使用して設定した設定と、ini ファイルに設定した設定では、有効な時間範囲が異なることに注意してください。 php スクリプトを実行すると、ini_set 設定はすぐに無効になります。
したがって、この記事は 2 つの部分に分かれており、最初の部分では php.ini 設定の原理について説明し、2 番目の部分では php 設定の動的変更について説明します。
php.ini の設定には大きく分けて、configuration_hash、EG (ini_directives)、PG、BG、PCRE_G、JSON_G、XXX_G などの 3 つのデータが含まれます。これら 3 種類のデータの意味が分からなくても問題ありません。以下で詳しく説明します。
1. INI 設定ファイルを解析する
php.ini は SAPI プロセス全体で有効である必要があるため、ini ファイルを解析し、それに応じて php 設定を構築する作業が SAPI の開始部分である必要があります。つまり、PHP の起動プロセス中に発生する必要があります。 PHP では、実際のリクエストが到着する前に、これらの設定を内部で生成する必要があります。
php_module_startup 関数である php のコアに反映されます。
php_module_startup は主に php の起動を担当しており、通常は SAPI の起動時に呼び出されます。ところで、もう 1 つの一般的な関数は php_request_startup で、これは各リクエストが到着したときに初期化する役割を果たします。php_module_startup と php_request_startup は 2 つの象徴的なアクションですが、それらの分析はこの記事の範囲を超えています。
たとえば、php が Apache のモジュールにフックされている場合、Apache が起動すると、php モジュールを含むすべてのモジュールがアクティブになります。 PHPモジュールを有効化するとphp_module_startupが呼び出されます。 php_module_startup 関数は多くの作業を完了します。php_module_startup 呼び出しが終了すると、php が開始され、リクエストを受け入れて応答できるようになったということになります。
php_module_startup 関数では、ini ファイルの解析に関連する実装は次のとおりです。
ご覧のとおり、php_init_config 関数が実際に呼び出され、ini ファイルの解析が完了します。解析作業は主に字句解析と文法解析を実行し、キーと値のペアを抽出して ini ファイルに保存します。 php.ini の形式は非常に単純で、等号の左側にキー、右側に値があります。 KV のペアが抽出されるたびに、php はそれらをどこに保存しますか?答えは、前述したconfiguration_hashです。
静的 HashTable 構成ハッシュ;
configuration_hash は、HashTable 型のデータ構造である php_ini.c で宣言されます。名前が示すように、実際にはハッシュ テーブルです。余談ですが、configuration_hash は php_ini.c ファイル内の静的変数であるため、php5.3 より前のバージョンでは取得できません。その後、php5.3 には、&configuration_hash を直接返す php_ini_get_configuration_hash インターフェイスが追加されました。これにより、さまざまな PHP 拡張機能で、configuration_hash を簡単に確認できるようになりました... なんと素晴らしいことでしょう...
4 つの点に注意してください:
まず、php_init_config は字句と構文以外の検証を実行しません。つまり、ini ファイルに行 hello=world を追加すると、これが正しくフォーマットされた構成項目である限り、最終的なconfiguration_hash にはキー hello と値 world を持つ要素が含まれ、configuration_hash はそれを反映します。 .ini ファイルを最大限に拡張します。
2 番目に、ini ファイルを使用すると、配列の形式で構成できます。たとえば、ini ファイルに次の 3 行を記述します。
最後に生成されたconfiguration_hashテーブルには、キーdrift.arrを持つ要素があり、その値は3つの数値1、2、3を含む配列になります。これは非常に珍しい構成方法です。
第三に、php では、デフォルトの php.ini ファイル (正確には php-%s.ini) に加えて、いくつかの追加の ini ファイルを構築することもできます。これらの ini ファイルは追加のディレクトリに配置されます。このディレクトリは環境変数 PHP_INI_SCAN_DIR で指定されており、php_init_config が php.ini を解析した後、このディレクトリを再度スキャンし、分析のためにディレクトリ内のすべての .ini ファイルを検索します。これらの追加の ini ファイルで生成された kv キーと値のペアも、configuration_hash に追加されます。
これは、場合によっては便利な機能です。PHP 拡張機能を自分たちで開発するが、構成を php.ini に混ぜたくない場合は、別の ini を作成し、PHP_INI_SCAN_DIR を通じてその場所を PHP に指示できます。もちろん、その欠点も明らかであり、それをサポートするには追加の環境変数を設定する必要があります。より良い解決策は、開発者が拡張機能内で php_parse_user_ini_file または zend_parse_ini_file を呼び出して、対応する ini ファイルを解析することです。
4 番目に、configuration_hash ではキーは文字列ですが、値の型は何でしょうか?答えも文字列です (上記の非常に特殊な配列を除く)。具体的には、次のような構成になります。
最終的なconfiguration_hashに実際に保存されるキーと値のペアは次のとおりです。
キー: "log_errors"
val : ""
キー: "log_errors_max_len"
val:「1024」
log_errors に注意してください。log_errors に格納されている値は「0」ですらない、実際の空の文字列です。また、log_errors_max_len は数値ではなく、1024 の文字列です。
分析のこの時点で、基本的に ini ファイルの解析に関連するすべてが明確に説明されました。簡単に要約すると次のようになります。
1.ini の解析は php_module_startup ステージで行われます
2. 解析結果はconfiguration_hashに保存されます。
2. 構成はモジュールに適用されます
PHP の一般的な構造は、OS との対話、PHP コードのコンパイル、メモリ ホスティングの提供などを担当する最下部の zend エンジンとして見ることができます。zend の上位層には多くのモジュールが配置されています。エンジン。コアモジュールは Core モジュールで、その他には Standard、PCRE、Date、Session などがあります。これらのモジュールには、php 拡張機能と呼ばれる別の名前もあります。各モジュールは、開発者が呼び出すための関数インターフェイスのセットを提供していることを簡単に理解できます。たとえば、explode、trim、array などの一般的に使用される組み込み関数は、Standard モジュールによって提供されます。
これらについて説明する必要がある理由は、php.ini には、php 自体、つまりコア モジュールの設定 (safe_mode、display_errors、max_execution_time など) に加えて、かなりの設定があるためです。他の異なるモジュール用の構成はほとんどありません。
たとえば、date モジュールは、一般的な日付、時刻、strtotime、その他の関数を提供します。 php.ini では、関連する設定は次のようになります。
これらのモジュールが独立した構成を持つことに加えて、zend エンジンも構成可能ですが、zend エンジンには構成可能な項目が非常に少なく、error_reporting、zend.enable_gc、および detect_unicode のみです。
前のセクションで説明したように、php_module_startup は php_init_config を呼び出します。その目的は、ini ファイルを解析し、configuration_hash を生成することです。それでは、次に php_module_startup で他に何が行われるのでしょうか?明らかに、configuration_hash の設定は、Zend、Core、Standard、SPL などのさまざまなモジュールに適用されます。もちろん、PHP には通常多くのモジュールが含まれており、これらのモジュールも PHP の起動中に順番に開始されるため、これは一晩で完了するプロセスではありません。次に、モジュール A の起動プロセス中に、モジュール A を構成するプロセスが発生します。
拡張機能開発の経験のある学生は、モジュール A が PHP_MINIT_FUNCTION(A) で開始されていることを直接指摘しますよね。
はい、モジュール A を設定する必要がある場合は、PHP_MINIT_FUNCTION で REGISTER_INI_ENTRIES() を呼び出して設定を完了できます。 REGISTER_INI_ENTRIES は、現在のモジュールに必要な構成項目の名前に基づいて、ユーザーが設定した構成値のconfiguration_hash を検索し、それをモジュール独自のグローバル空間に更新します。
2.1、モジュールのグローバル空間
configuration_hash から各モジュールに ini 設定を適用する方法を理解するには、まず php モジュールのグローバル空間を理解する必要があります。さまざまな PHP モジュールに対して、独自のストレージ スペースを開くことができ、このスペースはモジュールからグローバルに参照できます。一般に、モジュールに必要な ini 設定を保存するために使用されます。つまり、configuration_hash 内の構成アイテムは、最終的にはグローバル空間に保存されます。モジュールの実行中、このグローバル空間に直接アクセスしてモジュールのユーザー設定を取得するだけで済みます。もちろん、モジュールの実行中に中間データを記録するためにもよく使用されます。
bcmath モジュールを例に挙げてみましょう。bcmath は、数学的計算のためのインターフェイスを提供する PHP モジュールです。まず、その ini 設定を見てみましょう。
bcmath には設定項目が 1 つだけあります。php.ini の bcmath.scale を使用して bcmath モジュールを設定できます。
次に、bcmatch モジュールのグローバル空間定義を調べ続けます。 php_bcmath.h には次のステートメントがあります。
マクロを展開すると、次のようになります。
実際、zend_bcmath_globals タイプは bcmath モジュールのグローバル空間タイプです。ここでは zend_bcmath_globals 構造のみが宣言されており、bcmath.c には特定のインスタンス化定義があります。
//展開後は、zend_bcmath_globals bcmath_globals;
ZEND_DECLARE_MODULE_GLOBALS(bcmath)
変数 bcmath_globals の定義が ZEND_DECLARE_MODULE_GLOBALS で完了していることがわかります。
bcmath_globals は、4 つのフィールドを含む実際のグローバル空間です。最後のフィールド bc_precision は、ini 設定の bcmath.scale に対応します。 php.ini で bcmath.scale の値を設定し、bcmath モジュールの起動時に bcmath.scale の値が bcmath_globals.bc_precision に更新されます。
configuration_hash の値を各モジュールで定義された xxx_globals 変数に更新します。これは、いわゆる ini 設定をモジュールに適用することです。モジュールが開始されると、これらの構成が設定されます。したがって、後続の実行フェーズでは、php モジュールは、configuration_hash に再度アクセスする必要はなく、モジュール自体の XXX_globals にアクセスして、ユーザーが設定した構成を取得するだけで済みます。
bcmath_globals、ini 設定項目の 1 つのフィールドに加えて、他の 3 つのフィールドは何ですか?これはモジュールのグローバル空間の 2 番目の役割であり、ini 設定に使用されるだけでなく、モジュールの実行中に一部のデータを保存することもできます。
もう 1 つの例は json モジュールです。これも PHP で非常によく使用されるモジュールです。
json モジュールには ini 構成が必要なく、そのグローバル空間にはフィールド error_code が 1 つだけあることがわかります。 error_code は、json_decode または json_encode の最後の実行で発生したエラーを記録します。 json_last_error 関数はこの error_code を返し、ユーザーがエラーの原因を特定できるようにします。
モジュールのグローバル空間変数に簡単にアクセスするために、PHP では従来からいくつかのマクロが提案されてきました。たとえば、json_globals の error_code にアクセスしたい場合は、もちろん json_globals.error_code として直接記述することもできますが (マルチスレッド環境では使用できません)、より一般的な記述方法は、JSON_G マクロを定義することです。
JSON_G(error_code) を使用して json_globals.error_code にアクセスします。この記事の冒頭で、PG、BG、JSON_G、PCRE_G、XXX_G などについて触れました。これらのマクロは、PHP ソース コードでも非常に一般的です。 PG マクロは Core モジュールのグローバル変数にアクセスでき、BG マクロは Standard モジュールのグローバル変数にアクセスでき、PCRE_G は PCRE モジュールのグローバル変数にアクセスできます。
2.2. モジュールに必要な構成を判断するにはどうすればよいですか?
モジュールに必要な INI 構成の種類は、各モジュールで定義されます。たとえば、Core モジュールの場合、次の構成項目定義があります。
上記のコードは、php-src\main\main.c ファイルの 450 行目あたりにあります。 ZEND_INI_BEGIN、ZEND_INI_END、PHP_INI_ENTRY_EX、STD_PHP_INI_BOOLEAN など、多くのマクロが関係していますが、この記事では 1 つずつ詳しく説明しませんので、興味のある読者はご自身で分析してください。
上記のコードをマクロ展開すると、次のようになります。
私たちが確認したとおり、構成の定義は、基本的に zend_ini_entry タイプの数を定義するものです。zend_ini_entry 構造体のフィールドの具体的な内容は次のとおりです。
char *value; // 設定項目の値
uint value_length;
char *orig_value; // 設定項目の元の値
uint orig_value_length;
int orig_modifiable; // 構成アイテムの元の変更可能要素
int updated; // 変更されているかどうか、変更されている場合、orig_value は変更前の値を保存します
void (*displayer)(zend_ini_entry *ini_entry, int 型);
};
2.3、モジュールに構成を適用する - REGISTER_INI_ENTRIES
REGISTER_INI_ENTRIES は、さまざまな拡張機能の PHP_MINIT_FUNCTION でよく見られます。 REGISTER_INI_ENTRIES は主に 2 つのことを完了する役割を果たします: 1 つ目は、モジュールのグローバル空間 XXX_G を埋めることと、configuration_hash の値を XXX_G に同期することです。次に、EG(ini_directives) も生成します。
REGISTER_INI_ENTRIES もマクロであり、展開後は実際には zend_register_ini_entries メソッドになります。具体的に zend_register_ini_entries の実装を見てみましょう。
//configuration_hash に見つからない場合は、デフォルト値が使用されます。
If (!config_directive_success && hashed_ini_entry->on_modify) {
hashed_ini_entry->on_modify(hashed_ini_entry, hashed_ini_entry->value, hashed_ini_entry->value_length, hashed_ini_entry->mh_arg1, hashed_ini_entry->mh_arg2, hashed_ini_entry-phpcng tphpcnmh_arg3、ZEND_INI_STAGE_STARTUP TSRMLS_CC);
}
p;
}
成功を返します。
}
簡単に言うと、上記のコードのロジックは次のように表現できます。
1. モジュールによって宣言された ini 設定項目を EG (ini_directives) に追加します。 ini 設定項目の値は後で変更される可能性があることに注意してください。
2.configuration_hash 内の各モジュールに必要な ini を見つけてください。
それが見つかった場合は、この値がユーザーの ini ファイルに設定されており、ユーザーの設定が使用されることを意味します。
見つからない場合でも、モジュールは ini を宣言するときにデフォルト値を取得するため、問題ありません。
3. ini の値を XX_G に同期します。結局のところ、php の実行中、これらの XXX_global は依然として役割を果たします。具体的な処理は、ini の宣言時にモジュールで指定される、各 ini 設定に対応した on_modify メソッドを呼び出すことになります。
実際には関数ポインタである on_modify を詳しく見てみましょう。2 つの特定のコア モジュールの設定ステートメントを見てみましょう:
log_errors の場合、その on_modify は OnUpdateBool に設定され、log_errors_max_len の場合、その on_modify は OnUpdateLong に設定されます。
さらに、php.ini の設定が次のようになっていると仮定します。
OnUpdateBool 関数を詳しく見てみましょう。
// p は core_globals のアドレスと log_errors フィールドのオフセットを加えたものを表します
//取得したアドレスはlog_errorsフィールドのアドレス
p = (zend_bool *) (base (size_t) mh_arg1);
if (new_value_length == 2 && strcasecmp("on", new_value) == 0) {
*p = (zend_bool) 1;
}
else if (new_value_length == 3 && strcasecmp("yes", new_value) == 0) {
*p = (zend_bool) 1;
}
else if (new_value_length == 4 && strcasecmp("true", new_value) == 0) {
*p = (zend_bool) 1;
}
それ以外の場合{
//configuration_hash に格納される値は「On」ではなく文字列「1」です
// そこで、ここでは atoi を使って数値 1 に変換します
*p = (zend_bool) atoi(new_value);
}
成功を返します。
}
最も不可解なのは、おそらく mh_arg1 と mh_arg2 でしょう。実際、上記の zend_ini_entry 定義と比較すると、mh_arg1 と mh_arg2 はまだ理解しやすいです。 mh_arg1 はバイト オフセットを表し、mh_arg2 は XXX_globals のアドレスを表します。したがって、(char *)mh_arg2 mh_arg1 の結果は、XXX_globals 内のフィールドのアドレスになります。この場合、具体的には、core_globals 内の log_errors のアドレスを計算します。したがって、OnUpdateBool が最終的に実行されるとき
その機能は以下と同等です
OnUpdateBool を分析した後、OnUpdateLong を見てみましょう。それは一目瞭然です。
// log_errors_max_len のアドレスを取得します
p = (long *) (base (size_t) mh_arg1);
// 「1024」をlong型に変換してcore_globals.log_errors_max_lenに代入
*p = zend_atol(新しい値, 新しい値の長さ);
成功を返します。
}
最後に、zend_register_ini_entries 関数では、configuration_hash に設定がある場合、on_modify が呼び出されたときに hashed_ini_entry の value と value_length が更新されることに注意してください。つまり、ユーザーが php.ini で設定した場合、EG (ini_directives) には実際の設定値が保存されます。ユーザーが設定されていない場合、EG (ini_directives) は zend_ini_entry の宣言時に指定されたデフォルト値を保存します。
zend_register_ini_entries のdefault_value 変数の名前が適切ではないため、誤解を招きやすいです。実際、default_value はデフォルト値を表すのではなく、ユーザーが実際に構成した値を表します。
3. 概要
この時点で、configuration_hash、EG (ini_directives)、PG、BG、PCRE_G、JSON_G、XXX_G... の 3 つのデータがすべて明確に説明されました。
要約すると:
1、configuration_hash は、php.ini ファイルに構成を保存します。検証は実行されず、その値は文字列です。
2. EG (ini_directives) には、各モジュールで定義された zend_ini_entry が格納されます。ユーザーが php.ini (configuration_hash に存在) で設定した場合、その値は、configuration_hash の値に置き換えられますが、型は文字列のままです。
3. XXX_G、このマクロは、モジュールのグローバル空間にアクセスするために使用されます。このメモリ空間は、ini 設定の保存に使用でき、on_modify で指定された関数を通じて更新できます。そのデータ型は、XXX_G のフィールド宣言によって決まります。