Passed
Push — master ( c7ed6e...7a0a13 )
by Gabriel
03:10
created

SendgridTransport::populateSenders()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4.1755

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 8
c 2
b 0
f 0
dl 0
loc 11
ccs 7
cts 9
cp 0.7778
rs 10
cc 4
nc 6
nop 1
crap 4.1755
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
        $reply = $message->getReplyTo();
76 1
        if (is_array($reply)) {
0 ignored issues
show
introduced by
The condition is_array($reply) is always false.
Loading history...
77
            foreach ($reply as $address => $name) {
78
                $this->getMail()->setReplyTo($address, $name);
79
            }
80
        }
81 1
    }
82
83
    /**
84
     * @return null|Mail
85
     */
86 1
    public function getMail()
87
    {
88 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...
89
    }
90
91
    /**
92
     * @param null|Mail $mail
93
     */
94 1
    public function setMail($mail)
95
    {
96 1
        $this->mail = $mail;
97 1
    }
98
99
    /**
100
     * @param Message|SwiftSimpleMessage $message
101
     * @throws Exception
102
     */
103 1
    protected function populatePersonalization($message)
104
    {
105 1
        $emailsTos = $message->getTo();
106 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...
107
            throw new Exception('Cannot send email withought reciepients');
108
        }
109 1
        $personalizationIndex = 0;
110 1
        foreach ($emailsTos as $emailTo => $nameTo) {
111 1
            $personalization = $this->generatePersonalization($emailTo, $nameTo, $message, $personalizationIndex);
112 1
            $this->getMail()->addPersonalization($personalization);
113 1
            $personalizationIndex++;
114
        }
115 1
    }
116
117
    /**
118
     * @param $emailTo
119
     * @param $nameTo
120
     * @param Message $message
121
     * @param integer $i
122
     * @return Personalization
123
     * @throws SendGrid\Mail\TypeException
124
     */
125 1
    protected function generatePersonalization($emailTo, $nameTo, $message, $i)
126
    {
127 1
        $personalization = new Personalization();
128
129 1
        $email = new To($emailTo, $nameTo);
130 1
        $personalization->addTo($email);
131
132 1
        $personalization->setSubject($message->getSubject());
133
134 1
        $mergeTags = $message->getMergeTags();
135 1
        foreach ($mergeTags as $varKey => $value) {
136
            if (is_array($value)) {
137
                $value = $value[$i];
138
            }
139
            $value = (string) $value;
140
            $personalization->addSubstitution('{{' . $varKey . '}}', $value);
141
        }
142
143 1
        return $personalization;
144
    }
145
146
    /**
147
     * @param Message|SwiftSimpleMessage $message
148
     * @throws SendGrid\Mail\TypeException
149
     * @throws ReflectionException
150
     */
151 1
    protected function populateContent($message)
152
    {
153 1
        $contentType = $this->getMessagePrimaryContentType($message);
154
155 1
        $bodyHtml = $bodyText = null;
156
157 1
        if ($contentType === 'text/plain') {
158 1
            $bodyText = $message->getBody();
159
        } else {
160
            $bodyHtml = $message->getBody();
161
            $bodyText = (new Html2Text($bodyHtml))->getText();
162
        }
163
164 1
        foreach ($message->getChildren() as $child) {
165
            if ($child instanceof Swift_Image) {
166
                $images[] = [
167
                    'type' => $child->getContentType(),
168
                    'name' => $child->getId(),
169
                    'content' => base64_encode($child->getBody()),
170
                ];
171
            } elseif ($child instanceof Swift_Attachment && !($child instanceof Swift_Image)) {
172
                $this->addAttachment($child);
173
            } elseif ($child instanceof Swift_MimePart && $this->supportsContentType($child->getContentType())) {
174
                if ($child->getContentType() == "text/html") {
175
                    $bodyHtml = $child->getBody();
176
                } elseif ($child->getContentType() == "text/plain") {
177
                    $bodyText = $child->getBody();
178
                }
179
            }
180
        }
181
182 1
        $content = new Content("text/plain", $bodyText);
183 1
        $this->getMail()->addContent($content);
184
185 1
        $content = new Content("text/html", $bodyHtml);
186 1
        $this->getMail()->addContent($content);
187 1
    }
188
189
    /**
190
     * @param SwiftSimpleMessage $message
191
     * @return string
192
     * @throws ReflectionException
193
     */
194 1
    protected function getMessagePrimaryContentType(SwiftSimpleMessage $message)
195
    {
196 1
        $contentType = $message->getContentType();
197 1
        if ($this->supportsContentType($contentType)) {
198 1
            return $contentType;
199
        }
200
        // SwiftMailer hides the content type set in the constructor of Swift_Mime_Message as soon
201
        // as you add another part to the message. We need to access the protected property
202
        // _userContentType to get the original type.
203
        $messageRef = new ReflectionClass($message);
204
        if ($messageRef->hasProperty('_userContentType')) {
205
            $propRef = $messageRef->getProperty('_userContentType');
206
            $propRef->setAccessible(true);
207
            $contentType = $propRef->getValue($message);
208
        }
209
210
        return $contentType;
211
    }
212
213
    /**
214
     * @param string $contentType
215
     * @return bool
216
     */
217 1
    protected function supportsContentType($contentType)
218
    {
219 1
        return in_array($contentType, $this->getSupportedContentTypes());
220
    }
221
222
    /**
223
     * @return string[]
224
     */
225 1
    protected function getSupportedContentTypes()
226
    {
227
        return [
228 1
            'text/plain',
229
            'text/html',
230
        ];
231
    }
232
233
    /**
234
     * @param Swift_Attachment $attachment
235
     * @throws SendGrid\Mail\TypeException
236
     */
237
    protected function addAttachment($attachment)
238
    {
239
        $sgAttachment = new Attachment();
240
        $sgAttachment->setContent(base64_encode($attachment->getBody()));
241
        $sgAttachment->setType($attachment->getContentType());
242
        $sgAttachment->setFilename($attachment->getFilename());
243
        $sgAttachment->setDisposition("attachment");
244
        $sgAttachment->setContentID($attachment->getId());
245
        $this->getMail()->addAttachment($sgAttachment);
246
    }
247
248
    /**
249
     * @param Message|SwiftSimpleMessage $message
250
     * @throws SendGrid\Mail\TypeException
251
     */
252 1
    protected function populateCustomArg($message)
253
    {
254 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

254
        /** @scrutinizer ignore-call */ 
255
        $args = $message->getCustomArgs();
Loading history...
255 1
        foreach ($args as $key => $value) {
256
            if ($key == 'category') {
257
                $this->getMail()->addCategory($value);
258
            } else {
259
                $this->getMail()->addCustomArg($key, (string) $value);
260
            }
261
        }
262 1
    }
263
264
    /**
265
     * @return int
266
     * @throws Swift_TransportException
267
     */
268
    protected function sendApiCall()
269
    {
270
        $sendGrid = $this->createApi();
271
        try {
272
            $response = $sendGrid->send($this->getMail());
273
        } catch (Exception $exception) {
274
            throw new Swift_TransportException(
275
                'Error sending email Code [' . $exception->getMessage() . ']'
276
            );
277
        }
278
279
        if ($response->statusCode() == '202') {
280
            return 1;
281
        } else {
282
            throw new Swift_TransportException(
283
                'Error sending email Code [' . $response->statusCode() . ']. '
284
                . '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

284
                . 'HEADERS [' . /** @scrutinizer ignore-type */ print_r($response->headers())
Loading history...
285
                . $response->body()
286
            );
287
        }
288
    }
289
290
    /**
291
     * @return SendGrid
292
     * @throws Swift_TransportException
293
     */
294
    protected function createApi()
295
    {
296
        if ($this->getApiKey() === null) {
297
            throw new Swift_TransportException('Cannot create instance of \SendGrid while API key is NULL');
298
        }
299
300
        return new SendGrid($this->getApiKey());
301
    }
302
303
    /**
304
     * @param string $apiKey
305
     * @return $this
306
     */
307 1
    public function setApiKey($apiKey)
308
    {
309 1
        $this->apiKey = $apiKey;
310
311 1
        return $this;
312
    }
313
}
314