Completed
Pull Request — master (#27)
by
unknown
03:03
created

SimpleMail::addAttachmentVar()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 2
nop 2
crap 3
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
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
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
     * addAttachmentVar
276
     * 
277
     * @param string $data the data to send
278
     * @param string $filename the filename to use
279
     *
280
     * @return  self
281
     */
282
    public function addAttachmentVar($data, $filename = null)
283
    {
284
285
        if ($data == null  || $filename == null) throw new Exception("filename or data can't be null for memory attachment", 1);
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $filename of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 128 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
286 1
287
        $filename = $this->encodeUtf8($this->filterOther((string) $filename));
288 1
289 1
        $this->_attachments[] = array(
290
            'path' => null,
291
            'file' => $filename,
292
            'data' => chunk_split(base64_encode($data))
293
        );
294
        return $this;
295
    }
296
297 1
    /**
298
     * getAttachmentData
299 1
     *
300
     * @param string $path The path to the attachment file.
301
     *
302
     * @return string
303
     */
304
    public function getAttachmentData($path)
305
    {
306
        $filesize = filesize($path);
307
        $handle = fopen($path, "r");
308
        $attachment = fread($handle, $filesize);
309 2
        fclose($handle);
310
        return $attachment;
311 2
    }
312 2
313 1
    /**
314 1
     * addMailHeader
315 2
     *
316 2
     * @param string $header The header to add.
317
     * @param string $email  The email to add.
318
     * @param string $name   The name to add.
319
     *
320
     * @return self
321
     */
322
    public function addMailHeader($header, $email, $name = null)
323
    {
324 3
        $address = $this->formatHeader((string) $email, (string) $name);
325
        $this->_headers[] = sprintf('%s: %s', (string) $header, $address);
326 3
        return $this;
327
    }
328
329
    /**
330
     * addMailHeaders
331
     *
332
     * @param string $header The header to add.
333
     * @param array  $pairs  An array of name => email pairs.
334
     *
335
     * @return self
336 4
     */
337
    public function addMailHeaders($header, array $pairs)
338 4
    {
339
        if (count($pairs) === 0) {
340
            throw new InvalidArgumentException(
341
                'You must pass at least one name => email pair.'
342
            );
343
        }
344
        $addresses = array();
345
        foreach ($pairs as $name => $email) {
346 2
            $name = is_numeric($name) ? null : $name;
347
            $addresses[] = $this->formatHeader($email, $name);
348 2
        }
349 2
        $this->addGenericHeader($header, implode(',', $addresses));
350 2
        return $this;
351
    }
352 2
353
    /**
354
     * addGenericHeader
355
     *
356
     * @param string $header The generic header to add.
357
     * @param mixed  $value  The value of the header.
358
     *
359
     * @return self
360 1
     */
361
    public function addGenericHeader($header, $value)
362 1
    {
363 1
        $this->_headers[] = sprintf(
364 1
            '%s: %s',
365 1
            (string) $header,
366 1
            (string) $value
367 1
        );
368 1
        return $this;
369 1
    }
370 1
371
    /**
372 1
     * getHeaders
373 1
     *
374 1
     * Return the headers registered so far as an array.
375
     *
376 1
     * @return array
377
     */
378
    public function getHeaders()
379
    {
380
        return $this->_headers;
381
    }
382
383
    /**
384
     * setAdditionalParameters
385
     *
386 1
     * Such as "[email protected]
387
     *
388 1
     * @param string $additionalParameters The addition mail parameter.
389 1
     *
390
     * @return self
391 1
     */
392 1
    public function setParameters($additionalParameters)
393 1
    {
394 1
        $this->_params = (string) $additionalParameters;
395 1
        return $this;
396 1
    }
397 1
398 1
    /**
399
     * getAdditionalParameters
400 1
     *
401
     * @return string
402
     */
403
    public function getParameters()
404
    {
405
        return $this->_params;
406
    }
407
408
    /**
409 3
     * setWrap
410
     *
411 3
     * @param int $wrap The number of characters at which the message will wrap.
412 3
     *
413
     * @return self
414 3
     */
415 1
    public function setWrap($wrap = 78)
416
    {
417 1
        $wrap = (int) $wrap;
418
        if ($wrap < 1) {
419
            $wrap = 78;
420 2
        }
421 1
        $this->_wrap = $wrap;
422 1
        return $this;
423 1
    }
424 1
425
    /**
426
     * getWrap
427 2
     *
428
     * @return int
429
     */
430
    public function getWrap()
431
    {
432
        return $this->_wrap;
433
    }
434
435 1
    /**
436
     * hasAttachments
437 1
     * 
438
     * Checks if the email has any registered attachments.
439
     *
440
     * @return bool
441
     */
442
    public function hasAttachments()
443
    {
444
        return !empty($this->_attachments);
445 1
    }
446
447 1
    /**
448
     * assembleAttachment
449
     *
450
     * @return string
451
     */
452
    public function assembleAttachmentHeaders()
453
    {
454
        $head = array();
455
        $head[] = "MIME-Version: 1.0";
456
        $head[] = "Content-Type: multipart/mixed; boundary=\"{$this->_uid}\"";
457
458
        return join(PHP_EOL, $head);
459
    }
460
461 6
    /**
462
     * assembleAttachmentBody
463 6
     *
464 6
     * @return string
465 1
     */
466
    public function assembleAttachmentBody()
467 5
    {
468 5
        $body = array();
469
        $body[] = "This is a multi-part message in MIME format.";
470
        $body[] = "--{$this->_uid}";
471
        $body[] = "Content-type:text/html; charset=\"utf-8\"";
472
        $body[] = "Content-Transfer-Encoding: 7bit";
473
        $body[] = "";
474
        $body[] = $this->_message;
475
        $body[] = "";
476
        $body[] = "--{$this->_uid}";
477
478 8
        foreach ($this->_attachments as $attachment) {
479
            $body[] = $this->getAttachmentMimeTemplate($attachment);
480 8
        }
481 8
482 4
        return implode(PHP_EOL, $body) . '--';
483
    }
484 6
485
    /**
486
     * getAttachmentMimeTemplate
487
     *
488
     * @param array  $attachment An array containing 'file' and 'data' keys.
489
     *
490
     * @return string
491
     */
492
    public function getAttachmentMimeTemplate($attachment)
493
    {
494 8
        $file = $attachment['file'];
495
        $data = $attachment['data'];
496 8
497
        $head = array();
498
        $head[] = "Content-Type: application/octet-stream; name=\"{$file}\"";
499
        $head[] = "Content-Transfer-Encoding: base64";
500
        $head[] = "Content-Disposition: attachment; filename=\"{$file}\"";
501
        $head[] = "";
502
        $head[] = $data;
503
        $head[] = "";
504
        $head[] = "--{$this->_uid}";
505
506 4
        return implode(PHP_EOL, $head);
507
    }
508 4
509 4
    /**
510 4
     * send
511 4
     *
512 4
     * @return boolean
513 4
     * @throws \RuntimeException on no 'To: ' address to send to.
514
     */
515
    public function send()
516
    {
517
        $to = $this->getToForSend();
518
        $headers = $this->getHeadersForSend();
519
520
        if (empty($to)) {
521
            throw new \RuntimeException(
522
                'Unable to send, no To address has been set.'
523
            );
524
        }
525
526 12
        if ($this->hasAttachments()) {
527
            $message  = $this->assembleAttachmentBody();
528
            $headers .= PHP_EOL . $this->assembleAttachmentHeaders();
529 12
        } else {
530 12
            $message = $this->getWrapMessage();
531 12
        }
532 12
533 12
        return mail($to, $this->_subject, $message, $headers, $this->_params);
534 12
    }
535
536 12
    /**
537 12
     * debug
538 12
     *
539 12
     * @return string
540
     */
541
    public function debug()
542
    {
543
        return '<pre>' . print_r($this, true) . '</pre>';
544
    }
545
546
    /**
547
     * magic __toString function
548
     *
549
     * @return string
550
     */
551
    public function __toString()
552
    {
553 10
        return print_r($this, true);
554
    }
555
556 10
    /**
557 10
     * formatHeader
558 10
     *
559 10
     * Formats a display address for emails according to RFC2822 e.g.
560 10
     * Name <[email protected]>
561 10
     *
562 10
     * @param string $email The email address.
563 10
     * @param string $name  The display name.
564 10
     *
565 10
     * @return string
566
     */
567 10
    public function formatHeader($email, $name = null)
568 10
    {
569
        $email = $this->filterEmail((string) $email);
570
        if (empty($name)) {
571
            return $email;
572
        }
573
        $name = $this->encodeUtf8($this->filterName((string) $name));
574
        return sprintf('"%s" <%s>', $name, $email);
575
    }
576
577
    /**
578
     * encodeUtf8
579
     *
580
     * @param string $value The value to encode.
581 9
     *
582
     * @return string
583 9
     */
584
    public function encodeUtf8($value)
585
    {
586
        $value = trim($value);
587
        if (preg_match('/(\s)/', $value)) {
588
            return $this->encodeUtf8Words($value);
589
        }
590
        return $this->encodeUtf8Word($value);
591 3
    }
592
593 3
    /**
594 1
     * encodeUtf8Word
595
     *
596 2
     * @param string $value The word to encode.
597
     *
598
     * @return string
599
     */
600
    public function encodeUtf8Word($value)
601
    {
602
        return sprintf('=?UTF-8?B?%s?=', base64_encode($value));
603
    }
604 3
605
    /**
606 3
     * encodeUtf8Words
607 1
     *
608
     * @param string $value The words to encode.
609 2
     *
610
     * @return string
611
     */
612
    public function encodeUtf8Words($value)
613
    {
614
        $words = explode(' ', $value);
615
        $encoded = array();
616
        foreach ($words as $word) {
617 38
            $encoded[] = $this->encodeUtf8Word($word);
618
        }
619 38
        return join($this->encodeUtf8Word(' '), $encoded);
620
    }
621
622
    /**
623
     * filterEmail
624
     *
625
     * Removes any carriage return, line feed, tab, double quote, comma
626
     * and angle bracket characters before sanitizing the email address.
627 1
     *
628
     * @param string $email The email to filter.
629 1
     *
630
     * @return string
631
     */
632
    public function filterEmail($email)
633
    {
634
        $rule = array(
635
            "\r" => '',
636
            "\n" => '',
637
            "\t" => '',
638
            '"'  => '',
639
            ','  => '',
640
            '<'  => '',
641
            '>'  => ''
642
        );
643
        $email = strtr($email, $rule);
644
        $email = filter_var($email, FILTER_SANITIZE_EMAIL);
645
        return $email;
646
    }
647
648
    /**
649
     * filterName
650
     *
651
     * Removes any carriage return, line feed or tab characters. Replaces
652
     * double quotes with single quotes and angle brackets with square
653
     * brackets, before sanitizing the string and stripping out html tags.
654
     *
655
     * @param string $name The name to filter.
656
     *
657
     * @return string
658
     */
659
    public function filterName($name)
660
    {
661
        $rule = array(
662
            "\r" => '',
663
            "\n" => '',
664
            "\t" => '',
665
            '"'  => "'",
666
            '<'  => '[',
667
            '>'  => ']',
668
        );
669
        $filtered = filter_var(
670
            $name,
671
            FILTER_SANITIZE_STRING,
672
            FILTER_FLAG_NO_ENCODE_QUOTES
673
        );
674
        return trim(strtr($filtered, $rule));
675
    }
676
677
    /**
678
     * filterOther
679
     *
680
     * Removes ASCII control characters including any carriage return, line
681
     * feed or tab characters.
682
     *
683
     * @param string $data The data to filter.
684
     *
685
     * @return string
686
     */
687
    public function filterOther($data)
688
    {
689
        return filter_var($data, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);
690
    }
691
692
    /**
693
     * getHeadersForSend
694
     *
695
     * @return string
696
     */
697
    public function getHeadersForSend()
698
    {
699
        if (empty($this->_headers)) {
700
            return '';
701
        }
702
        return join(PHP_EOL, $this->_headers);
703
    }
704
705
    /**
706
     * getToForSend
707
     *
708
     * @return string
709
     */
710
    public function getToForSend()
711
    {
712
        if (empty($this->_to)) {
713
            return '';
714
        }
715
        return join(', ', $this->_to);
716
    }
717
718
    /**
719
     * getUniqueId
720
     *
721
     * @return string
722
     */
723
    public function getUniqueId()
724
    {
725
        return md5(uniqid(time()));
726
    }
727
728
    /**
729
     * getWrapMessage
730
     *
731
     * @return string
732
     */
733
    public function getWrapMessage()
734
    {
735
        return wordwrap($this->_message, $this->_wrap);
736
    }
737
}
738