以前の記事←で作成したメール送信クラスに添付ファイル送信機能をつけてみた。
使える全メソッドは下部で解説。
簡単な使い方は関数上部のコメントアウトを参照されたし。
<?php
/**
* sendmailを用いたシンプルなメール送信用クラス
* 単純に送信したい場合は以下の通り
* ※to, cc, bcc, のいずれか一つの指定は必須
*
* $mail = new simpleMailTransmission();
*
* $res = $mail
* ->to('to@local.host') // 必要に応じて
* ->cc('cc@local.host') // 必要に応じて
* ->Bcc('bcc@local.host') // 必要に応じて
* ->attachments('/path/to/file.ext') // 添付ファイル設定
* ->from('from@local.host') // 必須
* ->subject('タイトルを指定')
* ->send('本文を指定');
*
* ※添付ファイルの詳細設定は、attachments()関数上部のコメントアウトを参照
*
*/
class simpleMailTransmission {
// 送信先
protected $to = array();
// メールタイトル
protected $subject = '';
// メール本文
protected $message = '';
// ヘッダー情報
protected $header = '';
// 送信先一時格納変数(複数可)
protected $_to = array();
// 送信元設定
protected $_from = array();
// Cc格納変数
protected $_cc = array();
// Bcc格納変数
protected $_bcc = array();
// ヘッダー情報一時格納変数
protected $_header = '';
// テンプレートファイルパス
protected $_template = '';
// テンプレートに渡す変数
protected $_viewVars = array();
// 添付ファイル
protected $_attachments = array();
// 添付ファイル送信用バウンダリヘッダ
protected $_boundary = null;
// 言語設定
protected $_lang = 'ja';
// エンコーディング
protected $_encoding = 'UTF-8';
// キャラセット
protected $_charset = 'ISO-2022-JP';
// 送信時文字エンコード
protected $_transferEncoding = '7bit';
// 送信ヘッダー内「X-Mailer」設定
protected $_xMailer = 'GEKIOKOPUNPUNMARU';
/**
* 送信先のセット
* @param unknown_type $email
* @param unknown_type $name
* @return multitype:|mailTerminal
*/
public function to($email = null, $name = null) {
if($email === null) {
return $this->_to;
}
return $this->_setEmail('_to', $email, $name);
}
/**
* 送信先の追加
* @param unknown_type $email
* @param unknown_type $name
* @return mailTerminal
*/
public function addTo($email, $name = null) {
return $this->_addEmail('_to', $email, $name);
}
/**
* Ccのセット
* @param unknown_type $email
* @param unknown_type $name
* @return multitype:|mailTerminal
*/
public function cc($email = null, $name = null) {
if($email === null) {
return $this->_cc;
}
return $this->_setEmail('_cc', $email, $name);
}
/**
* Ccの追加
* @param unknown_type $email
* @param unknown_type $name
* @return mailTerminal
*/
public function addCc($email, $name = null) {
return $this->_addEmail('_cc', $email, $name);
}
/**
* Bccのセット
* @param unknown_type $email
* @param unknown_type $name
* @return multitype:|mailTerminal
*/
public function bcc($email = null, $name = null) {
if($email === null) {
return $this->_bcc;
}
return $this->_setEmail('_bcc', $email, $name);
}
/**
* Bccの追加
* @param unknown_type $email
* @param unknown_type $name
* @return mailTerminal
*/
public function addBcc($email, $name = null) {
return $this->_addEmail('_bcc', $email, $name);
}
/**
* セット用メソッド(配列可)
* @param unknown_type $varName
* @param unknown_type $email
* @param unknown_type $name
* @return mailTerminal
*/
protected function _setEmail($varName, $email, $name) {
if(is_array($email)) {
$list = array();
foreach($email as $key => $value) {
if(is_int($key)) {
$key = $value;
}
if(self::email($key)) {
$list[$key] = $value;
}
}
$this->{$varName} = $list;
return $this;
}
if(self::email($email)) {
if($name === null) {
$name = $email;
}
$this->{$varName} = array($email => $name);
}
return $this;
}
/**
* 追加用メソッド(配列可)
* @param unknown_type $varName
* @param unknown_type $email
* @param unknown_type $name
* @return mailTerminal
*/
protected function _addEmail($varName, $email, $name) {
if(is_array($email)) {
$list = array();
foreach($email as $key => $value) {
if(is_int($key)) {
$key = $value;
}
if(self::email($key)) {
$list[$key] = $value;
}
}
$this->{$varName} = array_merge($this->{$varName}, $list);
return $this;
}
if(self::email($email)) {
if($name === null) {
$name = $email;
}
$this->{$varName}[$email] = $name;
}
return $this;
}
/**
* 送信元の設定
* @param unknown_type $email
* @param unknown_type $name
* @return multitype:|mailTerminal
*/
public function from($email = null, $name = null) {
if($email === null) {
return $this->_from;
}
return $this->_setEmailSingle('_from', $email, $name);
}
/**
* 単一の値であることを保障するためのセット関数
* @param unknown_type $varName
* @param unknown_type $email
* @param unknown_type $name
* @return mailTerminal
*/
protected function _setEmailSingle($varName, $email, $name) {
$current = $this->{$varName};
$this->_setEmail($varName, $email, $name);
if(count($this->{$varName}) !== 1) {
$this->{$varName} = $current;
}
return $this;
}
/**
* メールタイトルのセット
* @param unknown_type $subject
* @return string|mailTerminal
*/
public function subject($subject = null) {
if($subject === null) {
return $this->subject;
}
$this->subject = (string)$subject;
return $this;
}
/**
* テンプレートファイルパスの指定
* @param unknown_type $template
* @return string|mailTerminal
*/
public function template($template = false) {
if($template === false) {
return $this->_template;
}
$this->_template = $template;
return $this;
}
/**
* テンプレートファイルに渡す変数を設定
* @param unknown_type $viewVars
* @return multitype:|mailTerminal
*/
public function viewVars($viewVars = null) {
if($viewVars === null) {
return $this->_viewVars;
}
$this->_viewVars = array_merge($this->_viewVars, (array)$viewVars);
return $this;
}
/**
* 添付ファイルの設定
*
* $mail->attachments('/path/to/file.ext');
*
* $mail->attachments(array('customName.ext' => '/path/to/file.ext'));
*
* $mail->attachments(array('customName.ext' => array(
* 'file' => '/path/to/file.ext',
* 'mimetype' => 'image/jpg',
* 'contentId' => 'qwerty',
* 'contentDisposition' => false
* ));
*
*/
public function attachments($attachments = null) {
if($attachments === null) {
return $this->_attachments;
}
$attach = array();
foreach((array)$attachments as $name => $fileInfo) {
if(!is_array($fileInfo)) {
$fileInfo = array('file' => $fileInfo);
}
if(empty($fileInfo['file'])) {
return 'File not specified.';
}
$fileInfo['file'] = realpath($fileInfo['file']);
if($fileInfo['file'] === false || !file_exists($fileInfo['file'])) {
return 'File not found.';
}
if(is_int($name)) {
$name = basename($fileInfo['file']);
}
if(!isset($fileInfo['mimetype'])) {
$fileInfo['mimetype'] = 'application/octet-stream';
}
$attach[$name] = $fileInfo;
}
$this->_attachments = $attach;
return $this;
}
/**
* 添付ファイルの追加
* @param unknown_type $attachments
* @return simpleMailTransmission
*/
public function addAttachments($attachments) {
$current = $this->_attachments;
$this->attachments($attachments);
$this->_attachments = array_merge($current, $this->_attachments);
return $this;
}
/**
* ヘッダーバウンダリの生成
* ※要添付ファイル
*/
protected function _createBoundary() {
if (!empty($this->_attachments)) {
$this->_boundary = md5(uniqid(time()));
}
}
/**
* 添付ファイルがある場合、
* メッセージにバウンダリーを挿入
* @return string
*/
protected function _addBoundaries() {
$this->_createBoundary();
$msg = '';
$contentIds = array_filter((array)self::_extract($this->_attachments, '{s}.contentId'));
$hasInlineAttachments = count($contentIds) > 0;
$hasAttachments = !empty($this->_attachments);
$boundary = $relBoundary = $textBoundary = $this->_boundary;
if ($hasInlineAttachments) {
$msg .= '--'.$boundary."\n";
$msg .= 'Content-Type: multipart/related; boundary="rel-'.$boundary.'"'."\n\n";
$relBoundary = $textBoundary = 'rel-'.$boundary;
}
if (isset($this->message)) {
if ($textBoundary !== $boundary || $hasAttachments) {
$msg .= '--'.$textBoundary."\n";
$msg .= 'Content-Type: text/plain; charset='.$this->_charset."\n";
$msg .= 'Content-Transfer-Encoding: '.$this->_transferEncoding."\n\n";
}
$msg .= $this->message."\n\n";
}
if ($hasInlineAttachments) {
$attachments = $this->_attachInlineFiles($relBoundary);
$msg .= $attachments."\n";
$msg .= '--'.$relBoundary.'--'."\n\n";
}
if ($hasAttachments) {
$attachments = $this->_attachFiles($boundary);
$msg .= $attachments."\n";
}
if($hasAttachments) {
$msg .= '--'.$boundary.'--'."\n\n";
}
return $msg;
}
/**
* コンテンツIDを持つ添付ファイルの追加
* @param unknown_type $boundary
* @return multitype:string unknown
*/
protected function _attachInlineFiles($boundary) {
$msg = '';
foreach($this->_attachments as $filename => $fileInfo) {
if(empty($fileInfo['contentId'])) continue;
$data = $this->_readFile($fileInfo['file']);
$msg .= '--'.$boundary."\n";
$msg .= 'Content-Type: '.$fileInfo['mimetype']."\n";
$msg .= 'Content-Transfer-Encoding: base64'."\n";
$msg .= 'Content-ID: <'.$fileInfo['contentId'].'>'."\n";
$msg .= 'Content-Disposition: inline; filename="'.$filename.'"'."\n\n";
$msg .= $data."\n\n";
}
return $msg;
}
/**
* コンテンツIDを持たない添付ファイルの追加
* @param unknown_type $boundary
* @return multitype:string
*/
protected function _attachFiles($boundary) {
$msg = '';
foreach($this->_attachments as $filename => $fileInfo) {
if (!empty($fileInfo['contentId'])) continue;
$data = $this->_readFile($fileInfo['file']);
$msg .= '--'.$boundary."\n";
$msg .= 'Content-Type: '.$fileInfo['mimetype']."\n";
$msg .= 'Content-Transfer-Encoding: base64'."\n";
if (!isset($fileInfo['contentDisposition']) || $fileInfo['contentDisposition']) {
$msg .= 'Content-Disposition: attachment; filename="'.$filename.'"'."\n\n";
}
$msg .= $data."\n\n";
}
return $msg;
}
/**
* 添付するファイルの読み込み(エンコード)
* @param unknown_type $path
* @return string
*/
protected function _readFile($path) {
return chunk_split(base64_encode(file_get_contents($path)));
}
/**
* メールアドレスのバリデーション設定
* @param unknown_type $check
* @return boolean
*/
protected function email($check) {
$hostName = '(?:[_a-z0-9][-_a-z0-9]*\.)*(?:[a-z0-9][-a-z0-9]{0,62})\.(?:(?:[a-z]{2}\.)?[a-z]{2,})';
$regex = '/^[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+)*@'.$hostName.'$/i';
return self::_check($check, $regex);
}
/**
* メールの送信開始トリガー
* @param unknown_type $plainMessage
* @return string|Ambigous <boolean, multitype:boolean >
*/
public function send($plainMessage = null) {
if(empty($this->_from)) {
return 'From is not specified.';
}
if(empty($this->_to) && empty($this->_cc) && empty($this->_bcc)) {
return 'You need to specify at least one destination for to, cc or bcc.';
}
if(!file_exists($this->_template)) {
$this->message = self::mce($plainMessage);
} else {
$this->message = self::mce($this->createMessage());
}
if(!empty($this->_attachments)) $this->message = $this->_addBoundaries();
$this->header = $this->createHeader();
$this->to = $this->createTo();
return self::_send();
}
/**
* 送信処理
* @return boolean|multitype:boolean
*/
protected function _send() {
mb_language($this->_lang);
mb_internal_encoding($this->_encoding);
if(!empty($this->_attachments)) return self::_sendWithAttachments();
if(empty($this->to)) {
return mb_send_mail(null, $this->subject, $this->message, $this->header);
} else {
$_errors = array();
foreach($this->to as $to) {
if(!mb_send_mail($to, $this->subject, $this->message, $this->header)) $_errors[$to] = false;
}
if(empty($_errors)) return true;
return $_errors;
}
}
protected function _sendWithAttachments() {
$this->message = str_replace("\n", "\r\n", $this->message);
$this->header = str_replace("\n", "\r\n", $this->header);
if(empty($this->to)) {
return mail(null, self::mem($this->subject), $this->message, $this->header);
} else {
$_errors = array();
foreach($this->to as $to) {
if(!mail($to, self::mem($this->subject), $this->message, $this->header)) $_errors[$to] = false;
}
if(empty($_errors)) return true;
return $_errors;
}
}
/**
* 追加ヘッダーの生成
* @return string
*/
protected function createHeader() {
foreach($this->_from as $email => $name) $this->_header .= 'From: '.self::mem($name).' <'.$email.'>'."\n";
if(!empty($this->_cc)) {
$this->_header .= 'Cc: ';
foreach($this->_cc as $email => $name) {
$this->_header .= self::mem($name).' <'.$email.'>'.",";
}
$this->_header = self::trimLastChar($this->_header)."\n";
}
if(!empty($this->_bcc)) {
$this->_header .= 'Bcc: ';
foreach($this->_bcc as $email => $name) {
$this->_header .= self::mem($name).' <'.$email.'>'.",";
}
$this->_header = self::trimLastChar($this->_header)."\n";
}
$this->_header .= 'X-Mailer: '.$this->_xMailer."\n";
if(!empty($this->_attachments)) {
$this->_header .= 'MIME-Version: 1.0'."\n";
$this->_header .= 'Content-Type: multipart/mixed; boundary='.$this->_boundary."\n";
$this->_header .= 'Content-Transfer-Encoding: '.$this->_transferEncoding."\n";
}
return $this->_header;
}
/**
* 送信先ヘッダーの生成
* @return multitype:string
*/
protected function createTo() {
$t = array();
if(!empty($this->_to)) {
foreach($this->_to as $email => $name) {
$t[] = self::mem($name).' <'.$email.'>';
}
}
return $t;
}
/**
* テンプレートより本文の生成
* @return Ambigous <string, mixed>
*/
protected function createMessage() {
$b = '';
$tmp = file_get_contents($this->_template);
if($tmp) {
$varArray = array();
if(!empty($this->_viewVars)) {
foreach($this->_viewVars as $varName => $value) $varArray['{%'.$varName.'%}'] = $value;
}
$b = str_replace(array_keys($varArray) ,array_values($varArray), $tmp);
}
return $b;
}
/**
* バリデーションの実行
* @param unknown_type $check
* @param unknown_type $regex
* @return boolean
*/
protected function _check($check, $regex) {
if(preg_match($regex, $check)) return true;
return false;
}
protected function mem($var) {
$internalEncoding = function_exists('mb_internal_encoding');
if($internalEncoding) {
$restore = mb_internal_encoding();
mb_internal_encoding($this->_encoding);
}
$return = mb_encode_mimeheader($var, $this->_encoding, 'B');
if($internalEncoding) mb_internal_encoding($restore);
return $return;
}
protected function mce($var) {
return mb_convert_encoding($var, $this->_charset);
}
protected function trimLastChar($var) {
return substr($var, 0, -1);
}
/**
* 配列の中から指定した文字列を抽出する
*
* - `{n}` Matches any numeric key, or integer.
* - `{s}` Matches any string key.
*
* @param array $data
* @param unknown_type $path
* @return multitype:|array|Ambigous <multitype:multitype: , multitype:unknown >
*/
protected function _extract(array $data, $path) {
if(empty($path) || !preg_match('/[{]/', $path)) return $data;
$tokens = explode('.', $path);
$_key = '__set_item__';
$context = array($_key => array($data));
foreach($tokens as $token) {
$next = array();
foreach($context[$_key] as $item) {
foreach((array)$item as $k => $v) {
if(self::_matchToken($k, $token)) {
$next[] = $v;
}
}
}
$context = array($_key => $next);
}
return $context[$_key];
}
/**
* トークンに対するキーチェック
* @param unknown_type $key
* @param unknown_type $token
* @return boolean
*/
protected function _matchToken($key, $token) {
if($token === '{n}') return is_numeric($key);
if($token === '{s}') return is_string($key);
return ($key === $token);
}
}
用意した全メソッドの使い方は以下の通り。
$mail = new simpleMailTransmission();
$res = $mail
->to('to@local.host')
->addTo('addTo@local.host')
->addTo('addTo2@local.host')
->cc('cc@local.host')
->addCc('addCc@local.host')
->addCc('addCc2@local.host')
->bcc('bcc@local.host')
->addBcc('addBcc@local.host')
->addBcc('addBcc2@local.host')
->from('from@local.host')
->subject('mailSubjectHere.')
->template('/path/to/template.ext')
->viewVars($params)
->attachments('/path/to/file1.ext')
->addAttachments('/path/to/file2.ext')
->addAttachments('/path/to/file3.ext')
->send();
var_dump($res);
複数のTo、Cc、Bcc、添付ファイルの送信に対応。
テンプレートの文法、変数の渡し方は前回の記事を参照されたし。