夜の Discovery このページをアンテナに追加 RSSフィード

セイバーメトリクス対応!野球の成績・チーム・試合管理の決定版!『ガチャスポ ベースボール』

2009-08-20

CakePHP1.2(+PHPMailer) から Gmail (または Google Apps)経由でメールを送る

CakePHP 1.2 の EmailComponent は TLS に対応していないため、Gmail 経由でメールを送ることができません。そのため、PHPMailer を利用して EmailComponent を突貫で作りました。(使用バージョンは CakePHP 1.2.3.8166 + PHPMailer 2.2.1)


ちなみに、Gmail(Google Apps) には1日500宛先までという送信制限があるので注意が必要して下さい。

詳しくは Gmail と Google Apps のメール送信制限 - 夜の Discovery をご覧下さい。



使用手順

phpmailer をダウンロードして、phpMailer という名前で vendors 以下にコピーします。

Codeworx Technologies

http://phpmailer.codeworxtech.com/


僕が PHP をあまり理解していないからかもしれませんが、class.smtp.php 内で未定義の変数を使っているためエラーとなるので修正します。

class.smtp.php 165行目から

(省略)
public function StartTLS() {
  $this->error = null; # to avoid confusion

  if(!$this->connected()) {
    $this->error = array("error" => "Called StartTLS() without being connected");
    return false;
  }
+  $extra = "";
  fputs($this->smtp_conn,"STARTTLS" . $extra . $this->CRLF);
(省略)

次に、app/controllers/components/email.php を作ります。*1

<?php 
class EmailComponent extends Object {
  var $from = FROM_EMAIL;
  var $fromName = FROM_NAME;
  var $smtpAuth = true;
  var $smtpUserName = SMTP_USERNAME;  // Gmail mail address
  var $smtpPassword = SMTP_PASSWORD;// Gmail password
  var $smtpHostNames= SMTP_HOST;  // smtp.gmail.com
  var $smtpPort = SMTP_PORT; // 587
  var $smtpSecureType = SMTP_SECURE_TYPE; // tls
  var $smtpError = null;
  var $to = null;
  var $toName = null;
  var $subject = null;
  var $cc = null;
  var $bcc = null;
  var $layout = null;
  var $elementDir = 'email';
  var $template = 'default';
  var $attachments = null;
  var $controller = null;

  function startup(&$controller) {
    $this->controller = &$controller;
  }

  function _element($template) {
    $temp_layout = $this->controller->layout;
    $this->controller->layout = '';

    ob_start();
    $this->controller->autoRender = false;

    App::import('Core', array('View'));
    $viewClass = new View($this->controller);

    $content = $viewClass->element($template , $this->controller->viewVars, true);
    echo $viewClass->renderLayout($content, $this->layout) ;

    $this->controller->autoRender = 'auto';
    $body = ob_get_clean();

    $this->controller->layout = $temp_layout;

    return $body;
  }

  function bodyText() {
    if ($this->layout == null) {
      $this->layout = 'email/text/default';
    }
    return $this->_element($this->elementDir . '/text/' . $this->template);
  }

  function bodyHtml() {
    if ($this->layout == null) {
      $this->layout = 'email/html/default';
    }
    return $this->_element($this->elementDir . '/html/' . $this->template);
  }

  function attach($filename, $asfile = '') {
    if (empty($this->attachments)) {
      $this->attachments = array();
      $this->attachments[0]['filename'] = $filename;
      $this->attachments[0]['asfile'] = $asfile;
    } else {
      $count = count($this->attachments);
      $this->attachments[$count+1]['filename'] = $filename;
      $this->attachments[$count+1]['asfile'] = $asfile;
    }
  }

  function base64($str, $charset="JIS", $transfer_encoding="B", $linefeed="\n") {
    return mb_encode_mimeheader($str, $charset, $transfer_encoding, $linefeed);
  }

  function send($html = false) {
    App::import('Vendor', 'PHPMailer', array('file' => 'phpMailer' . DS . 'class.phpmailer.php'));
    $mail = new PHPMailer();

    $mail->IsSMTP(); // set mailer to use SMTP
    if ($this->smtpAuth) {
      $mail->SMTPAuth = true;
      $mail->Host = $this->smtpHostNames;
      $mail->Username = $this->smtpUserName;
      $mail->Password = $this->smtpPassword;
    }
    $mail->From = $this->from;
    $mail->FromName = $this->base64(mb_convert_encoding($this->fromName, "JIS", "UTF-8"));
    $mail->AddReplyTo($this->from);

    // to
    if (is_array($this->to)) {
      foreach ($this->to as $to) {
        $mail->AddAddress($to);
      }
    } else {
      $mail->AddAddress($this->to, $this->base64(mb_convert_encoding($this->toName, "JIS", "UTF-8")));
    }
    // cc
    if(!empty($this->cc)) {
      if (is_array($this->cc)) {
        foreach ($this->cc as $cc) {
          $mail->AddCC($cc);
        }
      } else {
        $mail->AddCC($this->cc);
      }
    }
    // bcc
    if(!empty($this->bcc)) {
      if (is_array($this->bcc)) {
        foreach ($this->bcc as $bcc) {
          $mail->AddBcc($bcc);
        }
      } else {
        $mail->AddBcc($this->bcc);
      }
    }

    $mail->Port = $this->smtpPort;
    $mail->SMTPSecure = $this->smtpSecureType;
    $mail->CharSet  = "iso-2022-jp";
    $mail->Encoding  = "7bit";
    $mail->WordWrap = 70;  // set word wrap to 70 characters

    if (!empty($this->attachments)) {
      foreach ($this->attachments as $attachment) {
        if (empty($attachment['asfile'])) {
          $mail->AddAttachment($attachment['filename']);
        } else {
          $mail->AddAttachment($attachment['filename'], $attachment['asfile']);
        }
      }
    }

    $mail->IsHTML($html);  // set email format to HTML
    $mail->Subject = mb_convert_encoding($this->subject, "JIS", "UTF-8");
    if ($html) {
      $mail->Body = mb_convert_encoding($this->bodyHtml(), "JIS", "UTF-8");
      $mail->AltBody = mb_convert_encoding($this->bodyText(), "JIS", "UTF-8");
    } else {
      $mail->Body = mb_convert_encoding($this->bodyText(), "JIS", "UTF-8");
    }
    $result = $mail->Send();
    if($result == false) {
      $this->smtpError = $mail->ErrorInfo;
    }
    return $result;
  }
}
?>


コントローラはまず、EmailComponent を読み込みます。

class UsersController extends AppController {
(省略)
+  var $components = array('Email');

使い方

CakePHP の EmailComponent に似せています。

メールを送るには以下のようにします。

$this->set('user', $user); 
$this->Email->template = 'signup'; // views/elements/email/text/signup
$this->Email->to = $user['User']['pc_email']; 
$this->Email->subject = '登録が完了しました'; 
$this->Email->send();
if (!empty($this->Email->smtpError)) {
  $this->log($this->Email->smtpError, LOG_DEBUG);
}

このとき、実際に使われるテンプレートは以下のようになります。

layoutviews/layout/email/text/default
viewviews/elements/email/text/signup

ビューは例えば以下のようにします。

<?php echo $user['User']['name'] ?>さん

ユーザー登録が完了しました。

複数アドレスに送信

複数アドレスに送信したい場合、$this->Email->to, $this->Email->cc, $this->Email->bcc に配列を指定します。

$this->Email->to = array($user['User']['pc_email'], $user['User']['mobile_email']);

layout 変更

// layouts/email
$this->Email->layout = 'email';

html メール送信

$result = $this->Email->send(true);

html メールの場合、実際に使われるテンプレートは以下のようになります。

layoutviews/layout/email/html/default
elementviews/elements/email/html/signup

*1:FROM_EMAIL などの定数は config/core.php などで define して下さい

2008-08-02

Gmail と Google Apps のメール送信制限

会社のメールを自社管理から Google Apps に移行しようかなと少し前から考えていたのだけど、

さっき ザリガニが見ていた...。 さんを見ていたら、Gmail には送信制限があるということなので、少し詳しく調べてみました。


送信できるメールの制限数に達した - Google Apps 管理者向け

http://www.google.com/support/a/bin/answer.py?answer=59797&topic=14873

引用:

現在のところ、アカウントにつき 1 日 500 件までの外部の宛先にメールを送信できます。

これは、[宛先:]、[Cc:]、[Bcc:] 欄のメール アドレスを合わせた数です。

管理者の方はメーリング リストを使用してドメイン内のユーザー全員にメールを送信できます。


実はGmailには送信制限があった - 適宜覚書はてな異本

http://d.hatena.ne.jp/dacs/20080627/1214569620

引用:

  • 500メッセージ/日の送信数(これって最大500回「送信」ボタンを押すことが出来るという意味)
  • 1つのメッセージの送信先は、ユニークな500の宛先まで
  • 総計2000メッセージ/日の送信数(例えば500人の宛先グループに1メッセージを4回送信できるということ)

Googleの説明によるとこれらの制限は、スパム対策及び異常な酷使防止のために設定されているとのことである。同様の制限は、Google Appsのユーザにもかかっている。


1アカウントにつき500宛先/日

ということで、Gmail, Google Apps 共に1アカウントにつき1日500メールアドレスまでしか送信できないという制限があるようです。

そして、制限を越えると別のアカウントで送るか、24時間待たないといけないようです。


アカウントごとなので、個人や会社で使う際にはこの制限にひっかかることはほとんどないと思いますが、

サービスの SMTP サーバとして利用するには少し問題がありそうです。