【PHP】mb_decode_mimeheader の代わりになるものを作る(機種依存文字対策)

【PHP】mb_decode_mimeheader の代わりになるものを作る(機種依存文字対策)Web開発者の備忘録とある案件で、POP3でメールを受信する必要があり、そこでメールのデコードも行う事になったのですが。
最初、とある有名なメール受信ライブラリを使っていたのですが、どうにも機種依存文字が化けてしまう。。
そのライブラリを追っかけていったら、mb_decode_mimeheaderを最終的に使ってデコードをしていました。
でもこの関数は使えないことで有名。。

ということで探していたら、
http://d.hatena.ne.jp/hpool/20081108/1226078069
とすばらしい関数を提供してくれる人がおりましたのでそれを使う事に。

・・でも残念な事に添付ファイル名に機種依存文字が入った場合がうまくいかない。。
理由は、キャラクタセットがiso-2022-jpであるという前提で作られているため。

次の例でNGとなります。

Content-Type: image/jpeg;
name=”=?Shift_JIS?B?h0CLQI7tiMuRtpW2jpqDZYNYg2eCxYK3IIdBlLyKcL3N37C9IIdClLyKcINK?=
=?Shift_JIS?B?g2mC4JP8guqCxILcgreBQJa8kU+C4ILggsyCt4Kygq2St4KtgrWCxILdgtw=?=
=?Shift_JIS?B?grWC5YKkgUkgLSCDUoNzgVsuSlBH?=”

ということで作ったのが次の関数。上のサイトで示された関数をベースに改良を加えたものです。

※2010.04.28 色々と問題があったのでその修正版をUpしなおしました。
また、修正版プログラムでは、
PHP による日本語の文字コード判定スクリプト
ここで示されている「detect_encoding_ja()」という素晴らしい関数を使っております。あわせてご利用下さい。

function decode_mimeheader($str) {

	$enc_r = array(  // 変換するエンコーディング文字列を設定
		'iso-2022-jp' => 'iso-2022-jp-ms' ,
		'shift_jis'   => 'sjis-win'       ,
		'euc-jp'      => 'eucjp-win'      ,
		'utf-8'       => 'utf-8'
	);

	$str = str_replace('?==?' , "?=\n=?" , $str);  // 未改行文字対策

	$decode_str = '';
	foreach (preg_split('/\s/', $str) as $split_str) {  // SPACEで分割
		if (strlen($split_str) === 0) {
			$decode_str .= ($decode_str != '' ? ' ' : '');
			continue;
		}
		
		if (preg_match('/(.*?)=\?([^\?]+)\?([^\?]+)\?([^\?]+)\?=(.*?)$/', $split_str, $matches)) {  // =?[Charset]?[Text]?=形式の場合
			$enc_str  = strtolower($matches[2]);
			$from_enc = isset($enc_r[$enc_str]) ? $enc_r[$enc_str] : $enc_str;
			if ($from_enc != '') {
				$match_decode = null;
				switch (strtoupper($matches[3])) {
					case 'B':
						$match_decode = base64_decode($matches[4]);
						break;
					case 'Q':
						$match_decode = quoted_printable_decode($matches[4]);
						break;
				}
				if (!is_null($match_decode)) { // 変換に成功した場合のみ変換後テキストを追加し次の行へ
					$decode_str .= ($decode_str != '' ? ' ' : '') . mb_convert_encoding( $matches[1].$match_decode.$matches[5], mb_internal_encoding(), $from_enc );
					continue;
				}
			}
		}
		// 変換できなかった場合、又は =?[Charset]?[Text]?= 形式でなかった場合は、テキストのキャラクタセットを自動検出して変換する
		$decode_str .= ($decode_str != '' ? ' ' : '') . mb_convert_encoding( $split_str , mb_internal_encoding() , detect_encoding_ja( $split_str ) );
	}

	return $decode_str;
}

mb_internal_encoding()で設定されたキャラクタセットに変換された文字列が返ります。

バグ等ありましたら、報告よろしくお願いします。