Completed
Push — master ( d32337...505a31 )
by Chris
01:31
created

PostmarkCourier::sendNonTemplateEmail()   B

Complexity

Conditions 5
Paths 10

Size

Total Lines 30
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 30
ccs 24
cts 24
cp 1
rs 8.439
c 0
b 0
f 0
cc 5
eloc 24
nc 10
nop 1
crap 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Courier;
6
7
use Courier\Exceptions\TransmissionException;
8
use Courier\Exceptions\UnsupportedContentException;
9
use PhpEmail\Address;
10
use PhpEmail\Attachment;
11
use PhpEmail\Content;
12
use PhpEmail\Email;
13
use Postmark\Models\DynamicResponseModel;
14
use Postmark\Models\PostmarkException;
15
use Postmark\PostmarkClient;
16
use Psr\Log\LoggerInterface;
17
use Psr\Log\NullLogger;
18
19
class PostmarkCourier implements ConfirmingCourier
20
{
21
    use SavesReceipts;
22
23
    /**
24
     * @var PostmarkClient
25
     */
26
    private $client;
27
28
    /**
29
     * @var LoggerInterface
30
     */
31
    private $logger;
32
33
    /**
34
     * @param PostmarkClient       $client
35
     * @param LoggerInterface|null $logger
36
     */
37 6
    public function __construct(PostmarkClient $client, LoggerInterface $logger = null)
38
    {
39 6
        $this->client = $client;
40 6
        $this->logger = $logger ?: new NullLogger();
41
    }
42
43
    /**
44
     * @return array
45
     */
46 6
    protected function supportedContent(): array
47
    {
48
        return [
49 6
            Content\EmptyContent::class,
50
            Content\Contracts\SimpleContent::class,
51
            Content\Contracts\TemplatedContent::class,
52
        ];
53
    }
54
55
    /**
56
     * Determine if the content is supported by this courier.
57
     *
58
     * @param Content $content
59
     *
60
     * @return bool
61
     */
62 6
    protected function supportsContent(Content $content): bool
63
    {
64 6
        foreach ($this->supportedContent() as $contentType) {
65 6
            if ($content instanceof $contentType) {
66 6
                return true;
67
            }
68
        }
69
70 1
        return false;
71
    }
72
73
    /**
74
     * @param Email $email
75
     *
76
     * @throws TransmissionException
77
     * @throws UnsupportedContentException
78
     *
79
     * @return void
80
     */
81 6
    public function deliver(Email $email): void
82
    {
83 6
        $content = $email->getContent();
84
85 6
        if (!$this->supportsContent($content)) {
86 1
            throw new UnsupportedContentException($content);
87
        }
88
89
        switch (true) {
90 5
            case $content instanceof Content\TemplatedContent:
91 2
                $response = $this->sendTemplateEmail($email);
92 1
                break;
93
94 3
            case $content instanceof Content\SimpleContent:
95 1
                $response = $this->sendNonTemplateEmail($email);
96 1
                break;
97
98 2
            case $content instanceof Content\EmptyContent:
99 2
                $response = $this->sendNonTemplateEmail($email);
100 1
                break;
101
102
            default:
103
                // Should never get here
104
                // @codeCoverageIgnoreStart
105
                throw new UnsupportedContentException($content);
106
                // @codeCoverageIgnoreEnd
107
        }
108
109 3
        $this->saveReceipt($email, $response['MessageID']);
110
    }
111
112
    /**
113
     * @param Email $email
114
     *
115
     * @return DynamicResponseModel
116
     */
117 2
    protected function sendTemplateEmail(Email $email): DynamicResponseModel
118
    {
119
        try {
120 2
            return $this->client->sendEmailWithTemplate(
121 2
                $email->getFrom()->toRfc2822(),
122 2
                $this->buildRecipients(...$email->getToRecipients()),
123 2
                (int) $email->getContent()->getTemplateId(),
124 2
                $this->buildTemplateData($email),
0 ignored issues
show
Bug introduced by
$this->buildTemplateData($email) of type array is incompatible with the type object expected by parameter $templateModel of Postmark\PostmarkClient::sendEmailWithTemplate(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

124
                /** @scrutinizer ignore-type */ $this->buildTemplateData($email),
Loading history...
125 2
                false,
126 2
                null,
127 2
                true,
128 2
                $this->buildReplyTo($email),
129 2
                $this->buildRecipients(...$email->getCcRecipients()),
130 2
                $this->buildRecipients(...$email->getBccRecipients()),
131 2
                $this->buildHeaders($email),
132 2
                $this->buildAttachments($email),
133 2
                null
134
            );
135 1
        } catch (PostmarkException $pe) {
136 1
            $this->logError($pe);
137
138 1
            throw new TransmissionException($pe->postmarkApiErrorCode, $pe);
139
        }
140
    }
141
142
    /**
143
     * @param Email $email
144
     *
145
     * @return DynamicResponseModel
146
     */
147 3
    protected function sendNonTemplateEmail(Email $email): DynamicResponseModel
148
    {
149 3
        $content     = $email->getContent();
150 3
        $htmlContent = 'No message';
151 3
        $textContent = 'No message';
152 3
        if ($content instanceof Content\Contracts\SimpleContent) {
153 1
            $htmlContent = $content->getHtml() !== null ? $content->getHtml()->getBody() : null;
154 1
            $textContent = $content->getText() !== null ? $content->getText()->getBody() : null;
155
        }
156
157
        try {
158 3
            return $this->client->sendEmail(
159 3
                $email->getFrom()->toRfc2822(),
160 3
                $this->buildRecipients(...$email->getToRecipients()),
161 3
                $email->getSubject(),
162 3
                $htmlContent,
163 3
                $textContent,
164 3
                null,
165 3
                true,
166 3
                $this->buildReplyTo($email),
167 3
                $this->buildRecipients(...$email->getCcRecipients()),
168 3
                $this->buildRecipients(...$email->getBccRecipients()),
169 3
                $this->buildHeaders($email),
170 3
                $this->buildAttachments($email),
171 3
                null
172
            );
173 1
        } catch (PostmarkException $pe) {
174 1
            $this->logError($pe);
175
176 1
            throw new TransmissionException($pe->postmarkApiErrorCode, $pe);
177
        }
178
    }
179
180 5
    protected function buildReplyTo(Email $email): ?string
181
    {
182
        /** @var Address|null $replyTo */
183 5
        $replyTo = null;
184
185 5
        if (!empty($email->getReplyTos())) {
186
            // The Postmark API only supports one "Reply To"
187 2
            $replyTos = $email->getReplyTos();
188 2
            $replyTo  = reset($replyTos);
189 2
            $replyTo  = $replyTo->toRfc2822();
190
        }
191
192 5
        return $replyTo;
193
    }
194
195
    /**
196
     * @param Address[] $addresses
197
     *
198
     * @return string
199
     */
200
    protected function buildRecipients(Address ...$addresses): string
201
    {
202 5
        return implode(',', array_map(function (Address $address) {
203 5
            return $address->toRfc2822();
204 5
        }, $addresses));
205
    }
206
207
    /**
208
     * @param Email $email
209
     *
210
     * @return array
211
     */
212
    protected function buildAttachments(Email $email): array
213
    {
214 5
        return array_map(function (Attachment $attachment) {
215
            return [
216 2
                'Name'        => $attachment->getName(),
217 2
                'Content'     => $attachment->getBase64Content(),
218 2
                'ContentType' => $attachment->getContentType(),
219
            ];
220 5
        }, $email->getAttachments());
221
    }
222
223
    /**
224
     * @param Email $email
225
     *
226
     * @return array
227
     */
228 5
    protected function buildHeaders(Email $email): array
229
    {
230 5
        $headers = [];
231
232 5
        foreach ($email->getHeaders() as $header) {
233 2
            $headers[$header->getField()] = $header->getValue();
234
        }
235
236 5
        return $headers;
237
    }
238
239
    /**
240
     * @param Email $email
241
     *
242
     * @return array
243
     */
244 2
    protected function buildTemplateData(Email $email): array
245
    {
246 2
        $data = $email->getContent()->getTemplateData();
247
248
        // Add the subject from the email for dynamic replacement
249 2
        $data['subject'] = $email->getSubject();
250
251 2
        return $data;
252
    }
253
254
    /**
255
     * @param PostmarkException $pe
256
     *
257
     * @return void
258
     */
259 2
    protected function logError(PostmarkException $pe): void
260
    {
261 2
        $this->logger->error(
262 2
            'Received status {httpCode} and API code {apiCode} from Postmark with message: {message}',
263
            [
264 2
                'httpCode' => $pe->httpStatusCode,
265 2
                'apiCode'  => $pe->postmarkApiErrorCode,
266 2
                'message'  => $pe->message,
267
            ]
268
        );
269
    }
270
}
271