Help us understand the problem. What is going on with this article?

PHPでログイン機能を実装するチュートリアル #9

More than 3 years have passed since last update.
  1. 基本設計、ユーザーモデル
  2. オートローダー
  3. 例外・ログ
  4. PDO シングルトン SQLインジェクション

  5. ユーザーモデルの作成

  6. クラスの継承

  7. テンプレート・クラスの実装

  8. アカウントロック機能の実装

  9. メール送信機能の実装

  10. アカウントロック解除機能の実装

  11. CSRF対策

はじめに

 メール送信については、多くの入門書席にあるようなサンプルコードではうまくいかないことが多い。かつてこのような記事も書かせてもらいました。

 要点としては ちゃんとSMTPサーバーを通してメール送信しましょう ということだ。
 
 外部ライブラリに PHPMailerを利用しています。
 composer.jsonに以下を記述し、コマンド composer update でインストールできます。

composer.json
{
    "name": "MyApp",
    "description": "login",
    "require": {
        "php": ">=5.6.0",
        "phpmailer/phpmailer": "~5.2",
        "smarty/smarty": "~3.1"
    }
}

利用しやすく、記述は簡単に がモットーなので、以下のように利用できるように設計します。

Mail::send($email, $title, $body);

Mail.class.phpを作成します。

  • 要件にはないが、添付ファイル対応
  • メール本文にテンプレートを利用できるようにした。

 サービスが運用フェーズに入ると、意外と送信メールの書式を変更する場面が出てくる。その度にソースコードをGREPし、ファイルを探し、ヒアドキュメントを修正し…なんてことは非常に面倒。

 テンプレートエンジンを使うことで、HTMLだけではなく、こういったメールテンプレート機能にも応用できます。
 ポイントとしては、通常の display() ではなく、fetch() を使います。fetch() の場合、返り値をブラウザには返すことなく、メモリ上に確保します。
 
 リファレンス
 fetch()

テンプレートを利用した記述は以下のようになります。

$email = 'recipient@example.com';
$title = 'メール・タイトル';
$body = Mail::getTemplate('template.tpl', array('name'=>'おなまえ'));
Mail::send($email, $title, $body);

実装

classes/common/Mail.class.php
<?php

namespace MyApp\common;

/**
 * Mailer.php
 * @since 2015/07/24
 */
class Mail
{

    /**
     * 送信専用メール
     */
    const SEND_ONLY_EMAIL = "送信用メールアドレス";

    /**
     * 送信専用メール 表示名
     */
    const SEND_ONLY_EMAIL_NAME = 'MyApp';

    /**
     * 送信専用メール 言語名
     */
    const SEND_ONLY_EMAIL_LANGUAGE = "japanese";

    /**
     * 送信専用メール 文字コード
     */
    const SEND_ONLY_EMAIL_ENCODING = 'utf-8';

    /**
     * ホスト名
     */
    const MAIL_HOST = 'mail.example.com';

    /**
     * ポート
     */
    const MAIL_PORT = '25';

    /**
     * ユーザー名
     */
    const MAIL_USER = 'contact@example.com';

    /**
     * パスワード
     */
    const MAIL_PASS = 'smtp_password';

    /**
     * メールを送信する
     * @param string $strRecipient 宛先
     * @param string $strSubject 題名
     * @param string $strBody 本文
     * @param array('path'=>'', 'name'=>'') $binAttachment 添付ファイル
     * @throws \Exception
     */
    public static function send($strRecipient, $strSubject, $strBody, array $binAttachment = [])
    {
        Log::write('*** send_mail ***');

        if (empty($strRecipient)) {
            throw new \Exception('宛先が設定されていません。');
        }
        if (empty($strSubject)) {
            throw new \Exception('メールタイトルが設定されていません。');
        }
        if (empty($strBody)) {
            throw new \Exception('メール本文が設定されていません。');
        }

        mb_language(self::SEND_ONLY_EMAIL_LANGUAGE);
        mb_internal_encoding(self::SEND_ONLY_EMAIL_ENCODING);

        $from = self::SEND_ONLY_EMAIL;
        $fromname = self::SEND_ONLY_EMAIL_NAME;

        $mail = new \PHPMailer();

        $mail->IsSMTP();
        $mail->SMTPAuth = TRUE;
        $mail->Host = self::MAIL_HOST;
        $mail->Port = self::MAIL_PORT;
        $mail->Username = self::MAIL_USER;
        $mail->Password = self::MAIL_PASS;

        $mail->CharSet = "iso-2022-jp";
        $mail->Encoding = "7bit";

        $mail->AddAddress($strRecipient);
        $mail->From = $from;
        $mail->FromName = mb_encode_mimeheader(
            mb_convert_encoding(
                $fromname
                , "JIS"
                , self::SEND_ONLY_EMAIL_ENCODING
            )
        );
        $mail->Subject = mb_encode_mimeheader($strSubject);
        $mail->Body = mb_convert_encoding(
            $strBody
            , "JIS"
            , self::SEND_ONLY_EMAIL_ENCODING
        );

        foreach ($binAttachment as $attachment) {
            if (array_key_exists('path', $attachment) &&
                array_key_exists('name', $attachment) &&
                file_exists($attachment['path'])) {

                $mail->AddAttachment(
                    $attachment['path']
                    , $attachment['name']
                );
            } else if (array_key_exists('path', $attachment) &&
                file_exists($attachment['path'])) {

                $mail->AddAttachment($attachment['path']);
            } else if (file_exists($attachment)) {

                $mail->AddAttachment($attachment);
            }
        }

        if (!$mail->Send()) {
            throw new \Exception($mail->ErrorInfo);
        }
    }

    /**
     * メールテンプレートに変数を割り当てて本文を取得
     * @param string $templateName
     * @param array $args
     */
    static public function getTemplate($templateName, array $args = [])
    {
        $smarty = new \Smarty();
        $smarty->setTemplateDir(BASE_DIR . '/smarty/templates/mail/');
        foreach ($args as $k => $v) {
            $smarty->assign($k, $v);
        }
        return $smarty->fetch($templateName);
    }

}
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away