SimpleMail   C
last analyzed

Complexity

Total Complexity 56

Size/Duplication

Total Lines 682
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 56
lcom 1
cbo 0
dl 0
loc 682
ccs 181
cts 181
cp 1
rs 5.389
c 0
b 0
f 0

42 Methods

Rating   Name   Duplication   Size   Complexity  
A make() 0 4 1
A __construct() 0 4 1
A reset() 0 12 1
A setTo() 0 5 1
A getTo() 0 4 1
A setFrom() 0 5 1
A setCc() 0 4 1
A setBcc() 0 4 1
A setReplyTo() 0 4 1
A setHtml() 0 6 1
A setSubject() 0 7 1
A getSubject() 0 4 1
A setMessage() 0 5 1
A getMessage() 0 4 1
A addAttachment() 0 12 3
A getAttachmentData() 0 8 1
A addMailHeader() 0 6 1
A addMailHeaders() 0 15 4
A addGenericHeader() 0 9 1
A getHeaders() 0 4 1
A setParameters() 0 5 1
A getParameters() 0 4 1
A setWrap() 0 9 2
A getWrap() 0 4 1
A hasAttachments() 0 4 1
A assembleAttachmentHeaders() 0 8 1
A assembleAttachmentBody() 0 18 2
A getAttachmentMimeTemplate() 0 16 1
A send() 0 20 3
A debug() 0 4 1
A __toString() 0 4 1
A formatHeader() 0 9 2
A encodeUtf8() 0 8 2
A encodeUtf8Word() 0 4 1
A encodeUtf8Words() 0 9 2
A filterEmail() 0 15 1
A filterName() 0 17 1
A filterOther() 0 4 1
A getHeadersForSend() 0 7 2
A getToForSend() 0 7 2
A getUniqueId() 0 4 1
A getWrapMessage() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like SimpleMail 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 SimpleMail, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Simple Mail
4
 *
5
 * A simple PHP wrapper class for sending email using the mail() method.
6
 *
7
 * PHP version > 5.2
8
 *
9
 * LICENSE: This source file is subject to the MIT license, which is
10
 * available through the world-wide-web at the following URI:
11
 * http://github.com/eoghanobrien/php-simple-mail/LICENCE.txt
12
 *
13
 * @category  SimpleMail
14
 * @package   SimpleMail
15
 * @author    Eoghan O'Brien <[email protected]>
16
 * @copyright 2009 - 2017 Eoghan O'Brien
17
 * @license   http://github.com/eoghanobrien/php-simple-mail/LICENCE.txt MIT
18
 * @version   1.7.1
19
 * @link      http://github.com/eoghanobrien/php-simple-mail
20
 */
21
22
/**
23
 * Simple Mail class.
24
 *
25
 * @category  SimpleMail
26
 * @package   SimpleMail
27
 * @author    Eoghan O'Brien <[email protected]>
28
 * @copyright 2009 - 2017 Eoghan O'Brien
29
 * @license   http://github.com/eoghanobrien/php-simple-mail/LICENCE.txt MIT
30
 * @version   1.7.1
31
 * @link      http://github.com/eoghanobrien/php-simple-mail
32
 */
33
class SimpleMail
34
{
35
    /**
36
     * @var int $_wrap
37
     */
38
    protected $_wrap = 78;
39
40
    /**
41
     * @var array $_to
42
     */
43
    protected $_to = array();
44
45
    /**
46
     * @var string $_subject
47
     */
48
    protected $_subject;
49
50
    /**
51
     * @var string $_message
52
     */
53
    protected $_message;
54
55
    /**
56
     * @var array $_headers
57
     */
58
    protected $_headers = array();
59
60
    /**
61
     * @var string $_parameters
62
     */
63
    protected $_params;
64
65
    /**
66
     * @var array $_attachments
67
     */
68
    protected $_attachments = array();
69
70
    /**
71
     * @var string $_uid
72
     */
73
    protected $_uid;
74
75
    /**
76
     * Named constructor.
77
     *
78
     * @return static
79
     */
80
    public static function make()
81 38
    {
82
        return new SimpleMail();
83 38
    }
84 38
85
    /**
86
     * __construct
87
     *
88
     * Resets the class properties.
89
     */
90
    public function __construct()
91
    {
92
        $this->reset();
93 38
    }
94
95 38
    /**
96 38
     * reset
97 38
     *
98 38
     * Resets all properties to initial state.
99 38
     *
100 38
     * @return self
101 38
     */
102 38
    public function reset()
103 38
    {
104
        $this->_to = array();
105
        $this->_headers = array();
106
        $this->_subject = null;
107
        $this->_message = null;
108
        $this->_wrap = 78;
109
        $this->_params = null;
110
        $this->_attachments = array();
111
        $this->_uid = $this->getUniqueId();
112
        return $this;
113
    }
114 4
115
    /**
116 4
     * setTo
117 4
     *
118
     * @param string $email The email address to send to.
119
     * @param string $name  The name of the person to send to.
120
     *
121
     * @return self
122
     */
123
    public function setTo($email, $name)
124
    {
125
        $this->_to[] = $this->formatHeader((string) $email, (string) $name);
126
        return $this;
127 2
    }
128
129 2
    /**
130
     * getTo
131
     *
132
     * Return an array of formatted To addresses.
133
     *
134
     * @return array
135
     */
136
    public function getTo()
137
    {
138
        return $this->_to;
139 3
    }
140
141 3
    /**
142 3
     * setFrom
143 3
     *
144 3
     * @param string $email The email to send as from.
145
     * @param string $name  The name to send as from.
146
     *
147
     * @return self
148
     */
149
    public function setFrom($email, $name)
150
    {
151
        $this->addMailHeader('From', (string) $email, (string) $name);
152 1
        return $this;
153
    }
154 1
155
    /**
156
     * setCc
157
     *
158
     * @param array  $pairs  An array of name => email pairs.
159
     *
160
     * @return self
161
     */
162
    public function setCc(array $pairs)
163
    {
164 3
        return $this->addMailHeaders('Cc', $pairs);
165
    }
166 3
167 3
    /**
168
     * setBcc
169
     *
170
     * @param array  $pairs  An array of name => email pairs.
171
     *
172
     * @return self
173
     */
174
    public function setBcc(array $pairs)
175 1
    {
176
        return $this->addMailHeaders('Bcc', $pairs);
177 1
    }
178
179
    /**
180
     * setReplyTo
181
     *
182
     * @param string $email
183
     * @param string $name
184
     *
185
     * @return self
186
     */
187
    public function setReplyTo($email, $name = null)
188
    {
189 3
        return $this->addMailHeader('Reply-To', $email, $name);
190
    }
191 3
192 3
    /**
193 3
     * setHtml
194 3
     *
195 3
     * @return self
196 3
     */
197 3
    public function setHtml()
198 3
    {
199
        return $this->addGenericHeader(
200
            'Content-Type', 'text/html; charset="utf-8"'
201
        );
202
    }
203
204
    /**
205
     * setSubject
206
     *
207
     * @param string $subject The email subject
208 3
     *
209
     * @return self
210 3
     */
211 3
    public function setSubject($subject)
212 3
    {
213 3
        $this->_subject = $this->encodeUtf8(
214 3
            $this->filterOther((string) $subject)
215
        );
216
        return $this;
217
    }
218
219
    /**
220
     * getSubject function.
221
     *
222
     * @return string
223
     */
224
    public function getSubject()
225 3
    {
226
        return $this->_subject;
227 3
    }
228 3
229
    /**
230
     * setMessage
231
     *
232
     * @param string $message The message to send.
233
     *
234
     * @return self
235
     */
236
    public function setMessage($message)
237
    {
238
        $this->_message = str_replace("\n.", "\n..", (string) $message);
239
        return $this;
240 3
    }
241
242 3
    /**
243 3
     * getMessage
244 3
     *
245
     * @return string
246
     */
247
    public function getMessage()
248
    {
249
        return $this->_message;
250
    }
251
252
    /**
253
     * addAttachment
254
     *
255 1
     * @param string $path The file path to the attachment.
256
     * @param string $filename The filename of the attachment when emailed.
257 1
     * @param null $data
258 1
     * 
259 1
     * @return self
260
     */
261 1
    public function addAttachment($path, $filename = null, $data = null)
262 1
    {
263
        $filename = empty($filename) ? basename($path) : $filename;
264
        $filename = $this->encodeUtf8($this->filterOther((string) $filename));
265
        $data = empty($data) ? $this->getAttachmentData($path) : $data;
266
        $this->_attachments[] = array(
267
            'path' => $path,
268
            'file' => $filename,
269
            'data' => chunk_split(base64_encode($data))
270
        );
271
        return $this;
272 2
    }
273
274 2
    /**
275
     * getAttachmentData
276
     *
277
     * @param string $path The path to the attachment file.
278
     *
279
     * @return string
280
     */
281
    public function getAttachmentData($path)
282
    {
283
        $filesize = filesize($path);
284
        $handle = fopen($path, "r");
285
        $attachment = fread($handle, $filesize);
286 1
        fclose($handle);
287
        return $attachment;
288 1
    }
289 1
290
    /**
291
     * addMailHeader
292
     *
293
     * @param string $header The header to add.
294
     * @param string $email  The email to add.
295
     * @param string $name   The name to add.
296
     *
297 1
     * @return self
298
     */
299 1
    public function addMailHeader($header, $email, $name = null)
300
    {
301
        $address = $this->formatHeader((string) $email, (string) $name);
302
        $this->_headers[] = sprintf('%s: %s', (string) $header, $address);
303
        return $this;
304
    }
305
306
    /**
307
     * addMailHeaders
308
     *
309 2
     * @param string $header The header to add.
310
     * @param array  $pairs  An array of name => email pairs.
311 2
     *
312 2
     * @return self
313 1
     */
314 1
    public function addMailHeaders($header, array $pairs)
315 2
    {
316 2
        if (count($pairs) === 0) {
317
            throw new InvalidArgumentException(
318
                'You must pass at least one name => email pair.'
319
            );
320
        }
321
        $addresses = array();
322
        foreach ($pairs as $name => $email) {
323
            $name = is_numeric($name) ? null : $name;
324 3
            $addresses[] = $this->formatHeader($email, $name);
325
        }
326 3
        $this->addGenericHeader($header, implode(',', $addresses));
327
        return $this;
328
    }
329
330
    /**
331
     * addGenericHeader
332
     *
333
     * @param string $header The generic header to add.
334
     * @param mixed  $value  The value of the header.
335
     *
336 4
     * @return self
337
     */
338 4
    public function addGenericHeader($header, $value)
339
    {
340
        $this->_headers[] = sprintf(
341
            '%s: %s',
342
            (string) $header,
343
            (string) $value
344
        );
345
        return $this;
346 2
    }
347
348 2
    /**
349 2
     * getHeaders
350 2
     *
351
     * Return the headers registered so far as an array.
352 2
     *
353
     * @return array
354
     */
355
    public function getHeaders()
356
    {
357
        return $this->_headers;
358
    }
359
360 1
    /**
361
     * setAdditionalParameters
362 1
     *
363 1
     * Such as "[email protected]
364 1
     *
365 1
     * @param string $additionalParameters The addition mail parameter.
366 1
     *
367 1
     * @return self
368 1
     */
369 1
    public function setParameters($additionalParameters)
370 1
    {
371
        $this->_params = (string) $additionalParameters;
372 1
        return $this;
373 1
    }
374 1
375
    /**
376 1
     * getAdditionalParameters
377
     *
378
     * @return string
379
     */
380
    public function getParameters()
381
    {
382
        return $this->_params;
383
    }
384
385
    /**
386 1
     * setWrap
387
     *
388 1
     * @param int $wrap The number of characters at which the message will wrap.
389 1
     *
390
     * @return self
391 1
     */
392 1
    public function setWrap($wrap = 78)
393 1
    {
394 1
        $wrap = (int) $wrap;
395 1
        if ($wrap < 1) {
396 1
            $wrap = 78;
397 1
        }
398 1
        $this->_wrap = $wrap;
399
        return $this;
400 1
    }
401
402
    /**
403
     * getWrap
404
     *
405
     * @return int
406
     */
407
    public function getWrap()
408
    {
409 3
        return $this->_wrap;
410
    }
411 3
412 3
    /**
413
     * hasAttachments
414 3
     * 
415 1
     * Checks if the email has any registered attachments.
416
     *
417 1
     * @return bool
418
     */
419
    public function hasAttachments()
420 2
    {
421 1
        return !empty($this->_attachments);
422 1
    }
423 1
424 1
    /**
425
     * assembleAttachment
426
     *
427 2
     * @return string
428
     */
429
    public function assembleAttachmentHeaders()
430
    {
431
        $head = array();
432
        $head[] = "MIME-Version: 1.0";
433
        $head[] = "Content-Type: multipart/mixed; boundary=\"{$this->_uid}\"";
434
435 1
        return join(PHP_EOL, $head);
436
    }
437 1
438
    /**
439
     * assembleAttachmentBody
440
     *
441
     * @return string
442
     */
443
    public function assembleAttachmentBody()
444
    {
445 1
        $body = array();
446
        $body[] = "This is a multi-part message in MIME format.";
447 1
        $body[] = "--{$this->_uid}";
448
        $body[] = "Content-Type: text/html; charset=\"utf-8\"";
449
        $body[] = "Content-Transfer-Encoding: quoted-printable";
450
        $body[] = "";
451
        $body[] = quoted_printable_encode($this->_message);
452
        $body[] = "";
453
        $body[] = "--{$this->_uid}";
454
455
        foreach ($this->_attachments as $attachment) {
456
            $body[] = $this->getAttachmentMimeTemplate($attachment);
457
        }
458
459
        return implode(PHP_EOL, $body) . '--';
460
    }
461 6
462
    /**
463 6
     * getAttachmentMimeTemplate
464 6
     *
465 1
     * @param array  $attachment An array containing 'file' and 'data' keys.
466
     *
467 5
     * @return string
468 5
     */
469
    public function getAttachmentMimeTemplate($attachment)
470
    {
471
        $file = $attachment['file'];
472
        $data = $attachment['data'];
473
474
        $head = array();
475
        $head[] = "Content-Type: application/octet-stream; name=\"{$file}\"";
476
        $head[] = "Content-Transfer-Encoding: base64";
477
        $head[] = "Content-Disposition: attachment; filename=\"{$file}\"";
478 8
        $head[] = "";
479
        $head[] = $data;
480 8
        $head[] = "";
481 8
        $head[] = "--{$this->_uid}";
482 4
483
        return implode(PHP_EOL, $head);
484 6
    }
485
486
    /**
487
     * send
488
     *
489
     * @return boolean
490
     * @throws \RuntimeException on no 'To: ' address to send to.
491
     */
492
    public function send()
493
    {
494 8
        $to = $this->getToForSend();
495
        $headers = $this->getHeadersForSend();
496 8
497
        if (empty($to)) {
498
            throw new \RuntimeException(
499
                'Unable to send, no To address has been set.'
500
            );
501
        }
502
503
        if ($this->hasAttachments()) {
504
            $message  = $this->assembleAttachmentBody();
505
            $headers .= PHP_EOL . $this->assembleAttachmentHeaders();
506 4
        } else {
507
            $message = $this->getWrapMessage();
508 4
        }
509 4
510 4
        return mail($to, $this->_subject, $message, $headers, $this->_params);
511 4
    }
512 4
513 4
    /**
514
     * debug
515
     *
516
     * @return string
517
     */
518
    public function debug()
519
    {
520
        return '<pre>' . print_r($this, true) . '</pre>';
521
    }
522
523
    /**
524
     * magic __toString function
525
     *
526 12
     * @return string
527
     */
528
    public function __toString()
529 12
    {
530 12
        return print_r($this, true);
531 12
    }
532 12
533 12
    /**
534 12
     * formatHeader
535
     *
536 12
     * Formats a display address for emails according to RFC2822 e.g.
537 12
     * Name <[email protected]>
538 12
     *
539 12
     * @param string $email The email address.
540
     * @param string $name  The display name.
541
     *
542
     * @return string
543
     */
544
    public function formatHeader($email, $name = null)
545
    {
546
        $email = $this->filterEmail((string) $email);
547
        if (empty($name)) {
548
            return $email;
549
        }
550
        $name = $this->encodeUtf8($this->filterName((string) $name));
551
        return sprintf('"%s" <%s>', $name, $email);
552
    }
553 10
554
    /**
555
     * encodeUtf8
556 10
     *
557 10
     * @param string $value The value to encode.
558 10
     *
559 10
     * @return string
560 10
     */
561 10
    public function encodeUtf8($value)
562 10
    {
563 10
        $value = trim($value);
564 10
        if (preg_match('/(\s)/', $value)) {
565 10
            return $this->encodeUtf8Words($value);
566
        }
567 10
        return $this->encodeUtf8Word($value);
568 10
    }
569
570
    /**
571
     * encodeUtf8Word
572
     *
573
     * @param string $value The word to encode.
574
     *
575
     * @return string
576
     */
577
    public function encodeUtf8Word($value)
578
    {
579
        return sprintf('=?UTF-8?B?%s?=', base64_encode($value));
580
    }
581 9
582
    /**
583 9
     * encodeUtf8Words
584
     *
585
     * @param string $value The words to encode.
586
     *
587
     * @return string
588
     */
589
    public function encodeUtf8Words($value)
590
    {
591 3
        $words = explode(' ', $value);
592
        $encoded = array();
593 3
        foreach ($words as $word) {
594 1
            $encoded[] = $this->encodeUtf8Word($word);
595
        }
596 2
        return join($this->encodeUtf8Word(' '), $encoded);
597
    }
598
599
    /**
600
     * filterEmail
601
     *
602
     * Removes any carriage return, line feed, tab, double quote, comma
603
     * and angle bracket characters before sanitizing the email address.
604 3
     *
605
     * @param string $email The email to filter.
606 3
     *
607 1
     * @return string
608
     */
609 2
    public function filterEmail($email)
610
    {
611
        $rule = array(
612
            "\r" => '',
613
            "\n" => '',
614
            "\t" => '',
615
            '"'  => '',
616
            ','  => '',
617 38
            '<'  => '',
618
            '>'  => ''
619 38
        );
620
        $email = strtr($email, $rule);
621
        $email = filter_var($email, FILTER_SANITIZE_EMAIL);
622
        return $email;
623
    }
624
625
    /**
626
     * filterName
627 1
     *
628
     * Removes any carriage return, line feed or tab characters. Replaces
629 1
     * double quotes with single quotes and angle brackets with square
630
     * brackets, before sanitizing the string and stripping out html tags.
631
     *
632
     * @param string $name The name to filter.
633
     *
634
     * @return string
635
     */
636
    public function filterName($name)
637
    {
638
        $rule = array(
639
            "\r" => '',
640
            "\n" => '',
641
            "\t" => '',
642
            '"'  => "'",
643
            '<'  => '[',
644
            '>'  => ']',
645
        );
646
        $filtered = filter_var(
647
            $name,
648
            FILTER_SANITIZE_STRING,
649
            FILTER_FLAG_NO_ENCODE_QUOTES
650
        );
651
        return trim(strtr($filtered, $rule));
652
    }
653
654
    /**
655
     * filterOther
656
     *
657
     * Removes ASCII control characters including any carriage return, line
658
     * feed or tab characters.
659
     *
660
     * @param string $data The data to filter.
661
     *
662
     * @return string
663
     */
664
    public function filterOther($data)
665
    {
666
        return filter_var($data, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);
667
    }
668
669
    /**
670
     * getHeadersForSend
671
     *
672
     * @return string
673
     */
674
    public function getHeadersForSend()
675
    {
676
        if (empty($this->_headers)) {
677
            return '';
678
        }
679
        return join(PHP_EOL, $this->_headers);
680
    }
681
682
    /**
683
     * getToForSend
684
     *
685
     * @return string
686
     */
687
    public function getToForSend()
688
    {
689
        if (empty($this->_to)) {
690
            return '';
691
        }
692
        return join(', ', $this->_to);
693
    }
694
695
    /**
696
     * getUniqueId
697
     *
698
     * @return string
699
     */
700
    public function getUniqueId()
701
    {
702
        return md5(uniqid(time()));
703
    }
704
705
    /**
706
     * getWrapMessage
707
     *
708
     * @return string
709
     */
710
    public function getWrapMessage()
711
    {
712
        return wordwrap($this->_message, $this->_wrap);
713
    }
714
}
715