[Zend Framework] Zend_Mail: 日本語をふくむメールを送る(長いsubjectと添付ファイルにも対応)

Zend_Mail – 日本語文字化けに関してわからなかった点をメモしておく | deadwood」のつづき。
いちおう解決編です。
ZendFrameworkのバージョンは、1.12.3です。

解決しなかった点

最終的に独習で解決できなかった点です。
ご存じの方、ぜひ教えて下さい。

  • 仕様に沿ったメールが作成できているかテストする方法
  • メールまわりの仕様に関するまとまった情報
  • setHeaderEncoding(Zend_Mime::ENCODING_BASE64)をすべきなのか

追記(2013/05/24)

電子メールあれこれ | 「インターネット・プロトコル詳説」最新記事一覧 – ITmedia Keywords | telnetでMIMEヘッダをきちんとつけて日本語メールを送信する | メールの基礎知識 | Takahiro Takano – M2 と RFC2231 と他 | エンコードマニアックス – 各種エンコードやハッシュを一発作成 | 直接関係ないけれどもメモ Message header analyzer

ソース

このように文字化けしない表示を実現できました。
Zend_Mail 2013-05-24 18-03-58
ソースはこのような形。
コメントアウトしている setHeaderEncoding については後述します。

public function sendmailAction() {
    function mbCnv($string) {
        return mb_convert_encoding($string, 'ISO-2022-JP', 'UTF-8');
    }
    function mbMime($string) {
        return mb_encode_mimeheader(mbCnv($string), 'ISO-2022-JP', 'B');
    }
    $txt = '長い日本語のテキスト。文字化けるか確認します。元のアドレスのコピー。松本人志。さらに特定の文字が文字化けるか確認します。長い日本語のテキスト。文字化けるか確認します。元のアドレスのコピー。松本人志。さらに特定の文字が文字化けるか確認します。';
    $short = '松本人志';
    $mail = new Zend_Mail('ISO-2022-JP');
//    $mail->setHeaderEncoding(Zend_Mime::ENCODING_BASE64);
    $mail->setSubject(mbCnv($txt));
    $mail->addTo('to@test.com', mbMime($short));
    $mail->addCc('copy@test.com', mbMime($txt));
    $mail->setFrom('from@test.com', mbMime($short));
    $mail->setBodyText(mbCnv($txt), null, Zend_Mime::ENCODING_7BIT);
    $mail->setBodyHtml(mbCnv($txt), null, Zend_Mime::ENCODING_7BIT);
    $atImage = file_get_contents('http://www.foo.com/logo.png');
    $at = new Zend_Mime_Part($atImage);
    $at->type        = 'image/png';
    $at->mimeType    = Zend_Mime::TYPE_OCTETSTREAM;
    $at->disposition = Zend_Mime::DISPOSITION_INLINE;
    $at->encoding    = Zend_Mime::ENCODING_BASE64;
    $at->filename    = mbMime('日本語.png');
    $mail->addAttachment($at);
    $config = array('port' => 1025);
    $smtp = new Zend_Mail_Transport_Smtp('127.0.0.1', $config);
    try {
        $mail->send($smtp);
        echo '送信しました';
    } catch (Zend_Exception $e) {
        echo "エラー: " . $e->getMessage();
    }
}

前回分からなかった点のうち、subject以外から確認します。

  • 日本語の長い件名が途中から文字化けする
  • To, From などに付けられるラベル?に日本語を使うと文字化ける
  • 添付ファイル名に日本語が含まれると文字化ける

ISO-2022-JPにコンバート、ヘッダーはさらにbase64にエンコードする

結論的には、メールは「ISO-2022-JPにコンバート、ヘッダーはさらにbase64にエンコードする」ということでした。
「Zend_Mail(‘ISO-2022-JP’)」するだけでうまい事やってくれると期待しちゃうところですが、ひとつひとつ明示的に指定しなければいけないようです。

    function mbCnv($string) {
        return mb_convert_encoding($string, 'ISO-2022-JP', 'UTF-8');
    }
    function mbMime($string) {
        return mb_encode_mimeheader(mbCnv($string), 'ISO-2022-JP', 'B');
    }

テキストが入る所は、mb_convert_encoding で「ISO-2022-JP」にコンバートしています。
さらにヘッダー部に関しては、mb_encode_mimeheader で「B(base64)」にエンコードしています。
mb_encode_mimeheader の「ISO-2022-JP」は、このcharsetで入ってくるよ、という指定。
これがなかったりでごちゃごちゃになっていたのが原因でした。

日本語の長い件名(subject)が途中から文字化けする

公式で「ヘッダ、特に subject のエンコーディングは、油断のならない話題です。」などと言われていましたが、最終的にここが問題でした。
文字セット – Zend_Mail – Zend Framework」のサンプルソースが中途半端?なのも混乱に拍車をかけていました。
対処療法ですが、「setHeaderEncoding(Zend_Mime::ENCODING_BASE64)」がなければ、件名の文字化けが起きないことが分かりました。
これがない場合、「quotedprintable 方式でエンコード」として振る舞うとされています。
仕様としてなくてもかまわないのか、そもそもどの方式でエンコードされているのか確認できませんでした。
もうひとつの対処法は、Zend_Mailを継承したクラスを作成する方法です。
「setHeaderEncoding(Zend_Mime::ENCODING_BASE64)」あっても文字化けしません。

ちなみに原因について

長いSubjectが文字化けするのは Zend_Mime::encodeBase64Header の第3引数の Zend_Mime::LINELENGTH(72) と第4引数のZend_Mime::LINEEND(\n)に問題があります。ヘッダを Base64 にエンコードする際に 72文字ごとに「\n」が入ってしまうことが原因でした。なので LINELENGTH の数を増やしてやるか、LINEEND を空文字を指定してやれば解決します。

という事のようです。
ただ、いまの「function _encodeHeader」を見る限りは、「Zend_Mime::LINELENGTH(72)」という記述はなさそうです。
Zend_MailのSVNリポジトリ http://framework.zend.com/svn/framework/standard/trunk/library/Zend/Mail.php
なので、現在は「setHeaderEncoding(Zend_Mime::ENCODING_BASE64)」はいらないのではないかと推測しています。
これなしでもbase64でエンコーディングされていることが分かれば、ひとまずよさそうなんですがね。
わかりません。