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

Archangel   F

Complexity

Total Complexity 67

Size/Duplication

Total Lines 418
Duplicated Lines 14.83 %

Coupling/Cohesion

Components 6
Dependencies 0

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 67
c 3
b 0
f 0
lcom 6
cbo 0
dl 62
loc 418
rs 3.0612

19 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 15 3
A instance() 0 4 1
B check_required_fields() 0 10 9
A get_to() 0 4 1
C get_message() 17 69 20
A get_boundary() 0 6 2
A get_alternative_boundary() 0 6 2
C get_additional_headers() 0 26 12
A get_attachment_content() 0 10 1

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
13
class Archangel
14
{
15
16
    /** @var string $subject */
17
    protected $subject;
18
19
    /** @var array $to */
20
    protected $to = array();
21
22
    /** @var array $cc */
23
    protected $cc = array();
24
25
    /** @var array $bcc */
26
    protected $bcc = array();
27
28
    /** @var array $headers */
29
    protected $headers = array();
30
31
    /** @var string $plainMessage */
32
    protected $plainMessage;
33
34
    /** @var string $htmlMessage */
35
    protected $htmlMessage;
36
37
    /** @var array $attachments */
38
    protected $attachments = array();
39
40
    /** @var string MAILER_PATTERN */
41
    const MAILER_PATTERN = "PHP/%s";
42
43
    /** @var string LINE_BREAK */
44
    const LINE_BREAK = "\r\n";
45
46
    /** @var string BOUNDARY_PATTERN */
47
    const BOUNDARY_PATTERN = "PHP-mixed-%s";
48
49
    /** @var string BOUNDARY_SALT */
50
    const BOUNDARY_SALT = "Boundary Salt";
51
52
    /** @var string ALTERNATIVE_BOUNDARY_PATTERN */
53
    const ALTERNATIVE_BOUNDARY_PATTERN = "PHP-alternative-%s";
54
55
    /** @var string ALTERNATIVE_BOUNDARY_SALT */
56
    const ALTERNATIVE_BOUNDARY_SALT = "Alternative Boundary Salt";
57
58
    /**
59
     * @param string $mailer
60
     */
61
    public function __construct($mailer = null)
62
    {
63
        if (is_null($mailer)) {
64
            $mailer = sprintf(self::MAILER_PATTERN, phpversion());
65
        }
66
        $this->headers['X-Mailer'] = $mailer;
67
    }
68
69
    /**
70
     * Setter method for adding recipients
71
     *
72
     * @param string $address email address for the recipient
73
     * @param string $title   name of the recipient (optional)
74
75
     * @return object instantiated $this
76
     */
77 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...
78
    {
79
        if (!empty($title)) {
80
            $address = sprintf('"%s" <%s>', $title, $address);
81
        }
82
        array_push($this->to, $address);
83
84
        return $this;
85
    }
86
87
    /**
88
     * Setter method for adding cc recipients
89
     *
90
     * @param string $address  email address for the cc recipient
91
     * @param string $title    name of the cc recipient (optional)
92
     *
93
     * @return object instantiated $this
94
     */
95 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...
96
    {
97
        if (!empty($title)) {
98
            $address = sprintf('"%s" <%s>', $title, $address);
99
        }
100
        array_push($this->cc, $address);
101
102
        return $this;
103
    }
104
105
    /**
106
     * Setter method for adding bcc recipients
107
     *
108
     * @param string $addres  email address for the bcc recipient
0 ignored issues
show
Documentation introduced by
There is no parameter named $addres. Did you maybe mean $address?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
109
     * @param string $title   name of the bcc recipient (optional)
110
     *
111
     * @return object instantiated $this
112
     */
113 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...
114
    {
115
        if (!empty($title)) {
116
            $address = sprintf('"%s" <%s>', $title, $address);
117
        }
118
        array_push($this->bcc, $address);
119
120
        return $this;
121
    }
122
123
    /**
124
     * Setter method for setting the single 'from' field
125
     *
126
     * @param string $address email address for the sender
127
     * @param string $title   name of the sender (optional)
128
     *
129
     * @return object instantiated $this
130
     */
131 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...
132
    {
133
        if (!empty($title)) {
134
            $address = sprintf('"%s" <%s>', $title, $address);
135
        }
136
        $this->headers['From'] = $address;
137
138
        return $this;
139
    }
140
141
    /**
142
     * Setter method for setting the single 'reply-to' field
143
     *
144
     * @param string $address email address for the reply-to
145
     * @param string $title   name of the reply-to (optional)
146
     *
147
     * @return object instantiated $this
148
     */
149 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...
150
    {
151
        if (!empty($title)) {
152
            $address = sprintf('"%s" <%s>', $title, $address);
153
        }
154
        $this->headers['Reply-To'] = $address;
155
156
        return $this;
157
    }
158
159
    /**
160
     * Setter method for setting a subject
161
     *
162
     * @param string $subject subject for the email
163
     *
164
     * @return object instantiated $this
165
     */
166
    public function setSubject($subject)
167
    {
168
        $this->subject = $subject;
169
170
        return $this;
171
    }
172
173
    /**
174
     * Setter method for the plain text message
175
     *
176
     * @param string $message the plain-text message
177
     *
178
     * @return object instantiated $this
179
     */
180
    public function setPlainMessage($message)
181
    {
182
        $this->plainMessage = $message;
183
184
        return $this;
185
    }
186
187
    /**
188
     * Setter method for the html message
189
     *
190
     * @param string $message the html message
191
     *
192
     * @return object instantiated $this
193
     */
194
    public function setHTMLMessage($message)
195
    {
196
        $this->htmlMessage = $message;
197
198
        return $this;
199
    }
200
201
    /**
202
     * Setter method for adding attachments
203
     *
204
     * @param string $path  the full path of the attachment
205
     * @param string $type  mime type of the file
206
     * @param string $title the title of the attachment (optional)
207
     *
208
     * @return object instantiated $this
209
     */
210
    public function addAttachment($path, $type, $title = '')
211
    {
212
        array_push($this->attachments, array(
213
          'path' => $path,
214
          'type' => $type,
215
          'title' => $title,
216
        ));
217
218
        return $this;
219
    }
220
221
    /**
222
     * The executing step, the actual sending of the email
223
     * First checks to make sure the minimum fields are set (returns false if they are not)
224
     * Second it attempts to send the mail with php's mail() (returns false if it fails)
225
     *
226
     * return boolean whether or not the email was valid & sent
227
     */
228
    public function send()
229
    {
230
      if($this->passed_validation === FALSE)
0 ignored issues
show
Bug introduced by
The property passed_validation does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
231
        return false;
232
      
233
      if(!$this->check_required_fields())
234
        return false;
235
      
236
      $to = $this->get_to();
237
      $subject = $this->subject;
238
      $message = $this->get_message();
239
      $additional_headers = $this->get_additional_headers();
240
      
241
      return mail($to, $subject, $message, $additional_headers);
242
    }
243
244
    /**
245
     * Main instantiator for the class
246
     *
247
     * @return  object  instantiated $this
248
     */
249
    public static function instance()
250
    {
251
      return new Archangel();
252
    }
253
254
    /**
255
     * Private call to check the minimum required fields
256
     *
257
     * @return  boolean whether or not the email meets the minimum required fields
258
     */
259
    private function check_required_fields()
260
    {
261
      return (
262
        count($this->to_array) > 0 &&
0 ignored issues
show
Bug introduced by
The property to_array does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
263
        (isset($this->subject) && strlen($this->subject) > 0) &&
264
        (
265
          (isset($this->plain_message) && strlen($this->plain_message) > 0) ||
0 ignored issues
show
Bug introduced by
The property plain_message does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
266
          (isset($this->html_message) && strlen($this->html_message) > 0) ||
0 ignored issues
show
Bug introduced by
The property html_message does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
267
          (isset($this->attachment_array) && count($this->attachment_array) > 0)));
0 ignored issues
show
Bug introduced by
The property attachment_array does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
268
    }
269
270
    /**
271
     * Private function to collect the recipients from to_array
272
     *
273
     * @return  string  comma-separated lit of recipients
274
     */
275
    private function get_to()
276
    {
277
      return implode(', ', $this->to_array);
278
    }
279
280
    /**
281
     * Long, nasty creater of the actual message, with all the multipart logic you'd never want to see
282
     *
283
     * @return  string  email message
284
     */
285
    private function get_message()
286
    {
287
      $message = '';
288
      
289
      if(isset($this->attachment_array) && count($this->attachment_array) > 0)
290
        $message .= "--{$this->get_boundary()}" . self::$LINE_BREAK;
291
      
292
      if(
293
        isset($this->plain_message) && strlen($this->plain_message) > 0 &&
294
        isset($this->html_message) && strlen($this->html_message) > 0)
295
      {
296 View Code Duplication
        if(isset($this->attachment_array) && count($this->attachment_array) > 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...
297
        {
298
          $message .= "Content-Type: multipart/alternative; boundary={$this->get_alternative_boundary()}" . self::$LINE_BREAK;
299
          $message .= self::$LINE_BREAK;
300
        }
301
        $message .= "--{$this->get_alternative_boundary()}" . self::$LINE_BREAK;
302
        $message .= 'Content-Type: text/plain; charset="iso-8859"' . self::$LINE_BREAK;
303
        $message .= 'Content-Transfer-Encoding: 7bit' . self::$LINE_BREAK;
304
        $message .= self::$LINE_BREAK;
305
        $message .= $this->plain_message;
306
        $message .= self::$LINE_BREAK;
307
        $message .= "--{$this->get_alternative_boundary()}" . self::$LINE_BREAK;
308
        $message .= 'Content-Type: text/html; charset="iso-8859-1"' . self::$LINE_BREAK;
309
        $message .= 'Content-Transfer-Encoding: 7bit' . self::$LINE_BREAK;
310
        $message .= self::$LINE_BREAK;
311
        $message .= $this->html_message;
312
        $message .= self::$LINE_BREAK;
313
        $message .= "--{$this->get_alternative_boundary()}--" . self::$LINE_BREAK;
314
        $message .= self::$LINE_BREAK;
315
      }
316
      else if(isset($this->plain_message) && strlen($this->plain_message))
317
      {
318 View Code Duplication
        if(isset($this->attachment_array) && count($this->attachment_array) > 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...
319
        {
320
          $message .= 'Content-Type: text/plain; charset="iso-8859"' . self::$LINE_BREAK;
321
          $message .= 'Content-Transfer-Encoding: 7bit' . self::$LINE_BREAK;
322
          $message .= self::$LINE_BREAK;
323
        }
324
        $message .= $this->plain_message;
325
        $message .= self::$LINE_BREAK;
326
      }
327
      else if(isset($this->html_message) && strlen($this->html_message))
328
      {
329 View Code Duplication
        if(isset($this->attachment_array) && count($this->attachment_array) > 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...
330
        {
331
          $message .= 'Content-Type: text/html; charset="iso-8859-1"' . self::$LINE_BREAK;
332
          $message .= 'Content-Transfer-Encoding: 7bit' . self::$LINE_BREAK;
333
          $message .= self::$LINE_BREAK;
334
        }
335
        $message .= $this->html_message;
336
        $message .= self::$LINE_BREAK;
337
      }
338
      if(isset($this->attachment_array) && count($this->attachment_array) > 0)
339
      {
340
        foreach($this->attachment_array as $attachment)
341
        {
342
          $message .= "--{$this->get_boundary()}" . self::$LINE_BREAK;
343
          $message .= "Content-Type: {$attachment->type}; name=\"{$attachment->title}\"" . self::$LINE_BREAK;
344
          $message .= 'Content-Transfer-Encoding: base64' . self::$LINE_BREAK;
345
          $message .= 'Content-Disposition: attachment' . self::$LINE_BREAK;
346
          $message .= self::$LINE_BREAK;
347
          $message .= $this->get_attachment_content($attachment);
348
          $message .= self::$LINE_BREAK;
349
        }
350
        $message .= "--{$this->get_boundary()}--" . self::$LINE_BREAK;
351
      }
352
      return $message;
353
    }
354
355
    /**
356
     * Private holder for the boundry logic
357
     * Not called/created unless it's needed
358
     *
359
     * @return  string  boundary
360
     */
361
    private $boundary;
362
    private function get_boundary()
363
    {
364
      if(!isset($this->boundary))
365
        $this->boundary = sprintf(self::$BOUNDARY_FORMAT, md5(date('r', time()) . self::$BOUNDARY_SALT));
366
      return $this->boundary;
367
    }
368
369
    /**
370
     * Private holder for the alternative boundry logic
371
     * Not called/created unless it's needed
372
     *
373
     * @return  string  alternative boundary
374
     */
375
    private $alternative_boundary;
376
    private function get_alternative_boundary()
377
    {
378
      if(!isset($this->alternative_boundary))
379
        $this->alternative_boundary = sprintf(self::$ALTERNATIVE_BOUNDARY_FORMAT, md5(date('r', time()) . self::$ALTERNATIVE_BOUNDARY_SALT));
380
      return $this->alternative_boundary;
381
    }
382
383
    /**
384
     * Fetcher for the additional headers needed for multipart emails
385
     *
386
     * @return  string  headers needed for multipart
387
     */
388
    private function get_additional_headers()
389
    {
390
      $headers = '';
391
      foreach($this->header_array as $key => $value)
0 ignored issues
show
Bug introduced by
The property header_array does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
392
      {
393
        $headers .= "{$key}: {$value}" . self::$LINE_BREAK;
394
      }
395
      
396
      if(count($this->cc_array) > 0)
397
        $headers .= 'CC: ' . implode(', ', $this->cc_array) . self::$LINE_BREAK;
0 ignored issues
show
Bug introduced by
The property cc_array does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
398
      if(count($this->bcc_array) > 0)
0 ignored issues
show
Bug introduced by
The property bcc_array does not seem to exist. Did you mean cc_array?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
399
        $headers .= 'BCC: ' . implode(', ', $this->bcc_array) . self::$LINE_BREAK;
0 ignored issues
show
Bug introduced by
The property bcc_array does not seem to exist. Did you mean cc_array?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
400
      
401
      if(isset($this->attachment_array) && count($this->attachment_array) > 0)
402
        $headers .= "Content-Type: multipart/mixed; boundary=\"{$this->get_boundary()}\"";
403
      else if(
404
        isset($this->plain_message) && strlen($this->plain_message) > 0 &&
405
        isset($this->html_message) && strlen($this->html_message) > 0)
406
      {
407
        $headers .= "Content-Type: multipart/alternative; boundary=\"{$this->get_alternative_boundary()}\"";
408
      }
409
      else if(isset($this->html_message) && strlen($this->html_message) > 0)
410
        $headers .= 'Content-type: text/html; charset="iso-8859-1"';
411
      
412
      return $headers;
413
    }
414
415
    /**
416
     * File reader for attachments
417
     *
418
     * @return  string  binary representation of file, base64'd
419
     */
420
    private function get_attachment_content($attachment)
421
    {
422
      $handle = fopen($attachment->path, 'r');
423
      $contents = fread($handle, filesize($attachment->path));
424
      fclose($handle);
425
      
426
      $contents = base64_encode($contents);
427
      $contents = chunk_split($contents);
428
      return $contents;
429
    }
430
}
431