Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
95 / 95
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
Sendgrid
100.00% covered (success)
100.00%
95 / 95
100.00% covered (success)
100.00%
2 / 2
26
100.00% covered (success)
100.00%
1 / 1
 createClient
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 send
100.00% covered (success)
100.00%
87 / 87
100.00% covered (success)
100.00%
1 / 1
22
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <dev@noladev.com>
8 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Mail\Transport;
16
17use Pop\Http\Client;
18use Pop\Http\Auth;
19use Pop\Mail\Api\AbstractHttp;
20use Pop\Mail\Message;
21use Pop\Mail\Message\Text;
22use Pop\Mail\Message\Html;
23use Pop\Mail\Message\Attachment;
24
25/**
26 * Sendgrid API transport class
27 *
28 * @category   Pop
29 * @package    Pop\Mail
30 * @author     Nick Sagona, III <dev@noladev.com>
31 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
32 * @license    https://www.popphp.org/license     New BSD License
33 * @version    5.0.0
34 */
35class Sendgrid extends AbstractHttp implements TransportInterface
36{
37
38    /**
39     * Create the API client
40     *
41     * @param  array|string $options
42     * @throws Exception|Client\Exception|\Pop\Mail\Api\Exception
43     * @return Sendgrid
44     */
45    public function createClient(array|string $options): Sendgrid
46    {
47        if (is_string($options)) {
48            $options = $this->parseOptions($options);
49        }
50
51        if (!isset($options['api_url']) || !isset($options['api_key'])) {
52            throw new Exception('Error: The required client options were not provided.');
53        }
54
55        $request = new Client\Request($options['api_url'], 'POST');
56        $request->createAsJson();
57        $this->client = new Client($request, Auth::createBearer($options['api_key']), new Client\Handler\Stream());
58
59        return $this;
60    }
61
62    /**
63     * Send the message
64     *
65     * @param  Message $message
66     * @return mixed
67     */
68    public function send(Message $message): mixed
69    {
70        $headers         = $message->getHeaders();
71        $parts           = $message->getParts();
72        $personalHeaders = ['From', 'Reply-To', 'Subject', 'To', 'CC', 'BCC'];
73        $fields          = [
74            'personalizations' => [],
75            'content'          => [],
76            'subject'          => $message->getSubject()
77        ];
78
79        $toAddresses    = $message->getTo();
80        $ccAddresses    = $message->getCc();
81        $bccAddresses   = $message->getBcc();
82        $fromAddress    = $message->getFrom();
83        $replyToAddress = $message->getReplyTo();
84
85        $to  = [];
86        $cc  = [];
87        $bcc = [];
88
89        foreach ($toAddresses as $email => $name) {
90            if (!empty($name)) {
91                $to[] = [
92                    'email' => $email,
93                    'name'  => $name
94                ];
95            } else {
96                $to[] = [
97                    'email' => $email
98                ];
99            }
100        }
101
102        foreach ($ccAddresses as $email => $name) {
103            if (!empty($name)) {
104                $cc[] = [
105                    'email' => $email,
106                    'name'  => $name
107                ];
108            } else {
109                $cc[] = [
110                    'email' => $email
111                ];
112            }
113        }
114
115        foreach ($bccAddresses as $email => $name) {
116            if (!empty($name)) {
117                $bcc[] = [
118                    'email' => $email,
119                    'name'  => $name
120                ];
121            } else {
122                $bcc[] = [
123                    'email' => $email
124                ];
125            }
126        }
127
128        if (!empty($to)) {
129            $fields['personalizations'][] = ['to' => $to];
130        }
131        if (!empty($cc)) {
132            $fields['personalizations'][] = ['cc' => $cc];
133        }
134        if (!empty($bcc)) {
135            $fields['personalizations'][] = ['bcc' => $bcc];
136        }
137
138        $fields['from'] = ['email' => array_key_first($fromAddress)];
139
140
141        if (!empty($fromAddress[$fields['from']['email']])) {
142            $fields['from']['name'] = $fromAddress[$fields['from']['email']];
143        }
144
145        if (!empty($replyToAddress)) {
146            $fields['reply_to'] = ['email' => array_key_first($replyToAddress)];
147            if (!empty($replyToAddress[$fields['reply_to']['email']])) {
148                $fields['reply_to']['name'] = $replyToAddress[$fields['reply_to']['email']];
149            }
150        } else {
151            $fields['reply_to'] = $fields['from'];
152        }
153
154        foreach ($headers as $header => $value) {
155            if (!in_array($header, $personalHeaders)) {
156                if (!isset($fields['headers'])) {
157                    $fields['headers'] = [];
158                }
159                $fields['headers'][$header] = $value;
160            }
161        }
162
163        foreach ($parts as $part) {
164            if ($part instanceof Text) {
165                $fields['content'][] = [
166                    'type'  => 'text/plain',
167                    'value' => $part->getContent()
168                ];
169            } else if ($part instanceof Html) {
170                $fields['content'][] = [
171                    'type'  => 'text/html',
172                    'value' => $part->getContent()
173                ];
174            } else if ($part instanceof Attachment) {
175                if (!isset($fields['attachments'])) {
176                    $fields['attachments'] = [];
177                }
178                $contentType = $part->getContentType();
179                if (str_contains($contentType, ';')) {
180                    $contentType = trim(substr($contentType, 0, strpos($contentType, ';')));
181                }
182                $fields['attachments'][] = [
183                    'content'     => base64_encode(file_get_contents($part->getFilename())),
184                    'type'        => $contentType,
185                    'filename'    => $part->getBasename(),
186                    'disposition' => 'attachment'
187                ];
188            }
189        }
190
191        $this->client->setData($fields);
192        return $this->client->send();
193    }
194
195}