1. 実行時に構成を変更します
前の記事で述べたように、ini_set 関数は、PHP の実行中に PHP の一部の構成を動的に変更できます。これは一部にすぎず、すべての構成を動的に変更できるわけではないことに注意してください。変更可能な ini 設定については、http://php.net/manual/zh/configuration.changes を参照してください。モード.php
ini_set の実装に直接進みます。関数は少し長いですが、ロジックは非常に明確です。
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &varname, &varname_len, &new_value, &new_value_len) == FAILURE) {
戻る;
}
// 去EG(ini_directives)中获取配置の值
old_value = zend_ini_string(varname, varname_len 1, 0);
/* 変更すると解放される可能性があるため、コピーしてここに返します。 */
if (old_value) {
RETVAL_STRING(old_value, 1);
} その他 {
RETVAL_FALSE;
}
// セーフ モードがオンになっている場合、次の ini 設定にはファイル操作が含まれる可能性があり、uid の補助チェックが必要になる場合があります。
#define _CHECK_PATH(var, var_len, ini) php_ini_check_path(var, var_len, ini, sizeof(ini))
/* セーフモードとベースディレクトリのチェック */
If (PG(safe_mode) || PG(open_basedir)) {
If (_CHECK_PATH(varname, varname_len, "error_log") ||
_CHECK_PATH(varname, varname_len, "java.class.path") ||
_CHECK_PATH(varname, varname_len, "java.home") ||
_CHECK_PATH(varname, varname_len, "mail.log") ||
_CHECK_PATH(varname, varname_len, "java.library.path") ||
_CHECK_PATH(varname, varname_len, "vpopmail.directory")) {
If (PG(safe_mode) && (!php_checkuid(new_value, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
zval_dtor(return_value);
RETURN_FALSE;
}
if (php_check_open_basedir(new_value TSRMLS_CC)) {
zval_dtor(return_value);
RETURN_FALSE;
}
}
}
// セーフ モードでは、次の ini は保護されており、動的に変更されません。
If (PG(safe_mode)) {
If (!strncmp("max_execution_time", varname, sizeof("max_execution_time")) ||
!strncmp("memory_limit", varname, sizeof("memory_limit")) ||
!strncmp("child_terminate", varname, sizeof("child_terminate"))
) {
zval_dtor(return_value);
RETURN_FALSE;
}
}
// zend_alter_ini_entry_ex を呼び出して、ini 構成を動的に変更します
If (zend_alter_ini_entry_ex(varname, varname_len 1, new_value, new_value_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC) == FAILURE) {
zval_dtor(return_value);
RETURN_FALSE;
}
}
ご覧のとおり、いくつかの必要な検証作業に加えて、主なことは zend_alter_ini_entry_ex を呼び出すことです。
引き続き zend_alter_ini_entry_ex 関数でフォローアップしていきます。
// EG (ini_directives) で対応する ini_entry を検索します。
If (zend_hash_find(EG(ini_directives), name, name_length, (void **) &ini_entry) == FAILURE) {
失敗を返します。
}
// 変更されているかどうか、変更可能かどうか
変更可能 = ini_entry->modifiable;
変更済み = ini_entry->modified;
if (ステージ == ZEND_INI_STAGE_ACTIVATE && 変更タイプ == ZEND_INI_SYSTEM) {
ini_entry->modifiable = ZEND_INI_SYSTEM;
}
// 変更を強制するかどうか
If (!force_change) {
If (!(ini_entry->modifiable &modify_type)) {
失敗を返します。
}
}
// EG (modified_ini_directives) は、変更された ini_entry を保存するために使用されます
// 主に回復に使用します
If (!EG(modified_ini_directives)) {
ALLOC_HASHTABLE(EG(modified_ini_directives));
zend_hash_init(EG(modified_ini_directives), 8, NULL, NULL, 0);
}
//ini_entryの値、値の長さ、変更可能な範囲をorig_xxxに確保
// リクエスト終了時にini_entryを復元できるように
If (!modified) {
ini_entry->oig_value = ini_entry->value;
ini_entry->oig_value_length = ini_entry->value_length;
ini_entry->oig_modifiable = 変更可能;
ini_entry->modified = 1;
zend_hash_add(EG(modified_ini_directives), name, name_length, &ini_entry, sizeof(zend_ini_entry*), NULL);
}
重複 = estrndup(new_value, new_value_length);
//modify を呼び出して、XXX_G 内の対応する ini 設定を更新します。
If (!ini_entry->on_modify || ini_entry->on_modify(ini_entry,重複,new_value_length,ini_entry->mh_arg1,ini_entry->mh_arg2,ini_entry->mh_arg3,ステージ TSRMLS_CC)==成功) {
//上記と同様、複数回変更した場合は以前に変更した値を解放する必要がある
If (変更済み && ini_entry->orig_value != ini_entry->value) {
efree(ini_entry->value);
}
ini_entry->value = 重複;
ini_entry->value_length = new_value_length;
} その他 {
efree(重複);
失敗を返します。
}
成功を返します;
}
注意深く理解する必要があるロジックが 3 つあります。
1) ini_entry の Modified フィールドは、構成が動的に変更されたかどうかを示すために使用されます。 ini 設定が変更されると、modified は 1 に設定されます。上記のコードには重要なセクションがあります。
このコードは、php コードで ini_set を何度呼び出しても、最初の ini_set だけがこのロジックに入り、orig_value を設定することを意味します。 ini_set への 2 回目の呼び出し以降、この時点での Modified は 1 に設定されているため、このブランチは再度実行されません。したがって、ini_entry->orig_value は常に、最初の変更前の構成値 (つまり、最も元の構成) を保存します。
2) ini_set によって変更された設定をすぐに有効にするには、on_modify コールバック関数が必要です。
前の記事で説明したように、モジュールのグローバル変数を更新できるようにするために on_modify が呼び出されます。もう一度思い出してください、まず第一に、モジュールのグローバル変数の設定は文字列型ではなくなり、bool を使用する必要がある場合は bool を使用し、int を使用する必要がある場合は int を使用します。次に、各 ini_entry にはモジュールのグローバル変数のアドレスと対応するオフセットが格納されるため、on_modify はメモリを迅速に変更できます。さらに、on_modify が呼び出された後も、EG (ini_directives) の設定値が最新になるように、ini_entry->value をさらに更新する必要があることを忘れないでください。
3) 新しいハッシュ テーブル EG (modified_ini_directives) がここに表示されます。
EG (modified_ini_directives) は、動的に変更された ini 設定を保存するためにのみ使用されます。ini 設定が動的に変更された場合、その設定は EG (ini_directives) と EG (modified_ini_directives) の両方に存在します。各 ini_entry は変更されたフィールドでマークされているため、EG (ini_directives) を走査して変更されたすべての設定を取得することはできないでしょうか?
答えは「はい」です。個人的には、ここでの EG (modified_ini_directives) は主にパフォーマンス向上のためであり、EG (modified_ini_directives) を直接トラバースするだけで十分だと感じています。さらに、EG (modified_ini_directives) の初期化を zend_alter_ini_entry_ex まで延期することで、PHP のパフォーマンス最適化ポイントを詳細に確認することもできます。
2. 構成を復元する
ini_set の動作時間は php.ini ファイルの動作時間とは異なり、リクエストの実行が終了すると、ini_set は無効になります。さらに、コード内で ini_restore 関数が呼び出されると、以前に ini_set で設定された構成も無効になります。
各 php リクエストが実行されると、php_request_shutdown がトリガーされます。これと php_request_startup は、対応する 2 つのプロセスです。 php が apache/nginx でフックされている場合、http リクエストが処理されるたびに php_request_shutdown が呼び出されます。php が CLI モードで実行されている場合、スクリプトの実行後に php_request_shutdown も呼び出されます。
php_request_shutdown では、ini の回復処理が確認できます。
zend_deactivate と入力すると、zend_ini_deactivate 関数が呼び出され、zend_ini_deactivate が PHP 設定の復元を担当していることがわかります。
zend_ini_deactivate の実装を詳しく見てみましょう。
zend_hash_apply の観点から見ると、ini を復元する実際のタスクは最終的に zend_restore_ini_entry_wrapper コールバック関数に割り当てられます。
static int zend_restore_ini_entry_cb(zend_ini_entry *ini_entry, int ステージ TSRMLS_DC)
{
int 結果 = 失敗;
// 変更されたini項目のみを表示します
If (ini_entry->modified) {
If (ini_entry->on_modify) {
// orig_value を使用して XXX_G の関連フィールドをリセットします
zend_try {
result = ini_entry->on_modify(ini_entry, ini_entry->orig_value, ini_entry-phpcngtphpctorig_value_length, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry-phpc ngtphpcnmh_arg3, ステージ TS RMLS_CC);
zend_end_try();
}
If (ステージ == ZEND_INI_STAGE_RUNTIME && 結果 == FAILURE) {
/* 実行時の失敗は問題ありません */
1 を返します。
}
If (ini_entry->value != ini_entry->orig_value) {
efree(ini_entry->value);
}
//ini_entry自体は元の値に戻ります
ini_entry->value = ini_entry->orig_value;
ini_entry->value_length = ini_entry->oig_value_length;
ini_entry->modifiable = ini_entry->orig_modifiable;
ini_entry->modified = 0;
ini_entry->oig_value = NULL;
ini_entry->oig_value_length = 0;
ini_entry->orig_modifiable = 0;
}
0 を返します;
}
ロジックは非常に明確なので、読者も理解できると思います。 ini 設定の回復プロセスを要約します。
3. 構成の破棄
たとえば、sapi ライフサイクルの終了時には、Apache がシャットダウンされ、cli プログラムが実行されます。この段階に入ると、前述のconfiguration_hashやEG(ini_directives)などを破棄し、それらが使用していたメモリ空間を解放する必要があります。
1. PHP はすべてのモジュールを順番に終了し、各モジュールの PHP_MSHUTDOWN_FUNCTION で UNREGISTER_INI_ENTRIES を呼び出します。 UNREGISTER_INI_ENTRIES は REGISTER_INI_ENTRIES に対応しますが、UNREGISTER_INI_ENTRIES はモジュールのグローバル領域を解放する責任はありません。XXX_globals のメモリは静的データ領域に配置され、手動でリサイクルする必要はありません。
UNREGISTER_INI_ENTRIES が行う主な処理は、特定のモジュールの ini_entry 設定を EG (ini_directives) テーブルから削除することです。削除後、ini_entry 自体の領域は再利用されますが、ini_entry->value は再利用されない場合があります。
すべてのモジュールの PHP_MSHUTDOWN_FUNCTION が UNREGISTER_INI_ENTRIES を 1 回呼び出すと、コア モジュールの ini 設定のみが EG (ini_directives) に残ります。現時点では、UNREGISTER_INI_ENTRIES を手動で呼び出して、コア モジュール構成の削除を完了する必要があります。
// この時点では、Core モジュールの設定のみが EG (ini_directives) に残ります。
// ここで手動でクリーニングします
UNREGISTER_INI_ENTRIES();
//configuration_hash をリサイクルします
php_shutdown_config();
// EG(ini_directives) をリサイクルします
zend_ini_shutdown(TSRMLS_C);
...
}
UNREGISTER_INI_ENTRIES への手動呼び出しが完了すると、EG (ini_directives) には要素が含まれなくなります。理論的には、この時点の EG (ini_directives) は空のハッシュ テーブルです。
2.configuration_hash のリサイクルは EG (ini_directives) の後に発生します。上記のコードには、php_shutdown_config に関する関数呼び出しが含まれています。 php_shutdown_config は主に、configuration_hash のリサイクルを担当します。
zend_hash_destroy は、configuration_hash 自体の領域を解放しないことに注意してください。XXX_G によってアクセスされるモジュールのグローバル領域と同様、configuration_hash もグローバル変数であり、手動でリサイクルする必要はありません。
3. php_shutdown_config が完了した時点では、EG の独自領域 (ini_directives) だけが解放されていません。したがって、最後のステップでは zend_ini_shutdown を呼び出します。 zend_ini_shutdown は EG (ini_directives) を解放するために使用されます。前述したように、このときのEG(ini_directives)は理論的には空のハッシュテーブルであるため、HashTable自体が占有している領域を解放する必要があります。
4. 概要
画像を使用して、ini 設定に関連するプロセスを大まかに説明します。