Webアプリケーションにおいてメールの送信処理はユーザへの情報伝達手段として有効な方法です。 メールは読めるものを送って、読んでもらわないことには送ってないのと同じことです。 読んでもらえるかどうかはこちらではどうすることもできないので、せめて正しく送れるようにしておくしかないでしょう。 Zend Frameworkは多言語対応を考慮されている分、やはり日本語ピンポイントで使うためには冗長な記述が必要になってきます。 そこで必要項目を渡すだけで送信できる関数xmail()を自作することにします。 Zend_Mail関連のクラスには送信用に「Zend_Mail_Transport_Sendmail」と「Zend_Mail_Transport_Smtp」の2つが用意されています。 前者はPHP組み込みのmail()関数を使って送信を行うもので、後者は直接送信メールサーバとSMTPで会話して送信するものです。 mail()関数を使って送信するZend_Mail_Transport_Sendmailクラスの場合、PHPの設定ファイル(php.ini)に「SMTP = 送信メールサーバ」を設定していなければ送ることができないでしょう。 自分の環境内にメールサーバが稼動しているかもしくはsendmailプログラム(Windowsならsendmail.exe)があって「sendmail_path = /path/to/sendmail」の設定がされていれば「SMTP = localhost」や「SMTP = 127.0.0.1」設定でもいけるでしょう。 sendmailがなければ、「SMTP = プロバイダの送信メールサーバ」でsendmail_pathはコメント設定にすることになると思います。 Zend_Mail_Transport_SendmailクラスがSMTP設定を読み取って送信する方法は、詰まる所Zend_Mail_Transport_Smtpクラスの処理と同じことになります。 なのでZend_Mail_Transport_Smtpクラスを使うときにはSMTPサーバ(送信メールサーバ)情報を直接教えてやることになります。 sendmailがやってる仕事は、メール送信ヘッダの記述を解析して送信メールサーバとSMTPで会話する仲介役のようなものなので、実はメール送信はSMTPのやり取り方法さえ知っていればsendmailは不要で直接送信メールサーバと会話して送ることができるということになります。
Webアプリケーションにおいてメールの送信処理はユーザへの情報伝達手段として有効な方法です。
メールは読めるものを送って、読んでもらわないことには送ってないのと同じことです。 読んでもらえるかどうかはこちらではどうすることもできないので、せめて正しく送れるようにしておくしかないでしょう。
Zend Frameworkは多言語対応を考慮されている分、やはり日本語ピンポイントで使うためには冗長な記述が必要になってきます。
そこで必要項目を渡すだけで送信できる関数xmail()を自作することにします。
Zend_Mail関連のクラスには送信用に「Zend_Mail_Transport_Sendmail」と「Zend_Mail_Transport_Smtp」の2つが用意されています。
前者はPHP組み込みのmail()関数を使って送信を行うもので、後者は直接送信メールサーバとSMTPで会話して送信するものです。
mail()関数を使って送信するZend_Mail_Transport_Sendmailクラスの場合、PHPの設定ファイル(php.ini)に「SMTP = 送信メールサーバ」を設定していなければ送ることができないでしょう。 自分の環境内にメールサーバが稼動しているかもしくはsendmailプログラム(Windowsならsendmail.exe)があって「sendmail_path = /path/to/sendmail」の設定がされていれば「SMTP = localhost」や「SMTP = 127.0.0.1」設定でもいけるでしょう。
sendmailがなければ、「SMTP = プロバイダの送信メールサーバ」でsendmail_pathはコメント設定にすることになると思います。
Zend_Mail_Transport_SendmailクラスがSMTP設定を読み取って送信する方法は、詰まる所Zend_Mail_Transport_Smtpクラスの処理と同じことになります。 なのでZend_Mail_Transport_Smtpクラスを使うときにはSMTPサーバ(送信メールサーバ)情報を直接教えてやることになります。
sendmailがやってる仕事は、メール送信ヘッダの記述を解析して送信メールサーバとSMTPで会話する仲介役のようなものなので、実はメール送信はSMTPのやり取り方法さえ知っていればsendmailは不要で直接送信メールサーバと会話して送ることができるということになります。
Zend_Mail_Transport_Sendmailクラスを使った送信では、PHP組み込みのmail()関数を使っているのでメール関連の設定をしておく必要があります。 自マシンで送信メールサーバが稼動している場合 [php.ini] SMTP = localhost ; : ;sendmail_path = /path/to/sendmail ; : もちろんsendmailプログラムがあればsendmail_path設定のコメントを外してもOK 自マシンで送信メールサーバが稼動していなくてsendmailプログラムがない場合 [php.ini] SMTP = プロバイダの送信メールサーバ ; : ;sendmail_path = /path/to/sendmail ; : 自マシンで送信メールサーバが稼動していなくてsendmailプログラムがある場合 [php.ini] SMTP = localhost ; : sendmail_path = /path/to/sendmail ; : 当然先ほどのsendmailがない場合のように「SMTP = プロバイダの送信メールサーバ」設定にすることも可能です。 ※ localhostの名前解決ができていなければ127.0.0.1指定にした方がいいでしょう。 では早速、Zend_Mail_Transport_Sendmailクラスを使ったメール送信関数xmail()を自作してみたいと思います。 [htdocs/index.php] setBodyText(to_jis($body), "ISO-2022-JP", Zend_Mime::ENCODING_7BIT); $mail->setHeaderEncoding(Zend_Mime::ENCODING_BASE64); $mail->setFrom($from, mb_encode_mimeheader(to_jis($from_label), "ISO-2022-JP", "B")); $mail->addTo($to); // 送信先 $mail->setSubject(mb_encode_mimeheader(to_jis($subject), "ISO-2022-JP", "B")); $mail->setReplyTo($from); // 返信先を自分に $mail->addHeader('Content-Type', 'text/plain; charset=iso-2022-jp'); // 本文の文字コード $mail->addHeader('Errors-to', $from); // エラーなら自分に(不要ですが) $mail->addHeader('Content-Transfer-Encoding', '7bit'); // 先頭ビット使ってませんオーラ $mail->send(new Zend_Mail_Transport_Sendmail("-f{$from}"));// "-fメアド"でReturn-Path設定 } メールを日本語で送る時の基本は、ヘッダや本文すべての文字コードをJISにすることです。 JISコードにすることですべて7ビット表現にできるからです。 (これはメールをバケツリレーする経路に先頭ビットを欠落させるサーバがあった時の対策) JISコードに変換済みのヘッダ領域内に、たまたま「foo@example.com」のようなメールアドレスに似た文字列が並んだり、JISコードのエスケープシーケンスで多用される"("文字によって読み取れなかったりしたときに困るので、"@"や"("を使わないアスキー表現ができるBASE64エンコードを施す必要があります。 JISコードを更にBASE64エンコードした文字情報ですよ〜 とメールを読み取る側に教えてあげるためにmb_encoding_mimeheader()関数では「=?ISO-2022-JP?B?」と「?=」でサンドイッチするように指定しています。 mb系関数では、"JIS"指定と"ISO-2022-JP"指定では扱える文字範囲が異なります。より広い範囲を扱える"JIS"指定で変換するのがツウでしょう。 メール送信テストを行う場合には、とりあえず自分から自分自身のメールアドレスに送るのが簡易な方法なので xmail("自分のメールアドレス", "自分のメールアドレス", "件名", "本文"); として送ってみるといいでしょう。 実行してみたところ、日本語で送ることができました。 ∩( ・ω・)∩ばんじゃーい
Zend_Mail_Transport_Sendmailクラスを使った送信では、PHP組み込みのmail()関数を使っているのでメール関連の設定をしておく必要があります。
自マシンで送信メールサーバが稼動している場合
もちろんsendmailプログラムがあればsendmail_path設定のコメントを外してもOK
自マシンで送信メールサーバが稼動していなくてsendmailプログラムがない場合
自マシンで送信メールサーバが稼動していなくてsendmailプログラムがある場合
当然先ほどのsendmailがない場合のように「SMTP = プロバイダの送信メールサーバ」設定にすることも可能です。
※ localhostの名前解決ができていなければ127.0.0.1指定にした方がいいでしょう。
では早速、Zend_Mail_Transport_Sendmailクラスを使ったメール送信関数xmail()を自作してみたいと思います。
メールを日本語で送る時の基本は、ヘッダや本文すべての文字コードをJISにすることです。 JISコードにすることですべて7ビット表現にできるからです。 (これはメールをバケツリレーする経路に先頭ビットを欠落させるサーバがあった時の対策)
JISコードに変換済みのヘッダ領域内に、たまたま「foo@example.com」のようなメールアドレスに似た文字列が並んだり、JISコードのエスケープシーケンスで多用される"("文字によって読み取れなかったりしたときに困るので、"@"や"("を使わないアスキー表現ができるBASE64エンコードを施す必要があります。
JISコードを更にBASE64エンコードした文字情報ですよ〜 とメールを読み取る側に教えてあげるためにmb_encoding_mimeheader()関数では「=?ISO-2022-JP?B?」と「?=」でサンドイッチするように指定しています。
mb系関数では、"JIS"指定と"ISO-2022-JP"指定では扱える文字範囲が異なります。より広い範囲を扱える"JIS"指定で変換するのがツウでしょう。
メール送信テストを行う場合には、とりあえず自分から自分自身のメールアドレスに送るのが簡易な方法なので
として送ってみるといいでしょう。
実行してみたところ、日本語で送ることができました。 ∩( ・ω・)∩ばんじゃーい
Zend_Mail_Transport_Sendmailクラスを使ってメール送信する場合には、面倒なPHP設定をしておく必要がありました。 Zend_Mail_Transport_Smtpクラスなら面倒な設定抜きにメール送信をすることができます。 つまり、Zend_Mail_Transport_Sendmailクラスがphp.ini設定に基づいてメール送信を行うのに対し、Zend_Mail_Transport_Smtpクラスはphp.ini設定を見る代わりに直接送信メールサーバを指定する方法をとることになります。 setBodyText(to_jis($body), "ISO-2022-JP", Zend_Mime::ENCODING_7BIT); $mail->setHeaderEncoding(Zend_Mime::ENCODING_BASE64); $mail->setFrom($from, mb_encode_mimeheader(to_jis($from_label), "ISO-2022-JP", "B")); $mail->addTo($to); $mail->setSubject(mb_encode_mimeheader(to_jis($subject), "ISO-2022-JP", "B")); $mail->setReplyTo($from); $mail->addHeader('Content-Type', 'text/plain; charset=iso-2022-jp'); $mail->addHeader('Errors-to', $from); $mail->addHeader('Content-Transfer-Encoding', '7bit'); $mail->send(new Zend_Mail_Transport_Smtp($smtp)); } Zend_Mail_Transport_Sendmailクラスを使った時のxmail()関数とさほど変わりはありませんが、特徴的なところは送信メールサーバを指定する部分です。 これもテスト送信してみたところ、日本語を使ったメールが送信できました。 多言語に対応したZend_Mailを、日本語を使って送信するやり方はこんなところでしょう。。。 と思いきや、もう一つ大事なことがありました。件名が長すぎると途中経路のサーバで分割されてしまい、BASE64デコードがうまくいかずに日本語件名が文字化けするということがあるので、次はBASE64エンコード件名をどう分割するかを考えていきます。
Zend_Mail_Transport_Sendmailクラスを使ってメール送信する場合には、面倒なPHP設定をしておく必要がありました。 Zend_Mail_Transport_Smtpクラスなら面倒な設定抜きにメール送信をすることができます。
つまり、Zend_Mail_Transport_Sendmailクラスがphp.ini設定に基づいてメール送信を行うのに対し、Zend_Mail_Transport_Smtpクラスはphp.ini設定を見る代わりに直接送信メールサーバを指定する方法をとることになります。
Zend_Mail_Transport_Sendmailクラスを使った時のxmail()関数とさほど変わりはありませんが、特徴的なところは送信メールサーバを指定する部分です。
これもテスト送信してみたところ、日本語を使ったメールが送信できました。
多言語に対応したZend_Mailを、日本語を使って送信するやり方はこんなところでしょう。。。
と思いきや、もう一つ大事なことがありました。件名が長すぎると途中経路のサーバで分割されてしまい、BASE64デコードがうまくいかずに日本語件名が文字化けするということがあるので、次はBASE64エンコード件名をどう分割するかを考えていきます。
長すぎる日本語ヘッダを分割するということで、mb_encode_mimeheader()関数の挙動をいろいろ見ていたらとんでもない見落としがありました。 mb_encode_mimeheader("foo", "JIS", "B");の第二引数と、 mb_internal_encoding("JIS");の設定値が合致していないとどうやらまともな挙動をしてくれないようです。 なのでこれを合致させる設定にすると、 長すぎる日本語を含む件名を指定したmb_encode_mimeheader()関数の返す値は以下のような感じになります。 =?ISO-2022-JP?B?FooBarBazFugaHogeMoge/FooBarBazFugaHogeMoge/FooBarB=?= =?ISO-2022-JP?B?FooBarBazFugaHogeMoge/FooBarBazFugaHogeMoge/FooBarBazFug?= =?ISO-2022-JP?B?FooBarBazFugaHogeMoge/FooBarBazFugaHogeMoge/FooBarBa?= =?ISO-2022-JP?B?FooBarBazFugaHogeMoge/FooBarBazFugaHogeMoge/Foo/?= =?ISO-2022-JP?B?FooBarBazFugaHogeMoge/FooBarBazFugaHogeMoge/FooBarBazFu/?= =?ISO-2022-JP?B?FooBarBazFugaHogeMoge/FooBarBazFugaHogeMoge/FooBarBazFug?= =?ISO-2022-JP?B?FooBarBazFugaHogeMoge/FooBarBazFugaHogeMoge/FooBarBa?= mb_encode_mimeheader()関数はちゃんと分割までしてくれているのが判ります。 =?ISO-2022-JP?B?*****************************************************?=[改行コード] [space]=?ISO-2022-JP?B?**********************************************?=[改行コード] [space]=?ISO-2022-JP?B?**********************************************?=[改行コード] [space]=?ISO-2022-JP?B?**********************************************?=[改行コード] [space]=?ISO-2022-JP?B?**********************************************?=[改行コード] [space]=?ISO-2022-JP?B?**********************************************?=[改行コード] [space]=?ISO-2022-JP?B?**********************************************?= 分割位置には「改行コード+半角スペース」で再び=?ISO-2022-JP?B?と続く形式であることが判ります。 ここでZend_Mail側の定義として「すべてのヘッダフィールドの改行文字 (\n) を取り除きます」とあります。 (\n)を取り除くというのが、半角スペースに置き換えられるのかとか、(\r\n)のうちの(\n)だけを取り除くのかとかよくわからないので「改行コード+半角スペース」を1つの半角スペースに置き換えておく前処理を施すようにしたいと思います。 というのは、以下のことがからんでくるからです。 件名を複数行で指定するやり方はsetSubject()メソッドを使ってできそうもないので、一行で指定するしかありません。 なのでmb_encode_mimeheader()が返した値をsetSubject()メソッドに渡す際に「改行コード+半角スペース」があったら1つの半角スペースに置き換えるフィルター処理を施すようにします。 このフィルター処理は、下記の仕様上問題ないでしょう。 RFC822の「3.1.1. LONG HEADER FIELDS」 この仕様によると、1つのLWSP-char(半角スペースかタブ)があれば、改行しなくともいけると書いてあります。(多分) 以上を踏まえて、前回作成した自作のxmail()関数を書き直してみます。 setBodyText(to_jis($body), "ISO-2022-JP", Zend_Mime::ENCODING_7BIT); $mail->setHeaderEncoding(Zend_Mime::ENCODING_BASE64); $mail->setFrom($from, mb_encode_mimeheader(to_jis($from_label), "JIS", "B")); $mail->addTo($to); $mail->setSubject(preg_replace('/\s+/', ' ', mb_encode_mimeheader(to_jis($subject), "JIS", "B"))); $mail->setReplyTo($from); $mail->addHeader('Content-Type', 'text/plain; charset=iso-2022-jp'); $mail->addHeader('Errors-to', $from); $mail->addHeader('Content-Transfer-Encoding', '7bit'); $mail->send(new Zend_Mail_Transport_Smtp($smtp)); } mb_internal_encoding()設定とpreg_replace()フィルターを加えて、mb_encode_mimeheader()の第二引数を"ISO-2022-JP"から"JIS"指定に変えただけです。 これでめちゃめちゃキレのある日本語メール送信ができました。 環境によりますが、本文やヘッダに半角カタカナを使えるようになりました。(メーラーによっては自動的に全角変換?) このように日本語を含んだメールを扱うにはいろんな要素がからんでくるので知ってるつもりでも意外と苦戦します。 いろんなサイトを見てもかなり苦戦している人多いです。 ただ、メールを送っても最終的に読んでもらえなければね もぅね。。 次回は絶対に読んでもらえるメールをZend_Mailに追加実装する方法・・ ないか・・
長すぎる日本語ヘッダを分割するということで、mb_encode_mimeheader()関数の挙動をいろいろ見ていたらとんでもない見落としがありました。
mb_encode_mimeheader("foo", "JIS", "B");の第二引数と、 mb_internal_encoding("JIS");の設定値が合致していないとどうやらまともな挙動をしてくれないようです。
なのでこれを合致させる設定にすると、 長すぎる日本語を含む件名を指定したmb_encode_mimeheader()関数の返す値は以下のような感じになります。
=?ISO-2022-JP?B?FooBarBazFugaHogeMoge/FooBarBazFugaHogeMoge/FooBarB=?= =?ISO-2022-JP?B?FooBarBazFugaHogeMoge/FooBarBazFugaHogeMoge/FooBarBazFug?= =?ISO-2022-JP?B?FooBarBazFugaHogeMoge/FooBarBazFugaHogeMoge/FooBarBa?= =?ISO-2022-JP?B?FooBarBazFugaHogeMoge/FooBarBazFugaHogeMoge/Foo/?= =?ISO-2022-JP?B?FooBarBazFugaHogeMoge/FooBarBazFugaHogeMoge/FooBarBazFu/?= =?ISO-2022-JP?B?FooBarBazFugaHogeMoge/FooBarBazFugaHogeMoge/FooBarBazFug?= =?ISO-2022-JP?B?FooBarBazFugaHogeMoge/FooBarBazFugaHogeMoge/FooBarBa?=
mb_encode_mimeheader()関数はちゃんと分割までしてくれているのが判ります。
=?ISO-2022-JP?B?*****************************************************?=[改行コード] [space]=?ISO-2022-JP?B?**********************************************?=[改行コード] [space]=?ISO-2022-JP?B?**********************************************?=[改行コード] [space]=?ISO-2022-JP?B?**********************************************?=[改行コード] [space]=?ISO-2022-JP?B?**********************************************?=[改行コード] [space]=?ISO-2022-JP?B?**********************************************?=[改行コード] [space]=?ISO-2022-JP?B?**********************************************?=
分割位置には「改行コード+半角スペース」で再び=?ISO-2022-JP?B?と続く形式であることが判ります。
ここでZend_Mail側の定義として「すべてのヘッダフィールドの改行文字 (\n) を取り除きます」とあります。
(\n)を取り除くというのが、半角スペースに置き換えられるのかとか、(\r\n)のうちの(\n)だけを取り除くのかとかよくわからないので「改行コード+半角スペース」を1つの半角スペースに置き換えておく前処理を施すようにしたいと思います。
というのは、以下のことがからんでくるからです。
件名を複数行で指定するやり方はsetSubject()メソッドを使ってできそうもないので、一行で指定するしかありません。
なのでmb_encode_mimeheader()が返した値をsetSubject()メソッドに渡す際に「改行コード+半角スペース」があったら1つの半角スペースに置き換えるフィルター処理を施すようにします。
このフィルター処理は、下記の仕様上問題ないでしょう。
RFC822の「3.1.1. LONG HEADER FIELDS」 この仕様によると、1つのLWSP-char(半角スペースかタブ)があれば、改行しなくともいけると書いてあります。(多分)
以上を踏まえて、前回作成した自作のxmail()関数を書き直してみます。
mb_internal_encoding()設定とpreg_replace()フィルターを加えて、mb_encode_mimeheader()の第二引数を"ISO-2022-JP"から"JIS"指定に変えただけです。
これでめちゃめちゃキレのある日本語メール送信ができました。
環境によりますが、本文やヘッダに半角カタカナを使えるようになりました。(メーラーによっては自動的に全角変換?)
このように日本語を含んだメールを扱うにはいろんな要素がからんでくるので知ってるつもりでも意外と苦戦します。 いろんなサイトを見てもかなり苦戦している人多いです。
ただ、メールを送っても最終的に読んでもらえなければね もぅね。。
次回は絶対に読んでもらえるメールをZend_Mailに追加実装する方法・・ ないか・・