Mailer()

A PHP Class

About this class:

This class was written to replace PHP's generic mail() function. Using this class you can quickly and easily connect to an SMTP server with a user/pass and send an authenticated email.

With PHP's generic mail() function, your server generated or contact form emails won't pass SPF or DKIM checks which (increasingly these days) puts your message at risk of being flagged as spam, or being discarded by the destination server outright.

This class makes use of PHPMailer for the transport layer.


Usage:
/* Include the class file */
require_once 'class.Mailer.php';

/*
	Connection options.
	These can also be set in the class file to make setting up the instance quicker.
	You can also overwrite the options set in the class file by defining
	any or all of the options here. Options not defined here will use the defaults
	set in the class file.
*/
$conn_options = array(

	'host'			=> 'mail.somehost.com',
	'port'			=> '25',
	'user'			=> 'username',
	'pass'			=> 'password',
	'enc_type'		=> 'tls'

);

/* Array of recipients. Key is the address, value is the optional name */
$to = array(
	'user@domain.com' 	=> 'A Name',
	'another@user.com' 	=> '' // without the name
);

$headers = array(
	'CustomHeader'		=> 'value',
	'Another'		=> 'anotherValue' // You can define multiple headers
);

$subject = "An Email";
$body = 'A textual body.';

try {

	/*
		Method #1: The standard way.
	*/
	$mail = new Mailer($conn_options);
	$mail->from('me@address.com', 'My "optional" Name');
	$mail->to($to);
	$mail->headers($headers);

	if (!$mail->send($subject, $body)) {
		echo "Message send failed.";
	} else {
		echo "Message send succeeded.";
	}

	/*
		Method #2: Use method chaining.
	*/
	if (!$mail->to($to)->from('me@address.com', 'My "optional" Name')->send($subject, $body)) {
		echo "Email failed.";
	} else {
		echo "Email Sent!";
	}
} catch (MailerException $e) {
	echo $e;
}

Source:

/**
 * Mailer() - a PHP class for quickly sending SMTP mail.
 *
 * Mailer() is a class designed to send mail VIA SMTP in a quicker and simpler way.
 * The goal was to use SMTP for sending email so SPF records could be utilized, but I wanted the process to be as
 * quick and easy as the built in PHP mail() function. It makes use of PHPMailer ({@link https://github.com/PHPMailer/PHPMailer/})
 * PHP Version 5
 * @package Mailer
 * @version 1.0
 * @link http://www.duvalltech.com My website
 * @author Keith Duvall <keith@duvalltech.com>
 * @copyright 2015 Keith Duvall
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 * @note This program is distributed in the hope that it will be useful - WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.
 */

require_once 'phpmailer/PHPMailerAutoload.php';

/**
 * Mailer() - a PHP class for quickly sending SMTP mail.
 */
class Mailer
{

	/**
	 * Connection options
	 *
	 * A key => value array of connection options. You can define them here or at runtime.
	 * <ul>
	 * 		<li><b>host:</b> string containing the SMTP server hostname or its IPv4 address</li>
	 * 		<li><b>port:</b> integer containing the SMTP server port number</li>
	 * 		<li><b>user:</b> username if required for authentication</li>
	 * 		<li><b>pass:</b> password if required for authentication</li>
	 * 		<li><b>enc_type:</b> string containing the desired encryption type. Can be 'tls', 'ssl', or '' (for no encryption)</li>
	 * 		<li><b>debug_level:</b> int 0-4 for debugging connection issues. 4 is the most verbose. 0 is recommended for production.</li>
	 * </ul>
	 *
	 * @var mixed[] $conn_options
	 *
	 * @access private
	 */
	private $conn_options = array(

		'host'			=> 'localhost',
		'port'			=> 25,
		'user'			=> null,
		'pass'			=> null,
		'enc_type'		=> 'tls',
		'debug_level'	=> 0

	);

	/**
	 * Holds the PHPMailer() class instance.
	 *
	 * @var object
	 * @access private
	 */
	private $mail;

	/**
	 * Class constructor. Sets up the connection.
	 *
	 * @access public
	 * @example examples.php 3 20 Initializing the class
	 * @param mixed[] $options (default: array()) Key => Value array with connection parameters. Keys are optional and can be one or more of the following:
	 *											  <ul>
	 *											      <li><b>host:</b> a hostname or IPv4 address</li>
	 *											      <li><b>port:</b> SMTP server port number</li>
	 * 											      <li><b>user:</b> Optional username</li>
	 * 											      <li><b>pass:</b> Optional password</li>
	 *											      <li><b>enc_type:</b> Optional server encryption. Can be 'tls', 'ssl', or '' for none.</li>
	 *											  </ul>
	 *
	 * @return void
	 */
	public function __construct($options = array())
	{
		if (is_array($options)) {
			$this->conn_options = array_merge($this->conn_options, $options);
		} else {
			$this->ex('Class arguments must be empty or a key => value array of options.', __LINE__, __METHOD__);
		}

		$this->mail = new PHPMailer();
		$this->mail->IsSMTP();

		$this->mail->SMTPDebug = $this->conn_options['debug_level'];

		if (!$this->validate($this->conn_options['host'], 'host')) { $this->ex("{$this->conn_options['host']} is not a valid hostname or IP address"); }
		$this->mail->Host = $this->conn_options['host'];

		if (!$this->validate($this->conn_options['port'], 'port')) { $this->ex("{$this->conn_options['port']} is not a valid port number"); }
		$this->mail->Port = $this->conn_options['port'];

		if (!$this->validate($this->conn_options['enc_type'], 'enc_type')) { $this->ex('enc_type must be one of: "tls", "ssl", or "" (empty string).'); }
		$this->mail->SMTPSecure = $this->conn_options['enc_type'];

		if (($this->conn_options['user'] != null) || ($this->conn_options['pass'] != null)) {
			$this->mail->SMTPAuth = true;
			$this->mail->Username = $this->conn_options['user'];
			$this->mail->Password = $this->conn_options['pass'];
		}
	}

	/**
	 * Add one or more email recipients.
	 *
	 * @access public
	 * @example examples.php 24 11 Adding some recipients
	 * @param string[] $addresses Key => Value array where Key is the email address, and Value is a a string with the recipients name or an empty string for none.
	 * @return self
	 */
	public function to($addresses)
	{
		if (!is_array($addresses)) { $this->ex("\$address should be a key => value array", __LINE__, __METHOD__); }
		foreach ($addresses as $addr => $name)
		{
			if (!$this->validate($addr, 'email')) { $this->ex("{$addr} is not a valid email address.", __LINE__, __METHOD__); }
			$this->mail->addAddress($addr, $name);
		}
		return $this;
	}

	/**
	 * Add one or more CC recipients.
	 *
	 * @access public
	 * @example examples.php 36 11 Adding CC recipients
	 * @param string[] $addresses Key => Value array where Key is the email address, and Value is a a string with the recipients name or an empty string for none.
	 * @return self
	 */
	public function CC($addresses)
	{
		if (!is_array($addresses)) { $this->ex("\$address should be a key => value array", __LINE__, __METHOD__); }
		foreach ($addresses as $addr => $name)
		{
			if (!$this->validate($addr, 'email')) { $this->ex("{$addr} is not a valid email address.", __LINE__, __METHOD__); }
			$this->mail->addCC($addr, $name);
		}
		return $this;
	}

	/**
	 * Add one or more BCC recipients.
	 *
	 * @access public
	 * @example examples.php 48 11 Adding BCC recipients
	 * @param string[] $addresses Key => Value array where Key is the email address, and Value is a a string with the recipients name or an empty string for none.
	 * @return self
	 */
	public function BCC($addresses)
	{
		if (!is_array($addresses)) { $this->ex("\$address should be a key => value array", __LINE__, __METHOD__); }
		foreach ($addresses as $addr => $name)
		{
			if (!$this->validate($addr, 'email')) { $this->ex("{$addr} is not a valid email address.", __LINE__, __METHOD__); }
			$this->mail->addBCC($addr, $name);
		}
		return $this;
	}

	/**
	 * Set the From/Sender headers.
	 *
	 * @access public
	 * @example examples.php 60 6 setting the From field
	 * @param string $address From email address
	 * @param string $name (default: '') Optional From Name
	 * @return self
	 */
	public function from($address, $name = '')
	{
		if (!$this->validate($address, 'email')) { $this->ex("{$address} is not a valid email address.", __LINE__, __METHOD__); }
		if (!$this->mail->setFrom($address, $name)) { $this->ex('PHPMailer Exception: ' . $this->mail->ErrorInfo, __LINE__, __METHOD__); }
		return $this;
	}

	/**
	 * Add one or more Reply-To addresses.
	 *
	 * @access public
	 * @example examples.php 67 16 Adding Reply-To recipients
	 * @param string[] $addresses Key => Value array where Key is the email address, and Value is a a string with the recipients name or an empty string for none.
	 * @return self
	 */
	public function replyTo($addresses)
	{
		if (!is_array($addresses)) { $this->ex("\$address should be a key => value array", __LINE__, __METHOD__); }
		foreach ($addresses as $addr => $name)
		{
			if (!$this->validate($addr, 'email')) { $this->ex("{$addr} is not a valid email address.", __LINE__, __METHOD__); }
			$this->mail->addReplyTo($addr, $name);
		}
		return $this;
	}

	/**
	 * Add one or more headers to the message. $headers should be in the format of array('header_name' => 'header_value', 'header2_name' => 'header2_value', ...)
	 *
	 * @access public
	 * @example examples.php 84 11 Adding headers
	 * @param string[] $headers
	 * @return self
	 */
	public function headers($headers)
	{
		if (!is_array($headers)) { $this->ex('$headers should be an array of headers in the format of "header" => "header value"', __LINE__, __METHOD__); }
		foreach ($headers as $header => $value)
		{
			$this->mail->addCustomHeader($header, $value);
		}

		return $this;
	}

	/**
	 * Send the message.
	 *
	 * $options is a key => value array. Available options are:
	 * <ul>
	 *     <li><b>'html'</b> => bool (whether or not the message is an HTML message)</li>
	 *     <li><b>'alt_body'</b> => 'Optional plaintext body used for HTML emails to be used with mail clients that don't support HTML or have HTML turned off'</li>
	 * </ul>
	 *
	 * @access public
	 * @example examples.php 96 17 Sending an HTML Email
	 * @example examples.php 114 15 Sending a Plain Text Email
	 * @param string $subject
	 * @param string $body
	 * @param mixed[] $options (default: array('html' => false))
	 * @return bool
	 */
	public function send($subject, $body, $options = array('html' => false))
	{
		if (!is_array($options)) { $this->ex('$options should be a key => value array of options and values.', __LINE__, __METHOD__); }
		if ($options['html'] == true) {
			$this->mail->IsHTML(true);
			if (isset($options['alt_body'])) {
				$this->mail->AltBody = $options['alt_body'];
			}
		}

		$this->mail->Body = $body;
		$this->mail->Subject = $subject;

		if (!$this->mail->Send()) {
			$this->ex("Error sending message: " . $this->mail->ErrorInfo, __LINE__, __METHOD__);
			return false;
		}

		return true;
	}

	/**
	 * Throws a MailerException class of exception.
	 *
	 * @access private
	 * @param string $msg The error message
	 * @param string $line (default: null) Line number the exception was raised at.
	 * @param string $method (default: null) Class:method the exception was called in.
	 * @return void
	 */
	private function ex($msg, $line = null, $method = null)
	{
		$msg = "<pre><b>Exception:</b> {$msg}\n<b>Method:</b> {$method}, <b>Line:</b> {$line}, <b>Path:</b> " . __FILE__ . "</pre>";
		throw new MailerException($msg);
	}

	/**
	 * Validates $input to ensure it matches expected $type.
	 *
	 * @access private
	 * @param mixed $input Input to validate
	 * @param string $type (default: 'email') Expected input type. Can be a string containing one of: email, host, port, or enc_type (ssl, tls, or an empty string).
	 *
	 * @return bool
	 */
	private function validate($input, $type = 'email')
	{
		switch ($type)
		{
			case 'email':
				if (filter_var($input, FILTER_VALIDATE_EMAIL) === false) {
					return false;
				}
				break;
			case 'host':
				$ip = gethostbyname($input);
				if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) {
					return false;
				}
				break;
			case 'port':
				if (filter_var($input, FILTER_VALIDATE_INT, array('options' => array('min_range' => 0))) === false) {
					return false;
				}
				break;
			case 'enc_type':
				if (($input != 'tls') && ($input != 'ssl') && ($input != '')) {
					return false;
				}
				break;
		}
		return true;
	}
}


/**
 * MailerException class.
 *
 * @extends Exception
 */
class MailerException extends Exception
{

	/**
	 * __construct function.
	 *
	 * @access public
	 * @param mixed $message
	 * @param int $code (default: 0)
	 * @param Exception $previous (default: null)
	 * @return void
	 */
	public function __construct($message, $code = 0, Exception $previous = null) {
		parent::__construct($message, $code, $previous);
	}

	/**
	 * __toString function.
	 *
	 * @access public
	 * @return string
	 */
	public function __toString() {
		return $this->message;
	}

}