以前の記事←で作成したメール送信クラスに添付ファイル送信機能をつけてみた。
使える全メソッドは下部で解説。
簡単な使い方は関数上部のコメントアウトを参照されたし。
<?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、添付ファイルの送信に対応。
テンプレートの文法、変数の渡し方は前回の記事を参照されたし。