同僚は、set_error_handler を使用すると 100% 再現できるコアを見つけました。洗練された再現コードは次のとおりです (インターネットにアクセスできない環境である必要があります)。
関数 err_handler(){
終了;
true を返します;
}
set_error_handler('err_handler');
$client = file_get_contents("http://www.laruence.com/ServiceNoWse.asmx?WSDL");
Web サーバーに配置されたこのコードは、最初にアクセスされたときには正常に動作し、2 回目と 3 回目にはコアが表示されます。
gdb の追跡後、コアが php_stream_display_wrapper_errors 関数内にあることが判明しました。err_stack でエラー メッセージ文字列を処理すると、コア スタックは次のようになりました。
#0 0x000000302af6ff20 in strlen () /lib64/tls/libc.so.6
#1 0x0000002a989d97c1 in php_stream_display_wrapper_errors (wrapper=0x2a98e884a0、path=変数 "path" は使用できません。
) /home/huixc/package/php-5.2.14/main/streams/streams.c:151
#2 0x0000002a989dca22 _php_stream_open_wrapper_ex 内 (path=0x76e7c8 "http://www.laruence.com/ServiceNoWse.asmx?WSDL", mode=0x2a98ae3087 "rb", options=8,opened_path=0x0,
context=0x76e808) /home/huixc/package/php-5.2.14/main/streams/streams.c:1893
#3 0x0000002a98966541 in zif_file_get_contents (ht=-1729541984, return_value=0x76e738, return_value_ptr=変数「return_value_ptr」は使用できません。
) /home/huixc/package/php-5.2.14/ext/standard/file.c:541
#4 zend_do_fcall_common_helper_SPEC (execute_data=0x7fffffca30) の 0x0000002a98a2c05e (/home/huixc/package/php-5.2.14/Zend/zend_vm_execute.h:200
)#5 0x0000002a98a2b671 の実行 (op_array=0x769890) at /home/huixc/package/php-5.2.14/Zend/zend_vm_execute.h:92
#6 0x0000002a98a0c734 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /home/huixc/package/php-5.2.14/Zend/zend.c:1134
#7 0x0000002a989c965d php_execute_script (primary_file=0x7fbffff00) at /home/huixc/package/php-5.2.14/main/main.c:2036
#8 0x0000002a98a9bd36 php_handler (r=0x8f1ba8) (/home/huixc/package/php-5.2.14/sapi/apache2handler/sapi_apache2.c:639
)
30分以上追跡した結果、ついにその理由が分かりました
これは、PHP では、終了の場合、ユーザー スクリプトが終了した後の最終作業を達成するために実際に set/longjmp が使用され、エラー コードの場合、出力されるエラー メッセージの数がラッパーの err_count に基づいて決定されるためです。
そして、エラーメッセージを出力した後、ラップエラーメッセージをクリアし、エラーカウントをゼロに設定します。
void php_stream_tidy_wrapper_error_log(php_stream_wrapper *ラッパー TSRMLS_DC)
{
if (ラッパー) {
/* エラースタックを整理します */
int i;
for (i = 0; i
efree(wrapper->err_stack[i]);
}
if (ラッパー->err_stack) {
efree(wrapper->err_stack);
}
ラッパー->err_stack = NULL;
ラッパー->err_count = 0;
}
}
ただし、エラーメッセージが表示された後、すぐに php_error_docref1 がトリガーされ、その後、コード内で設定された error_handler がトリガーされますが、最終的に、longjmp は SOAP 処理時に設定されたジャンプポイントに到達します。これにより、ペアがスキップされるため、php_stream_tidy_wrapper_error_log が呼び出されるため、リクエストが 2 回目に行われるとき、err_count は正しくゼロに初期化されず、最後のリクエストのエラー数が維持されます。
したがって、エラーメッセージを出力するときは、
……
for (i = 0, l = 0; i
l += strlen(wrapper->err_stack[i]) //コアが切れています
if (i
l += ブレン;
}
}
当面は、この問題を解決するには、ストリームの php_stream_display_wrapper_errors 関数で php_error_docref1 を呼び出す前に、 php_stream_tidy_wrapper_error_log;
を使用します。