CentOS 6.7
nginx 1.8.0
PHP 7.0.0
の環境でphp-fpmでPHPを実行している環境で、
特定の処理が落ちて
502 Bad Gateway
となる原因をここ2日程探っていましたが、原因が判明しました。
ログには
yyyy/mm/dd hh:mi:ss [error] 24873#0: *2 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: xxx.xxx.xxx.xxx, server: xxx.xxx.xxxx, request: “GET /test.php HTTP/1.1”, upstream: “fastcgi://unix:/xxx/php-fpm.sock:”, host: “xxx.xxx.xxxx”
のように出るだけなので、通常は
php-fpm.sock
の所有者の設定廻りを疑って設定ファイルを見直すのですが、
そこに間違いがない上に他に手がかりとなる情報が何も出ないのでお手上げ状態でした・・・。
結局、ところどころに exit 入れつつどこまで動くか探っていくと・・・
mb_send_mail
を通るとダメなことが判明。
ここから今度は
bool mb_send_mail ( string $to , string $subject , string $message [, string $additional_headers = NULL [, string $additional_parameter = NULL ]] )
のみで、ダメになる時と同様のパラメータを入力して、コマンドラインからPHPで実行してみると・・・
セグメンテーション違反です (コアダンプ)
おぉう・・・ やっぱりこれが原因か、segment faultで落ちているのでどこにも詳細なログがでなかった訳です・・・・。
PHP側がおちるので、クライアント側に戻すことができなくなって
502 Bad Gateway
となったということですね。
次に必須パラメータ$to~$messageまで書いたテストプログラムを実行してみました。
これは日本語なし、日本語ありともに動作OK。
次に、$additional_headers を追加すると・・・
セグメンテーション違反です (コアダンプ)
むぅ・・・
大体ポイントがつかめてきたので此処からはgoogle先生の出番です。
色々調べてみると、やはりPHP 7.0.0のバグっぽいですね。
https://bugs.php.net/bug.php?id=71066
にバグが上がってました。
一般的には$additional_headersの指定を
$additional_headers = <<<EOD
MIME-Version: 1.0
Content-type: text/plain; charset=ISO-2022-JP
Content-Transfer-Encoding: 7bit
From: $from_name <$own_email>
Reply-To: $from_name <$own_email>EOD;
のようにするかと思いますが、
Content-type: text/plain; charset=ISO-2022-JP
Content-Transfer-Encoding: 7bit
を指定するとPHP 7.0.0の現状ではセグメント違反で落ちます。
色々と試してみましたが・・・
charsetや7bitの指定が内容の文字エンコーディングと一致しないとかではなく、
全文英数の文章でも落ちるので、何を指定しても無駄そうです^^;
というわけで、この指定を諦めるしかないのですが、mb_languageの設定をしておけば適宜処理して送ってくれるもようです?
ですので、ざっくりこの2行を削って一旦解決。
今度ソース側を除いて原因でも調べてみますかね・・・
12/30追記:
ソースからコンパイルしている方は、上のPHPバグレポートでのFixのgitの差分にもありますが、
--- a/ext/mbstring/mbstring.c
+++ b/ext/mbstring/mbstring.c
@@ -4109,11 +4109,12 @@ PHP_FUNCTION(mb_send_mail)
_php_mbstr_parse_mail_headers(&ht_headers, headers, headers_len);
}
- if ((s = zend_hash_str_find_ptr(&ht_headers, "CONTENT-TYPE", sizeof("CONTENT-TYPE") - 1))) {
+ if ((s = zend_hash_str_find(&ht_headers, "CONTENT-TYPE", sizeof("CONTENT-TYPE") - 1))) {
char *tmp;
char *param_name;
char *charset = NULL;
+ ZEND_ASSERT(Z_TYPE_P(s) == IS_STRING);
p = strchr(Z_STRVAL_P(s), ';');
if (p != NULL) {
という対応でOKなようです。