1
|
|
|
<?php |
2
|
|
|
namespace Sichikawa\LaravelSendgridDriver\Transport; |
3
|
|
|
|
4
|
|
|
use GuzzleHttp\Client; |
5
|
|
|
use GuzzleHttp\ClientInterface; |
6
|
|
|
use GuzzleHttp\Psr7\Response; |
7
|
|
|
use Illuminate\Mail\Transport\Transport; |
8
|
|
|
use Swift_Attachment; |
9
|
|
|
use Swift_Image; |
10
|
|
|
use Swift_Mime_Message; |
11
|
|
|
use Swift_MimePart; |
12
|
|
|
|
13
|
|
|
class SendgridV3Transport extends Transport |
14
|
|
|
{ |
15
|
|
|
const MAXIMUM_FILE_SIZE = 7340032; |
16
|
|
|
const SMTP_API_NAME = 'sendgrid/x-smtpapi'; |
17
|
|
|
const BASE_URL = 'https://api.sendgrid.com/v3/mail/send'; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @var Client |
21
|
|
|
*/ |
22
|
|
|
private $client; |
23
|
|
|
private $options; |
24
|
|
|
private $attachments; |
25
|
|
|
private $numberOfRecipients; |
26
|
|
|
|
27
|
|
View Code Duplication |
public function __construct(ClientInterface $client, $api_key) |
|
|
|
|
28
|
|
|
{ |
29
|
|
|
$this->client = $client; |
|
|
|
|
30
|
|
|
$this->options = [ |
31
|
|
|
'headers' => [ |
32
|
|
|
'Authorization' => 'Bearer ' . $api_key, |
33
|
|
|
'Content-Type' => 'application/json', |
34
|
|
|
], |
35
|
|
|
]; |
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* {@inheritdoc} |
40
|
|
|
*/ |
41
|
|
|
public function send(Swift_Mime_Message $message, &$failedRecipients = null) |
42
|
|
|
{ |
43
|
|
|
$this->beforeSendPerformed($message); |
44
|
|
|
|
45
|
|
|
$payload = $this->options; |
46
|
|
|
|
47
|
|
|
$data = [ |
48
|
|
|
'personalizations' => $this->getPersonalizations($message), |
49
|
|
|
'from' => $this->getFrom($message), |
50
|
|
|
'subject' => $message->getSubject(), |
51
|
|
|
'content' => $this->getContents($message), |
52
|
|
|
]; |
53
|
|
|
|
54
|
|
|
if ($reply_to = $this->getReplyTo($message)) { |
55
|
|
|
$data['reply_to'] = $reply_to; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
$attachments = $this->getAttachments($message); |
59
|
|
|
if (count($attachments) > 0) { |
60
|
|
|
$data['attachments'] = $attachments; |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
$data = $this->setParameters($message, $data); |
64
|
|
|
|
65
|
|
|
$payload['json'] = $data; |
66
|
|
|
|
67
|
|
|
$response = $this->post($payload); |
68
|
|
|
|
69
|
|
|
$message->getHeaders()->addTextHeader('X-Message-Id', $response->getHeaderLine('X-Message-Id')); |
70
|
|
|
|
71
|
|
|
$this->sendPerformed($message); |
72
|
|
|
|
73
|
|
|
return $this->numberOfRecipients ?: $this->numberOfRecipients($message); |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* @param Swift_Mime_Message $message |
78
|
|
|
* @return array |
79
|
|
|
*/ |
80
|
|
|
private function getPersonalizations(Swift_Mime_Message $message) |
81
|
|
|
{ |
82
|
|
|
$setter = function (array $addresses) { |
83
|
|
|
$recipients = []; |
84
|
|
|
foreach ($addresses as $email => $name) { |
85
|
|
|
$address = []; |
86
|
|
|
$address['email'] = $email; |
87
|
|
|
if ($name) { |
88
|
|
|
$address['name'] = $name; |
89
|
|
|
} |
90
|
|
|
$recipients[] = $address; |
91
|
|
|
} |
92
|
|
|
return $recipients; |
93
|
|
|
}; |
94
|
|
|
|
95
|
|
|
$personalization['to'] = $setter($message->getTo()); |
|
|
|
|
96
|
|
|
|
97
|
|
|
if ($cc = $message->getCc()) { |
98
|
|
|
$personalization['cc'] = $setter($cc); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
if ($bcc = $message->getBcc()) { |
102
|
|
|
$personalization['bcc'] = $setter($bcc); |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
return [$personalization]; |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Get From Addresses. |
110
|
|
|
* |
111
|
|
|
* @param Swift_Mime_Message $message |
112
|
|
|
* @return array |
113
|
|
|
*/ |
114
|
|
|
private function getFrom(Swift_Mime_Message $message) |
115
|
|
|
{ |
116
|
|
|
if ($message->getFrom()) { |
117
|
|
|
foreach ($message->getFrom() as $email => $name) { |
118
|
|
|
return ['email' => $email, 'name' => $name]; |
119
|
|
|
} |
120
|
|
|
} |
121
|
|
|
return []; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Get ReplyTo Addresses. |
126
|
|
|
* |
127
|
|
|
* @param Swift_Mime_Message $message |
128
|
|
|
* @return array |
129
|
|
|
*/ |
130
|
|
|
private function getReplyTo(Swift_Mime_Message $message) |
131
|
|
|
{ |
132
|
|
|
if ($message->getReplyTo()) { |
133
|
|
|
foreach ($message->getReplyTo() as $email => $name) { |
134
|
|
|
return ['email' => $email, 'name' => $name]; |
135
|
|
|
} |
136
|
|
|
} |
137
|
|
|
return null; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Get contents. |
142
|
|
|
* |
143
|
|
|
* @param Swift_Mime_Message $message |
144
|
|
|
* @return array |
145
|
|
|
*/ |
146
|
|
|
private function getContents(Swift_Mime_Message $message) |
147
|
|
|
{ |
148
|
|
|
$content = []; |
149
|
|
|
foreach ($message->getChildren() as $attachment) { |
150
|
|
|
if ($attachment instanceof Swift_MimePart) { |
151
|
|
|
$content[] = [ |
152
|
|
|
'type' => 'text/plain', |
153
|
|
|
'value' => $attachment->getBody(), |
154
|
|
|
]; |
155
|
|
|
break; |
156
|
|
|
} |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
if (empty($content) || strpos($message->getContentType(), 'multipart') !== false) { |
160
|
|
|
$content[] = [ |
161
|
|
|
'type' => 'text/html', |
162
|
|
|
'value' => $message->getBody(), |
163
|
|
|
]; |
164
|
|
|
} |
165
|
|
|
return $content; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* @param Swift_Mime_Message $message |
170
|
|
|
* @return array |
171
|
|
|
*/ |
172
|
|
|
private function getAttachments(Swift_Mime_Message $message) |
173
|
|
|
{ |
174
|
|
|
$attachments = []; |
175
|
|
|
foreach ($message->getChildren() as $attachment) { |
176
|
|
|
if ((!$attachment instanceof Swift_Attachment && !$attachment instanceof Swift_Image) |
177
|
|
|
|| $attachment->getFilename() === self::SMTP_API_NAME |
178
|
|
|
|| !strlen($attachment->getBody()) > self::MAXIMUM_FILE_SIZE |
179
|
|
|
) { |
180
|
|
|
continue; |
181
|
|
|
} |
182
|
|
|
$attachments[] = [ |
183
|
|
|
'content' => base64_encode($attachment->getBody()), |
184
|
|
|
'filename' => $attachment->getFilename(), |
185
|
|
|
'type' => $attachment->getContentType(), |
186
|
|
|
'disposition' => $attachment->getDisposition(), |
187
|
|
|
'content_id' => $attachment->getId(), |
188
|
|
|
]; |
189
|
|
|
} |
190
|
|
|
return $this->attachments = $attachments; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* Set Request Body Parameters |
195
|
|
|
* |
196
|
|
|
* @param Swift_Mime_Message $message |
197
|
|
|
* @param array $data |
198
|
|
|
* @return array |
199
|
|
|
* @throws \Exception |
200
|
|
|
*/ |
201
|
|
|
protected function setParameters(Swift_Mime_Message $message, $data) |
202
|
|
|
{ |
203
|
|
|
$this->numberOfRecipients = 0; |
204
|
|
|
|
205
|
|
|
$smtp_api = []; |
206
|
|
View Code Duplication |
foreach ($message->getChildren() as $attachment) { |
|
|
|
|
207
|
|
|
if (!$attachment instanceof Swift_Image |
208
|
|
|
|| !in_array(self::SMTP_API_NAME, [$attachment->getFilename(), $attachment->getContentType()]) |
209
|
|
|
) { |
210
|
|
|
continue; |
211
|
|
|
} |
212
|
|
|
$smtp_api = $attachment->getBody(); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
if (!is_array($smtp_api)) { |
216
|
|
|
return $data; |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
foreach ($smtp_api as $key => $val) { |
220
|
|
|
|
221
|
|
|
switch ($key) { |
222
|
|
|
|
223
|
|
|
case 'personalizations': |
224
|
|
|
$this->setPersonalizations($data, $val); |
225
|
|
|
continue 2; |
226
|
|
|
|
227
|
|
|
case 'attachments': |
228
|
|
|
$val = array_merge($this->attachments, $val); |
229
|
|
|
break; |
230
|
|
|
|
231
|
|
|
case 'unique_args': |
232
|
|
|
throw new \Exception('Sendgrid v3 now uses custom_args instead of unique_args'); |
233
|
|
|
|
234
|
|
|
case 'custom_args': |
235
|
|
|
foreach ($val as $name => $value) { |
236
|
|
|
if (!is_string($value)) { |
237
|
|
|
throw new \Exception('Sendgrid v3 custom arguments have to be a string.'); |
238
|
|
|
} |
239
|
|
|
} |
240
|
|
|
break; |
241
|
|
|
|
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
array_set($data, $key, $val); |
245
|
|
|
} |
246
|
|
|
return $data; |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
private function setPersonalizations(&$data, $personalizations) |
250
|
|
|
{ |
251
|
|
|
foreach ($personalizations as $index => $params) { |
252
|
|
|
foreach ($params as $key => $val) { |
253
|
|
|
if (in_array($key, ['to', 'cc', 'bcc'])) { |
254
|
|
|
array_set($data, 'personalizations.' . $index . '.' . $key, [$val]); |
255
|
|
|
++$this->numberOfRecipients; |
256
|
|
|
} else { |
257
|
|
|
array_set($data, 'personalizations.' . $index . '.' . $key, $val); |
258
|
|
|
} |
259
|
|
|
} |
260
|
|
|
} |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* @param $payload |
265
|
|
|
* @return Response |
266
|
|
|
*/ |
267
|
|
|
private function post($payload) |
268
|
|
|
{ |
269
|
|
|
return $this->client->post('https://api.sendgrid.com/v3/mail/send', $payload); |
270
|
|
|
} |
271
|
|
|
} |
272
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.