Passed
Push — master ( 9803c1...d3b367 )
by Gabriel
02:49
created

SendgridTransport::createApi()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 10
ccs 0
cts 5
cp 0
crap 6
rs 10
1
<?php
2
3
namespace Nip\Mail\Transport;
4
5
use Exception;
6
use Html2Text\Html2Text;
7
use Nip\Mail\Message;
8
use SendGrid;
9
use SendGrid\Attachment;
10
use SendGrid\Content;
11
use SendGrid\Email;
12
use SendGrid\Mail;
13
use SendGrid\Personalization;
14
use SendGrid\ReplyTo;
15
use Swift_Attachment;
16
use Swift_Image;
17
use Swift_Mime_Message as MessageInterface;
18
use Swift_MimePart;
19
use Swift_TransportException;
20
21
/**
22
 * Class SendgridTransport
23
 * @package Nip\Mail\Transport
24
 */
25
class SendgridTransport extends AbstractTransport
26
{
27
    /** @var string|null */
28
    protected $apiKey;
29
30
    /**
31
     * @var null|Mail|MessageInterface
32
     */
33
    protected $mail = null;
34
35
    /**
36
     * {@inheritdoc}
37
     */
38
    public function send(\Swift_Mime_Message $message, &$failedRecipients = null)
39
    {
40
        $this->initMail();
41
42
        $this->populateSenders($message);
43
        $this->populatePersonalization($message);
44
        $this->populateContent($message);
45
        $this->populateCustomArg($message);
46
47
        $sg = $this->createApi();
48
49
        /** @var SendGrid\Response $response */
50
        $response = $sg->client->mail()->send()->post($this->getMail());
0 ignored issues
show
Bug introduced by
The method post() does not exist on SendGrid\Response. ( Ignorable by Annotation )

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

50
        $response = $sg->client->mail()->send()->/** @scrutinizer ignore-call */ post($this->getMail());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
51
52
        if ($response->statusCode() == '202') {
53
            return 1;
54
        } else {
55
            throw new Swift_TransportException(
56
                'Error sending email Code [' . $response->statusCode() . ']. '
57
                . 'HEADERS [' . print_r($response->headers())
58
                . $response->body()
59
            );
60
        }
61
    }
62
63
    public function initMail()
64
    {
65
        $this->setMail(new Mail());
66
    }
67
68
    /**
69
     * @param Message|MessageInterface $message
70
     */
71
    protected function populateSenders($message)
72
    {
73
        $from = $message->getFrom();
74
        foreach ($from as $address => $name) {
75
            $email = new Email($name, $address);
76
            $this->getMail()->setFrom($email);
77
78
            $reply_to = new ReplyTo($address);
79
            $this->getMail()->setReplyTo($reply_to);
80
        }
81
    }
82
83
    /**
84
     * @return null|Mail
85
     */
86
    public function getMail()
87
    {
88
        return $this->mail;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->mail also could return the type Swift_Mime_Message which is incompatible with the documented return type SendGrid\Mail|null.
Loading history...
89
    }
90
91
    /**
92
     * @param null|Mail $mail
93
     */
94
    public function setMail($mail)
95
    {
96
        $this->mail = $mail;
97
    }
98
99
    /**
100
     * @param Message|MessageInterface $message
101
     * @throws Exception
102
     */
103
    protected function populatePersonalization($message)
104
    {
105
        $emailsTos = $message->getTo();
106
        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
        $i = 0;
110
        foreach ($emailsTos as $emailTo => $nameTo) {
111
            $personalization = $this->generatePersonalization($emailTo, $nameTo, $message, $i);
112
            $this->getMail()->addPersonalization($personalization);
113
            $i++;
114
        }
115
    }
116
117
    /**
118
     * @param $emailTo
119
     * @param $nameTo
120
     * @param Message $message
121
     * @param integer $i
122
     * @return Personalization
123
     */
124
    protected function generatePersonalization($emailTo, $nameTo, $message, $i)
125
    {
126
        $personalization = new Personalization();
127
128
        $email = new Email($nameTo, $emailTo);
129
        $personalization->addTo($email);
130
131
        $personalization->setSubject($message->getSubject());
132
133
        $mergeTags = $message->getMergeTags();
134
        foreach ($mergeTags as $varKey => $value) {
135
            if (is_array($value)) {
136
                $value = $value[$i];
137
            }
138
            $value = (string) $value;
139
            $personalization->addSubstitution('{{' . $varKey . '}}', $value);
140
        }
141
142
        return $personalization;
143
    }
144
145
    /**
146
     * @param Message|MessageInterface $message
147
     */
148
    protected function populateContent($message)
149
    {
150
        $contentType = $this->getMessagePrimaryContentType($message);
151
152
        $bodyHtml = $bodyText = null;
153
154
        if ($contentType === 'text/plain') {
155
            $bodyText = $message->getBody();
156
        } else {
157
            $bodyHtml = $message->getBody();
158
            $bodyText = (new Html2Text($bodyHtml))->getText();
159
        }
160
161
        foreach ($message->getChildren() as $child) {
162
            if ($child instanceof Swift_Image) {
163
                $images[] = [
164
                    'type' => $child->getContentType(),
165
                    'name' => $child->getId(),
166
                    'content' => base64_encode($child->getBody()),
167
                ];
168
            } elseif ($child instanceof Swift_Attachment && !($child instanceof Swift_Image)) {
169
                $this->addAttachment($child);
170
            } elseif ($child instanceof Swift_MimePart && $this->supportsContentType($child->getContentType())) {
171
                if ($child->getContentType() == "text/html") {
172
                    $bodyHtml = $child->getBody();
173
                } elseif ($child->getContentType() == "text/plain") {
174
                    $bodyText = $child->getBody();
175
                }
176
            }
177
        }
178
179
        $content = new Content("text/plain", $bodyText);
180
        $this->getMail()->addContent($content);
181
182
        $content = new Content("text/html", $bodyHtml);
183
        $this->getMail()->addContent($content);
184
    }
185
186
    /**
187
     * @param MessageInterface $message
188
     * @return string
189
     */
190
    protected function getMessagePrimaryContentType(MessageInterface $message)
191
    {
192
        $contentType = $message->getContentType();
193
        if ($this->supportsContentType($contentType)) {
194
            return $contentType;
195
        }
196
        // SwiftMailer hides the content type set in the constructor of Swift_Mime_Message as soon
197
        // as you add another part to the message. We need to access the protected property
198
        // _userContentType to get the original type.
199
        $messageRef = new \ReflectionClass($message);
200
        if ($messageRef->hasProperty('_userContentType')) {
201
            $propRef = $messageRef->getProperty('_userContentType');
202
            $propRef->setAccessible(true);
203
            $contentType = $propRef->getValue($message);
204
        }
205
206
        return $contentType;
207
    }
208
209
    /**
210
     * @param string $contentType
211
     * @return bool
212
     */
213
    protected function supportsContentType($contentType)
214
    {
215
        return in_array($contentType, $this->getSupportedContentTypes());
216
    }
217
218
    /**
219
     * @return string[]
220
     */
221
    protected function getSupportedContentTypes()
222
    {
223
        return [
224
            'text/plain',
225
            'text/html',
226
        ];
227
    }
228
229
    /**
230
     * @param Swift_Attachment $attachment
231
     */
232
    protected function addAttachment($attachment)
233
    {
234
        $sgAttachment = new Attachment();
235
        $sgAttachment->setContent(base64_encode($attachment->getBody()));
236
        $sgAttachment->setType($attachment->getContentType());
237
        $sgAttachment->setFilename($attachment->getFilename());
238
        $sgAttachment->setDisposition("attachment");
239
        $sgAttachment->setContentID($attachment->getId());
240
        $this->getMail()->addAttachment($sgAttachment);
241
    }
242
243
    /**
244
     * @param Message|MessageInterface $message
245
     */
246
    protected function populateCustomArg($message)
247
    {
248
        $args = $message->getCustomArgs();
0 ignored issues
show
Bug introduced by
The method getCustomArgs() does not exist on Swift_Mime_Message. It seems like you code against a sub-type of Swift_Mime_Message such as Nip\Mail\Message or 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
        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
    }
257
258
    /**
259
     * @return SendGrid
260
     * @throws Swift_TransportException
261
     */
262
    protected function createApi()
263
    {
264
        if ($this->getApiKey() === null) {
265
            throw new Swift_TransportException('Cannot create instance of \Mandrill while API key is NULL');
266
        }
267
268
269
        $sg = new SendGrid($this->getApiKey());
270
271
        return $sg;
272
    }
273
274
    /**
275
     * @return null|string
276
     */
277
    public function getApiKey()
278
    {
279
        return $this->apiKey;
280
    }
281
282
    /**
283
     * @param string $apiKey
284
     * @return $this
285
     */
286 1
    public function setApiKey($apiKey)
287
    {
288 1
        $this->apiKey = $apiKey;
289
290 1
        return $this;
291
    }
292
}
293