Completed
Push — master ( 335027...a03352 )
by Shingo
04:39
created

SendgridV3Transport::post()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
namespace Sichikawa\LaravelSendgridDriver\Transport;
3
4
use GuzzleHttp\Client;
5
use GuzzleHttp\ClientInterface;
6
use GuzzleHttp\Psr7\Response;
7
use Illuminate\Mail\Transport\Transport;
8
use Swift_Attachment;
9
use Swift_Image;
10
use Swift_Mime_Message;
11
use Swift_MimePart;
12
13
class SendgridV3Transport extends Transport
14
{
15
    const MAXIMUM_FILE_SIZE = 7340032;
16
    const SMTP_API_NAME = 'sendgrid/x-smtpapi';
17
    const BASE_URL = 'https://api.sendgrid.com/v3/mail/send';
18
19
    /**
20
     * @var Client
21
     */
22
    private $client;
23
    private $options;
24
    private $attachments;
25
    private $numberOfRecipients;
26
27 View Code Duplication
    public function __construct(ClientInterface $client, $api_key)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
28
    {
29
        $this->client = $client;
0 ignored issues
show
Documentation Bug introduced by
$client is of type object<GuzzleHttp\ClientInterface>, but the property $client was declared to be of type object<GuzzleHttp\Client>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
30
        $this->options = [
31
            'headers' => [
32
                'Authorization' => 'Bearer ' . $api_key,
33
                'Content-Type'  => 'application/json',
34
            ],
35
        ];
36
    }
37
38
    /**
39
     * {@inheritdoc}
40
     */
41
    public function send(Swift_Mime_Message $message, &$failedRecipients = null)
42
    {
43
        $this->beforeSendPerformed($message);
44
45
        $payload = $this->options;
46
47
        $data = [
48
            'personalizations' => $this->getPersonalizations($message),
49
            'from'             => $this->getFrom($message),
50
            'subject'          => $message->getSubject(),
51
            'content'          => $this->getContents($message),
52
        ];
53
54
        if ($reply_to = $this->getReplyTo($message)) {
55
            $data['reply_to'] = $reply_to;
56
        }
57
58
        $attachments = $this->getAttachments($message);
59
        if (count($attachments) > 0) {
60
            $data['attachments'] = $attachments;
61
        }
62
63
        $data = $this->setParameters($message, $data);
64
65
        $payload['json'] = $data;
66
67
        $response = $this->post($payload);
68
69
        $message->getHeaders()->addTextHeader('X-Message-Id', $response->getHeaderLine('X-Message-Id'));
70
71
        $this->sendPerformed($message);
72
73
        return $this->numberOfRecipients ?: $this->numberOfRecipients($message);
74
    }
75
76
    /**
77
     * @param Swift_Mime_Message $message
78
     * @return array
79
     */
80
    private function getPersonalizations(Swift_Mime_Message $message)
81
    {
82
        $setter = function (array $addresses) {
83
            $recipients = [];
84
            foreach ($addresses as $email => $name) {
85
                $address = [];
86
                $address['email'] = $email;
87
                if ($name) {
88
                    $address['name'] = $name;
89
                }
90
                $recipients[] = $address;
91
            }
92
            return $recipients;
93
        };
94
95
        $personalization['to'] = $setter($message->getTo());
0 ignored issues
show
Coding Style Comprehensibility introduced by
$personalization was never initialized. Although not strictly required by PHP, it is generally a good practice to add $personalization = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
96
97
        if ($cc = $message->getCc()) {
98
            $personalization['cc'] = $setter($cc);
99
        }
100
101
        if ($bcc = $message->getBcc()) {
102
            $personalization['bcc'] = $setter($bcc);
103
        }
104
105
        return [$personalization];
106
    }
107
108
    /**
109
     * Get From Addresses.
110
     *
111
     * @param Swift_Mime_Message $message
112
     * @return array
113
     */
114
    private function getFrom(Swift_Mime_Message $message)
115
    {
116
        if ($message->getFrom()) {
117
            foreach ($message->getFrom() as $email => $name) {
118
                return ['email' => $email, 'name' => $name];
119
            }
120
        }
121
        return [];
122
    }
123
124
    /**
125
     * Get ReplyTo Addresses.
126
     *
127
     * @param Swift_Mime_Message $message
128
     * @return array
129
     */
130
    private function getReplyTo(Swift_Mime_Message $message)
131
    {
132
        if ($message->getReplyTo()) {
133
            foreach ($message->getReplyTo() as $email => $name) {
134
                return ['email' => $email, 'name' => $name];
135
            }
136
        }
137
        return null;
138
    }
139
140
    /**
141
     * Get contents.
142
     *
143
     * @param Swift_Mime_Message $message
144
     * @return array
145
     */
146
    private function getContents(Swift_Mime_Message $message)
147
    {
148
        $content = [];
149
        foreach ($message->getChildren() as $attachment) {
150
            if ($attachment instanceof Swift_MimePart) {
151
                $content[] = [
152
                    'type'  => 'text/plain',
153
                    'value' => $attachment->getBody(),
154
                ];
155
                break;
156
            }
157
        }
158
159
        if (empty($content) || strpos($message->getContentType(), 'multipart') !== false) {
160
            $content[] = [
161
                'type'  => 'text/html',
162
                'value' => $message->getBody(),
163
            ];
164
        }
165
        return $content;
166
    }
167
168
    /**
169
     * @param Swift_Mime_Message $message
170
     * @return array
171
     */
172
    private function getAttachments(Swift_Mime_Message $message)
173
    {
174
        $attachments = [];
175
        foreach ($message->getChildren() as $attachment) {
176
            if ((!$attachment instanceof Swift_Attachment && !$attachment instanceof Swift_Image)
177
                || $attachment->getFilename() === self::SMTP_API_NAME
178
                || !strlen($attachment->getBody()) > self::MAXIMUM_FILE_SIZE
179
            ) {
180
                continue;
181
            }
182
            $attachments[] = [
183
                'content'     => base64_encode($attachment->getBody()),
184
                'filename'    => $attachment->getFilename(),
185
                'type'        => $attachment->getContentType(),
186
                'disposition' => $attachment->getDisposition(),
187
                'content_id'  => $attachment->getId(),
188
            ];
189
        }
190
        return $this->attachments = $attachments;
191
    }
192
193
    /**
194
     * Set Request Body Parameters
195
     *
196
     * @param Swift_Mime_Message $message
197
     * @param array $data
198
     * @return array
199
     * @throws \Exception
200
     */
201
    protected function setParameters(Swift_Mime_Message $message, $data)
202
    {
203
        $this->numberOfRecipients = 0;
204
205
        $smtp_api = [];
206 View Code Duplication
        foreach ($message->getChildren() as $attachment) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
207
            if (!$attachment instanceof Swift_Image
208
                || !in_array(self::SMTP_API_NAME, [$attachment->getFilename(), $attachment->getContentType()])
209
            ) {
210
                continue;
211
            }
212
            $smtp_api = $attachment->getBody();
213
        }
214
215
        if (!is_array($smtp_api)) {
216
            return $data;
217
        }
218
219
        foreach ($smtp_api as $key => $val) {
220
221
            switch ($key) {
222
223
                case 'personalizations':
224
                    $this->setPersonalizations($data, $val);
225
                    continue 2;
226
227
                case 'attachments':
228
                    $val = array_merge($this->attachments, $val);
229
                    break;
230
231
                case 'unique_args':
232
                    throw new \Exception('Sendgrid v3 now uses custom_args instead of unique_args');
233
234
                case 'custom_args':
235
                    foreach ($val as $name => $value) {
236
                        if (!is_string($value)) {
237
                            throw new \Exception('Sendgrid v3 custom arguments have to be a string.');
238
                        }
239
                    }
240
                    break;
241
242
            }
243
244
            array_set($data, $key, $val);
245
        }
246
        return $data;
247
    }
248
249
    private function setPersonalizations(&$data, $personalizations)
250
    {
251
        foreach ($personalizations as $index => $params) {
252
            foreach ($params as $key => $val) {
253
                if (in_array($key, ['to', 'cc', 'bcc'])) {
254
                    array_set($data, 'personalizations.' . $index . '.' . $key, [$val]);
255
                    ++$this->numberOfRecipients;
256
                } else {
257
                    array_set($data, 'personalizations.' . $index . '.' . $key, $val);
258
                }
259
            }
260
        }
261
    }
262
263
    /**
264
     * @param $payload
265
     * @return Response
266
     */
267
    private function post($payload)
268
    {
269
        return $this->client->post('https://api.sendgrid.com/v3/mail/send', $payload);
270
    }
271
}
272