Completed
Push — master ( 40184f...590734 )
by Loz
01:01
created

Mailer::buildMessage()   C

Complexity

Conditions 9
Paths 256

Size

Total Lines 55
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 55
ccs 31
cts 31
cp 1
rs 5.4159
c 0
b 0
f 0
cc 9
eloc 32
nc 256
nop 8
crap 9

How to fix   Long Method    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace Kinglozzer\SilverStripeMailgunner;
4
5
use Debug;
6
use Exception;
7
use Mailer as SilverstripeMailer;
8
use Mailgun\HttpClientConfigurator;
9
use Mailgun\Mailgun;
10
use Mailgun\Messages\BatchMessage;
11
use Mailgun\Messages\MessageBuilder;
12
use SS_Log;
13
use SapphireTest;
14
15
class Mailer extends SilverstripeMailer
16
{
17
    /**
18
     * @var string
19
     * @config
20
     */
21
    private static $api_domain = '';
22
23
    /**
24
     * @var string
25
     * @config
26
     */
27
    private static $api_endpoint = '';
28
29
    /**
30
     * @var string
31
     * @config
32
     */
33
    private static $api_key = '';
34
35
    /**
36
     * @var boolean
37
     * @config
38
     */
39
    private static $debug = false;
40
41
    /**
42
     * An array of temporary file handles opened to store attachments
43
     * @var array
44
     */
45
    protected $tempFileHandles = [];
46
47
    /**
48
     * @var Mailgun
49
     */
50
    protected $mailgunClient;
51
52
    /**
53
     * {@inheritdoc}
54
     */
55 12
    public function __construct()
56
    {
57 12
        $config = $this->config();
58 12
        $configurator = new HttpClientConfigurator();
59 12
        $configurator->setApiKey($config->api_key);
60 12
        $configurator->setDebug($config->debug);
61
62 12
        if ($config->api_endpoint) {
63
            $configurator->setEndpoint($config->api_endpoint);
64
        }
65
66 12
        $this->setMailgunClient(Mailgun::configure($configurator));
67 12
    }
68
69
    /**
70
     * @param Mailgun $client
71
     * @return self
72
     */
73 12
    public function setMailgunClient(Mailgun $client)
74
    {
75 12
        $this->mailgunClient = $client;
76 12
        return $this;
77
    }
78
79
    /**
80
     * @return Mailgun
81
     */
82 1
    public function getMailgunClient()
83
    {
84 1
        return $this->mailgunClient;
85
    }
86
87
    /**
88
     * {@inheritdoc}
89
     */
90 1
    public function sendPlain($to, $from, $subject, $plainContent, $attachments = [], $headers = [])
91
    {
92 1
        return $this->sendMessage($to, $from, $subject, $htmlContent = '', $plainContent, $attachments, $headers);
93
    }
94
95
    /**
96
     * {@inheritdoc}
97
     */
98 1
    public function sendHTML($to, $from, $subject, $htmlContent, $attachments = [], $headers = [], $plainContent = '')
99
    {
100 1
        return $this->sendMessage($to, $from, $subject, $htmlContent, $plainContent, $attachments, $headers);
101
    }
102
103
    /**
104
     * @param string $to
105
     * @param string $from
106
     * @param string $subject
107
     * @param string $content
108
     * @param string $plainContent
109
     * @param array $attachments
110
     * @param array $headers
111
     */
112 3
    protected function sendMessage($to, $from, $subject, $content, $plainContent, $attachments, $headers)
113
    {
114 3
        $domain = $this->config()->api_domain;
115 3
        $client = $this->getMailgunClient();
116 3
        $attachments = $this->prepareAttachments($attachments);
117
118 3
        if (isset($headers['X-Mailgunner-Batch-Message'])) {
119 1
            $builder = $client->BatchMessage($domain);
0 ignored issues
show
Deprecated Code introduced by
The method Mailgun\Mailgun::BatchMessage() has been deprecated with message: Will be removed in 3.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
120 1
            unset($headers['X-Mailgunner-Batch-Message']);
121 1
        } else {
122 2
            $builder = $client->MessageBuilder();
0 ignored issues
show
Deprecated Code introduced by
The method Mailgun\Mailgun::MessageBuilder() has been deprecated with message: Will be removed in 3.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
123
        }
124
125
        try {
126 3
            $this->buildMessage($builder, $to, $from, $subject, $content, $plainContent, $attachments, $headers);
127
128 3
            if ($builder instanceof BatchMessage) {
129 1
                $builder->finalize();
130 1
            } else {
131 2
                $client->sendMessage($domain, $builder->getMessage(), $builder->getFiles());
0 ignored issues
show
Deprecated Code introduced by
The method Mailgun\Mailgun::sendMessage() has been deprecated with message: Use Mailgun->message() instead. Will be removed in 3.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
132
            }
133 3
        } catch (Exception $e) {
134
            // Close and remove any temp files created for attachments
135 1
            $this->closeTempFileHandles();
136
            // Throwing the exception would break SilverStripe's Email API expectations, so we log
137
            // errors and show a message (which is hidden in live mode)
138 1
            SS_Log::log('Mailgun error: ' . $e->getMessage(), SS_Log::ERR);
139 1
            if (!SapphireTest::is_running_test()) {
140
                Debug::message('Mailgun error: ' . $e->getMessage());
141
            }
142
143 1
            return false;
144
        }
145
146 2
        $this->closeTempFileHandles();
147
148
        // This is a stupid API :(
149 2
        return [$to, $subject, $content, $headers, ''];
150
    }
151
152
    /**
153
     * @param MessageBuilder $builder
154
     * @param string $to
155
     * @param string $from
156
     * @param string $subject
157
     * @param string $content
158
     * @param string $plainContent
159
     * @param array $attachments
160
     * @param array $headers
161
     */
162 2
    protected function buildMessage(
163
        MessageBuilder $builder,
164
        $to,
165
        $from,
166
        $subject,
167
        $content,
168
        $plainContent,
169
        array $attachments,
170
        array $headers
171
    ) {
172
        // Add base info
173 2
        $parsedFrom = $this->parseAddresses($from);
174 2
        foreach ($parsedFrom as $email => $name) {
175 2
            $builder->setFromAddress($email, ['full_name' => $name]);
176 2
        }
177
178 2
        $builder->setSubject($subject);
179 2
        $builder->setHtmlBody($content);
180 2
        $builder->setTextBody($plainContent);
181
182
        // Add attachments
183 2
        foreach ($attachments as $attachment) {
184 2
            $builder->addAttachment($attachment['filePath'], $attachment['remoteName']);
185 2
        }
186
187
        // Parse Cc & Bcc headers out if they're set
188 2
        $ccAddresses = isset($headers['Cc']) ? $headers['Cc'] : '';
189 2
        $bccAddresses = isset($headers['Bcc']) ? $headers['Bcc'] : '';
190
191
        // We handle these ourselves, so can remove them from the list of headers
192 2
        unset($headers['Cc']);
193 2
        unset($headers['Bcc']);
194
195
        // Add remaining custom headers
196 2
        foreach ($headers as $name => $data) {
197 2
            $builder->addCustomHeader($name, $data);
198 2
        }
199
200
        // Add recipients. This is done last as the 'BatchMessage' message builder
201
        // will trigger sends for every 1000 addresses
202 2
        $to = $this->parseAddresses($to);
203 2
        foreach ($to as $email => $name) {
204 2
            $builder->addToRecipient($email, ['full_name' => $name]);
205 2
        }
206
207 2
        $ccAddresses = $this->parseAddresses($ccAddresses);
208 2
        foreach ($ccAddresses as $email => $name) {
209 2
            $builder->addCcRecipient($email, ['full_name' => $name]);
210 2
        }
211
212 2
        $bccAddresses = $this->parseAddresses($bccAddresses);
213 2
        foreach ($bccAddresses as $email => $name) {
214 2
            $builder->addBccRecipient($email, ['full_name' => $name]);
215 2
        }
216 2
    }
217
218
    /**
219
     * @todo This can't deal with mismatched quotes, or commas in names.
220
     *       E.g. "Smith, John" <[email protected]> or "John O'smith" <[email protected]>
221
     * @param string
222
     * @return array
223
     */
224 3
    protected function parseAddresses($addresses)
225
    {
226 3
        $parsed = [];
227
228 3
        $expr = '/\s*["\']?([^><,;"\']+)["\']?\s*((?:<[^><,]+>)?)\s*/';
229 3
        if (preg_match_all($expr, $addresses, $matches, PREG_SET_ORDER) > 0) {
230 3
            foreach ($matches as $result) {
231 3
                if (empty($result[2])) {
232
                    // If we couldn't parse out a name
233 3
                    $parsed[$result[1]] = '';
234 3
                } else {
235 3
                    $email = trim($result[2], '<>');
236 3
                    $parsed[$email] = trim($result[1]);
237
                }
238 3
            }
239 3
        }
240
241 3
        return $parsed;
242
    }
243
244
    /**
245
     * Prepare attachments for sending. SilverStripe extracts the content and
246
     * passes that to the mailer, so to save encoding it we just write them all
247
     * to individual files and let Mailgun deal with the rest.
248
     *
249
     * @todo Can we handle this better?
250
     * @param array $attachments
251
     * @return array
252
     */
253 2
    protected function prepareAttachments(array $attachments)
254
    {
255 2
        $prepared = [];
256
257 2
        foreach ($attachments as $attachment) {
258 2
            $tempFile = $this->writeToTempFile($attachment['contents']);
259
260 2
            $prepared[] = [
261 2
                'filePath' => $tempFile,
262 2
                'remoteName' => $attachment['filename']
263 2
            ];
264 2
        }
265
266 2
        return $prepared;
267
    }
268
269
    /**
270
     * @param string $contents
271
     * @return string
272
     */
273 2
    protected function writeToTempFile($contents)
274
    {
275 2
        $tempFile = tempnam(sys_get_temp_dir(), 'SS_MG_TMP');
276 2
        $fileHandle = fopen($tempFile, 'r+');
277 2
        fwrite($fileHandle, $contents);
278
279 2
        $this->tempFileHandles[] = [
280 2
            'handle' => $fileHandle,
281
            'path' => $tempFile
282 2
        ];
283
284 2
        return $tempFile;
285
    }
286
287
    /**
288
     * @return void
289
     */
290 1
    protected function closeTempFileHandles()
291
    {
292 1
        foreach ($this->tempFileHandles as $key => $data) {
293 1
            fclose($data['handle']);
294 1
            unlink($data['path']);
295 1
            unset($this->tempFileHandles[$key]);
296 1
        }
297 1
    }
298
}
299