Passed
Push — master ( 8a2723...59c734 )
by Gabriel
01:51
created

SendgridTransport::sendApiCall()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
eloc 13
c 0
b 0
f 0
dl 0
loc 18
ccs 0
cts 12
cp 0
rs 9.8333
cc 3
nc 3
nop 0
crap 12
1
<?php
2
3
namespace Nip\Mail\Transport;
4
5
use Exception;
6
use Html2Text\Html2Text;
7
use Nip\Mail\Message;
8
use ReflectionClass;
9
use ReflectionException;
10
use SendGrid;
11
use SendGrid\Mail\Attachment;
12
use SendGrid\Mail\Content;
13
use SendGrid\Mail\Mail;
14
use SendGrid\Mail\Personalization;
15
use SendGrid\Mail\To;
16
use Swift_Attachment;
17
use Swift_Image;
18
use Swift_Mime_SimpleMessage as SwiftSimpleMessage;
19
use Swift_MimePart;
20
use Swift_TransportException;
21
22
/**
23
 * Class SendgridTransport
24
 * @package Nip\Mail\Transport
25
 */
26
class SendgridTransport extends AbstractTransport
27
{
28
    /** @var string|null */
29
    protected $apiKey;
30
31
    /**
32
     * @var null|Mail|SwiftSimpleMessage
33
     */
34
    protected $mail = null;
35
36
    /**
37
     * @return null|string
38
     */
39
    public function getApiKey()
40
    {
41
        return $this->apiKey;
42
    }
43
44
    /**
45
     * {@inheritdoc}
46
     * @throws Exception
47
     */
48 1
    public function send(SwiftSimpleMessage $message, &$failedRecipients = null)
49
    {
50 1
        $this->initMail();
51
52 1
        $this->populateSenders($message);
53 1
        $this->populatePersonalization($message);
54 1
        $this->populateContent($message);
55 1
        $this->populateCustomArg($message);
56
57 1
        return $this->sendApiCall();
58
    }
59
60 1
    public function initMail()
61
    {
62 1
        $this->setMail(new Mail());
63 1
    }
64
65
    /**
66
     * @param Message|SwiftSimpleMessage $message
67
     */
68 1
    protected function populateSenders($message)
69
    {
70 1
        $from = $message->getFrom();
71 1
        foreach ($from as $address => $name) {
72 1
            $this->getMail()->setFrom($address, $name);
73 1
            $this->getMail()->setReplyTo($address, $name);
74
        }
75 1
    }
76
77
    /**
78
     * @return null|Mail
79
     */
80 1
    public function getMail()
81
    {
82 1
        return $this->mail;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->mail also could return the type Swift_Mime_SimpleMessage which is incompatible with the documented return type SendGrid\Mail\Mail|null.
Loading history...
83
    }
84
85
    /**
86
     * @param null|Mail $mail
87
     */
88 1
    public function setMail($mail)
89
    {
90 1
        $this->mail = $mail;
91 1
    }
92
93
    /**
94
     * @param Message|SwiftSimpleMessage $message
95
     * @throws Exception
96
     */
97 1
    protected function populatePersonalization($message)
98
    {
99 1
        $emailsTos = $message->getTo();
100 1
        if (!is_array($emailsTos) or count($emailsTos) < 1) {
0 ignored issues
show
introduced by
The condition is_array($emailsTos) is always true.
Loading history...
101
            throw new Exception('Cannot send email withought reciepients');
102
        }
103 1
        $personalizationIndex = 0;
104 1
        foreach ($emailsTos as $emailTo => $nameTo) {
105 1
            $personalization = $this->generatePersonalization($emailTo, $nameTo, $message, $personalizationIndex);
106 1
            $this->getMail()->addPersonalization($personalization);
107 1
            $personalizationIndex++;
108
        }
109 1
    }
110
111
    /**
112
     * @param $emailTo
113
     * @param $nameTo
114
     * @param Message $message
115
     * @param integer $i
116
     * @return Personalization
117
     * @throws SendGrid\Mail\TypeException
118
     */
119 1
    protected function generatePersonalization($emailTo, $nameTo, $message, $i)
120
    {
121 1
        $personalization = new Personalization();
122
123 1
        $email = new To($emailTo, $nameTo);
124 1
        $personalization->addTo($email);
125
126 1
        $personalization->setSubject($message->getSubject());
127
128 1
        $mergeTags = $message->getMergeTags();
129 1
        foreach ($mergeTags as $varKey => $value) {
130
            if (is_array($value)) {
131
                $value = $value[$i];
132
            }
133
            $value = (string) $value;
134
            $personalization->addSubstitution('{{' . $varKey . '}}', $value);
135
        }
136
137 1
        return $personalization;
138
    }
139
140
    /**
141
     * @param Message|SwiftSimpleMessage $message
142
     * @throws SendGrid\Mail\TypeException
143
     * @throws ReflectionException
144
     */
145 1
    protected function populateContent($message)
146
    {
147 1
        $contentType = $this->getMessagePrimaryContentType($message);
148
149 1
        $bodyHtml = $bodyText = null;
150
151 1
        if ($contentType === 'text/plain') {
152 1
            $bodyText = $message->getBody();
153
        } else {
154
            $bodyHtml = $message->getBody();
155
            $bodyText = (new Html2Text($bodyHtml))->getText();
156
        }
157
158 1
        foreach ($message->getChildren() as $child) {
159
            if ($child instanceof Swift_Image) {
160
                $images[] = [
161
                    'type' => $child->getContentType(),
162
                    'name' => $child->getId(),
163
                    'content' => base64_encode($child->getBody()),
164
                ];
165
            } elseif ($child instanceof Swift_Attachment && !($child instanceof Swift_Image)) {
166
                $this->addAttachment($child);
167
            } elseif ($child instanceof Swift_MimePart && $this->supportsContentType($child->getContentType())) {
168
                if ($child->getContentType() == "text/html") {
169
                    $bodyHtml = $child->getBody();
170
                } elseif ($child->getContentType() == "text/plain") {
171
                    $bodyText = $child->getBody();
172
                }
173
            }
174
        }
175
176 1
        $content = new Content("text/plain", $bodyText);
177 1
        $this->getMail()->addContent($content);
178
179 1
        $content = new Content("text/html", $bodyHtml);
180 1
        $this->getMail()->addContent($content);
181 1
    }
182
183
    /**
184
     * @param SwiftSimpleMessage $message
185
     * @return string
186
     * @throws ReflectionException
187
     */
188 1
    protected function getMessagePrimaryContentType(SwiftSimpleMessage $message)
189
    {
190 1
        $contentType = $message->getContentType();
191 1
        if ($this->supportsContentType($contentType)) {
192 1
            return $contentType;
193
        }
194
        // SwiftMailer hides the content type set in the constructor of Swift_Mime_Message as soon
195
        // as you add another part to the message. We need to access the protected property
196
        // _userContentType to get the original type.
197
        $messageRef = new ReflectionClass($message);
198
        if ($messageRef->hasProperty('_userContentType')) {
199
            $propRef = $messageRef->getProperty('_userContentType');
200
            $propRef->setAccessible(true);
201
            $contentType = $propRef->getValue($message);
202
        }
203
204
        return $contentType;
205
    }
206
207
    /**
208
     * @param string $contentType
209
     * @return bool
210
     */
211 1
    protected function supportsContentType($contentType)
212
    {
213 1
        return in_array($contentType, $this->getSupportedContentTypes());
214
    }
215
216
    /**
217
     * @return string[]
218
     */
219 1
    protected function getSupportedContentTypes()
220
    {
221
        return [
222 1
            'text/plain',
223
            'text/html',
224
        ];
225
    }
226
227
    /**
228
     * @param Swift_Attachment $attachment
229
     * @throws SendGrid\Mail\TypeException
230
     */
231
    protected function addAttachment($attachment)
232
    {
233
        $sgAttachment = new Attachment();
234
        $sgAttachment->setContent(base64_encode($attachment->getBody()));
235
        $sgAttachment->setType($attachment->getContentType());
236
        $sgAttachment->setFilename($attachment->getFilename());
237
        $sgAttachment->setDisposition("attachment");
238
        $sgAttachment->setContentID($attachment->getId());
239
        $this->getMail()->addAttachment($sgAttachment);
240
    }
241
242
    /**
243
     * @param Message|SwiftSimpleMessage $message
244
     * @throws SendGrid\Mail\TypeException
245
     */
246 1
    protected function populateCustomArg($message)
247
    {
248 1
        $args = $message->getCustomArgs();
0 ignored issues
show
Bug introduced by
The method getCustomArgs() does not exist on Swift_Mime_SimpleMessage. It seems like you code against a sub-type of Swift_Mime_SimpleMessage such as Nip\Mail\Message. ( Ignorable by Annotation )

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

248
        /** @scrutinizer ignore-call */ 
249
        $args = $message->getCustomArgs();
Loading history...
249 1
        foreach ($args as $key => $value) {
250
            if ($key == 'category') {
251
                $this->getMail()->addCategory($value);
252
            } else {
253
                $this->getMail()->addCustomArg($key, (string) $value);
254
            }
255
        }
256 1
    }
257
258
    /**
259
     * @return int
260
     * @throws Swift_TransportException
261
     */
262
    protected function sendApiCall()
263
    {
264
        $sendGrid = $this->createApi();
265
        try {
266
            $response = $sendGrid->send($this->getMail());
267
        } catch (Exception $exception) {
268
            throw new Swift_TransportException(
269
                'Error sending email Code [' . $exception->getMessage() . ']'
270
            );
271
        }
272
273
        if ($response->statusCode() == '202') {
274
            return 1;
275
        } else {
276
            throw new Swift_TransportException(
277
                'Error sending email Code [' . $response->statusCode() . ']. '
278
                . 'HEADERS [' . print_r($response->headers())
279
                . $response->body()
280
            );
281
        }
282
    }
283
284
    /**
285
     * @return SendGrid
286
     * @throws Swift_TransportException
287
     */
288
    protected function createApi()
289
    {
290
        if ($this->getApiKey() === null) {
291
            throw new Swift_TransportException('Cannot create instance of \SendGrid while API key is NULL');
292
        }
293
294
        return new SendGrid($this->getApiKey());
295
    }
296
297
    /**
298
     * @param string $apiKey
299
     * @return $this
300
     */
301 1
    public function setApiKey($apiKey)
302
    {
303 1
        $this->apiKey = $apiKey;
304
305 1
        return $this;
306
    }
307
}
308