Issues (2)

src/PostmarkCourier.php (2 issues)

Labels
Severity
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
The method getTemplateId() 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 ignore-call  annotation

120
                (int) $email->getContent()->/** @scrutinizer ignore-call */ getTemplateId(),
Loading history...
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 ignore-call  annotation

244
        $data = $email->getContent()->/** @scrutinizer ignore-call */ getTemplateData();
Loading history...
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