Swift Mailerをiso-2022-jpで送信するための自前ヘッダークラス

symfonyでは1.0系ではPHPMailerというライブラリをメール送信で利用できるようにコアに含めてくれていました。
しかし、symfony1.1以降はコアにメール送信のライブラリを含めなくなってしまいました。

これは、symfonyが他のライブラリへの依存をできる限り排除しようとするポリシーになったからだと思われます。

そして、symfonyのcook bookではSwift Mailerというライブラリを使ったメール送信方法が紹介されています。

何故PHPMailerではなくSwift Mailerかというところの詳しい説明はありませんが、よりオブジェクト指向としてしっかり作られているという点ではないかと個人的には思っています。


しかし、日本語(iso-2022-jp)を扱うことが簡単ではありません。
詳しくは以下のアシアルさんところの記事を読んでみてください。

Swift_Mailer の内部的な処理としてヘッダーのエスケープ処理(Swift_Message_Headers)が記述されているので、mb_encode_mimeheader()を使用してエンコードする処理に変更する必要があります(もしくはSwift_Message_Headersを直接変更してしまいます)。

ただ、Swift Mailerでコアクラスに修正せずにiso-2022-jpで送信することはできたのでメモとして残しておきます。

まず、どうしても外部から変更が不可能なのがSubjectのエンコード処理でした。
なので、以下のようなファイルをコアライブラリの階層をまねて作成します。

* <アプリケーションルート>/lib/vender/SwiftMailer/Swift/Message/jpHeaders.php

<?php
Swift_Classloader::load('Swift_Message_Headers');
class Swift_Message_jpHeaders extends Swift_Message_Headers
{
  public function __construct()
  {
    $this->setCharset('iso-2022-jp');
  }
  public function getEncoded($name)
  {
    if ($this->getCharset() == 'iso-2022-jp' && (strtolower($name) == "subject")) {
      $encoded_value = (array)$this->get($name);
      foreach ($encoded_value as $key => $value ) {
        $encoded_value[$key] = mb_encode_mimeheader($value, $this->getCharset(), 'B', "\n");
      }
      //If there are multiple values in this header, put them on separate lines, cleared by commas
      $lname = strtolower($name);
      //Append attributes if there are any
      $this->cached[$lname] = implode("," . $this->LE . " ", $encoded_value);
      if (!empty($this->attributes[$lname])) $this->cached[$lname] .= $this->buildAttributes($this->cached[$lname], $lname);
      return $this->cached[$lname];
    } else {
      return parent::getEncoded($name);
    }
  }
}

subjectの時だけ自前のencode処理を行うようにしています。それ以外の$this->cachedやらなんやらはよく調べていないのですが、親メソッドではごにょごにょしていたので合わせて処理するようにしてあります。

そして、この自前Headerを使ってメール送信を行うように以下のようなアクションを使います。

  public function executeSendSwiftMailer(sfWebRequest $request)
  {
    // Create the mailer and message objects
    mb_language('ja');
    $charset = 'iso-2022-jp';
    $mailer = new Swift(new Swift_Connection_NativeMail());
    $message = new Swift_Message();
    // Send
    $subject = 'メール送信テストです';
    $type = 'text/plain';
    $encoding = '7bit';
    $to = new Swift_Address('hoge@example.com', mb_encode_mimeheader('ほげ', $charset, 'B', "\n"));
    $from = new Swift_Address('huga@example.com', mb_encode_mimeheader('ふが', $charset, 'B', "\n"));
    $body = mb_convert_encoding('本文です!', $charset, mb_internal_encoding());

    $message->setHeaders(new Swift_Message_jpHeaders()); // <= 自前ヘッダークラスを指定する
    $message->setContentType($type);
    $message->setSubject($subject);

    $message->setEncoding($encoding);
    $message->setCharset($charset);
    $message->setBody($body);
    $mailer->send($message, $to, $from);
    $mailer->disconnect();
    return sfView::NONE;
  }

$message->setHeadersで自前のヘッダークラスを指定しています。
また、ヘッダークラスを上書きした場合はsetContentTypeも再度指定する必要がありました。
この辺りの内部構造を理解しきれていません。。


その他のmb_convert_encoding処理も自前クラスを作りカプセル化してしまおうかと思ったのですが、かなりややこしい事になりそうだったので諦めました...orz


これでSwiftMailerでも日本語メールが送信できましたが、私自身の結論はPHPMailerQdmailの利用を検討することにしますw


また、近いうちにPHPMailerを使ったjpMailPluginをsymfony1.1以降に対応するように修正したいと思います。
基本的な送信ぐらいであればPHPMailerとQdmailを同じインターフェースで使えるようにできるといいな。そうしよう。