Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
abmmhasan committed Sep 8, 2024
1 parent 8b36cb8 commit 1c6e0e8
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 79 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
}
},
"require": {
"php": ">=8.0"
"php": ">=8.0",
"ext-fileinfo": "*"
},
"require-dev": {
"captainhook/captainhook": "^5.23",
Expand Down
34 changes: 14 additions & 20 deletions src/Email/Emailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,30 +95,24 @@ public function attachment($filePath, $filename = null)

public function send()
{
// Instantiate the builder
$builder = new EmailBuilder();

// Build headers using the user-provided input
$headers = $builder->buildHeaders(
$this->from,
$this->cc,
$this->bcc,
$this->replyTo,
$this->attachments
);

// Build the email body (plaintext will be auto-generated if not provided)
$message = $builder->buildBody(
$this->plainText,
$this->htmlContent,
$this->attachments
);
$builder = new EmailBuilder($this->from);
$builder->setCommonHeaders($this->to, $this->subject, $this->cc, $this->bcc, $this->replyTo);
// $builder->setIdHeaders();

// Choose the appropriate sender method (SMTP or generic)
if ($this->smtpConfigured) {
return (new SMTPSender($this->from, $this->smtpConfig))->send($this->to, $message, $headers);
return (new SMTPSender($this->from, $this->smtpConfig))->send(
$this->to,
$builder->setBody($this->htmlContent, $this->plainText, $this->attachments),
$builder->getHeaders()
);
}
return (new GenericSender())->send($this->to, $this->subject, $message, $headers);
return (new GenericSender())->send(
$this->to,
$this->subject,
$builder->setBody($this->htmlContent, $this->plainText, $this->attachments),
$builder->getHeaders()
);
}


Expand Down
116 changes: 58 additions & 58 deletions src/Email/System/EmailBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,125 +4,125 @@

class EmailBuilder
{
private $boundaryAlternative;
private $boundaryMixed = null;
private string $boundaryAlternative;
private ?string $boundaryMixed = null;

public function __construct()
{
$this->boundaryAlternative = sha1(uniqid(time(), true));
}

public function buildHeaders($from, $cc = [], $bcc = [], $replyTo = '', $attachments = [])
{
return $this->formatFromHeader($from)
. $this->formatReplyToHeader($replyTo)
. $this->formatCcBccHeaders($cc, $bcc)
. $this->formatMimeHeader($attachments);
}
private array $headers = [];

private function formatFromHeader($from)
public function __construct(private array $from)
{
return "From: =?UTF-8?B?" . base64_encode($from['name']) . "?= <{$from['email']}>\r\n";
}

private function formatReplyToHeader($replyTo)
{
return "Reply-To: {$replyTo}\r\n";
$this->boundaryAlternative = sha1(uniqid(time(), true));
}

private function formatCcBccHeaders($cc, $bcc)
public function setCommonHeaders(array $to, string $subject, array $cc = [], $bcc = [], $replyTo = '')
{
$headers = "";
$this->headers = [
'Date: ' . date('r'),
'From: =?UTF-8?B?' . base64_encode($this->from['name']) . '?= <' . $this->from['email'] . '>',
'To: ' . implode(',', $to),
'Reply-To: ' . $replyTo,
'Subject: ' . $subject
];
if (!empty($cc)) {
$headers .= "Cc: " . implode(',', $cc) . "\r\n";
$this->headers[] = "Cc: " . implode(',', $cc);
}
if (!empty($bcc)) {
$headers .= "Bcc: " . implode(',', $bcc) . "\r\n";
$this->headers[] = "Bcc: " . implode(',', $bcc);
}
return $headers;
return $this;
}

private function formatMimeHeader($attachments)
public function setIdHeaders(string $messageId, string $inReplyTo = '', array $references = [])
{
if (!empty($attachments)) {
$this->boundaryMixed = sha1(uniqid(time(), true));
return "MIME-Version: 1.0\r\nContent-Type: multipart/mixed; boundary=\"{$this->boundaryMixed}\"\r\n";
} else {
return "MIME-Version: 1.0\r\nContent-Type: multipart/alternative; boundary=\"{$this->boundaryAlternative}\"\r\n";
$domain = substr(strrchr($this->from['email'], "@"), 1);

$this->headers[] = "Message-ID: <$messageId>";

if (!empty($inReplyTo)) {
$this->headers[] = "In-Reply-To: <$inReplyTo@$domain>";
}
if (!empty($references)) {
$this->headers[] = 'References: ' . implode(
' ',
array_map(function ($ref) use ($domain) {
return "<$ref@$domain>";
}, $references)
);
}
return $this;
}

public function buildBody($plainText, $htmlContent = '', $attachments = [])
public function setBody($htmlContent, $plainText = '', $attachments = [])
{
if (empty($plainText) && !empty($htmlContent)) {
$plainText = strip_tags($htmlContent);
}

$this->headers[] = 'MIME-Version: 1.0';
$message = $this->buildAlternativeBody($plainText, $htmlContent);
if (!empty($attachments)) {
$message = $this->wrapWithMixedBoundary($message, $attachments);
$this->boundaryMixed = sha1(uniqid(time(), true));
$this->headers[] = "Content-Type: multipart/mixed; boundary=\"$this->boundaryMixed\"";
return $this->wrapWithMixedBoundary($message, $attachments);
}

$this->headers[] = "Content-Type: multipart/alternative; boundary=\"$this->boundaryAlternative\"";
return $message;
}

public function getHeaders()
{
return implode("\r\n", $this->headers);
}

private function buildAlternativeBody($plainText, $htmlContent)
{
$message = "";
$message .= $this->buildPlainTextPart($plainText);
$message = $this->buildPlainTextPart($plainText);
if (!empty($htmlContent)) {
$message .= $this->buildHtmlPart($htmlContent);
}
$message .= "--{$this->boundaryAlternative}--\r\n";
$message .= "--$this->boundaryAlternative--\r\n";
return $message;
}

private function wrapWithMixedBoundary($message, $attachments)
{
$wrappedMessage = "--{$this->boundaryMixed}\r\n";
$wrappedMessage .= "Content-Type: multipart/alternative; boundary=\"{$this->boundaryAlternative}\"\r\n\r\n";
$wrappedMessage .= $message;
$wrappedMessage .= $this->buildAttachmentsPart($attachments);
$wrappedMessage .= "--{$this->boundaryMixed}--\r\n";
return $wrappedMessage;
return "--$this->boundaryMixed\r\n"
. "Content-Type: multipart/alternative; boundary=\"$this->boundaryAlternative\"\r\n\r\n"
. $message
. $this->buildAttachmentsPart($attachments)
. "--$this->boundaryMixed--\r\n";
}

private function buildPlainTextPart($plainText)
{
return "--{$this->boundaryAlternative}\r\n"
return "--$this->boundaryAlternative\r\n"
. "Content-Type: text/plain; charset=UTF-8\r\n"
. "Content-Transfer-Encoding: 7bit\r\n\r\n"
. "{$plainText}\r\n\r\n";
. "$plainText\r\n\r\n";
}

private function buildHtmlPart($htmlContent)
{
return "--{$this->boundaryAlternative}\r\n"
return "--$this->boundaryAlternative\r\n"
. "Content-Type: text/html; charset=UTF-8\r\n"
. "Content-Transfer-Encoding: quoted-printable\r\n\r\n"
. $this->encodeQuotedPrintable($htmlContent) . "\r\n\r\n";
. quoted_printable_encode($htmlContent) . "\r\n\r\n";
}

private function buildAttachmentsPart($attachments)
{
$attachmentsPart = "";
foreach ($attachments as $attachment) {
$filePath = $attachment['path'];
$fileName = $attachment['name'];
$fileName = rawurlencode($attachment['name']);
$fileType = mime_content_type($filePath);
$fileContent = chunk_split(base64_encode(file_get_contents($filePath)));

$attachmentsPart .= "--{$this->boundaryMixed}\r\n"
. "Content-Type: $fileType; name*=\"UTF-8''" . rawurlencode($fileName) . "\"\r\n"
. "Content-Disposition: attachment; filename*=\"UTF-8''" . rawurlencode($fileName) . "\"\r\n"
$attachmentsPart .= "--$this->boundaryMixed\r\n"
. "Content-Type: $fileType; name*=\"UTF-8''" . $fileName . "\"\r\n"
. "Content-Disposition: attachment; filename*=\"UTF-8''" . $fileName . "\"\r\n"
. "Content-Transfer-Encoding: base64\r\n\r\n"
. "$fileContent\r\n\r\n";
}
return $attachmentsPart;
}

private function encodeQuotedPrintable($string)
{
return quoted_printable_encode($string);
}
}

0 comments on commit 1c6e0e8

Please sign in to comment.