Completed
Push — master ( 12b3bb...db2dee )
by Loz
05:12
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 7
Bugs 1 Features 3
Metric Value
c 7
b 1
f 3
dl 0
loc 55
ccs 31
cts 31
cp 1
rs 5.4159
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 Mailer as SilverstripeMailer;
6
use Mailgun\Mailgun;
7
use Mailgun\Messages\MessageBuilder;
8
9
class Mailer extends SilverstripeMailer
10
{
11
    /**
12
     * @var string
13
     * @config
14
     */
15
    private static $api_domain = '';
16
17
    /**
18
     * @var string
19
     * @config
20
     */
21
    private static $api_endpoint = 'api.mailgun.net';
22
23
    /**
24
     * @var string
25
     * @config
26
     */
27
    private static $api_key = '';
28
29
    /**
30
     * @var boolean
31
     * @config
32
     */
33
    private static $api_ssl = true;
34
35
    /**
36
     * @var string
37
     * @config
38
     */
39
    private static $api_version = 'v3';
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
        $this->setMailgunClient(new Mailgun(
59 12
            $config->api_key,
60 12
            $config->api_endpoint,
61 12
            $config->api_version,
62 12
            $config->api_ssl
63 12
        ));
64 12
    }
65
66
    /**
67
     * @param Mailgun $client
68
     * @return self
69
     */
70 12
    public function setMailgunClient(Mailgun $client)
71
    {
72 12
        $this->mailgunClient = $client;
73 12
        return $this;
74
    }
75
76
    /**
77
     * @return Mailgun
78
     */
79 1
    public function getMailgunClient()
80
    {
81 1
        return $this->mailgunClient;
82
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87 1
    public function sendPlain($to, $from, $subject, $plainContent, $attachments = [], $headers = [])
88
    {
89 1
        $this->sendMessage($to, $from, $subject, $htmlContent = '', $plainContent, $attachments, $headers);
90 1
    }
91
92
    /**
93
     * {@inheritdoc}
94
     */
95 1
    public function sendHTML($to, $from, $subject, $htmlContent, $attachments = [], $headers = [], $plainContent = '')
96
    {
97 1
        $this->sendMessage($to, $from, $subject, $htmlContent, $plainContent, $attachments, $headers);
98 1
    }
99
100
    /**
101
     * @param string $to
102
     * @param string $from
103
     * @param string $subject
104
     * @param string $content
105
     * @param string $plainContent
106
     * @param array $attachments
107
     * @param array $headers
108
     */
109 3
    protected function sendMessage($to, $from, $subject, $content, $plainContent, $attachments, $headers)
110
    {
111 3
        $domain = $this->config()->api_domain;
112 3
        $client = $this->getMailgunClient();
113 3
        $attachments = $this->prepareAttachments($attachments);
114
115 3
        if (isset($headers['X-Mailgunner-Batch-Message'])) {
116 1
            $builder = $client->BatchMessage($domain);
117 1
            unset($headers['X-Mailgunner-Batch-Message']);
118 1
        } else {
119 2
            $builder = $client->MessageBuilder();
120
        }
121
122
        try {
123 3
            $this->buildMessage($builder, $to, $from, $subject, $content, $plainContent, $attachments, $headers);
124
125 3
            if ($builder instanceof \Mailgun\Messages\BatchMessage) {
126 1
                $builder->finalize();
127 1
            } else {
128 2
                $client->sendMessage($domain, $builder->getMessage(), $builder->getFiles());
129
            }
130 3
        } catch (\Exception $e) {
131
            // Close and remove any temp files created for attachments, then let the exception bubble up
132 1
            $this->closeTempFileHandles();
133 1
            throw $e;
134
        }
135
136 2
        $this->closeTempFileHandles();
137 2
    }
138
139
    /**
140
     * @param MessageBuilder $builder
141
     * @param string $to
142
     * @param string $from
143
     * @param string $subject
144
     * @param string $content
145
     * @param string $plainContent
146
     * @param array $attachments
147
     * @param array $headers
148
     */
149 2
    protected function buildMessage(
150
        MessageBuilder $builder,
151
        $to,
152
        $from,
153
        $subject,
154
        $content,
155
        $plainContent,
156
        array $attachments,
157
        array $headers
158
    ) {
159
        // Add base info
160 2
        $parsedFrom = $this->parseAddresses($from);
161 2
        foreach ($parsedFrom as $email => $name) {
162 2
            $builder->setFromAddress($email, ['full_name' => $name]);
163 2
        }
164
        
165 2
        $builder->setSubject($subject);
166 2
        $builder->setHtmlBody($content);
167 2
        $builder->setTextBody($plainContent);
168
169
        // Add attachments
170 2
        foreach ($attachments as $attachment) {
171 2
            $builder->addAttachment($attachment['filePath'], $attachment['remoteName']);
172 2
        }
173
174
        // Parse Cc & Bcc headers out if they're set
175 2
        $ccAddresses = isset($headers['Cc']) ? $headers['Cc'] : '';
176 2
        $bccAddresses = isset($headers['Bcc']) ? $headers['Bcc'] : '';
177
178
        // We handle these ourselves, so can remove them from the list of headers
179 2
        unset($headers['Cc']);
180 2
        unset($headers['Bcc']);
181
182
        // Add remaining custom headers
183 2
        foreach ($headers as $name => $data) {
184 2
            $builder->addCustomHeader($name, $data);
185 2
        }
186
187
        // Add recipients. This is done last as the 'BatchMessage' message builder
188
        // will trigger sends for every 1000 addresses
189 2
        $to = $this->parseAddresses($to);
190 2
        foreach ($to as $email => $name) {
191 2
            $builder->addToRecipient($email, ['full_name' => $name]);
192 2
        }
193
194 2
        $ccAddresses = $this->parseAddresses($ccAddresses);
195 2
        foreach ($ccAddresses as $email => $name) {
196 2
            $builder->addCcRecipient($email, ['full_name' => $name]);
197 2
        }
198
199 2
        $bccAddresses = $this->parseAddresses($bccAddresses);
200 2
        foreach ($bccAddresses as $email => $name) {
201 2
            $builder->addBccRecipient($email, ['full_name' => $name]);
202 2
        }
203 2
    }
204
205
    /**
206
     * @todo This can't deal with mismatched quotes, or commas in names.
207
     *       E.g. "Smith, John" <[email protected]> or "John O'smith" <[email protected]>
208
     * @param string
209
     * @return array
210
     */
211 3
    protected function parseAddresses($addresses)
212
    {
213 3
        $parsed = [];
214
215 3
        $expr = '/\s*["\']?([^><,;"\']+)["\']?\s*((?:<[^><,]+>)?)\s*/';
216 3
        if (preg_match_all($expr, $addresses, $matches, PREG_SET_ORDER) > 0) {
217 3
            foreach ($matches as $result) {
218 3
                if (empty($result[2])) {
219
                    // If we couldn't parse out a name
220 3
                    $parsed[$result[1]] = '';
221 3
                } else {
222 3
                    $email = trim($result[2], '<>');
223 3
                    $parsed[$email] = trim($result[1]);
224
                }
225 3
            }
226 3
        }
227
228 3
        return $parsed;
229
    }
230
231
    /**
232
     * Prepare attachments for sending. SilverStripe extracts the content and
233
     * passes that to the mailer, so to save encoding it we just write them all
234
     * to individual files and let Mailgun deal with the rest.
235
     *
236
     * @todo Can we handle this better?
237
     * @param array $attachments
238
     * @return array
239
     */
240 2
    protected function prepareAttachments(array $attachments)
241
    {
242 2
        $prepared = [];
243
            
244 2
        foreach ($attachments as $attachment) {
245 2
            $tempFile = $this->writeToTempFile($attachment['contents']);
246
            
247 2
            $prepared[] = [
248 2
                'filePath' => $tempFile,
249 2
                'remoteName' => $attachment['filename']
250 2
            ];
251 2
        }
252
253 2
        return $prepared;
254
    }
255
256
    /**
257
     * @param string $contents
258
     * @return string
259
     */
260 2
    protected function writeToTempFile($contents)
261
    {
262 2
        $tempFile = tempnam(sys_get_temp_dir(), 'SS_MG_TMP');
263 2
        $fileHandle = fopen($tempFile, 'r+');
264 2
        fwrite($fileHandle, $contents);
265
266 2
        $this->tempFileHandles[] = [
267 2
            'handle' => $fileHandle,
268
            'path' => $tempFile
269 2
        ];
270
271 2
        return $tempFile;
272
    }
273
274
    /**
275
     * @return void
276
     */
277 1
    protected function closeTempFileHandles()
278
    {
279 1
        foreach ($this->tempFileHandles as $key => $data) {
280 1
            fclose($data['handle']);
281 1
            unlink($data['path']);
282 1
            unset($this->tempFileHandles[$key]);
283 1
        }
284 1
    }
285
}
286