mb_encode_mimeheader関数で文字化けする php8.1 対応版

PHPバージョン8.1は、ノーメンテで使い続けているソースコードに対して厳しいだけでなく、常に修正し続けているソースでもエラーとなってしまう事が有ります。

今回は、メール送信時のメールヘッダーを形成する際に使う、mb_encode_mimeheader関数について。

PHP8.0までエラーも警告も無い状態で使っていたメール送信ロジックだったのですが、PHP8.1ではメール送信者やタイトルで化けるようになりました。
100%化けるトラブルではないので、テストケースによってはスルーされてしまうかもしれません。

で、調べてみると、PHP8.1だからエラーが出るという資料が無く、過去のバージョンでの対応策ならありました。
1.mb_internal_encodingをISO-2022-JPに指定し、変換処理後に元に戻す
2.mb_encode_mimeheaderに渡す前に、文字数を既定の74バイト以下に加工して渡す
3.そもそもUTF-8メールならばこのようなことは起きない。
らしいですね。
確かにUTF-8で出力したメールは化けていません。Outlookとスマホ、自前のガラケーで確認しました。
UTF-8メールにしてしまおうかとも考えましたが、ガラケーが対応できないことが多いらしいので、3Gガラケーが日本から消える2025年まではISO-2022-JPメールとすることにしました、が・・・

茨の道を選んでしまいました。

仕様ではmb_encode_mimeheaderが勝手に74バイトごとに区切ってエンコードしてくれるはずなんですが、エンコードした結果文字列を直後にmb_decode_mimeheaderで戻しても元の文字列にならない文字列があります。
なので、上記手法2の考えで、74バイト=全角37文字以下で化けない文字数を探ったら、おおよそ全角20~24文字前後まで少なくすると化けないようになったんですが、全角20文字と設定した場合にちょうど20文字の文字列を処理するとお化け文字が追加されるんです。
なので、手法1の処理も追加したら、トラブった文字列はクリアー出来ましたが別の文字列が化けるようになりました。

困った。

なので、一度エンコードした文字をデコードし、それらが異なった場合は文字数を減らして処理し、変換前後で同じ文字数になるまで対象文字列を短くするようループさせるロジックとしてみたら、どんな文字列でも化けない関数が出来ました。

まぁ、全角10文字ぐらいで処理すれば化けないんですが、メールのヘッダということを考えると転送量を少なくするロジックが良いはずなので、出来るだけバイト数が少なくなるようにしておくべきかと。

// スクリプトのエンコード
define("ENCODE","UTF-8");
// メールに使うエンコード
define("TRANS", "ISO-2022-JP-MS");

date_default_timezone_set("Asia/Tokyo");
mb_internal_encoding(ENCODE);
mb_regex_encoding(ENCODE);
mb_language("ja");

// ----------------------------------------------------------------- Function -*
// mail header FUNCTION
// ----------------------------------------------------------------------------*
function trans_and_encode_mimeheader($str) {
	// 既定では半角74バイト=全角37文字まで文字化けは起きないはず
	$split = 37;
	// まずは対象文字列をiso-2022-jpへエンコード
	$str = mb_convert_encoding($str, TRANS, ENCODE);
	// 内部エンコードもiso-2022-jpに
	mb_internal_encoding(TRANS);
	// 出力用配列初期化
	$return = array();
	// 処理する文字の文字数
	$pos = 0;

	// 文字数が与えられた文章の文字数まで繰り返す
	while ( $pos < mb_strlen($str,TRANS)):
		// 短くする文字数
		$back = 0;
		
		// 変換前後の文字列が一致するまでループさせる
		do {
			// 処理する文字列を切り出す
			$out = mb_strimwidth($str, $pos, ($split - $back), "", TRANS);
			
			// エンコード
			$enc = mb_encode_mimeheader($out, TRANS, "B");
			// デコード
			$dec = mb_decode_mimeheader($enc);
			// 減らす文字数をインクリメント
			$back++;
			
		} while($out!==$dec);
		
		// 出力配列にセット
		$return[] = $enc;
		// 次に処理する文字開始位置をセット
		$pos += mb_strlen($out, TRANS);
		
	endwhile;
	
	// 内部エンコードをUTF-8に戻す
	mb_internal_encoding(ENCODE);
	
	// 出来上がった配列をヘッダに適した形に整形して引き渡す
	return implode("\n ", $return);

} // end of function ----------

	

とりあえず今はこれでうまく動いています。

こちらの記事もお読みください

Webブラウザのプッシュ通知機能を使った詐欺まがいの行為... 最近、パソコンでWebページを閲覧していると、こんな通知を見たことがありませんか? 例えば、乗りものニュース https://trafficnews.jp/ サイトでは以下のような表示が出ます。 例はMozilla Firefoxで、同じようにGoogle Chromeはアドレスバー...
ミスリーディングソフト(詐欺まがいソフトにご注意ください。... .re-date{ padding:0.5em; background-color:#eee; font-size:0.8em; } 公開 2015-5-15 最終更新 2019-3-14 フリーソフトは機能的で便利な一方、インストールする時にそのフリーソフトとはほぼ関係のないソ...
【知らないと損をする】印刷広告・チラシからのWebページへのアクセス数を知ろう!QRコード活用術... .re-date{ padding:0.5em; background-color:#eee; font-size:0.8em; } 公開 2015-5-20 最終更新 2019-3-15 DMやチラシを配られている方からよく伺うのは「配ることに意味がある」「配らないと不安だから配る」「...

Written by

佐藤誠

Secured By miniOrange