Completed
Push — master ( a49d14...9cdc95 )
by Jacob
02:01
created

Archangel.class.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * This is the main class for Archangel mailer
5
 * For licensing and examples:
6
 *
7
 * @see https://github.com/jacobemerick/archangel
8
 *
9
 * @author jacobemerick (http://home.jacobemerick.com/)
10
 * @version 1.0 (2013-04-12)
11
 */
12
13
final class Archangel
14
{
15
16
	/**
17
	 * These variables are set with setter methods below
18
	 */
19
	private $subject;
20
	private $reply_to;
21
	private $plain_message;
22
	private $html_message;
23
24
	/**
25
	 * Holder for error handling and validation
26
	 */
27
	private $passed_validation = TRUE;
28
	private $validation_error = array();
29
30
	/**
31
	 * Holders for some of the more list-y variable handling
32
	 */
33
	private $to_array = array();
34
	private $cc_array = array();
35
	private $bcc_array = array();
36
	private $header_array = array();
37
	private $attachment_array = array();
38
39
	/**
40
	 * Static pieces that really don't need to change
41
	 */
42
	private static $MAILER = 'PHP/%s';
43
	private static $LINE_BREAK = "\r\n";
44
	private static $BOUNDARY_FORMAT = 'PHP-mixed-%s';
45
	private static $BOUNDARY_SALT = 'Boundary Salt';
46
	private static $ALTERNATIVE_BOUNDARY_FORMAT = 'PHP-alternative-%s';
47
	private static $ALTERNATIVE_BOUNDARY_SALT = 'Alternative Boundary Salt';
48
49
	/**
50
	 * Standard constructor, sets some of the base (unchanging) fields
51
	 */
52
	public function __construct()
53
	{
54
		$this->header_array['X-Mailer'] = sprintf(self::$MAILER, phpversion());
55
	}
56
57
	/**
58
	 * Setter method for adding recipients
59
	 * This class only sends a single email, so all recipients can see each other
60
	 *
61
	 * @param	string	$address	email address for the recipient
62
	 * @param	string	$title		name of the recipient (optional)
63
	 * @return	object	instantiated $this
64
	 */
65 View Code Duplication
	public function addTo($address, $title = '')
66
	{
67
		if($this->is_valid_email_address($address) && $this->is_valid_email_title($title))
68
			$this->to_array[] = ($title != '') ? "\"{$title}\" <{$address}>" : "{$address}";
69
		
70
		return $this;
71
	}
72
73
	/**
74
	 * Setter method for adding cc recipients
75
	 *
76
	 * @param	string	$address	email address for the cc recipient
77
	 * @param	string	$title		name of the cc recipient (optional)
78
	 * @return	object	instantiated $this
79
	 */
80 View Code Duplication
	public function addCC($address, $title = '')
81
	{
82
		if($this->is_valid_email_address($address) && $this->is_valid_email_title($title))
83
			$this->cc_array[] = ($title != '') ? "\"{$title}\" <{$address}>" : "{$address}";
84
		
85
		return $this;
86
	}
87
88
	/**
89
	 * Setter method for adding bcc recipients
90
	 *
91
	 * @param	string	$address	email address for the bcc recipient
92
	 * @param	string	$title		name of the bcc recipient (optional)
93
	 * @return	object	instantiated $this
94
	 */
95 View Code Duplication
	public function addBCC($address, $title = '')
96
	{
97
		if($this->is_valid_email_address($address) && $this->is_valid_email_title($title))
98
			$this->bcc_array[] = ($title != '') ? "\"{$title}\" <{$address}>" : "{$address}";
99
		
100
		return $this;
101
	}
102
103
	/**
104
	 * Setter method for setting the single 'from' field
105
	 *
106
	 * @param	string	$address	email address for the sender
107
	 * @param	string	$title		name of the sender (optional)
108
	 * @return	object	instantiated $this
109
	 */
110 View Code Duplication
	public function setFrom($address, $title = '')
111
	{
112
		if($this->is_valid_email_address($address) && $this->is_valid_email_title($title))
113
			$this->header_array['From'] = ($title != '') ? "\"{$title}\" <{$address}>" : "{$address}";
114
		
115
		return $this;
116
	}
117
118
	/**
119
	 * Setter method for setting the single 'reply-to' field
120
	 *
121
	 * @param	string	$address	email address for the reply-to
122
	 * @param	string	$title		name of the reply-to (optional)
123
	 * @return	object	instantiated $this
124
	 */
125 View Code Duplication
	public function setReplyTo($address, $title = '')
126
	{
127
		if($this->is_valid_email_address($address) && $this->is_valid_email_title($title))
128
			$this->header_array['Reply-To'] = ($title != '') ? "\"{$title}\" <{$address}>" : "{$address}";
129
		
130
		return $this;
131
	}
132
133
	/**
134
	 * Setter method for setting a subject
135
	 *
136
	 * @param	string	$subject	subject for the email
137
	 * @return	object	instantiated $this
138
	 */
139
	public function setSubject($subject)
140
	{
141
		if($this->is_valid_subject($subject))
142
			$this->subject = $subject;
143
		
144
		return $this;
145
	}
146
147
	/**
148
	 * Setter method for the plain text message
149
	 *
150
	 * @param	string	$message	the plain-text message
151
	 * @return	object	insantiated $this
152
	 */
153
	public function setPlainMessage($message)
154
	{
155
		$this->plain_message = $message;
156
		
157
		return $this;
158
	}
159
160
	/**
161
	 * Setter method for the html message
162
	 *
163
	 * @param	string	$message	the html message
164
	 * @return	object	insantiated $this
165
	 */
166
	public function setHTMLMessage($message)
167
	{
168
		$this->html_message = $message;
169
		
170
		return $this;
171
	}
172
173
	/**
174
	 * Setter method for adding attachments
175
	 *
176
	 * @param	string	$path	the full path of the attachment
177
	 * @param	string	$type	mime type of the file
178
	 * @param	string	$title	the title of the attachment (optional)
179
	 * @return	object	insantiated $this
180
	 */
181
	public function addAttachment($path, $type, $title = '')
182
	{
183
		$this->attachment_array[] = (object) array(
184
			'path' => $path,
185
			'type' => $type,
186
			'title' => $title);
187
		
188
		return $this;
189
	}
190
191
	/**
192
	 * The executing step, the actual sending of the email
193
	 * First checks to make sure the minimum fields are set (returns false if they are not)
194
	 * Second it attempts to send the mail with php's mail() (returns false if it fails)
195
	 *
196
	 * return	boolean	whether or not the email was valid & sent
197
	 */
198
	public function send()
199
	{
200
		if($this->passed_validation === FALSE)
201
			return false;
202
		
203
		if(!$this->check_required_fields())
204
			return false;
205
		
206
		$to = $this->get_to();
207
		$subject = $this->subject;
208
		$message = $this->get_message();
209
		$additional_headers = $this->get_additional_headers();
210
		
211
		return mail($to, $subject, $message, $additional_headers);
212
	}
213
214
	/**
215
	 * Main instantiator for the class
216
	 *
217
	 * @return	object	instantiated $this
218
	 */
219
	public static function instance()
220
	{
221
		return new Archangel();
222
	}
223
224
	/**
225
	 * Private call to check the minimum required fields
226
	 *
227
	 * @return	boolean	whether or not the email meets the minimum required fields
228
	 */
229
	private function check_required_fields()
230
	{
231
		return (
232
			count($this->to_array) > 0 &&
233
			(isset($this->subject) && strlen($this->subject) > 0) &&
234
			(
235
				(isset($this->plain_message) && strlen($this->plain_message) > 0) ||
236
				(isset($this->html_message) && strlen($this->html_message) > 0) ||
237
				(isset($this->attachment_array) && count($this->attachment_array) > 0)));
238
	}
239
240
	/**
241
	 * Private function to collect the recipients from to_array
242
	 *
243
	 * @return	string	comma-separated lit of recipients
244
	 */
245
	private function get_to()
246
	{
247
		return implode(', ', $this->to_array);
248
	}
249
250
	/**
251
	 * Long, nasty creater of the actual message, with all the multipart logic you'd never want to see
252
	 *
253
	 * @return	string	email message
254
	 */
255
	private function get_message()
256
	{
257
		$message = '';
258
		
259
		if(isset($this->attachment_array) && count($this->attachment_array) > 0)
260
			$message .= "--{$this->get_boundary()}" . self::$LINE_BREAK;
261
		
262
		if(
263
			isset($this->plain_message) && strlen($this->plain_message) > 0 &&
264
			isset($this->html_message) && strlen($this->html_message) > 0)
265
		{
266 View Code Duplication
			if(isset($this->attachment_array) && count($this->attachment_array) > 0)
267
			{
268
				$message .= "Content-Type: multipart/alternative; boundary={$this->get_alternative_boundary()}" . self::$LINE_BREAK;
269
				$message .= self::$LINE_BREAK;
270
			}
271
			$message .= "--{$this->get_alternative_boundary()}" . self::$LINE_BREAK;
272
			$message .= 'Content-Type: text/plain; charset="iso-8859"' . self::$LINE_BREAK;
273
			$message .= 'Content-Transfer-Encoding: 7bit' . self::$LINE_BREAK;
274
			$message .= self::$LINE_BREAK;
275
			$message .= $this->plain_message;
276
			$message .= self::$LINE_BREAK;
277
			$message .= "--{$this->get_alternative_boundary()}" . self::$LINE_BREAK;
278
			$message .= 'Content-Type: text/html; charset="iso-8859-1"' . self::$LINE_BREAK;
279
			$message .= 'Content-Transfer-Encoding: 7bit' . self::$LINE_BREAK;
280
			$message .= self::$LINE_BREAK;
281
			$message .= $this->html_message;
282
			$message .= self::$LINE_BREAK;
283
			$message .= "--{$this->get_alternative_boundary()}--" . self::$LINE_BREAK;
284
			$message .= self::$LINE_BREAK;
285
		}
286
		else if(isset($this->plain_message) && strlen($this->plain_message))
287
		{
288 View Code Duplication
			if(isset($this->attachment_array) && count($this->attachment_array) > 0)
289
			{
290
				$message .= 'Content-Type: text/plain; charset="iso-8859"' . self::$LINE_BREAK;
291
				$message .= 'Content-Transfer-Encoding: 7bit' . self::$LINE_BREAK;
292
				$message .= self::$LINE_BREAK;
293
			}
294
			$message .= $this->plain_message;
295
			$message .= self::$LINE_BREAK;
296
		}
297
		else if(isset($this->html_message) && strlen($this->html_message))
298
		{
299 View Code Duplication
			if(isset($this->attachment_array) && count($this->attachment_array) > 0)
300
			{
301
				$message .= 'Content-Type: text/html; charset="iso-8859-1"' . self::$LINE_BREAK;
302
				$message .= 'Content-Transfer-Encoding: 7bit' . self::$LINE_BREAK;
303
				$message .= self::$LINE_BREAK;
304
			}
305
			$message .= $this->html_message;
306
			$message .= self::$LINE_BREAK;
307
		}
308
		if(isset($this->attachment_array) && count($this->attachment_array) > 0)
309
		{
310
			foreach($this->attachment_array as $attachment)
311
			{
312
				$message .= "--{$this->get_boundary()}" . self::$LINE_BREAK;
313
				$message .= "Content-Type: {$attachment->type}; name=\"{$attachment->title}\"" . self::$LINE_BREAK;
314
				$message .= 'Content-Transfer-Encoding: base64' . self::$LINE_BREAK;
315
				$message .= 'Content-Disposition: attachment' . self::$LINE_BREAK;
316
				$message .= self::$LINE_BREAK;
317
				$message .= $this->get_attachment_content($attachment);
318
				$message .= self::$LINE_BREAK;
319
			}
320
			$message .= "--{$this->get_boundary()}--" . self::$LINE_BREAK;
321
		}
322
		return $message;
323
	}
324
325
	/**
326
	 * Private holder for the boundry logic
327
	 * Not called/created unless it's needed
328
	 *
329
	 * @return	string	boundary
330
	 */
331
	private $boundary;
332
	private function get_boundary()
333
	{
334
		if(!isset($this->boundary))
335
			$this->boundary = sprintf(self::$BOUNDARY_FORMAT, md5(date('r', time()) . self::$BOUNDARY_SALT));
336
		return $this->boundary;
337
	}
338
339
	/**
340
	 * Private holder for the alternative boundry logic
341
	 * Not called/created unless it's needed
342
	 *
343
	 * @return	string	alternative boundary
344
	 */
345
	private $alternative_boundary;
346
	private function get_alternative_boundary()
347
	{
348
		if(!isset($this->alternative_boundary))
349
			$this->alternative_boundary = sprintf(self::$ALTERNATIVE_BOUNDARY_FORMAT, md5(date('r', time()) . self::$ALTERNATIVE_BOUNDARY_SALT));
350
		return $this->alternative_boundary;
351
	}
352
353
	/**
354
	 * Fetcher for the additional headers needed for multipart emails
355
	 *
356
	 * @return	string	headers needed for multipart
357
	 */
358
	private function get_additional_headers()
359
	{
360
		$headers = '';
361
		foreach($this->header_array as $key => $value)
362
		{
363
			$headers .= "{$key}: {$value}" . self::$LINE_BREAK;
364
		}
365
		
366
		if(count($this->cc_array) > 0)
367
			$headers .= 'CC: ' . implode(', ', $this->cc_array) . self::$LINE_BREAK;
368
		if(count($this->bcc_array) > 0)
369
			$headers .= 'BCC: ' . implode(', ', $this->bcc_array) . self::$LINE_BREAK;
370
		
371
		if(isset($this->attachment_array) && count($this->attachment_array) > 0)
372
			$headers .= "Content-Type: multipart/mixed; boundary=\"{$this->get_boundary()}\"";
373
		else if(
374
			isset($this->plain_message) && strlen($this->plain_message) > 0 &&
375
			isset($this->html_message) && strlen($this->html_message) > 0)
376
		{
377
			$headers .= "Content-Type: multipart/alternative; boundary=\"{$this->get_alternative_boundary()}\"";
378
		}
379
		else if(isset($this->html_message) && strlen($this->html_message) > 0)
380
			$headers .= 'Content-type: text/html; charset="iso-8859-1"';
381
		
382
		return $headers;
383
	}
384
385
	/**
386
	 * File reader for attachments
387
	 *
388
	 * @return	string	binary representation of file, base64'd
389
	 */
390
	private function get_attachment_content($attachment)
391
	{
392
		$handle = fopen($attachment->path, 'r');
393
		$contents = fread($handle, filesize($attachment->path));
394
		fclose($handle);
395
		
396
		$contents = base64_encode($contents);
397
		$contents = chunk_split($contents);
398
		return $contents;
399
	}
400
401
	/**
402
	 * stub for email address checking
403
	 */
404
	private function is_valid_email_address($string)
405
	{
406
		if(strlen($string) < 1)
407
			return $this->fail_validation("{$string} is an invalid email address!");
408
		
409
		return true;
410
	}
411
412
	/**
413
	 * stub for email title checking
414
	 */
415
	private function is_valid_email_title($string)
416
	{
417
		return true;
418
	}
419
420
	/**
421
	 * stub for subject checking
422
	 */
423
	private function is_valid_subject($string)
424
	{
425
		if(strlen($string) < 1)
426
			return $this->fail_validation("{$string} is an invalid email subject!");
427
		
428
		return true;
429
	}
430
431
	/**
432
	 * holder for all validation fails
433
	 */
434
	private function fail_validation($message)
435
	{
436
		$this->passed_validation = FALSE;
437
		$this->validation_error[] = $message;
438
		
439
		return false;
440
	}
441
442
	public function get_validation_errors()
443
	{
444
		return $this->validation_error();
0 ignored issues
show
The method validation_error() does not exist on Archangel. Did you maybe mean get_validation_errors()?

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...
445
	}
446
447
}