Completed
Push — master ( 2a7fda...42e194 )
by Jacob
02:15
created

Archangel   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 397
Duplicated Lines 16.37 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 6
Bugs 1 Features 0
Metric Value
wmc 50
c 6
b 1
f 0
lcom 1
cbo 0
dl 65
loc 397
rs 8.6206

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 2
A addTo() 9 9 2
A addCC() 9 9 2
A addBCC() 9 9 2
A setFrom() 9 9 2
A setReplyTo() 9 9 2
A setSubject() 0 6 1
A setPlainMessage() 0 6 1
A setHTMLMessage() 0 6 1
A addAttachment() 0 10 1
A send() 0 13 2
B checkRequiredFields() 0 19 6
A buildTo() 0 4 1
C buildMessage() 14 57 11
C buildHeaders() 6 24 8
A buildAttachmentContent() 0 14 2
A getBoundary() 0 7 2
A getAlternativeBoundary() 0 7 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Archangel often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Archangel, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Jacobemerick\Archangel;
4
5
/**
6
 * This is the main class for Archangel mailer
7
 * For licensing and examples:
8
 * @see https://github.com/jacobemerick/archangel
9
 *
10
 * @author jacobemerick (http://home.jacobemerick.com/)
11
 */
12
class Archangel
13
{
14
15
    /** @var string $subject */
16
    protected $subject;
17
18
    /** @var array $to */
19
    protected $to = array();
20
21
    /** @var array $cc */
22
    protected $cc = array();
23
24
    /** @var array $bcc */
25
    protected $bcc = array();
26
27
    /** @var array $headers */
28
    protected $headers = array();
29
30
    /** @var string $plainMessage */
31
    protected $plainMessage;
32
33
    /** @var string $htmlMessage */
34
    protected $htmlMessage;
35
36
    /** @var array $attachments */
37
    protected $attachments = array();
38
39
    /** @var string $boundary */
40
    protected $boundary;
41
42
    /** @var string $alternativeBoundary */
43
    protected $alternativeBoundary;
44
45
    /** @var string LINE_BREAK */
46
    const LINE_BREAK = "\r\n";
47
48
    /**
49
     * @param string $mailer
50
     */
51
    public function __construct($mailer = null)
52
    {
53
        if (is_null($mailer)) {
54
            $mailer = sprintf('PHP/%s', phpversion());
55
        }
56
        $this->headers['X-Mailer'] = $mailer;
57
    }
58
59
    /**
60
     * Setter method for adding recipients
61
     *
62
     * @param string $address email address for the recipient
63
     * @param string $title   name of the recipient (optional)
64
65
     * @return object instantiated $this
66
     */
67 View Code Duplication
    public function addTo($address, $title = '')
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
        if (!empty($title)) {
70
            $address = sprintf('"%s" <%s>', $title, $address);
71
        }
72
        array_push($this->to, $address);
73
74
        return $this;
75
    }
76
77
    /**
78
     * Setter method for adding cc recipients
79
     *
80
     * @param string $address email address for the cc recipient
81
     * @param string $title   name of the cc recipient (optional)
82
     *
83
     * @return object instantiated $this
84
     */
85 View Code Duplication
    public function addCC($address, $title = '')
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...
86
    {
87
        if (!empty($title)) {
88
            $address = sprintf('"%s" <%s>', $title, $address);
89
        }
90
        array_push($this->cc, $address);
91
92
        return $this;
93
    }
94
95
    /**
96
     * Setter method for adding bcc recipients
97
     *
98
     * @param string $address email address for the bcc recipient
99
     * @param string $title   name of the bcc recipient (optional)
100
     *
101
     * @return object instantiated $this
102
     */
103 View Code Duplication
    public function addBCC($address, $title = '')
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...
104
    {
105
        if (!empty($title)) {
106
            $address = sprintf('"%s" <%s>', $title, $address);
107
        }
108
        array_push($this->bcc, $address);
109
110
        return $this;
111
    }
112
113
    /**
114
     * Setter method for setting the single 'from' field
115
     *
116
     * @param string $address email address for the sender
117
     * @param string $title   name of the sender (optional)
118
     *
119
     * @return object instantiated $this
120
     */
121 View Code Duplication
    public function setFrom($address, $title = '')
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...
122
    {
123
        if (!empty($title)) {
124
            $address = sprintf('"%s" <%s>', $title, $address);
125
        }
126
        $this->headers['From'] = $address;
127
128
        return $this;
129
    }
130
131
    /**
132
     * Setter method for setting the single 'reply-to' field
133
     *
134
     * @param string $address email address for the reply-to
135
     * @param string $title   name of the reply-to (optional)
136
     *
137
     * @return object instantiated $this
138
     */
139 View Code Duplication
    public function setReplyTo($address, $title = '')
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...
140
    {
141
        if (!empty($title)) {
142
            $address = sprintf('"%s" <%s>', $title, $address);
143
        }
144
        $this->headers['Reply-To'] = $address;
145
146
        return $this;
147
    }
148
149
    /**
150
     * Setter method for setting a subject
151
     *
152
     * @param string $subject subject for the email
153
     *
154
     * @return object instantiated $this
155
     */
156
    public function setSubject($subject)
157
    {
158
        $this->subject = $subject;
159
160
        return $this;
161
    }
162
163
    /**
164
     * Setter method for the plain text message
165
     *
166
     * @param string $message the plain-text message
167
     *
168
     * @return object instantiated $this
169
     */
170
    public function setPlainMessage($message)
171
    {
172
        $this->plainMessage = $message;
173
174
        return $this;
175
    }
176
177
    /**
178
     * Setter method for the html message
179
     *
180
     * @param string $message the html message
181
     *
182
     * @return object instantiated $this
183
     */
184
    public function setHTMLMessage($message)
185
    {
186
        $this->htmlMessage = $message;
187
188
        return $this;
189
    }
190
191
    /**
192
     * Setter method for adding attachments
193
     *
194
     * @param string $path  the full path of the attachment
195
     * @param string $type  mime type of the file
196
     * @param string $title the title of the attachment (optional)
197
     *
198
     * @return object instantiated $this
199
     */
200
    public function addAttachment($path, $type, $title = '')
201
    {
202
        array_push($this->attachments, array(
203
          'path' => $path,
204
          'type' => $type,
205
          'title' => $title,
206
        ));
207
208
        return $this;
209
    }
210
211
    /**
212
     * The executing step, the actual sending of the email
213
     * First checks to make sure the minimum fields are set (returns false if they are not)
214
     * Second it attempts to send the mail with php's mail() (returns false if it fails)
215
     *
216
     * return boolean whether or not the email was valid & sent
217
     */
218
    public function send()
219
    {
220
        if (!$this->checkRequiredFields()) {
221
            return false;
222
        }
223
224
        $to = $this->buildTo();
225
        $subject = $this->subject;
226
        $message = $this->buildMessage();
227
        $headers = $this->buildHeaders();
228
229
        return mail($to, $subject, $message, $headers);
230
    }
231
232
    /**
233
     * Call to check the minimum required fields
234
     *
235
     * @return boolean whether or not the email meets the minimum required fields
236
     */
237
    protected function checkRequiredFields()
238
    {
239
        if (empty($this->to)) {
240
            return false;
241
        }
242
        if (empty($this->subject)) {
243
            return false;
244
        }
245
246
        if (
247
            empty($this->plainMessage) &&
248
            empty($this->htmlMessage) &&
249
            empty($this->attachments)
250
        ) {
251
            return false;
252
        }
253
254
        return true;
255
    }
256
257
    /**
258
     * Build the recipients from 'to'
259
     *
260
     * @return string comma-separated lit of recipients
261
     */
262
    protected function buildTo()
263
    {
264
       return implode(', ', $this->to);
265
    }
266
267
    /**
268
     * Long, nasty creater of the actual message, with all the multipart logic you'd never want to see
269
     *
270
     * @return string email message
271
     */
272
    protected function buildMessage()
273
    {
274
        $messageString = '';
275
        
276
        if (!empty($this->attachments)) {
277
            $messageString .= "--{$this->getBoundary()}" . self::LINE_BREAK;
278
        }
279
        if (!empty($this->plainMessage) && !empty($this->htmlMessage)) {
280 View Code Duplication
            if (!empty($this->attachments)) {
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...
281
                $messageString .= "Content-Type: multipart/alternative; boundary={$this->getAlternativeBoundary()}" . self::LINE_BREAK;
282
                $messageString .= self::LINE_BREAK;
283
            }
284
            $messageString .= "--{$this->getAlternativeBoundary()}" . self::LINE_BREAK;
285
            $messageString .= 'Content-Type: text/plain; charset="iso-8859"' . self::LINE_BREAK;
286
            $messageString .= 'Content-Transfer-Encoding: 7bit' . self::LINE_BREAK;
287
            $messageString .= self::LINE_BREAK;
288
            $messageString .= $this->plainMessage;
289
            $messageString .= self::LINE_BREAK;
290
            $messageString .= "--{$this->getAlternativeBoundary()}" . self::LINE_BREAK;
291
            $messageString .= 'Content-Type: text/html; charset="iso-8859-1"' . self::LINE_BREAK;
292
            $messageString .= 'Content-Transfer-Encoding: 7bit' . self::LINE_BREAK;
293
            $messageString .= self::LINE_BREAK;
294
            $messageString .= $this->htmlMessage;
295
            $messageString .= self::LINE_BREAK;
296
            $messageString .= "--{$this->getAlternativeBoundary()}--" . self::LINE_BREAK;
297
            $messageString .= self::LINE_BREAK;
298
        } else if (!empty($this->plainMessage)) {
299 View Code Duplication
            if (!empty($this->attachments)) {
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...
300
                $messageString .= 'Content-Type: text/plain; charset="iso-8859"' . self::LINE_BREAK;
301
                $messageString .= 'Content-Transfer-Encoding: 7bit' . self::LINE_BREAK;
302
                $messageString .= self::LINE_BREAK;
303
            }
304
            $messageString .= $this->plainMessage;
305
            $messageString .= self::LINE_BREAK;
306
        } else if (!empty($this->htmlMessage)) {
307 View Code Duplication
            if (!empty($this->attachments)) {
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...
308
                $messageString .= 'Content-Type: text/html; charset="iso-8859-1"' . self::LINE_BREAK;
309
                $messageString .= 'Content-Transfer-Encoding: 7bit' . self::LINE_BREAK;
310
                $messageString .= self::LINE_BREAK;
311
            }
312
            $messageString .= $this->htmlMessage;
313
            $messageString .= self::LINE_BREAK;
314
        }
315
        if (!empty($this->attachments)) {
316
            foreach ($this->attachments as $attachment) {
317
                $messageString .= "--{$this->getBoundary()}" . self::LINE_BREAK;
318
                $messageString .= "Content-Type: {$attachment['type']}; name=\"{$attachment['title']}\"" . self::LINE_BREAK;
319
                $messageString .= 'Content-Transfer-Encoding: base64' . self::LINE_BREAK;
320
                $messageString .= 'Content-Disposition: attachment' . self::LINE_BREAK;
321
                $messageString .= self::LINE_BREAK;
322
                $messageString .= $this->buildAttachmentContent($attachment);
323
                $messageString .= self::LINE_BREAK;
324
            }
325
            $messageString .= "--{$this->getBoundary()}--" . self::LINE_BREAK;
326
        }
327
        return $messageString;
328
    }
329
330
331
    /**
332
     * Builder for the additional headers needed for multipart emails
333
     *
334
     * @return string headers needed for multipart
335
     */
336
    protected function buildHeaders()
337
    {
338
        $headerString = '';
339
        foreach ($this->headers as $key => $value) {
340
            $headerString .= sprintf('%s: %s', $key, $value) . self::LINE_BREAK;
341
        }
342
343 View Code Duplication
        if (!empty($this->cc)) {
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...
344
            $headerString .= 'CC: ' . implode(', ', $this->cc) . self::LINE_BREAK;
345
        }
346 View Code Duplication
        if (!empty($this->bcc)) {
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...
347
            $headerString .= 'BCC: ' . implode(', ', $this->bcc) . self::LINE_BREAK;
348
        }
349
        
350
        if (!empty($this->attachments)) {
351
            $headerString .= "Content-Type: multipart/mixed; boundary=\"{$this->getBoundary()}\"";
352
        } else if (!empty($this->plainMessage) && !empty($this->htmlMessage)) {
353
            $headerString .= "Content-Type: multipart/alternative; boundary=\"{$this->getAlternativeBoundary()}\"";
354
        } else if (!empty($this->htmlMessage)) {
355
            $headerString .= 'Content-type: text/html; charset="iso-8859-1"';
356
        }
357
358
        return $headerString;
359
    }
360
361
    /**
362
     * File reader for attachments
363
     *
364
     * @return string binary representation of file, base64'd
365
     */
366
    protected function buildAttachmentContent($attachment)
367
    {
368
        if (!file_exists($attachment['path'])) {
369
            return ''; // todo log error
370
        }
371
372
        $handle = fopen($attachment['path'], 'r');
373
        $contents = fread($handle, filesize($attachment['path']));
374
        fclose($handle);
375
376
        $contents = base64_encode($contents);
377
        $contents = chunk_split($contents);
378
        return $contents;
379
    }
380
381
    /**
382
     * Holder for the boundry logic
383
     * Not called/created unless it's needed
384
     *
385
     * @return  string  boundary
386
     */
387
    protected function getBoundary()
388
    {
389
        if (!isset($this->boundary)) {
390
            $this->boundary = sprintf('PHP-mixed-%s', uniqid());
391
        }
392
        return $this->boundary;
393
    }
394
395
    /**
396
     * Holder to create the alternative boundry logic
397
     * Not called/created unless it's needed
398
     *
399
     * @return string alternative boundary
400
     */
401
    protected function getAlternativeBoundary()
402
    {
403
        if (!isset($this->alternativeBoundary)) {
404
            $this->alternativeBoundary = sprintf('PHP-alternative-%s', uniqid());
405
        }
406
        return $this->alternativeBoundary;
407
    }
408
}
409