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