Completed
Push — master ( acd156...724220 )
by Michał
01:52
created

Sendgrid::processAddresses()   B

Complexity

Conditions 5
Paths 16

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 8.5906
c 0
b 0
f 0
cc 5
eloc 11
nc 16
nop 2
1
<?php namespace nyx\notify\transports\mail\drivers;
2
3
// Internal dependencies
4
use nyx\notify\transports\mail;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, nyx\notify\transports\mail\drivers\mail.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
5
6
/**
7
 * Sendgrid Mail Driver
8
 *
9
 * @package     Nyx\Notify
10
 * @version     0.1.0
11
 * @author      Michal Chojnacki <[email protected]>
12
 * @copyright   2012-2017 Nyx Dev Team
13
 * @link        https://github.com/unyx/nyx
14
 * @todo        Check whether the X-SMTPAPI header is automatically handled by the Web API and if not, parse its
15
 *              contents out manually and merge them with the payload.
16
 */
17
class Sendgrid implements mail\interfaces\Driver
18
{
19
    /**
20
     * The traits of a Sendgrid Mail Driver instance.
21
     */
22
    use traits\CountsRecipients;
23
24
    /**
25
     * @var string  The API key (bearer token) used to authorize requests to Sendgrid's API.
26
     */
27
    protected $key;
28
29
    /**
30
     * @var \GuzzleHttp\ClientInterface The underlying HTTP Client instance.
31
     */
32
    protected $client;
33
34
    /**
35
     * Creates a new Sendgrid Mail Driver instance.
36
     *
37
     * @param   \GuzzleHttp\ClientInterface $client     The HTTP Client to use.
38
     * @param   string                      $key        The API key to be used to authorize requests to SparkPost's API.
39
     */
40
    public function __construct(\GuzzleHttp\ClientInterface $client, string $key)
41
    {
42
        $this->key    = $key;
43
        $this->client = $client;
44
    }
45
46
    /**
47
     * {@inheritDoc}
48
     */
49 View Code Duplication
    public function send(\Swift_Mime_Message $message, &$failedRecipients = null)
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...
50
    {
51
        $this->client->request('POST', 'https://api.sendgrid.com/v3/mail/send', [
52
            'headers' => [
53
                'Authorization' => 'Bearer '.$this->key
54
            ],
55
            'json' => $this->messageToPayload($message),
56
        ]);
57
58
        return $this->countRecipients($message);
59
    }
60
61
    /**
62
     * Converts a MIME Message to an array payload in a structure understood by Sendgrid's "mail/send" endpoint.
63
     *
64
     * @param   \Swift_Mime_Message $message    The Message to convert.
65
     * @return  array                           The resulting payload.
66
     */
67 View Code Duplication
    protected function messageToPayload(\Swift_Mime_Message $message) : array
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...
68
    {
69
        $payload = [
70
            'Subject' => $message->getSubject()
71
        ];
72
73
        $this->processHeaders($message, $payload);
74
        $this->processAddresses($message, $payload);
75
        $this->processMimeEntities($message, $payload);
76
        $this->processAttachments($message, $payload);
77
78
        return $payload;
79
    }
80
81
    /**
82
     * Processes the MIME headers of a MIME Message into the payload structure passed in by reference.
83
     *
84
     * @param   \Swift_Mime_Message $message    The MIME Message to process.
85
     * @param   array&              $payload    A reference to the payload structure.
0 ignored issues
show
Documentation introduced by
The doc-type array& could not be parsed: Unknown type name "array&" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
86
     */
87
    protected function processHeaders(\Swift_Mime_Message $message, array &$payload)
88
    {
89
        if (!$headers = $message->getHeaders()->getAll()) {
90
            return;
91
        }
92
93
        $payload['Headers'] = [];
94
95
        /** @var \Swift_Mime_Header $header */
96
        foreach ($headers as $header) {
97
98
            // Omit headers which are handled elsewhere.
99 View Code Duplication
            if (in_array($fieldName = $header->getFieldName(), ['Subject', 'Content-Type', 'MIME-Version', 'Date', 'From', 'To'])) {
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...
100
                continue;
101
            }
102
103
            if ($header instanceof \Swift_Mime_Headers_UnstructuredHeader || $header instanceof \Swift_Mime_Headers_OpenDKIMHeader) {
104
                $payload['Headers'][] = [
105
                    "Name"  => $fieldName,
106
                    "Value" => $header->getValue()
107
                ];
108
109
                continue;
110
            }
111
112
            // All other headers are handled in the same fashion.
113
            $payload['Headers'][] = [
114
                "Name"  => $fieldName,
115
                "Value" => $header->getFieldBody()
116
            ];
117
        }
118
    }
119
120
    /**
121
     * Processes the fields containing e-mail addresses (from, to, cc, etc.) in the MIME Message into
122
     * the payload structure passed in by reference.
123
     *
124
     * @param   \Swift_Mime_Message $message    The MIME Message to process.
125
     * @param   array&              $payload    A reference to the payload structure.
0 ignored issues
show
Documentation introduced by
The doc-type array& could not be parsed: Unknown type name "array&" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
126
     */
127
    protected function processAddresses(\Swift_Mime_Message $message, array &$payload)
128
    {
129
        // Sendgrid expect the 'from' field to be a single entry at most, according to the docs.
130
        $payload['from'] = $this->processFirstAddress($message->getFrom());
131
132
        if (!isset($payload['personalizations'])) {
133
            $payload['personalizations'] = [];
134
        }
135
136
        $payload['personalizations']['to'] = $this->processAllAddresses($message->getTo());
137
138
        if ($cc = $message->getCc()) {
139
            $payload['personalizations']['cc'] = $this->processAllAddresses($cc);
140
        }
141
142
        if ($bcc = $message->getBcc()) {
143
            $payload['personalizations']['bcc'] = $this->processAllAddresses($bcc);
144
        }
145
146
        if ($replyTo = $message->getReplyTo()) {
147
            $payload['reply_to'] = $this->processFirstAddress($replyTo);
148
        }
149
    }
150
151
    /**
152
     * Parses the first and only the first address from the given array of e-mail addresses into a structure
153
     * understood by Sendgrid's API.
154
     *
155
     * @param   array   $addresses  The e-mail addresses to parse.
156
     * @return  array
157
     */
158
    protected function processFirstAddress(array $addresses) : array
159
    {
160 View Code Duplication
        foreach ($addresses as $email => $name) {
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...
161
            return $name ? [
162
                'email' => $email,
163
                'name'  => $name
164
            ] : [
165
                'email' => $email
166
            ];
167
        }
168
169
        return [];
170
    }
171
172
    /**
173
     * Parses the given array of e-mail addresses into a structure understood by Sendgrid's API.
174
     *
175
     * @param   array   $addresses  The e-mail addresses to parse.
176
     * @return  array
177
     */
178
    protected function processAllAddresses(array $addresses) : array
179
    {
180
        $result = [];
181
182 View Code Duplication
        foreach ($addresses as $email => $name) {
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...
183
            $result[] = $name ? [
184
                'email' => $email,
185
                'name'  => $name
186
            ] : [
187
                'email' => $email
188
            ];
189
        }
190
191
        return $result;
192
    }
193
194
    /**
195
     * Processes the MIME entities in the MIME Message into the payload structure passed in by reference.
196
     *
197
     * @param   \Swift_Mime_Message $message    The MIME Message to process.
198
     * @param   array&              $payload    A reference to the payload structure.
0 ignored issues
show
Documentation introduced by
The doc-type array& could not be parsed: Unknown type name "array&" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
199
     */
200
    protected function processMimeEntities(\Swift_Mime_Message $message, array &$payload)
201
    {
202
        switch ($message->getContentType()) {
203
            case 'text/html':
204
            case 'multipart/alternative':
205 View Code Duplication
            case 'multipart/mixed':
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...
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
206
207
                // text/plain, if present, must be the first key according to the docs.
208
                if ($plain = $this->getMimePart($message, 'text/plain')) {
209
                    $payload['content'][] = [
210
                        'type'  => 'text/plain',
211
                        'value' => $plain->getBody()
212
                    ];
213
                }
214
215
                $payload['content'][] = [
216
                    'type'  => 'text/html',
217
                    'value' => $message->getBody()
218
                ];
219
220
                break;
221
222 View Code Duplication
            default:
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...
Coding Style introduced by
The default body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a default statement must start on the line immediately following the statement.

switch ($expr) {
    default:
        doSomething(); //right
        break;
}


switch ($expr) {
    default:

        doSomething(); //wrong
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
223
224
                $payload['content'][] = [
225
                    'type'  => 'text/plain',
226
                    'value' => $message->getBody()
227
                ];
228
229
                if ($html = $this->getMimePart($message, 'text/html')) {
230
                    $payload['content'][] = [
231
                        'type'  => 'text/html',
232
                        'value' => $html->getBody()
233
                    ];
234
                }
235
        }
236
    }
237
238
    /**
239
     * Returns the MIME part of the specified content type contained in the given MIME message, if it's present.
240
     *
241
     * @param   \Swift_Mime_Message     $message    The MIME Message to process.
242
     * @param   string                  $mimeType   The content type the part to return must be of.
243
     * @return  \Swift_Mime_MimeEntity
244
     */
245 View Code Duplication
    protected function getMimePart(\Swift_Mime_Message $message, string $mimeType)
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...
246
    {
247
        foreach ($message->getChildren() as $part) {
248
            if (0 === strpos($part->getContentType(), $mimeType) && !$part instanceof \Swift_Mime_Attachment) {
249
                return $part;
250
            }
251
        }
252
    }
253
254
    /**
255
     * Processes the attachments in the MIME Message into the payload structure passed in by reference.
256
     *
257
     * @param   \Swift_Mime_Message $message    The MIME Message to process.
258
     * @param   array&              $payload    A reference to the payload structure.
0 ignored issues
show
Documentation introduced by
The doc-type array& could not be parsed: Unknown type name "array&" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
259
     */
260 View Code Duplication
    protected function processAttachments(\Swift_Mime_Message $message, array &$payload)
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...
261
    {
262
        if (!$children = $message->getChildren()) {
263
            return;
264
        }
265
266
        $payload['attachments'] = [];
267
268
        foreach ($children as $attachment) {
269
270
            // Omit all MIME Entities that aren't attachments.
271
            if (!$attachment instanceof \Swift_Mime_Attachment) {
272
                continue;
273
            }
274
275
            $data = [
276
                'filename'    => $attachment->getFilename(),
277
                'content'     => base64_encode($attachment->getBody()),
278
                'type'        => $attachment->getContentType(),
279
                'disposition' => $attachment->getDisposition(),
280
            ];
281
282
            if ($attachment->getDisposition() !== 'attachment' && null !== $cid = $attachment->getId()) {
283
                $data['content_id'] = 'cid:'.$cid;
284
            }
285
286
            $payload['attachments'][] = $data;
287
        }
288
    }
289
}
290