Completed
Push — master ( 85dea1...7e6628 )
by Jacob
02:09
created

Archangel::buildHtmlMessageHeader()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 8
rs 9.4285
cc 1
eloc 5
nc 1
nop 0
1
<?php
2
3
namespace Jacobemerick\Archangel;
4
5
use Psr\Log\LoggerAwareInterface;
6
use Psr\Log\LoggerInterface;
7
use Psr\Log\NullLogger;
8
9
/**
10
 * This is the main class for Archangel mailer
11
 * For licensing and examples:
12
 * @see https://github.com/jacobemerick/archangel
13
 *
14
 * @author jacobemerick (http://home.jacobemerick.com/)
15
 */
16
class Archangel implements LoggerAwareInterface
17
{
18
19
    /** @var string $subject */
20
    protected $subject;
21
22
    /** @var array $toAddresses */
23
    protected $toAddresses = array();
24
25
    /** @var array $headers */
26
    protected $headers = array();
27
28
    /** @var string $plainMessage */
29
    protected $plainMessage;
30
31
    /** @var string $htmlMessage */
32
    protected $htmlMessage;
33
34
    /** @var array $attachments */
35
    protected $attachments = array();
36
37
    /** @var string $boundaryMixed */
38
    protected $boundaryMixed;
39
40
    /** @var string $boundaryAlternative */
41
    protected $boundaryAlternative;
42
43
    /** @var LoggerInterface */
44
    protected $logger;
45
46
    /** @var string LINE_BREAK */
47
    const LINE_BREAK = "\r\n";
48
49
    /**
50
     * @param string $mailer
51
     */
52
    public function __construct($mailer = null)
53
    {
54
        if (is_null($mailer)) {
55
            $mailer = sprintf('PHP/%s', phpversion());
56
        }
57
        $this->headers['X-Mailer'] = $mailer;
58
59
        $this->logger = new NullLogger();
60
        $this->boundaryMixed = sprintf('PHP-mixed-%s', uniqid());
61
        $this->boundaryAlternative = sprintf('PHP-alternative-%s', uniqid());
62
    }
63
64
    /**
65
     * @param LoggerInterface $logger
66
     *
67
     * @return $this;
0 ignored issues
show
Documentation introduced by
The doc-type $this; could not be parsed: Expected "|" or "end of type", but got ";" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
68
     */
69
    public function setLogger(LoggerInterface $logger)
70
    {
71
        $this->logger = $logger;
72
73
        return $this;
74
    }
75
76
    /**
77
     * Setter method for adding recipients
78
     *
79
     * @param string $address email address for the recipient
80
     * @param string $title   name of the recipient (optional)
81
82
     * @return object instantiated $this
83
     */
84
    public function addTo($address, $title = '')
85
    {
86
        array_push(
87
            $this->toAddresses,
88
            $this->formatEmailAddress($address, $title)
89
        );
90
91
        return $this;
92
    }
93
94
    /**
95
     * Setter method for adding cc recipients
96
     *
97
     * @param string $address email address for the cc recipient
98
     * @param string $title   name of the cc recipient (optional)
99
     *
100
     * @return object instantiated $this
101
     */
102 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...
103
    {
104
        if (!isset($this->headers['CC'])) {
105
            $this->headers['CC'] = array();
106
        }
107
108
        array_push(
109
            $this->headers['CC'],
110
            $this->formatEmailAddress($address, $title)
111
        );
112
113
        return $this;
114
    }
115
116
    /**
117
     * Setter method for adding bcc recipients
118
     *
119
     * @param string $address email address for the bcc recipient
120
     * @param string $title   name of the bcc recipient (optional)
121
     *
122
     * @return object instantiated $this
123
     */
124 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...
125
    {
126
        if (!isset($this->headers['BCC'])) {
127
            $this->headers['BCC'] = array();
128
        }
129
130
        array_push(
131
            $this->headers['BCC'],
132
            $this->formatEmailAddress($address, $title)
133
        );
134
135
        return $this;
136
    }
137
138
    /**
139
     * Setter method for setting the single 'from' field
140
     *
141
     * @param string $address email address for the sender
142
     * @param string $title   name of the sender (optional)
143
     *
144
     * @return object instantiated $this
145
     */
146
    public function setFrom($address, $title = '')
147
    {
148
        $this->headers['From'] = $this->formatEmailAddress($address, $title);
149
150
        return $this;
151
    }
152
153
    /**
154
     * Setter method for setting the single 'reply-to' field
155
     *
156
     * @param string $address email address for the reply-to
157
     * @param string $title   name of the reply-to (optional)
158
     *
159
     * @return object instantiated $this
160
     */
161
    public function setReplyTo($address, $title = '')
162
    {
163
        $this->headers['Reply-To'] = $this->formatEmailAddress($address, $title);
164
165
        return $this;
166
    }
167
168
    /**
169
     * @param string $address
170
     * @param string $title
171
     *
172
     * @return string
173
     */
174
    protected function formatEmailAddress($address, $title)
175
    {
176
        if (!empty($title)) {
177
            $address = sprintf('"%s" <%s>', $title, $address);
178
        }
179
        return $address;
180
    }
181
182
    /**
183
     * Setter method for setting a subject
184
     *
185
     * @param string $subject subject for the email
186
     *
187
     * @return object instantiated $this
188
     */
189
    public function setSubject($subject)
190
    {
191
        $this->subject = $subject;
192
193
        return $this;
194
    }
195
196
    /**
197
     * Setter method for the plain text message
198
     *
199
     * @param string $message the plain-text message
200
     *
201
     * @return object instantiated $this
202
     */
203
    public function setPlainMessage($message)
204
    {
205
        $this->plainMessage = $message;
206
207
        return $this;
208
    }
209
210
    /**
211
     * Setter method for the html message
212
     *
213
     * @param string $message the html message
214
     *
215
     * @return object instantiated $this
216
     */
217
    public function setHTMLMessage($message)
218
    {
219
        $this->htmlMessage = $message;
220
221
        return $this;
222
    }
223
224
    /**
225
     * Setter method for adding attachments
226
     *
227
     * @param string $path  the full path of the attachment
228
     * @param string $type  mime type of the file
229
     * @param string $title the title of the attachment (optional)
230
     *
231
     * @return object instantiated $this
232
     */
233
    public function addAttachment($path, $type, $title = '')
234
    {
235
        array_push($this->attachments, array(
236
          'path' => $path,
237
          'type' => $type,
238
          'title' => $title,
239
        ));
240
241
        return $this;
242
    }
243
244
    /**
245
     * The executing step, the actual sending of the email
246
     * First checks to make sure the minimum fields are set (returns false if they are not)
247
     * Second it attempts to send the mail with php's mail() (returns false if it fails)
248
     *
249
     * return boolean whether or not the email was valid & sent
250
     */
251
    public function send()
252
    {
253
        if (!$this->checkRequiredFields()) {
254
            return false;
255
        }
256
257
        $recipients = $this->buildTo();
258
        $subject = $this->subject;
259
        $message = (empty($this->attachments)) ? $this->buildMessage() : $this->buildHtmlMessage();
0 ignored issues
show
Bug introduced by
The method buildHtmlMessage() does not exist on Jacobemerick\Archangel\Archangel. Did you maybe mean buildHtmlMessageHeader()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
260
        $headers = $this->buildHeaders();
261
262
        return mail($recipients, $subject, $message, $headers);
263
    }
264
265
    /**
266
     * Call to check the minimum required fields
267
     *
268
     * @return boolean whether or not the email meets the minimum required fields
269
     */
270
    protected function checkRequiredFields()
271
    {
272
        if (empty($this->toAddresses)) {
273
            return false;
274
        }
275
        if (empty($this->subject)) {
276
            return false;
277
        }
278
279
        if (empty($this->plainMessage) && empty($this->htmlMessage) && empty($this->attachments)) {
280
            return false;
281
        }
282
283
        return true;
284
    }
285
286
    /**
287
     * Build the recipients from 'to'
288
     *
289
     * @return string comma-separated lit of recipients
290
     */
291
    protected function buildTo()
292
    {
293
        return implode(', ', $this->toAddresses);
294
    }
295
296
    /**
297
     * Returns a simple email message without attachments
298
     *
299
     * @return string email message
300
     */
301
    protected function buildMessage()
302
    {
303
        if (empty($this->plainMessage) && empty($this->htmlMessage)) {
304
            return '';
305
        }
306
        if (!empty($this->plainMessage) && empty($this->htmlMessage)) {
307
            return $this->plainMessage;
308
        }
309
        if (empty($this->plainMessage) && !empty($this->htmlMessage)) {
310
            return $this->htmlMessage;
311
        }
312
313
        $message = array();
314
        array_push($message, "--{$this->boundaryAlternative}");
315
        $message += $this->buildPlainMessageHeaders();
0 ignored issues
show
Bug introduced by
The method buildPlainMessageHeaders() does not exist on Jacobemerick\Archangel\Archangel. Did you maybe mean buildPlainMessageHeader()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
316
        array_push($message, $this->plainMessage);
317
        array_push($message, "--{$this->boundaryAlternative}");
318
        $message += $this->buildHtmlMessageHeaders();
0 ignored issues
show
Bug introduced by
The method buildHtmlMessageHeaders() does not exist on Jacobemerick\Archangel\Archangel. Did you maybe mean buildHtmlMessageHeader()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
319
        array_push($message, $this->htmlMessage);
320
        array_push($message, "--{$this->boundaryAlternative}--");
321
322
        return implode(self::LINE_BREAK, $message);
323
    }
324
325
    /**
326
     * Build multi-part message with attachments
327
     *
328
     * @return string email message
329
     */
330
    protected function buildMessageWithAttachments()
331
    {
332
        $message = array();
333
334 View Code Duplication
        if (!empty($this->plainMessage) || !empty($this->htmlMessage)) {
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...
335
            array_push($message, "--{$this->boundaryMixed}");
336
        }
337
338
        if (!empty($this->plainMessage) && !empty($this->htmlMessage)) {
339
            array_push($message, "Content-Type: multipart/alternative; boundary={$this->boundaryAlternative}");
340
            array_push($message, '');
341
            array_push($message, "--{$this->boundaryAlternative}");
342
            $message += $this->buildPlainMessageHeaders();
0 ignored issues
show
Bug introduced by
The method buildPlainMessageHeaders() does not exist on Jacobemerick\Archangel\Archangel. Did you maybe mean buildPlainMessageHeader()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
343
            array_push($message, $this->plainMessage);
344
            array_push($message, "--{$this->boundaryAlternative}");
345
            $message += $this->buildHtmlMessageHeaders();
0 ignored issues
show
Bug introduced by
The method buildHtmlMessageHeaders() does not exist on Jacobemerick\Archangel\Archangel. Did you maybe mean buildHtmlMessageHeader()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
346
            array_push($message, $this->htmlMessage);
347
            array_push($message, "--{$this->boundaryAlternative}--");
348
            array_push($message, '');
349
        } elseif (!empty($this->plainMessage)) {
350
            $message += $this->buildPlainMessageHeaders();
0 ignored issues
show
Bug introduced by
The method buildPlainMessageHeaders() does not exist on Jacobemerick\Archangel\Archangel. Did you maybe mean buildPlainMessageHeader()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
351
            array_push($message, $this->plainMessage);
352
        } elseif (!empty($this->htmlMessage)) {
353
            $message += $this->buildHtmlMessageHeaders();
0 ignored issues
show
Bug introduced by
The method buildHtmlMessageHeaders() does not exist on Jacobemerick\Archangel\Archangel. Did you maybe mean buildHtmlMessageHeader()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
354
            array_push($message, $this->htmlMessage);
355
        }
356
        foreach ($this->attachments as $attachment) {
357
            array_push($message, "--{$this->boundaryMixed}");
358
            array_push($message, "Content-Type: {$attachment['type']}; name=\"{$attachment['title']}\"");
359
            array_push($message, 'Content-Transfer-Encoding: base64');
360
            array_push($message, 'Content-Disposition: attachment');
361
            array_push($message, '');
362
            array_push($message, $this->buildAttachmentContent($attachment['path']));
363
        }
364
        array_push($message, "--{$this->boundaryMixed}--");
365
366
        return implode(self::LINE_BREAK, $message);
367
    }
368
369
370
    /**
371
     * Shared holder for the plain message header
372
     *
373
     * @return array
374
     */
375
    protected function buildPlainMessageHeader()
376
    {
377
        return array(
378
            'Content-Type: text/plain; charset="iso-8859"',
379
            'Content-Transfer-Encoding: 7bit',
380
            '',
381
        );
382
    }
383
384
    /**
385
     * Shared holder for the html message header
386
     *
387
     * @return array
388
     */
389
    protected function buildHtmlMessageHeader()
390
    {
391
        return array(
392
            'Content-Type: text/html; charset="iso-8859-1"',
393
            'Content-Transfer-Encoding: 7bit',
394
            '',
395
        );
396
    }
397
398
    /**
399
     * Builder for the additional headers needed for multipart emails
400
     *
401
     * @return string headers needed for multipart
402
     */
403
    protected function buildHeaders()
404
    {
405
        $headers = array();
406
        foreach ($this->headers as $key => $value) {
407
            if ($key == 'CC' || $key == 'BCC') {
408
                $value = implode(', ', $value);
409
            }
410
            array_push($headers, sprintf('%s: %s', $key, $value));
411
        }
412
413
        if (!empty($this->attachments)) {
414
            array_push(
415
                $headers,
416
                "Content-Type: multipart/mixed; boundary=\"{$this->boundaryMixed}\""
417
            );
418 View Code Duplication
        } elseif (!empty($this->plainMessage) && !empty($this->htmlMessage)) {
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...
419
            array_push(
420
                $headers,
421
                "Content-Type: multipart/alternative; boundary=\"{$this->boundaryAlternative}\""
422
            );
423
        } elseif (!empty($this->htmlMessage)) {
424
            array_push(
425
                $headers,
426
                'Content-type: text/html; charset="iso-8859-1"'
427
            );
428
        }
429
430
        return implode(self::LINE_BREAK, $headers);
431
    }
432
433
    /**
434
     * File reader for attachments
435
     *
436
     * @param string $path filepath of the attachment
437
     *
438
     * @return string binary representation of file, base64'd
439
     */
440
    protected function buildAttachmentContent($path)
441
    {
442
        if (!file_exists($path)) {
443
            $this->logger->error("Could not find file {$path} for attaching to Archangel mail.");
444
            return '';
445
        }
446
447
        $handle = fopen($path, 'r');
448
        $contents = fread($handle, filesize($path));
449
        fclose($handle);
450
451
        $contents = base64_encode($contents);
452
        $contents = chunk_split($contents);
453
        return $contents;
454
    }
455
}
456