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
この投稿へのコメント