1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace Camuthig\Courier\Postmark; |
||||
6 | |||||
7 | use Courier\ConfirmingCourier; |
||||
8 | use Courier\Exceptions\TransmissionException; |
||||
9 | use Courier\Exceptions\UnsupportedContentException; |
||||
10 | use Courier\SavesReceipts; |
||||
11 | use PhpEmail\Address; |
||||
12 | use PhpEmail\Attachment; |
||||
13 | use PhpEmail\Content; |
||||
14 | use PhpEmail\Email; |
||||
15 | use Postmark\Models\DynamicResponseModel; |
||||
16 | use Postmark\Models\PostmarkException; |
||||
17 | use Postmark\PostmarkClient; |
||||
18 | use Psr\Log\LoggerInterface; |
||||
19 | use Psr\Log\NullLogger; |
||||
20 | |||||
21 | class PostmarkCourier implements ConfirmingCourier |
||||
22 | { |
||||
23 | use SavesReceipts; |
||||
24 | |||||
25 | /** |
||||
26 | * @var PostmarkClient |
||||
27 | */ |
||||
28 | private $client; |
||||
29 | |||||
30 | /** |
||||
31 | * @var LoggerInterface |
||||
32 | */ |
||||
33 | private $logger; |
||||
34 | |||||
35 | /** |
||||
36 | * @param PostmarkClient $client |
||||
37 | * @param LoggerInterface|null $logger |
||||
38 | */ |
||||
39 | 7 | public function __construct(PostmarkClient $client, LoggerInterface $logger = null) |
|||
40 | { |
||||
41 | 7 | $this->client = $client; |
|||
42 | 7 | $this->logger = $logger ?: new NullLogger(); |
|||
43 | } |
||||
44 | |||||
45 | /** |
||||
46 | * @return array |
||||
47 | */ |
||||
48 | 7 | protected function supportedContent(): array |
|||
49 | { |
||||
50 | return [ |
||||
51 | 7 | Content\Contracts\SimpleContent::class, |
|||
52 | Content\Contracts\TemplatedContent::class, |
||||
53 | ]; |
||||
54 | } |
||||
55 | |||||
56 | /** |
||||
57 | * Determine if the content is supported by this courier. |
||||
58 | * |
||||
59 | * @param Content $content |
||||
60 | * |
||||
61 | * @return bool |
||||
62 | */ |
||||
63 | 7 | protected function supportsContent(Content $content): bool |
|||
64 | { |
||||
65 | 7 | foreach ($this->supportedContent() as $contentType) { |
|||
66 | 7 | if ($content instanceof $contentType) { |
|||
67 | 7 | return true; |
|||
68 | } |
||||
69 | } |
||||
70 | |||||
71 | 1 | return false; |
|||
72 | } |
||||
73 | |||||
74 | /** |
||||
75 | * @param Email $email |
||||
76 | * |
||||
77 | * @throws TransmissionException |
||||
78 | * @throws UnsupportedContentException |
||||
79 | * |
||||
80 | * @return void |
||||
81 | */ |
||||
82 | 7 | public function deliver(Email $email): void |
|||
83 | { |
||||
84 | 7 | $content = $email->getContent(); |
|||
85 | |||||
86 | 7 | if (!$this->supportsContent($content)) { |
|||
87 | 1 | throw new UnsupportedContentException($content); |
|||
88 | } |
||||
89 | |||||
90 | switch (true) { |
||||
91 | 6 | case $content instanceof Content\TemplatedContent: |
|||
92 | 3 | $response = $this->sendTemplateEmail($email); |
|||
93 | 2 | break; |
|||
94 | |||||
95 | 3 | case $content instanceof Content\SimpleContent: |
|||
96 | 3 | $response = $this->sendNonTemplateEmail($email); |
|||
97 | 2 | break; |
|||
98 | |||||
99 | default: |
||||
100 | // Should never get here |
||||
101 | // @codeCoverageIgnoreStart |
||||
102 | throw new UnsupportedContentException($content); |
||||
103 | // @codeCoverageIgnoreEnd |
||||
104 | } |
||||
105 | |||||
106 | 4 | $this->saveReceipt($email, $response['MessageID']); |
|||
107 | } |
||||
108 | |||||
109 | /** |
||||
110 | * @param Email $email |
||||
111 | * |
||||
112 | * @return DynamicResponseModel |
||||
113 | */ |
||||
114 | 3 | protected function sendTemplateEmail(Email $email): DynamicResponseModel |
|||
115 | { |
||||
116 | try { |
||||
117 | 3 | return $this->client->sendEmailWithTemplate( |
|||
118 | 3 | $email->getFrom()->toRfc2822(), |
|||
119 | 3 | $this->buildRecipients(...$email->getToRecipients()), |
|||
120 | 3 | (int) $email->getContent()->getTemplateId(), |
|||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
121 | 3 | $this->buildTemplateData($email), |
|||
122 | 3 | false, |
|||
123 | 3 | null, |
|||
124 | 3 | true, |
|||
125 | 3 | $this->buildReplyTo($email), |
|||
126 | 3 | $this->buildRecipients(...$email->getCcRecipients()), |
|||
127 | 3 | $this->buildRecipients(...$email->getBccRecipients()), |
|||
128 | 3 | $this->buildHeaders($email), |
|||
129 | 3 | $this->buildAttachments($email), |
|||
130 | 3 | null |
|||
131 | ); |
||||
132 | 1 | } catch (PostmarkException $pe) { |
|||
133 | 1 | $this->logError($pe); |
|||
134 | |||||
135 | 1 | throw new TransmissionException($pe->postmarkApiErrorCode, $pe); |
|||
136 | } |
||||
137 | } |
||||
138 | |||||
139 | /** |
||||
140 | * @param Email $email |
||||
141 | * |
||||
142 | * @return DynamicResponseModel |
||||
143 | */ |
||||
144 | 3 | protected function sendNonTemplateEmail(Email $email): DynamicResponseModel |
|||
145 | { |
||||
146 | 3 | $content = $email->getContent(); |
|||
147 | 3 | $htmlContent = 'No message'; |
|||
148 | 3 | $textContent = 'No message'; |
|||
149 | 3 | if ($content instanceof Content\Contracts\SimpleContent) { |
|||
150 | 3 | $htmlContent = $content->getHtml() !== null ? $content->getHtml()->getBody() : null; |
|||
151 | 3 | $textContent = $content->getText() !== null ? $content->getText()->getBody() : null; |
|||
152 | } |
||||
153 | |||||
154 | try { |
||||
155 | 3 | return $this->client->sendEmail( |
|||
156 | 3 | $email->getFrom()->toRfc2822(), |
|||
157 | 3 | $this->buildRecipients(...$email->getToRecipients()), |
|||
158 | 3 | $email->getSubject(), |
|||
159 | 3 | $htmlContent, |
|||
160 | 3 | $textContent, |
|||
161 | 3 | null, |
|||
162 | 3 | true, |
|||
163 | 3 | $this->buildReplyTo($email), |
|||
164 | 3 | $this->buildRecipients(...$email->getCcRecipients()), |
|||
165 | 3 | $this->buildRecipients(...$email->getBccRecipients()), |
|||
166 | 3 | $this->buildHeaders($email), |
|||
167 | 3 | $this->buildAttachments($email), |
|||
168 | 3 | null |
|||
169 | ); |
||||
170 | 1 | } catch (PostmarkException $pe) { |
|||
171 | 1 | $this->logError($pe); |
|||
172 | |||||
173 | 1 | throw new TransmissionException($pe->postmarkApiErrorCode ?? 500, $pe); |
|||
174 | } |
||||
175 | } |
||||
176 | |||||
177 | 6 | protected function buildReplyTo(Email $email): ?string |
|||
178 | { |
||||
179 | /** @var Address|null $replyTo */ |
||||
180 | 6 | $replyTo = null; |
|||
181 | |||||
182 | 6 | if (!empty($email->getReplyTos())) { |
|||
183 | // The Postmark API only supports one "Reply To" |
||||
184 | 2 | $replyTos = $email->getReplyTos(); |
|||
185 | 2 | $replyTo = reset($replyTos); |
|||
186 | 2 | $replyTo = $replyTo->toRfc2822(); |
|||
187 | } |
||||
188 | |||||
189 | 6 | return $replyTo; |
|||
190 | } |
||||
191 | |||||
192 | /** |
||||
193 | * @param Address[] $addresses |
||||
194 | * |
||||
195 | * @return string |
||||
196 | */ |
||||
197 | protected function buildRecipients(Address ...$addresses): string |
||||
198 | { |
||||
199 | 6 | return implode(',', array_map(function (Address $address) { |
|||
200 | 6 | return $address->toRfc2822(); |
|||
201 | 6 | }, $addresses)); |
|||
202 | } |
||||
203 | |||||
204 | /** |
||||
205 | * @param Email $email |
||||
206 | * |
||||
207 | * @return array |
||||
208 | */ |
||||
209 | protected function buildAttachments(Email $email): array |
||||
210 | { |
||||
211 | 6 | return array_map(function (Attachment $attachment) { |
|||
212 | return [ |
||||
213 | 4 | 'Name' => $attachment->getName(), |
|||
214 | 4 | 'Content' => $attachment->getBase64Content(), |
|||
215 | 4 | 'ContentType' => $attachment->getRfc2822ContentType(), |
|||
216 | 4 | 'ContentID' => $attachment->getContentId(), |
|||
217 | ]; |
||||
218 | 6 | }, array_merge($email->getAttachments(), $email->getEmbedded())); |
|||
219 | } |
||||
220 | |||||
221 | /** |
||||
222 | * @param Email $email |
||||
223 | * |
||||
224 | * @return array |
||||
225 | */ |
||||
226 | 6 | protected function buildHeaders(Email $email): array |
|||
227 | { |
||||
228 | 6 | $headers = []; |
|||
229 | |||||
230 | 6 | foreach ($email->getHeaders() as $header) { |
|||
231 | 4 | $headers[$header->getField()] = $header->getValue(); |
|||
232 | } |
||||
233 | |||||
234 | 6 | return $headers; |
|||
235 | } |
||||
236 | |||||
237 | /** |
||||
238 | * @param Email $email |
||||
239 | * |
||||
240 | * @return array |
||||
241 | */ |
||||
242 | 3 | protected function buildTemplateData(Email $email): array |
|||
243 | { |
||||
244 | 3 | $data = $email->getContent()->getTemplateData(); |
|||
0 ignored issues
–
show
The method
getTemplateData() does not exist on PhpEmail\Content . It seems like you code against a sub-type of PhpEmail\Content such as PhpEmail\Content\Contracts\TemplatedContent .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
245 | |||||
246 | // Add the subject from the email for dynamic replacement |
||||
247 | 3 | $data['subject'] = $email->getSubject(); |
|||
248 | |||||
249 | 3 | return $data; |
|||
250 | } |
||||
251 | |||||
252 | /** |
||||
253 | * @param PostmarkException $pe |
||||
254 | * |
||||
255 | * @return void |
||||
256 | */ |
||||
257 | 2 | protected function logError(PostmarkException $pe): void |
|||
258 | { |
||||
259 | 2 | $this->logger->error( |
|||
260 | 2 | 'Received status {httpCode} and API code {apiCode} from Postmark with message: {message}', |
|||
261 | [ |
||||
262 | 2 | 'httpCode' => $pe->httpStatusCode, |
|||
263 | 2 | 'apiCode' => $pe->postmarkApiErrorCode, |
|||
264 | 2 | 'message' => $pe->message, |
|||
265 | ] |
||||
266 | ); |
||||
267 | } |
||||
268 | } |
||||
269 |