マルチパート構成の添付ファイル付きHTMLメールを送信
PHPでHTMLメールを送信する際、テキストとHTMLのマルチパート構成や添付ファイル、Cc/Bccの設定など実務で必要な要素は多岐にわたりますが、
このページのコードをコピペすれば、そのまま使えます。
UTF-8対応の実用的なPHPコードを使い、HTMLメール送信の仕組みを解説していきます。
添付ファイル付きのHTMLメールをPHPで送るには、テキスト+HTMLのマルチパート構成と、適切なboundary制御が欠かせません。
mb_send_mail()では限界があるため、独自にmultipart/mixed構成を組む必要があります。
このページでは、RFC準拠の手法を元に、実際に動くコードを提示します。
mamsendmail.phpの準備
本ページ下部にあるmamsendmail.phpファイルを準備する。
メールの送信
<?php
include(__DIR__.'/mamsendmail.php');
mb_language("Japanese");
mb_internal_encoding("UTF-8"); //UTF-8でメールを送信する
//■送り元
$from="mamさんより<from@mam-mam.net>";
//■送り先
// To又はCcには最低1つの送り先を入れる(Bccのみでは送れない場合がある)
// 複数送り先はカンマ(,)区切りで入れる
$to= "To1さんへ<to1@mam-mam.net>, To2さんへ<to2@mam-mam.net>";
$cc= "<cc1@mam-mam.net>, Cc2さんへ<cc2@mam-mam.net>, cc3@mam-mam.net";
$bcc="Bcc1さんへ<bcc1@mam-mam.net>, bcc2@mam-mam.net, <bcc3@mam-mam.net>";
//■件名
$subject="件名: 長い件名 を入れることもできます。半角も入れられます。23784sdarew!#$%&()=-~^|";
//■テキスト本文 不要なら $textBody="";
$textBody="タイトル\r\n 本文\r\n テストメール";
//■HTML本文 不要なら $htmlBody="";
$htmlBody="
<html>
<head>
<style>
h3{color:red;font-size:32px;font-weight:bold;}
p{color:black;font-size:20px;}
img{width:100%;max-width:500px;}
</style>
</head>
<body>
<h3>タイトル</h3>
<p>
本文を入れたりしています。<br>
<img src=\"cid:img1\"><br>
<img src=\"cid:img2\"><br>
<img src=\"cid:img3\"><br>
テストメールです。
</p>
</body>
</html>";
//■添付ファイル 不要なら $files=[];
$files=[__DIR__."/../imgs/0001s.jpg", __DIR__."/../imgs/0002s.jpg", __DIR__."/../imgs/0003s.jpg"];
//メールの送信
mamSendMail::send(
$from,
$to,$cc,$bcc,
$subject,
$textBody, $htmlBody,
$files
);
mamsendmail.php
<?php
mb_language("Japanese");
mb_internal_encoding("UTF-8"); //UTF-8でメールを送信する
/*
mamSendMail::send(引数) UTF-8メール送信クラス
引数の意味
$from(必須)
1つだけメールアドレスを入れる
例1: $from="from@mam-mam.net";
例2: $from="<from@mam-mam.net>";
例3: $from="斎藤<from@mam-mam.net>";
$to,$cc,$bcc($to又は$ccに宛先アドレスが1つ以上必須[bccだけでは送信できない場合がある])
カンマ(,)区切りで複数の送り先アドレスを入れることができる
例: $to="佐藤様<to1@mam-mam.net>, 山田様<to2@mam-mam.net>, to3@mam-mam.net, <to4@mam-mam.net>";
ccとbccが不要であれば空文字にする $cc="";$bcc="";
$subject
メールの件名
$textBody
テキストメール本文
例: $textBody="テスト\r\n 本文\r\n";
$htmlBody
HTMLメール本文
例: $htmlBody="<html><body><h1 style='font-size:20px;'>テスト</h1><p>本文</p></body></html>";
注意事項:
・「$textBody="何か入っている",$htmlText="何か入っている"」
⇒マルチパート(テキストパートとHTMLパート)メールで送信
・「$textBody="",$htmlText="何か入っている"」
⇒HTMLメールで送信
・「$textBody="何か入っている",$htmlText=""」
⇒テキストメールで送信
$files
必要であればファイル名を配列で入れる
例:$files=["C:/temp/1.jpg","C:/temp/2.png","C:/temp/3.png"]"
不要であれば空配列を指定するか省略する
$files=[];
*/
class mamSendMail{
public static function send(
string $from,
string $to, string $cc, string $bcc,
string $subject,
string $textBody, string $htmlBody,
array $files=[]
){
//ファイルの確認
for($i=0;$i<count($files);$i++){
if(!file_exists($files[$i])){
unset($files[$i]);
$files=array_values($files);
}
}
$fFrom=self::mamExplodeMailAddress($from);
if(count($fFrom)==0){return false;}
if(empty($fFrom[0]["name"])){
$cFrom=$fFrom[0]["add"];
}else{
$cFrom=mb_encode_mimeheader($fFrom[0]["name"],"UTF-8","B","\r\n",10).$fFrom[0]["add"];
}
$fTo=self::mamExplodeMailAddress($to);
//if(count($fTo)==0){return false;}
$cTo="";
for($i=0;$i<count($fTo);$i++){
if(!empty($cTo)){$cTo.=",";}
if(empty($fTo[$i]["name"])){
$cTo.=$fTo[$i]["add"];
}else{
$cTo.=mb_encode_mimeheader($fTo[$i]["name"],"UTF-8").$fTo[$i]["add"];
}
}
$fCc=self::mamExplodeMailAddress($cc);
$cCc="";
for($i=0;$i<count($fCc);$i++){
if(!empty($cCc)){$cCc.=",";}
if(empty($fCc[$i]["name"])){
$cCc.=$fCc[$i]["add"];
}else{
$cCc.=mb_encode_mimeheader($fCc[$i]["name"],"UTF-8").$fCc[$i]["add"];
}
}
$fBcc=self::mamExplodeMailAddress($bcc);
$cBcc="";
for($i=0;$i<count($fBcc);$i++){
if(!empty($cBcc)){$cBcc.=",";}
if(empty($fBcc[$i]["name"])){
$cBcc.=$fBcc[$i]["add"];
}else{
$cBcc.=mb_encode_mimeheader($fBcc[$i]["name"],"UTF-8").$fBcc[$i]["add"];
}
}
$cSubject=mb_encode_mimeheader($subject);
$cHeaders=[
"From"=>$cFrom,
"Reply-To"=>$cFrom,
"Content-Transfer-Encoding"=>"base64",
"X-Mailer"=>"PHP Mailer",
];
if(!empty($textBody)&&!empty($htmlBody)){
//テキスト+HTML メールの場合
$fHtmlBody=chunk_split(base64_encode($htmlBody));
$fTextBody=chunk_split(base64_encode($textBody));
$fBoundary="b1".uniqid("",true);
if(count($files)==0){
//添付ファイル無し
$cHeaders["Content-Type"]='multipart/alternative; boundary="'.$fBoundary.'"';
$cBody=
"--".$fBoundary."\r\n".
"Content-Type: text/plain; charset=UTF-8\r\n".
"Content-Disposition: inline\r\n".
"Content-Transfer-Encoding: base64\r\n".
"\r\n".
$fTextBody.
"\r\n".
"--".$fBoundary."\r\n".
"Content-Type: text/html; charset=UTF-8\r\n".
"Content-Disposition: inline\r\n".
"Content-Transfer-Encoding: base64\r\n".
"\r\n".
$fHtmlBody.
"\r\n".
"--".$fBoundary."--\r\n";
}else{
//添付ファイル有り
$fBoundary2="b2".uniqid("",true);
$cHeaders["Content-Type"]='multipart/mixed; boundary="'.$fBoundary.'"';
$cBody=
"--".$fBoundary."\r\n".
"Content-Type: multipart/alternative; boundary=\"".$fBoundary2."\"\r\n".
"\r\n".
"--".$fBoundary2."\r\n".
"Content-Type: text/plain; charset=UTF-8\r\n".
"Content-Disposition: inline\r\n".
"Content-Transfer-Encoding: base64\r\n".
"\r\n".
$fTextBody.
"\r\n".
"--".$fBoundary2."\r\n".
"Content-Type: text/html; charset=UTF-8\r\n".
"Content-Disposition: inline\r\n".
"Content-Transfer-Encoding: base64\r\n".
"\r\n".
$fHtmlBody.
"\r\n".
"--".$fBoundary2."--\r\n".
"\r\n".
"--".$fBoundary."\r\n";
for($i=0;$i<count($files);$i++){
if(preg_match('/\.gif$/i',$files[$i])){
$hd="image/gif";
$cd="inline";
}else if(preg_match('/\.jpg$|\.jpeg$/i',$files[$i])){
$hd="image/jpeg";
$cd="inline";
}else if(preg_match('/\.png$/i',$files[$i])){
$hd="image/png";
$cd="inline";
}else{
$hd="application/octet-stream";
$cd="attachment";
}
$cBody.=
"Content-Type: ".$hd."; name=\"".basename($files[$i])."\"\r\n".
"Content-Disposition: ".$cd."; filename=\"".basename($files[$i])."\"\r\n".
"Content-Transfer-Encoding: base64\r\n".
"Content-ID: <img".($i+1).">\r\n".
"Content-Location: ".basename($files[$i])."\r\n".
"\r\n".
chunk_split(base64_encode(file_get_contents($files[$i])));
if($i==(count($files)-1)){
$cBody.="--".$fBoundary."--\r\n";
}else{
$cBody.="--".$fBoundary."\r\n";
}
}
}
}else if(!empty($htmlBody)){
//HTMLメールのみ
if(count($files)==0){
$cHeaders["Content-Type"]='text/html;charset=utf-8';
$cBody=chunk_split(base64_encode($htmlBody));
}else{
$fBoundary="b1".uniqid("",true);
$cHeaders["Content-Type"]='multipart/mixed; boundary="'.$fBoundary.'"';
$fHtmlBody=chunk_split(base64_encode($htmlBody));
$cBody=
"--".$fBoundary."\r\n".
"Content-Type: text/html; charset=UTF-8\r\n".
"Content-Disposition: inline\r\n".
"Content-Transfer-Encoding: base64\r\n".
"\r\n".
$fHtmlBody."\r\n".
"--".$fBoundary."\r\n";
for($i=0;$i<count($files);$i++){
if(preg_match('/\.gif$/i',$files[$i])){
$hd="image/gif";
$cd="inline";
}else if(preg_match('/\.jpg$|\.jpeg$/i',$files[$i])){
$hd="image/jpeg";
$cd="inline";
}else if(preg_match('/\.png$/i',$files[$i])){
$hd="image/png";
$cd="inline";
}else{
$hd="application/octet-stream";
$cd="attachment";
}
$cBody.=
"Content-Type: ".$hd."; name=\"".basename($files[$i])."\"\r\n".
"Content-Disposition: ".$cd."; filename=\"".basename($files[$i])."\"\r\n".
"Content-Transfer-Encoding: base64\r\n".
"Content-ID: <img".($i+1).">\r\n".
"Content-Location: ".basename($files[$i])."\r\n".
"\r\n".
chunk_split(base64_encode(file_get_contents($files[$i])));
if($i==(count($files)-1)){
$cBody.="--".$fBoundary."--\r\n";
}else{
$cBody.="--".$fBoundary."\r\n";
}
}
}
}else{
//テキストメールのみ
if(count($files)==0){
$cHeaders["Content-Type"]='text/plain;charset=utf-8';
$cBody=chunk_split(base64_encode($textBody));
}else{
$fBoundary="b1".uniqid("",true);
$cHeaders["Content-Type"]='multipart/mixed; boundary="'.$fBoundary.'"';
$fTextBody=chunk_split(base64_encode($textBody));
$cBody=
"--".$fBoundary."\r\n".
"Content-Type: text/plain; charset=UTF-8\r\n".
"Content-Disposition: inline\r\n".
"Content-Transfer-Encoding: base64\r\n".
"\r\n".
$fTextBody."\r\n".
"--".$fBoundary."\r\n";
for($i=0;$i<count($files);$i++){
if(preg_match('/\.gif$/i',$files[$i])){
$hd="image/gif";
$cd="inline";
}else if(preg_match('/\.jpg$|\.jpeg$/i',$files[$i])){
$hd="image/jpeg";
$cd="inline";
}else if(preg_match('/\.png$/i',$files[$i])){
$hd="image/png";
$cd="inline";
}else{
$hd="application/octet-stream";
$cd="attachment";
}
$cBody.=
"Content-Type: ".$hd."; name=\"".basename($files[$i])."\"\r\n".
"Content-Disposition: ".$cd."; filename=\"".basename($files[$i])."\"\r\n".
"Content-Transfer-Encoding: base64\r\n".
"Content-ID: <img".($i+1).">\r\n".
"Content-Location: ".basename($files[$i])."\r\n".
"\r\n".
chunk_split(base64_encode(file_get_contents($files[$i])));
if($i==(count($files)-1)){
$cBody.="--".$fBoundary."--\r\n";
}else{
$cBody.="--".$fBoundary."\r\n";
}
}
}
}
if(!empty($cCc)){
$cHeaders["Cc"]=$cCc;
}
if(!empty($cBcc)){
$cHeaders["Bcc"]=$cBcc;
}
mail($cTo,$cSubject,$cBody,$cHeaders);
}
private static function mamExplodeMailAddress(string $add){
$add=trim($add);
if(empty($add)){return [];}
$addresses=explode(",",$add);
$ret=[];
foreach($addresses as $address){
$mail=["name"=>"", "add"=>$address];
$s=mb_strpos($address,"<",0,"UTF-8");
if($s!==false && $s>0){
$e=mb_strpos($address,">",$s,"UTF-8");
if($e!==false && $e>0){
$mail["name"]=trim(mb_substr($address,0,$s));
$mail["add"] =mb_substr($address,$s,$e);
}
}
$ret[]=$mail;
}
return $ret;
}
}
