Sending Email in PHP

Sending Email in PHP can be accomplished in many different ways:

The mail() function is universal, easy-to-use, and requires no additional configuration. The mail() function, if not used correctly, is vulnerable to attacks and can turn a web site into a spam hub. Pear::Mail is aging, and not adequately up to PHP 5 coding standards. SwiftMailer is fully featured, resistant against Email injection attacks, and more flexible. However, it is not universally deployed on all servers. Composer has been deployed on the server here at Deep Dive Coding and SwiftMailer is the preferred method for sending Email.

Installing SwiftMailer Using Composer

Create a composer.json File


To use either PHPMailer or SwiftMailer, Composer must be installed. Composer is a package manager for PHP that automatically manages packages, their dependencies, and their versions. At its core, Composer uses a file called composer.json. This file enumerates all the composer packages imported into the project and configures the composer command to download all the requested packages. To install SwiftMailer, create a composer.json file in the root of the git project:

{
"require": {
"swiftmailer/swiftmailer": "@stable"
}
}

Ignore Composer Generated Files


Composer generates files that will pollute a git repository and present a security risk if committed to git. All these files are in the vendor directory. Add the following line to the .gitignore file:

/vendor

Commit and push the .gitignore file.

Execute Composer to Install SwiftMailer


This will tell composer to install the latest stable SwiftMailer. Add the composer.json file to git and commit and push it.

Once the composer.json file is committed, SSH to the project and execute the following commands:

cd public_html/project-name
composer install

This will tell composer to read the composer.json file and download all the specified packages. After this process is complete, composer will generate two items:

  • vendor: directory containing all the code downloaded
  • composer.lock: automatically generated file containing package data

Download the composer.lock file and add, commit, and push it to git. The composer.json and composer.lock files should be the only composer related files on git.

Example Form

<form class="form-horizontal well" action="email.php">
	<div class="form-group">
		<label for="name">Name</label>
		<div class="input-group">
			<div class="input-group-addon">
				<i class="fa fa-user" aria-hidden="true"></i>
			</div>
			<input type="text" class="form-control" id="name" name="name" placeholder="Name">
		</div>
	</div>
	<div class="form-group">
		<label for="email">Email address</label>
		<div class="input-group">
			<div class="input-group-addon">
				<i class="fa fa-envelope" aria-hidden="true"></i>
			</div>
			<input type="email" class="form-control" id="email" name="email" placeholder="Email">
		</div>
	</div>
	<div class="form-group">
		<label for="subject">Subject</label>
		<div class="input-group">
			<div class="input-group-addon">
				<i class="fa fa-pencil" aria-hidden="true"></i>
			</div>
			<input type="text" class="form-control" id="subject" name="subject" placeholder="Subject">
		</div>
	</div>
	<div class="form-group">
		<label for="message">Message</label>
		<div class="input-group">
			<div class="input-group-addon">
				<i class="fa fa-comment" aria-hidden="true"></i>
			</div>
			<textarea class="form-control" rows="5" id="message" name="message" placeholder="Message"></textarea>
		</div>
	</div>
	<button class="btn btn-success" type="submit"><i class="fa fa-paper-plane"></i> Send</button>
	<button class="btn btn-warning" type="reset"><i class="fa fa-ban"></i> Reset</button>
</form>

Example Code

<?php
/**
 * require all composer dependencies; requiring the autoload file loads all composer packages at once
 * while this is convenient, this may load too much if your composer configuration grows to many classes
 * if this is a concern, load "/vendor/swiftmailer/autoload.php" instead to load just SwiftMailer
 **/
require_once(dirname(dirname(dirname(dirname(__DIR__)))) . "/vendor/autoload.php");

try {
	// sanitize the inputs from the form: name, email, subject, and message
	// this assumes jQuery (not Angular will be submitting the form, so we're using the $_POST superglobal
	$name = filter_input(INPUT_POST, "name", FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
	$email = filter_input(INPUT_POST, "email", FILTER_SANITIZE_EMAIL);
	$subject = filter_input(INPUT_POST, "subject", FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
	$message = filter_input(INPUT_POST, "message", FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);

	// create Swift message
	$swiftMessage = Swift_Message::newInstance();

	// attach the sender to the message
	// this takes the form of an associative array where the Email is the key for the real name
	$swiftMessage->setFrom([$email => $name]);

	/**
	 * attach the recipients to the message
	 * notice this an array that can include or omit the the recipient's real name
	 * use the recipients' real name where possible; this reduces the probability of the Email being marked as spam
	 **/
	$recipients = ["deepdivecoder@cnm.edu" => "Deep Dive Coder"];
	$swiftMessage->setTo($recipients);

	// attach the subject line to the message
	$swiftMessage->setSubject($subject);

	/**
	 * attach the actual message to the message
	 * here, we set two versions of the message: the HTML formatted message and a special filter_var()ed
	 * version of the message that generates a plain text version of the HTML content
	 * notice one tactic used is to display the entire $confirmLink to plain text; this lets users
	 * who aren't viewing HTML content in Emails still access your links
	 **/
	$swiftMessage->setBody($message, "text/html");
	$swiftMessage->addPart(html_entity_decode($message), "text/plain");

	/**
	 * send the Email via SMTP; the SMTP server here is configured to relay everything upstream via CNM
	 * this default may or may not be available on all web hosts; consult their documentation/support for details
	 * SwiftMailer supports many different transport methods; SMTP was chosen because it's the most compatible and has the best error handling
	 * @see http://swiftmailer.org/docs/sending.html Sending Messages - Documentation - SwitftMailer
	 **/
	$smtp = Swift_SmtpTransport::newInstance("localhost", 25);
	$mailer = Swift_Mailer::newInstance($smtp);
	$numSent = $mailer->send($swiftMessage, $failedRecipients);

	/**
	 * the send method returns the number of recipients that accepted the Email
	 * so, if the number attempted is not the number accepted, this is an Exception
	 **/
	if($numSent !== count($recipients)) {
		// the $failedRecipients parameter passed in the send() method now contains contains an array of the Emails that failed
		throw(new RuntimeException("unable to send email"));
	}

	// report a successful send
	echo "<div class=\"alert alert-success\" role=\"alert\">Email successfully sent.</div>";
} catch(Exception $exception) {
	echo "<div class=\"alert alert-danger\" role=\"alert\"><strong>Oh snap!</strong> Unable to send email: " . $exception->getMessage() . "</div>";
}