Completed
Pull Request — master (#9)
by Jacob
02:54
created

Archangel::send()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
c 4
b 1
f 0
dl 0
loc 13
rs 9.4285
cc 2
eloc 8
nc 2
nop 0
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
      $message = '';
275
      
276
      if(isset($this->attachments) && count($this->attachments) > 0)
277
        $message .= "--{$this->getBoundary()}" . self::LINE_BREAK;
278
      
279
      if(
280
        isset($this->plainMessage) && strlen($this->plainMessage) > 0 &&
281
        isset($this->htmlMessage) && strlen($this->htmlMessage) > 0)
282
      {
283 View Code Duplication
        if(isset($this->attachments) && count($this->attachments) > 0)
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...
284
        {
285
          $message .= "Content-Type: multipart/alternative; boundary={$this->getAlternativeBoundary()}" . self::LINE_BREAK;
286
          $message .= self::LINE_BREAK;
287
        }
288
        $message .= "--{$this->getAlternativeBoundary()}" . self::LINE_BREAK;
289
        $message .= 'Content-Type: text/plain; charset="iso-8859"' . self::LINE_BREAK;
290
        $message .= 'Content-Transfer-Encoding: 7bit' . self::LINE_BREAK;
291
        $message .= self::LINE_BREAK;
292
        $message .= $this->plainMessage;
293
        $message .= self::LINE_BREAK;
294
        $message .= "--{$this->getAlternativeBoundary()}" . self::LINE_BREAK;
295
        $message .= 'Content-Type: text/html; charset="iso-8859-1"' . self::LINE_BREAK;
296
        $message .= 'Content-Transfer-Encoding: 7bit' . self::LINE_BREAK;
297
        $message .= self::LINE_BREAK;
298
        $message .= $this->htmlMessage;
299
        $message .= self::LINE_BREAK;
300
        $message .= "--{$this->getAlternativeBoundary()}--" . self::LINE_BREAK;
301
        $message .= self::LINE_BREAK;
302
      }
303
      else if(isset($this->plainMessage) && strlen($this->plainMessage))
304
      {
305 View Code Duplication
        if(isset($this->attachments) && count($this->attachments) > 0)
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...
306
        {
307
          $message .= 'Content-Type: text/plain; charset="iso-8859"' . self::LINE_BREAK;
308
          $message .= 'Content-Transfer-Encoding: 7bit' . self::LINE_BREAK;
309
          $message .= self::LINE_BREAK;
310
        }
311
        $message .= $this->plainMessage;
312
        $message .= self::LINE_BREAK;
313
      }
314
      else if(isset($this->htmlMessage) && strlen($this->htmlMessage))
315
      {
316 View Code Duplication
        if(isset($this->attachments) && count($this->attachments) > 0)
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...
317
        {
318
          $message .= 'Content-Type: text/html; charset="iso-8859-1"' . self::LINE_BREAK;
319
          $message .= 'Content-Transfer-Encoding: 7bit' . self::LINE_BREAK;
320
          $message .= self::LINE_BREAK;
321
        }
322
        $message .= $this->htmlMessage;
323
        $message .= self::LINE_BREAK;
324
      }
325
      if(isset($this->attachments) && count($this->attachments) > 0)
326
      {
327
        foreach($this->attachments as $attachment)
328
        {
329
          $message .= "--{$this->getBoundary()}" . self::LINE_BREAK;
330
          $message .= "Content-Type: {$attachment['type']}; name=\"{$attachment['title']}\"" . self::LINE_BREAK;
331
          $message .= 'Content-Transfer-Encoding: base64' . self::LINE_BREAK;
332
          $message .= 'Content-Disposition: attachment' . self::LINE_BREAK;
333
          $message .= self::LINE_BREAK;
334
          $message .= $this->buildAttachmentContent($attachment);
335
          $message .= self::LINE_BREAK;
336
        }
337
        $message .= "--{$this->getBoundary()}--" . self::LINE_BREAK;
338
      }
339
      return $message;
340
    }
341
342
    /**
343
     * Private holder for the boundry logic
344
     * Not called/created unless it's needed
345
     *
346
     * @return  string  boundary
347
     */
348
    protected function getBoundary()
349
    {
350
        if (!isset($this->boundary)) {
351
            $this->boundary = sprintf('PHP-mixed-%s', uniqid());
352
        }
353
        return $this->boundary;
354
    }
355
356
    /**
357
     * Holder to create the alternative boundry logic
358
     * Not called/created unless it's needed
359
     *
360
     * @return string alternative boundary
361
     */
362
    protected function getAlternativeBoundary()
363
    {
364
        if (!isset($this->alternativeBoundary)) {
365
            $this->alternativeBoundary = sprintf('PHP-alternative-%s', uniqid());
366
        }
367
        return $this->alternativeBoundary;
368
    }
369
370
    /**
371
     * Fetcher for the additional headers needed for multipart emails
372
     *
373
     * @return  string  headers needed for multipart
374
     */
375
    protected function buildHeaders()
376
    {
377
      $headers = '';
378
      foreach($this->headers as $key => $value)
379
      {
380
        $headers .= "{$key}: {$value}" . self::LINE_BREAK;
381
      }
382
      
383 View Code Duplication
      if(count($this->cc) > 0)
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...
384
        $headers .= 'CC: ' . implode(', ', $this->cc) . self::LINE_BREAK;
385 View Code Duplication
      if(count($this->bcc) > 0)
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...
386
        $headers .= 'BCC: ' . implode(', ', $this->bcc) . self::LINE_BREAK;
387
      
388
      if(isset($this->attachments) && count($this->attachments) > 0)
389
        $headers .= "Content-Type: multipart/mixed; boundary=\"{$this->getBoundary()}\"";
390
      else if(
391
        isset($this->plainMessage) && strlen($this->plainMessage) > 0 &&
392
        isset($this->htmlMessage) && strlen($this->htmlMessage) > 0)
393
      {
394
        $headers .= "Content-Type: multipart/alternative; boundary=\"{$this->getAlternativeBoundary()}\"";
395
      }
396
      else if(isset($this->htmlMessage) && strlen($this->htmlMessage) > 0)
397
        $headers .= 'Content-type: text/html; charset="iso-8859-1"';
398
      
399
      return $headers;
400
    }
401
402
    /**
403
     * File reader for attachments
404
     *
405
     * @return string binary representation of file, base64'd
406
     */
407
    protected function buildAttachmentContent($attachment)
408
    {
409
        if (!file_exists($attachment['path'])) {
410
            return ''; // todo log error
411
        }
412
413
        $handle = fopen($attachment['path'], 'r');
414
        $contents = fread($handle, filesize($attachment['path']));
415
        fclose($handle);
416
417
        $contents = base64_encode($contents);
418
        $contents = chunk_split($contents);
419
        return $contents;
420
    }
421
}
422