Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
52 / 52
100.00% covered (success)
100.00%
7 / 7
CRAP
100.00% covered (success)
100.00%
1 / 1
PhpMailerAdapter
100.00% covered (success)
100.00%
52 / 52
100.00% covered (success)
100.00%
7 / 7
20
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 send
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 configureSMTP
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
4
 configureRecipients
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 configureContent
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 configureAttachments
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
3
 configureHeaders
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3// ╔════════════════════════════════════════════════════════════╗
4// ║ MIT Licence (#Expat) - https://opensource.org/licenses/MIT ║
5// ║ Copyright 2026 Frederic Poeydomenge <dyno@phexium.com>     ║
6// ╚════════════════════════════════════════════════════════════╝
7
8declare(strict_types=1);
9
10namespace Phexium\Plugin\Mailer\Adapter;
11
12use Override;
13use Phexium\Plugin\Mailer\EmailAddress;
14use Phexium\Plugin\Mailer\Exception\MailSendFailedException;
15use Phexium\Plugin\Mailer\Message;
16use Phexium\Plugin\Mailer\Port\MailerInterface;
17use PHPMailer\PHPMailer\Exception as PHPMailerException;
18use PHPMailer\PHPMailer\PHPMailer;
19
20final readonly class PhpMailerAdapter implements MailerInterface
21{
22    public function __construct(
23        private string $host,
24        private int $port,
25        private string $username = '',
26        private string $password = '',
27        private string $encryption = '',
28    ) {}
29
30    #[Override]
31    public function send(Message $message): void
32    {
33        $mailer = new PHPMailer(true);
34
35        try {
36            $this->configureSMTP($mailer);
37            $this->configureRecipients($mailer, $message);
38            $this->configureContent($mailer, $message);
39            $this->configureAttachments($mailer, $message);
40            $this->configureHeaders($mailer, $message);
41
42            $mailer->send();
43        } catch (PHPMailerException $e) {
44            throw MailSendFailedException::withReason($e->getMessage(), $e);
45        }
46    }
47
48    private function configureSMTP(PHPMailer $mailer): void
49    {
50        $mailer->isSMTP();
51        $mailer->Host = $this->host;
52        $mailer->Port = $this->port;
53
54        if ($this->username !== '') {
55            $mailer->SMTPAuth = true;
56            $mailer->Username = $this->username;
57            $mailer->Password = $this->password;
58        }
59
60        if ($this->encryption === 'tls') {
61            $mailer->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
62        } elseif ($this->encryption === 'ssl') {
63            $mailer->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
64        }
65    }
66
67    private function configureRecipients(PHPMailer $mailer, Message $message): void
68    {
69        $mailer->setFrom($message->from->getAddress(), $message->from->getName() ?? '');
70
71        foreach ($message->to as $to) {
72            $mailer->addAddress($to->getAddress(), $to->getName() ?? '');
73        }
74
75        foreach ($message->cc as $cc) {
76            $mailer->addCC($cc->getAddress(), $cc->getName() ?? '');
77        }
78
79        foreach ($message->bcc as $bcc) {
80            $mailer->addBCC($bcc->getAddress());
81        }
82
83        if ($message->replyTo instanceof EmailAddress) {
84            $mailer->addReplyTo($message->replyTo->getAddress(), $message->replyTo->getName() ?? '');
85        }
86    }
87
88    private function configureContent(PHPMailer $mailer, Message $message): void
89    {
90        $mailer->Subject = $message->subject;
91
92        if ($message->htmlBody !== null) {
93            $mailer->isHTML(true);
94            $mailer->Body = $message->htmlBody;
95        } elseif ($message->textBody !== null) {
96            $mailer->Body = $message->textBody;
97        }
98    }
99
100    private function configureAttachments(PHPMailer $mailer, Message $message): void
101    {
102        foreach ($message->attachments as $attachment) {
103            if ($attachment->isFile()) {
104                $mailer->addAttachment(
105                    $attachment->getPath(),
106                    $attachment->getFilename(),
107                    PHPMailer::ENCODING_BASE64,
108                    $attachment->getMimeType() ?? '',
109                );
110            } else {
111                $mailer->addStringAttachment(
112                    $attachment->getContent(),
113                    $attachment->getFilename(),
114                    PHPMailer::ENCODING_BASE64,
115                    $attachment->getMimeType() ?? '',
116                );
117            }
118        }
119    }
120
121    private function configureHeaders(PHPMailer $mailer, Message $message): void
122    {
123        foreach ($message->headers as $header) {
124            $mailer->addCustomHeader($header->name, $header->value);
125        }
126    }
127}