Completed
Pull Request — master (#17)
by Chris
01:47
created

PostmarkCourier::buildHeaders()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 1
crap 2
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(
96 1
                    $email,
97 1
                    $content->getHtml() !== null ? $content->getHtml()->getBody() : null,
98 1
                    $content->getText() !== null ? $content->getText()->getBody() : null
99
                );
100 1
                break;
101
102 2
            case $content instanceof Content\EmptyContent:
103 2
                $response = $this->sendNonTemplateEmail($email, 'No message', 'No message');
104 1
                break;
105
106
            default:
107
                // Should never get here
108
                // @codeCoverageIgnoreStart
109
                throw new UnsupportedContentException($content);
110
                // @codeCoverageIgnoreEnd
111
        }
112
113 3
        $this->saveReceipt($email, $response['MessageID']);
114
    }
115
116
    /**
117
     * @param Email $email
118
     *
119
     * @return DynamicResponseModel
120
     */
121 2
    protected function sendTemplateEmail(Email $email): DynamicResponseModel
122
    {
123
        try {
124 2
            return $this->client->sendEmailWithTemplate(
125 2
                $email->getFrom()->toRfc2822(),
126 2
                $this->buildRecipients(...$email->getToRecipients()),
127 2
                (int) $email->getContent()->getTemplateId(),
128 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

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