1<?php 2/***************************************************\ 3 * 4 * Mailer (https://github.com/txthinking/Mailer) 5 * 6 * A lightweight PHP SMTP mail sender. 7 * Implement RFC0821, RFC0822, RFC1869, RFC2045, RFC2821 8 * 9 * Support html body, don't worry that the receiver's 10 * mail client can't support html, because Mailer will 11 * send both text/plain and text/html body, so if the 12 * mail client can't support html, it will display the 13 * text/plain body. 14 * 15 * Create Date 2012-07-25. 16 * Under the MIT license. 17 * 18 \***************************************************/ 19namespace Tx\Mailer; 20 21class Message 22{ 23 /** 24 * from name 25 */ 26 protected $fromName; 27 28 /** 29 * from email 30 */ 31 protected $fromEmail; 32 33 /** 34 * fake from name 35 */ 36 protected $fakeFromName; 37 38 /** 39 * fake from email 40 */ 41 protected $fakeFromEmail; 42 43 /** 44 * to email 45 */ 46 protected $to = array(); 47 48 /** 49 * cc email 50 */ 51 protected $cc = array(); 52 53 /** 54 * bcc email 55 */ 56 protected $bcc = array(); 57 58 /** 59 * mail subject 60 */ 61 protected $subject; 62 63 /** 64 * mail body 65 */ 66 protected $body; 67 68 /** 69 *mail attachment 70 */ 71 protected $attachment = array(); 72 73 /** 74 * message header 75 */ 76 protected $header = array(); 77 78 /** 79 * charset 80 */ 81 protected $charset = "UTF-8"; 82 83 /** 84 * header multipart boundaryMixed 85 */ 86 protected $boundaryMixed; 87 88 /** 89 * header multipart alternative 90 */ 91 protected $boundaryAlternative; 92 93 /** 94 * $this->CRLF 95 * @var string 96 */ 97 protected $CRLF = "\r\n"; 98 99 100 /** 101 * Address for the reply-to header 102 * @var string 103 */ 104 protected $replyToName; 105 106 /** 107 * Address for the reply-to header 108 * @var string 109 */ 110 protected $replyToEmail; 111 112 113 public function setReplyTo($name, $email) 114 { 115 $this->replyToName = $name; 116 $this->replyToEmail = $email; 117 return $this; 118 } 119 120 121 /** 122 * set mail from 123 * @param string $name 124 * @param string $email 125 * @return $this 126 */ 127 public function setFrom($name, $email) 128 { 129 $this->fromName = $name; 130 $this->fromEmail = $email; 131 return $this; 132 } 133 134 135 /** 136 * set mail fake from 137 * @param string $name 138 * @param string $email 139 * @return $this 140 */ 141 public function setFakeFrom($name, $email) 142 { 143 $this->fakeFromName = $name; 144 $this->fakeFromEmail = $email; 145 return $this; 146 } 147 148 /** 149 * add mail receiver 150 * @param string $name 151 * @param string $email 152 * @return $this 153 */ 154 public function addTo($name, $email) 155 { 156 $this->to[$email] = $name; 157 return $this; 158 } 159 160 /** 161 * add cc mail receiver 162 * @param string $name 163 * @param string $email 164 * @return $this 165 */ 166 public function addCc($name, $email) 167 { 168 $this->cc[$email] = $name; 169 return $this; 170 } 171 172 /** 173 * add bcc mail receiver 174 * @param string $name 175 * @param string $email 176 * @return $this 177 */ 178 public function addBcc($name, $email) 179 { 180 $this->bcc[$email] = $name; 181 return $this; 182 } 183 184 /** 185 * set mail subject 186 * @param string $subject 187 * @return $this 188 */ 189 public function setSubject($subject) 190 { 191 $this->subject = $subject; 192 return $this; 193 } 194 195 /** 196 * set mail body 197 * @param string $body 198 * @return $this 199 */ 200 public function setBody($body) 201 { 202 $this->body = $body; 203 return $this; 204 } 205 206 /** 207 * add mail attachment 208 * @param $name 209 * @param $path 210 * @return $this 211 */ 212 public function addAttachment($name, $path) 213 { 214 $this->attachment[$name] = $path; 215 return $this; 216 } 217 218 /** 219 * @return string 220 */ 221 public function getFromName() 222 { 223 return $this->fromName; 224 } 225 226 /** 227 * @return string 228 */ 229 public function getFromEmail() 230 { 231 return $this->fromEmail; 232 } 233 234 235 /** 236 * @return string 237 */ 238 public function getFakeFromName() 239 { 240 return $this->fakeFromName; 241 } 242 243 /** 244 * @return string 245 */ 246 public function getFakeFromEmail() 247 { 248 return $this->fakeFromEmail; 249 } 250 251 /** 252 * @return mixed 253 */ 254 public function getTo() 255 { 256 return $this->to; 257 } 258 259 /** 260 * @return mixed 261 */ 262 public function getCc() 263 { 264 return $this->cc; 265 } 266 267 /** 268 * @return mixed 269 */ 270 public function getBcc() 271 { 272 return $this->bcc; 273 } 274 275 /** 276 * @return mixed 277 */ 278 public function getSubject() 279 { 280 return $this->subject; 281 } 282 283 /** 284 * @return mixed 285 */ 286 public function getBody() 287 { 288 return $this->body; 289 } 290 291 /** 292 * @return array 293 */ 294 public function getAttachment() 295 { 296 return $this->attachment; 297 } 298 299 /** 300 * Create mail header 301 * @return $this 302 */ 303 protected function createHeader() 304 { 305 $this->header['Date'] = date('r'); 306 307 $fromName = ""; 308 $fromEmail = $this->fromEmail; 309 if(!empty($this->fromName)){ 310 $fromName = sprintf("=?utf-8?B?%s?= ", base64_encode($this->fromName)); 311 } 312 if(!empty($this->fakeFromEmail)){ 313 if(!empty($this->fakeFromName)){ 314 $fromName = sprintf("=?utf-8?B?%s?= ", base64_encode($this->fakeFromName)); 315 } 316 $fromEmail = $this->fakeFromEmail; 317 } 318 $this->header['Return-Path'] = $fromEmail; 319 $this->header['From'] = $fromName . "<" . $fromEmail .">"; 320 321 $this->header['To'] = ''; 322 foreach ($this->to as $toEmail => $toName) { 323 if(!empty($toName)){ 324 $toName = sprintf("=?utf-8?B?%s?= ", base64_encode($toName)); 325 } 326 $this->header['To'] .= $toName . "<" . $toEmail . ">, "; 327 } 328 $this->header['To'] = substr($this->header['To'], 0, -2); 329 $this->header['Cc'] = ''; 330 foreach ($this->cc as $toEmail => $toName) { 331 if(!empty($toName)){ 332 $toName = sprintf("=?utf-8?B?%s?= ", base64_encode($toName)); 333 } 334 $this->header['Cc'] .= $toName . "<" . $toEmail . ">, "; 335 } 336 $this->header['Cc'] = substr($this->header['Cc'], 0, -2); 337 $this->header['Bcc'] = ''; 338 foreach ($this->bcc as $toEmail => $toName) { 339 if(!empty($toName)){ 340 $toName = sprintf("=?utf-8?B?%s?= ", base64_encode($toName)); 341 } 342 $this->header['Bcc'] .= $toName . "<" . $toEmail . ">, "; 343 } 344 $this->header['Bcc'] = substr($this->header['Bcc'], 0, -2); 345 346 $replyToName = ""; 347 if(!empty($this->replyToEmail)){ 348 if(!empty($this->replyToName)){ 349 $replyToName = sprintf("=?utf-8?B?%s?= ", base64_encode($this->replyToName)); 350 } 351 $this->header['Reply-To'] = $replyToName . "<" . $this->replyToEmail . ">"; 352 } 353 354 if(empty($this->subject)){ 355 $subject = ''; 356 }else{ 357 $subject = sprintf("=?utf-8?B?%s?= ", base64_encode($this->subject)); 358 } 359 $this->header['Subject'] = $subject; 360 361 $this->header['Message-ID'] = '<' . md5(uniqid()) . $this->fromEmail . '>'; 362 $this->header['X-Priority'] = '3'; 363 $this->header['X-Mailer'] = 'Mailer (https://github.com/txthinking/Mailer)'; 364 $this->header['MIME-Version'] = '1.0'; 365 if (!empty($this->attachment)){ 366 $this->boundaryMixed = md5(md5(time().'TxMailer').uniqid()); 367 $this->header['Content-Type'] = "multipart/mixed; \r\n\tboundary=\"" . $this->boundaryMixed . "\""; 368 } 369 $this->boundaryAlternative = md5(md5(time().'TXMailer').uniqid()); 370 return $this; 371 } 372 373 /** 374 * @brief createBody create body 375 * 376 * @return string 377 */ 378 protected function createBody() 379 { 380 $in = ""; 381 $in .= "Content-Type: multipart/alternative; boundary=\"$this->boundaryAlternative\"" . $this->CRLF; 382 $in .= $this->CRLF; 383 $in .= "--" . $this->boundaryAlternative . $this->CRLF; 384 $in .= "Content-Type: text/plain; charset=\"" . $this->charset . "\"" . $this->CRLF; 385 $in .= "Content-Transfer-Encoding: base64" . $this->CRLF; 386 $in .= $this->CRLF; 387 $in .= chunk_split(base64_encode($this->body)) . $this->CRLF; 388 $in .= $this->CRLF; 389 $in .= "--" . $this->boundaryAlternative . $this->CRLF; 390 $in .= "Content-Type: text/html; charset=\"" . $this->charset ."\"" . $this->CRLF; 391 $in .= "Content-Transfer-Encoding: base64" . $this->CRLF; 392 $in .= $this->CRLF; 393 $in .= chunk_split(base64_encode($this->body)) . $this->CRLF; 394 $in .= $this->CRLF; 395 $in .= "--" . $this->boundaryAlternative . "--" . $this->CRLF; 396 return $in; 397 } 398 399 /** 400 * @brief createBodyWithAttachment create body with attachment 401 * 402 * @return string 403 */ 404 protected function createBodyWithAttachment() 405 { 406 $in = ""; 407 $in .= $this->CRLF; 408 $in .= $this->CRLF; 409 $in .= '--' . $this->boundaryMixed . $this->CRLF; 410 $in .= "Content-Type: multipart/alternative; boundary=\"$this->boundaryAlternative\"" . $this->CRLF; 411 $in .= $this->CRLF; 412 $in .= "--" . $this->boundaryAlternative . $this->CRLF; 413 $in .= "Content-Type: text/plain; charset=\"" . $this->charset . "\"" . $this->CRLF; 414 $in .= "Content-Transfer-Encoding: base64" . $this->CRLF; 415 $in .= $this->CRLF; 416 $in .= chunk_split(base64_encode($this->body)) . $this->CRLF; 417 $in .= $this->CRLF; 418 $in .= "--" . $this->boundaryAlternative . $this->CRLF; 419 $in .= "Content-Type: text/html; charset=\"" . $this->charset ."\"" . $this->CRLF; 420 $in .= "Content-Transfer-Encoding: base64" . $this->CRLF; 421 $in .= $this->CRLF; 422 $in .= chunk_split(base64_encode($this->body)) . $this->CRLF; 423 $in .= $this->CRLF; 424 $in .= "--" . $this->boundaryAlternative . "--" . $this->CRLF; 425 foreach ($this->attachment as $name => $path){ 426 $in .= $this->CRLF; 427 $in .= '--' . $this->boundaryMixed . $this->CRLF; 428 $in .= "Content-Type: application/octet-stream; name=\"". $name ."\"" . $this->CRLF; 429 $in .= "Content-Transfer-Encoding: base64" . $this->CRLF; 430 $in .= "Content-Disposition: attachment; filename=\"" . $name . "\"" . $this->CRLF; 431 $in .= $this->CRLF; 432 $in .= chunk_split(base64_encode(file_get_contents($path))) . $this->CRLF; 433 } 434 $in .= $this->CRLF; 435 $in .= $this->CRLF; 436 $in .= '--' . $this->boundaryMixed . '--' . $this->CRLF; 437 return $in; 438 } 439 440 public function toString() 441 { 442 $in = ''; 443 $this->createHeader(); 444 foreach ($this->header as $key => $value) { 445 $in .= $key . ': ' . $value . $this->CRLF; 446 } 447 if (empty($this->attachment)) { 448 $in .= $this->createBody(); 449 } else { 450 $in .= $this->createBodyWithAttachment(); 451 } 452 $in .= $this->CRLF . $this->CRLF . "." . $this->CRLF; 453 return $in; 454 } 455 456} 457