SwiftMailerでJIS(iso-2022-jp)のメールを送る件
メールフォーマーとしては「そろそろUTF-8で受信できないメーラーは絶滅したんじゃないか?」と度々おもいますが、思い出したようにお客様よりクレームがはいり、結局iso-2022-jpで送るように修正する事になります。
有象無象のうさんくさい古びたメーラーや、うさんくさい変換処理をおこなうレガシープロバイダのMTA相手にメールを送るなら、鉄板はいまだqdmailですが、流石にもうしんどい*1ので、SwiftMailerを使いたい、というのが正直な所ですね。
2〜3年位前まではSwiftMailerはUTF-8以外は結構無理ゲーで、jisで送るのは結構面倒だったと記憶していますが(2年も前!というべき?)、最近はちゃんと対応され、楽になっています。
で、まあ、今日久々にSwiftMailerでjis対応したメールフォーム書こうとググったら、予想以上に昔の情報にばかり行き着いたので、メモです。
勿論この記事もいつかは古くなるので、みなさん信じてはいけません。
いま(20150320)では古い手法
- mb_encode_mimeheader
- mb_convert_encoding
- 「SwiftMailerにモンキーパッチ」
- 「問題に対処するために、自作のクラスをつくりました」
このあたりは今では不要です。
こういう方法をつかっていると、一見動くのですが、SubjectやFROMが長い文字のときに突然文字化けしたり、突然文字化けが直ったりしてつらい気分になります。
今のやり方
フツウにSwiftmailerで送信できる準備ができているとして。
前処理
<?php \Swift::init(function () { \Swift_DependencyContainer::getInstance() ->register('mime.qpheaderencoder') ->asAliasOf('mime.base64headerencoder'); \Swift_Preferences::getInstance()->setCharset('iso-2022-jp'); });
送信部(メッセージ組み立て部)
<?php $to_user_message = \Swift_Message::newInstance() ->setSubject("UTF-8の自由名文字列") ->setFrom([ 'from@mail.addr' => '送信者のお名前']) ->setTo([ 'to@mail.addr' ]) ->setBody("本文もUTF-8で適当にいれればよいです") ->setCharset('iso-2022-jp') ->setEncoder(new \Swift_Mime_ContentEncoder_PlainContentEncoder('7bit')); // 最後二行が追加、そこまではUTF-8と同じ
前処理をいれて、あとはメッセージ作る時に文字コードとエンコードを7bitにするだけですね、簡単!!
時代は良くなりました。
注意
http://swiftmailer.org/docs/japanese.html
公式には前処理だけでも大丈夫みたいなことがかいてあるんだけれど、実際には「->setEncoder(new \Swift_Mime_ContentEncoder_PlainContentEncoder('7bit'));」をいれないと、Content-Transfer-Encodingが quoted-printableのままになります(パッと見文字化けはしてないんですが、改行がおかしくなる)。
前処理にこれも指定できるんじゃないの?っておもってるんですけど、ググってぱっとみつからなかったので、ま、いっかーって思ってます。
こちらからは以上です。
余談、Transferについて
なんでSwiftMailerってデバッグやテスト時に、実際に送信しないでログとかに書き出すTransportがないんですかね?NullTransportがあるんだから、ないのかなっておもって見たけど無い。
- 20150331追記-
FileSpoolというやつがあるっぽい…んだけど出てくるのはSwift_MessageのをSerializeしたものか〜、LoggerとNullを組み合わせよう!というのが正しい道なのかな。
(参考URL: PHPカンファレンス関西2014で「あなたの知らないSwift Mailerの世界」というタイトルの発表をしてきた - 雑文発散(2014-06-28) )
- /追記-
シブシブ偽sendmailをカマして取り出してるんですけど、大体不便ですね。
以下はモンキーパッチ的なゴミクラスですが、こういう事をやりたいだけなんですけど。
<?php /* * Original from SwiftMailer Swift_NullTransport * https://github.com/swiftmailer/swiftmailer */ namespace Uzulla\Mail\SwiftMailer; class TransportDumpTransport implements \Swift_Transport { private $_eventDispatcher; public function __construct(\Swift_Events_EventDispatcher $eventDispatcher) { $this->_eventDispatcher = $eventDispatcher; } public function isStarted(){return true;} public function start(){} public function stop(){} public function send(\Swift_Mime_Message $message, &$failedRecipients = null) { if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) { $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed'); if ($evt->bubbleCancelled()) { return 0; } } if ($evt) { $evt->setResult(\Swift_Events_SendEvent::RESULT_SUCCESS); $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed'); } $count = ( count((array)$message->getTo()) + count((array)$message->getCc()) + count((array)$message->getBcc()) ); // ここらへんでPHPerらしく好き勝手にしよう!!!!!!!!!!!!!! error_log('===dump==='); error_log('from'); error_log(print_r($message->getFrom(), 1)); error_log('to'); error_log(print_r($message->getTo(), 1)); error_log('subject'); error_log(print_r($message->getSubject(), 1)); error_log('body'); error_log(print_r($message->getBody(), 1)); error_log('===dump_end==='); return $count; } public function registerPlugin(\Swift_Events_EventListener $plugin) { $this->_eventDispatcher->bindEventListener($plugin); } } class DumpTransport extends TransportDumpTransport { public function __construct() { call_user_func_array( array($this, '\Uzulla\Mail\SwiftMailer\TransportDumpTransport::__construct'), \Swift_DependencyContainer::getInstance() ->createDependenciesFor('transport.null') ); } public static function newInstance(){return new self();} }