SendgridRestTransport::generatePersonalization()   A
last analyzed

Complexity

Conditions 4
Paths 6

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 4
eloc 15
c 2
b 1
f 0
nc 6
nop 3
dl 0
loc 25
rs 9.7666
1
<?php
2
declare(strict_types=1);
3
4
namespace Nip\Mail\Transport;
5
6
use Exception;
7
use Html2Text\Html2Text;
8
use Nip\Mail\Message;
9
use Psr\EventDispatcher\EventDispatcherInterface;
10
use Psr\Log\LoggerInterface;
11
use SendGrid;
12
use SendGrid\Mail\Attachment;
13
use SendGrid\Mail\Content;
14
use SendGrid\Mail\Mail;
15
use SendGrid\Mail\Personalization;
16
use SendGrid\Mail\To;
17
use Symfony\Component\Mailer\Exception\TransportException;
18
use Symfony\Component\Mailer\SentMessage;
19
use Symfony\Component\Mailer\Transport\AbstractTransport;
20
use Symfony\Component\Mime\Address;
21
use Symfony\Component\Mime\Part\DataPart;
22
23
/**
24
 * Class SendgridTransport
25
 * @package Nip\Mail\Transport
26
 */
27
class SendgridRestTransport extends AbstractTransport
28
{
29
    /** @var string|null */
30
    protected ?string $apiKey;
31
32
    public function __construct(EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null)
33
    {
34
        parent::__construct($dispatcher, $logger);
35
    }
36
37
    /**
38
     * @return null|string
39
     */
40
    public function getApiKey(): ?string
41
    {
42
        return $this->apiKey;
43
    }
44
45
    /**
46
     * @throws SendGrid\Mail\TypeException
47
     */
48
    protected function doSend(SentMessage $message): void
49
    {
50
        $mail = new Mail();
51
52
        $message = $message->getOriginalMessage();
53
54
        /** @var Message $message */
55
        $this->populateSenders($message, $mail);
56
        $this->populatePersonalization($message, $mail);
57
        $this->populateContent($message, $mail);
58
        $this->populateCustomArg($message, $mail);
59
60
        $this->sendApiCall($mail);
61
    }
62
63
64
    /**
65
     * @param \Symfony\Component\Mime\Message $message
66
     * @param Mail $mail
67
     * @throws SendGrid\Mail\TypeException
68
     */
69
    protected function populateSenders(\Symfony\Component\Mime\Message $message, Mail $mail)
70
    {
71
        $addresses = $message->getFrom();
0 ignored issues
show
introduced by
The method getFrom() does not exist on Symfony\Component\Mime\Message. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

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

71
        /** @scrutinizer ignore-call */ 
72
        $addresses = $message->getFrom();
Loading history...
72
        foreach ($addresses as $address) {
73
            $mail->setFrom($address->getAddress(), $address->getName());
74
            $mail->setReplyTo($address->getAddress(), $address->getName());
75
        }
76
77
        $reply = $message->getReplyTo();
0 ignored issues
show
introduced by
The method getReplyTo() does not exist on Symfony\Component\Mime\Message. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

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

77
        /** @scrutinizer ignore-call */ 
78
        $reply = $message->getReplyTo();
Loading history...
78
        foreach ($reply as $address) {
79
            $mail->setReplyTo($address->getAddress(), $address->getName());
80
        }
81
82
83
    }
84
85
    /**
86
     * @param Message $message
87
     * @param Mail $mail
88
     * @throws SendGrid\Mail\TypeException
89
     */
90
    protected function populatePersonalization(Message $message, Mail $mail)
91
    {
92
        $emailsTos = $message->getTo();
93
        $personalizationIndex = 0;
94
        foreach ($emailsTos as $emailTo) {
95
            $personalization = $this->generatePersonalization($emailTo, $message, $personalizationIndex);
96
            $mail->addPersonalization($personalization);
97
            $personalizationIndex++;
98
        }
99
    }
100
101
    /**
102
     * @param Address $emailTo
103
     * @param Message $message
104
     * @param integer $i
105
     * @return Personalization
106
     * @throws SendGrid\Mail\TypeException
107
     */
108
    protected function generatePersonalization(Address $emailTo, Message $message, $i): Personalization
109
    {
110
        $personalization = new Personalization();
111
112
        $email = new To($emailTo->getAddress(), $emailTo->getName());
113
        $personalization->addTo($email);
114
115
        $bcc = $message->getBcc();
116
        foreach ($bcc as $address) {
117
            $email = new SendGrid\Mail\Bcc($address->getAddress(), $address->getName());
118
            $personalization->addBcc($email);
119
        }
120
121
        $personalization->setSubject($message->getSubject());
122
123
        $mergeTags = $message->getMergeTags();
124
        foreach ($mergeTags as $varKey => $value) {
125
            if (is_array($value)) {
126
                $value = $value[$i];
127
            }
128
            $value = (string)$value;
129
            $personalization->addSubstitution('{{' . $varKey . '}}', $value);
130
        }
131
132
        return $personalization;
133
    }
134
135
    /**
136
     * @param Message $message
137
     * @param Mail $mail
138
     * @throws SendGrid\Mail\TypeException
139
     */
140
    protected function populateContent(Message $message, Mail $mail)
141
    {
142
        foreach ($message->getAttachments() as $attachment) {
143
            $this->addAttachment($attachment, $mail);
144
        }
145
        $bodyHtml = $message->getHtmlBody();
146
        $bodyText = $message->getTextBody();
147
148
        $bodyText = $bodyText ?? (new Html2Text($bodyHtml))->getText();
149
150
        $content = new Content("text/plain", $bodyText);
0 ignored issues
show
Bug introduced by
It seems like $bodyText can also be of type resource; however, parameter $value of SendGrid\Mail\Content::__construct() does only seem to accept null|string, maybe add an additional type check? ( Ignorable by Annotation )

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

150
        $content = new Content("text/plain", /** @scrutinizer ignore-type */ $bodyText);
Loading history...
151
        $mail->addContent($content);
152
153
        $content = new Content("text/html", $bodyHtml);
154
        $mail->addContent($content);
155
    }
156
157
    /**
158
     * @param DataPart $attachment
159
     * @param Mail $mail
160
     * @throws SendGrid\Mail\TypeException
161
     */
162
    protected function addAttachment(DataPart $attachment, $mail)
163
    {
164
        $headers = $attachment->getPreparedHeaders();
165
        $filename = $headers->getHeaderParameter('Content-Disposition', 'filename');
166
        $disposition = $headers->getHeaderBody('Content-Disposition');
167
168
        $sgAttachment = new Attachment();
169
        $sgAttachment->setContent(str_replace("\r\n", '', $attachment->bodyToString()));
170
        $sgAttachment->setType($headers->get('Content-Type')->getBody());
171
        $sgAttachment->setFilename($filename);
172
        $sgAttachment->setDisposition($disposition);
173
174
        if ('inline' === $disposition) {
175
            $sgAttachment->setContentID($filename);
176
        }
177
178
        $mail->addAttachment($sgAttachment);
179
    }
180
181
    /**
182
     * @param Message $message
183
     * @param $mail
184
     */
185
    protected function populateCustomArg(Message $message, $mail)
186
    {
187
        $args = $message->getCustomArgs();
188
        foreach ($args as $key => $value) {
189
            if ($key == 'category') {
190
                $mail->addCategory($value);
191
            } else {
192
                $mail->addCustomArg($key, (string)$value);
193
            }
194
        }
195
    }
196
197
    /**
198
     * @return bool
199
     * @throws TransportException
200
     */
201
    protected function sendApiCall($mail): bool
202
    {
203
        $sendGrid = $this->createApi();
204
        try {
205
            $response = $sendGrid->send($mail);
206
        } catch (Exception $exception) {
207
            throw new TransportException(
208
                'Error sending email Code [' . $exception->getMessage() . ']'
209
            );
210
        }
211
212
        if ($response->statusCode() == '202') {
213
            return true;
214
        } else {
215
            throw new TransportException(
216
                'Error sending email Code [' . $response->statusCode() . ']. '
217
                . 'HEADERS [' . print_r($response->headers())
0 ignored issues
show
Bug introduced by
Are you sure print_r($response->headers()) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

217
                . 'HEADERS [' . /** @scrutinizer ignore-type */ print_r($response->headers())
Loading history...
218
                . $response->body()
219
            );
220
        }
221
    }
222
223
    /**
224
     * @return SendGrid
225
     * @throws TransportException
226
     */
227
    protected function createApi(): SendGrid
228
    {
229
        if ($this->getApiKey() === null) {
230
            throw new TransportException('Cannot create instance of \SendGrid while API key is NULL');
231
        }
232
233
        return new SendGrid($this->getApiKey());
234
    }
235
236
    /**
237
     * @param string $apiKey
238
     * @return $this
239
     */
240
    public function setApiKey($apiKey): self
241
    {
242
        $this->apiKey = $apiKey;
243
244
        return $this;
245
    }
246
247
    public function __toString(): string
248
    {
249
        return 'sendgrid';
250
    }
251
}
252