symfony1.4の「SwiftMailer」を日本語対応(iso-2022-jp)してみた
symfony1.2までは「jpMailPlugin」を利用していましたが、1.4へとバージョンアップの機会に
「SwiftMailer」を使用することにしました。
基本的に携帯への送信が必須となるので、日本語(JISコード)対応しなければいけません。
とりあえずsymfonyのマニュアル通りに送信してみることに・・・ mb_language('ja'); mb_internal_encoding('UTF-8'); $message = Swift_Message::newInstance() ->setFrom('from@hoge.com') ->setTo('to@hoge.com') ->setSubject(mb_encode_mimeheader('日本語のサブジェクト', 'iso-2022-jp', 'B', "\r\n")) ->setBody(mb_convert_encoding('日本語のBody', 'iso-2022-jp', mb_internal_encoding())); $this->getMailer()->send($message);
結果・・・Subject,Bodyともに文字化け。(これは、予想通り)
文字化けしたメールのヘッダ
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
↓ こうしたい
Content-Type: text/plain; charset=iso-2022-jp
Content-Transfer-Encoding: 7bit
文字コードを指定してテスト
$message = Swift_Message~ // 文字コード指定 $message->setCharset('iso-2022-jp'); // メール本文のエンコード指定 $message->setEncoder('7bit'); → ここでPHPエラー。
なにやら、getName() メソッドが無いよ!と怒られてしまいます。
setEncoder()の引数を確認してみると、「Swift_Mime_ContentEncoder」というオブジェクト になっています。
SwiftMailerのドキュメントを見ても何もなし・・・。
しょうがないので、ソースを追っていくと、Swift_Encodingというクラスに定義されている模様。
エンコード指定部を、
$message->setEncoder(Swift_Encoding::get7BitEncoding());
にしてみると成功!
これでヘッダは期待通りの出力になり、body部の文字化けもなくなりましたが、 Subjectだけが文字化け・・・
どうも、Subjectが長いと文字化けするみたい。
ググって見ると、どうも前のバージョンのSwift Mailerも長い日本語は文字化けするという情報が。
う~ん、厄介だな~。
結局、Subjectで使用するヘッダのクラスごと変更することに。
/** * 日本語対応(iso-2022-jp)メールヘッダクラス */ class kzl_Jp_Swift_Mime_Headers_UnstructuredHeader extends Swift_Mime_Headers_UnstructuredHeader { // override public function getFieldBody() { if (!$this->getCachedValue()) { // ISO-2022-JP対応 if (strcasecmp($this->getCharset(), 'iso-2022-jp') === 0) { // subjectをセットする際にエンコードするのでここでは何もしない $this->setCachedValue($this->getValue()); // 本来はこちらが正しいと思う // $this->setCachedValue(mb_encode_mimeheader($this->getValue(), // $this->getCharset(), 'B', "\r\n")); } else { parent::getFieldBody(); } } return $this->getCachedValue(); } }
上記クラスを作成し、libフォルダ以下へ保存。
使用方法は、
// Subjectヘッダを削除 $message->getHeaders()->remove('Subject'); // 新ヘッダクラスのインスタンス作成。 $subjectHeader = new kzl_Jp_Swift_Mime_Headers_UnstructuredHeader('Subject', new Swift_Mime_HeaderEncoder_Base64HeaderEncoder()); // 文字コードセット $header->setCharset('iso-2022-jp'); // ヘッダを設定 $message->getHeaders()->set($header);
これで、長いSubjectの文字化けもなくなりました。
メール処理を実装する度にこの処理を記述するのは手間がかかるので、最終的に
Swift_Messageを継承して以下のクラスを 作成しました。
From・To等のアドレス+名称のエンコードも、このクラス内で処理しています。
(本来は、Subjectのヘッダのように、独自のクラス内でエンコードするのが正しいと思うけど・・・)
<?php /** * 日本語送信用Swift_Messageクラス */ class kzl_Jp_Swift_Message extends Swift_Message { public function __construct($subject = null, $body = null) { mb_language('ja'); mb_internal_encoding('UTF-8'); $contentType = 'text/plain'; $charset = 'iso-2022-jp'; call_user_func_array( array($this, 'Swift_Mime_SimpleMessage::__construct'), Swift_DependencyContainer::getInstance() ->createDependenciesFor('mime.message') ); // Subjectヘッダのクラスを自前のクラスに変更 $this->getHeaders()->remove('Subject'); $header = new kzl_Jp_Swift_Mime_Headers_UnstructuredHeader( 'Subject', new Swift_Mime_HeaderEncoder_Base64HeaderEncoder() ); $header->setCharset($charset); $this->getHeaders()->set($header); // ヘッダのソート(一応あわせておく) $this->sortHeader(); // charset, Content-Typeの設定 $this->setCharset($charset); $this->setContentType($contentType); // Content-Transfer-Encodingの設定 $this->setEncoder(Swift_Encoding::get7BitEncoding()); $this->setSubject($subject); $this->setBody($body); } // override public static function newInstance($subject = null, $body = null, $contentType = null, $charset = null) { // content-type ,charsetは固定なので無視する return new self($subject, $body); } // ---------- Swift_Mime_SimpleMessage のメソッドをoverride ---------- public function setSubject($subject) { return parent::setSubject($this->_mb_encode_mimeheader($subject)); } public function setFrom($addresses, $name = null) { if(isset($name)) { $name = $this->_mb_encode_mimeheader($name); } return parent::setFrom($addresses, $name); } public function setTo($addresses, $name = null) { if(isset($name)) { $name = $this->_mb_encode_mimeheader($name); } return parent::setTo($addresses, $name); } public function setBody($body, $contentType = null, $charset = null) { // content-type ,charsetは固定なので無視する return parent::setBody(mb_convert_encoding( $body, $this->getCharset(), mb_internal_encoding())); } // ~省略。 CC,BCC等も同様にoverride // ---------------------------------------------------- /** * Swift_Mime_SimpleMessageのコンストラクタ内からコピー。 * ヘッダの順番を設定している模様。 */ private function sortHeader() { $this->getHeaders()->defineOrdering(array( 'Return-Path', 'Sender', 'Message-ID', 'Date', 'Subject', 'From', 'Reply-To', 'To', 'Cc', 'Bcc', 'MIME-Version', 'Content-Type', 'Content-Transfer-Encoding' )); } /** * ヘッダの日本語文字列エンコード処理 * @param string $value * @return string */ private function _mb_encode_mimeheader($value) { return mb_encode_mimeheader($value, $this->getCharset(), 'B', "\r\n"); } } ?>
最終的な、メール送信処理は以下のようになります。
$message = kzl_Jp_Swift_Message::newInstance() ->setFrom('from@hoge.com', '日本語の名前From') ->setTo('to@hoge.com', '日本語の名前To') ->setSubject('日本語のながいながい件名です。') ->setBody("日本語の本文。\r\n改行もOK"); $this->getMailer()->send($message);
エンコード処理等の記述もなくなり、大分すっきりしました。
それにしても、symfony1.4の情報少ないな~。
皆さん、まだ様子見なのかな。
ソースがほしい方はこちらから-kzlJpSwiftMailerPlugin-
(CC,BCC等のエンコードも実装済み。)
クラスファイルは2本しかないので、libフォルダへ突っ込むなり、Pluginとして使用するなりご自由に。
※ ご利用による事故・不具合による責任は追いかねますので、十分に検証の上自己責任でご利用ください。
—2010/3/5 追記—
ダウンロードファイル内に、日本語対応のメール用Webデバッグパネルを含めました。
Pluginとして利用する場合は、自動で有効になります。
—2010/9/22 追記—
ご指摘により、kzl_Jp_Swift_Message内、addBcc, setSenderの変数名のタイプミスを修正しました。
「あさり」様ありがとう御座いました
この投稿へのトラックバック
トラックバックはありません。
- トラックバック URL
この投稿へのコメント