Edited at

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

More than 1 year has 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);
}

}