facebook google plus twitter
Webucator's Free PHP Tutorial

Lesson: Sending Email with PHP

Welcome to our free PHP tutorial. This tutorial is based on Webucator's Introduction to PHP Training course.

In this lesson, we show you how to use PHP's built-in mail() function, and then show you how to use PHPMailer, an excellent email extension, which provides more features than the built-in mail() function.

Lesson Goals

  • To send emails using PHP's built-in mail() function.
  • To send email using PHPMailer, a PHP extension with more features than mail().

mail()

PHP has a built-in mail() function, which returns true on success and false on failure. Note that the function can't actually know if the email was sent. It only knows if it was successfully able to hand off the task to the mail server. Also note that for mail() to work, you need to have a mail server configured.

mail() Parameters
Parameter Description
$to The address to send the email to.
$subject The email's subject.
$message The body of the email.
$additional_headers Optional. An array of additional headers (e.g, From, Reply-To)
$additional_parameters Optional. An array of any additional parameters you may want to send to your mail server.

Code Sample:

Email/Demos/mail.php
<?php
  ini_set('display_errors', '1');
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="../../static/styles/normalize.css">
<link rel="stylesheet" href="../../static/styles/styles.css">
<title>Mail()</title>
</head>
<body>
<main>
<?php
  $to = 'somebody@example.com';
  $headers = array('From' => 'you@youremail.com');
  $subject = 'Test Email';
  $message = 'Test Message';

  if(mail($to,$subject,$message,$headers)) {
    echo "Message Sent (Maybe)";
  } else {
     echo "Message Not Sent";
  }
?>
</main>
</body>
</html>

Code Explanation

Visit http://localhost:8888/Webucator/php/Email/Demos/mail.php to run this page. For this example to work, you will have to change the email addresses in the file and you will need to have a mail server configured, which you likely do not, but don't worry. You may be able to use this when you are using a production server, but you're probably going to want to do it differently. We're going to show you a better way to send emails next.

Shortcomings of mail()

The mail() function has many limitations.

  • No support for SMTP authentication.
  • Difficult to send HTML-formatted emails.
  • Difficult to add attachments.

Luckily, there are extensions that do provide these features.

Setting Up PHPMailer

A very good email extension is PHPMailer, which is available for free at https://github.com/PHPMailer/PHPMailer.

Get and Install the Latest Version of PHPMailer

  1. In your browser, navigate to https://github.com/PHPMailer/PHPMailer, click the Clone or download button and then click Download ZIP.PHPMailer download
  2. Download and unzip the file and open the resulting PHPMailer-master folder:PHPMailer source
  3. Copy the src directory and paste it in the includes folder that you created (or found) outside of the web root. Rename the src directory "PHPMailer":Include PHPMailer

Finding the Web Root

Open http://localhost:8888/Webucator/php/PHPBasics/Demos/find-document-root.php to find the location of the web (or document) root. If you have taken this course straight through and haven't changed your setup, the includes folder should be next to the htdocs folder and have a file called config.php in it.

Run http://localhost:8888/Webucator/php/Email/Demos/PHPMailer-include-test.php in your browser to test that you are able to include PHPMailer. It should look like this:PHPMailer Include Test

Mail Server

To send email, you need access to a mail server. While it is possible to set up a mail server on your own computer, in this course, we assume you are sending email using a hosted email service, such as hotmail.com, gmail.com, or yahoo.com. For production websites, your hosting provider or IT team would provide you with the settings you need to connect to your mail server.

Be Careful with your Password!

Storing your personal password in a file is risky. If you choose to do it for this course, you should be sure to remove it once you are done. A safer alternative is to create another free email account with a different password for testing.

A Note on gmail.com

To send email with PHP and gmail.com, you will need to enable less secure apps in your gmail.com account. One option is to enable less secure apps to test your code and then disable it again when you're done.

See https://support.google.com/accounts/answer/6010255 to learn more about this.

We need to let PHPMailer know what mail server we are using, how to log in, and from whom to send emails. We will use a function for creating a new reusable, pre-configured PHPMailer object in the mail_config.php file in our includes directory outside of the web root.

Including a Mail Configuration File

Duration: 10 to 15 minutes.

In this exercise, you will add the configuration file shown below to your includes folder outside of your web root for sending emails.

Code Sample:

Email/Exercises/mail-config-sample.php
<?php
  /*
    This config file is specific to the development machine.
    The config file on production will have the same
      functions, but they will return different values.
  */
  //Import PHPMailer classes into the global namespace
  use PHPMailer\PHPMailer\PHPMailer;
  use PHPMailer\PHPMailer\Exception;

  require_once "PHPMailer/PHPMailer.php";
  require_once "PHPMailer/SMTP.php";
  require_once "PHPMailer/Exception.php";

  function createMailer($debug=false) {
    $debugMode = $debug ? 2 : 0;
    $mail = new PHPMailer(true);

    // Change these values to match your settings
    $mail->Host = "smtp.live.com"; // hotmail.com or outlook.com
    $mail->Port = 587;
    $mail->Username = 'you@hotmail.com'; // SMTP account username
    $mail->Password = 'yourP@ssw0rd'; // SMTP account password
    $mail->setFrom('you@hotmail.com', 'Your Name');
    
    // Uncomment the next line and change the email address
    //   if you don't want replies to go to the from address
    // $mail->addReplyTo('donotreply@example.com');
  
    // Don't change values below this
    $mail->IsSMTP(); // use Simple Mail Transfer Protocol
    $mail->SMTPAuth = true; // enable SMTP authentication
    $mail->SMTPSecure = 'tls'; // use Transport Layer Security
    $mail->isHTML(true); // send as HTML
    $mail->SMTPDebug = $debugMode; // Debugging. 0 = no debug output
  
    return $mail;
  }
?>

Code Explanation

  1. In Email/Exercises, you will find the mail-config-sample.php file shown above. Copy and paste that file into the includes folder outside of your web root and then rename it mail-config.php:Finder includes/mail-config.php
  2. In your editor, open mail-config.php from the includes directory.
  3. Note the first few lines:
    use PHPMailer\PHPMailer\PHPMailer;
    use PHPMailer\PHPMailer\Exception;
    
    require_once "PHPMailer/PHPMailer.php";
    require_once "PHPMailer/SMTP.php";
    require_once "PHPMailer/Exception.php";
    These are required to send email with PHPMailer. The two use statements bring PHPMailer's PHPMailer and Exception classes into the global namespace. Namespaces are beyond the scope of this course. It suffices to know that these lines need to be there so that we can use PHPMailer to send email and its related Exception class to catch PHPMailer-specific exceptions. The require_once statements bring in the PHPMailer files you have placed in the includes folder.
  4. Below the require_once statements, you will see the createMailer() function, which creates and returns a configured PHPMailer object that you can use to send email, but first you will have to change the configuration.
  5. Leave the $debugMode line as is. This is used to set the debug mode of PHPMailer. If you pass true to the createMailer() function, debug mode will be turned on via the SMTPDebug property.
  6. Change the values of $mail->Host to the host of your email account:
    1. For hotmail.com and outlook.com email addresses, the value should be 'smtp.live.com'.
    2. For gmail.com, the value should be 'smtp.gmail.com'.
    3. For yahoo.com, the value should be 'smtp.mail.yahoo.com'.
    4. If you don't have one of the above email addresses, you will need to either create one or look up the settings for the service you have.
  7. You probably won't need to change the value of $mail->Port as most services use 587.
  8. Change the values of the $mail->Username and $mail->Password to the username (email address) and password that you use to log in to your email account.
  9. Change the values passed in to $mail->setFrom to the email address from which you want to send emails (most likely the same email you used for your username) and to your name, respectively.
  10. By default, replies will go to the sender's email address. If you want them to go to a different address, uncomment the $mail->addReplyTo() line and replace the email address with the one you want replies to go to.
  11. Save mail-config.php.
  12. Run http://localhost:8888/Webucator/php/Email/Exercises/send-email-test.php in your browser to test that you are able to send email. Enter your email in the form field and click Send. It should output some debugging information followed by a success message:Send Email Test: SuccessCheck your email. You should receive an email shortly.
    1. If the email fails to send, you should get debugging information followed by a failure message:Send Email Test: FailThe reasons for failure will vary. In the screenshot above, the email failed because it couldn't authenticate. This probably means the username or password was wrong. If yours fails, read through the debugging information to see if you can figure out why it failed. If you have set up an email account, then it likely has something to do with your email configuration in mail-config.php.
    2. If you get a success message, but do not receive an email, check your spam. If you still don't find the email, log into your email account and look in your sent items to see if an email was sent. If it was not, then you'll have to do some more debugging to figure out what's going on. The reasons will vary and can be tricky to figure out.

Sending Email with PHPMailer

Now that you know that you are able to send email with PHPMailer, let's take a look at how send-email-test.php is written.

Code Sample:

Email/Exercises/send-email-test.php
---- C O D E   O M I T T E D ----
<?php
  if (!isset($_POST['send'])) {
?>
  <form method="post" action="send-email-test.php">
    <label for="email">Email:</label>
    <input id="email" name="email">
    <button name="send">Send Test Email</button>
  </form>
<?php
  } else {
    require_once 'mail-config.php';

    $now = date('r'); // Formatted date / time
  
    try {
      $mail = createMailer(true);
      $mail->addAddress($_POST['email'], 'Webucator Student');
      $mail->Subject = 'Test Email';
      $mail->Body = "<p><strong>Email works!</strong> - $now</p>";
      $mail->AltBody = "Email works! - $now";
  
      $mail->send();
      echo "<p class='success'>Test email sent.</p>";
    } catch (Exception $e) {
      echo "<p class='error'>We could not send your email.</p>";
      echo "<h3>Mailer Exception: </h3>" . $mail->ErrorInfo;
    }
  }
?>
---- C O D E   O M I T T E D ----

Code Explanation

If the form is submitted, we attempt to send the email:

  1. We include mail-config.php and set $now to hold a formatted date, so we can include that in the email message.
  2. Within a try block, we...
    1. Create a PHPMailer object, $mail, by calling createMailer(). We pass true to turn debugging on.
    2. We set the recipient's email and name using the $mail->addAddress() method.
    3. We set the subject ($mail->Subject), HTML body ($mail->Body), and text body ($mail->AltBody). It's a good idea to send a text body in case the recipient's email client doesn't support HTML, though most do these days.
    4. Finally, we try to send the email using $mail->send(). If this fails, an exception will occur, which will take us to the catch block.
  3. In the catch block, we output the reason for the exception. We use $mail->ErrorInfo to output the error, which is useful during development. A common exception is a failure to connect to the SMTP server.

PHPMailer Methods and Properties

The following tables show some of the more common methods and properties of PHPMailer, many of which we have already seen. For complete documentation, see http://phpmailer.github.io/PHPMailer/classes/PHPMailer.PHPMailer.PHPMailer.html.

PHPMailer Methods
Method Description
addAddress() Adds a "To" address and name.
addAttachment() Adds an attachment from a path on the file system.
addBCC() Adds a "bcc" address.
addCC() Adds a "cc" address.
addReplyTo() Adds a "Reply-to" address. Without this, replies will go to the sender's email.
isHTML() Sets message type to HTML.
isSMTP() Sets Mailer to send message using Simple Mail Transfer Protocol (SMTP).
send() Attempts to send message. If the message is not sent successfully then an exception occurs.
setFrom() Adds a "From" address and name.
PHPMailer Properties
Property Description
AltBody Sets the text-only body of the message.
Body Sets the Body of the message. This can be either an HTML or text body.
ErrorInfo Holds the most recent mailer error message.
Host Sets the SMTP hosts. All hosts must be separated by semicolons.
Password Sets SMTP password.
Port Sets the port. Usually 587.
SMTPAuth Sets SMTP authentication. Utilizes the Username and Password properties.
SMTPDebug SMTP class debug output mode.
SMTPSecure Sets the encryption method. Usually set to 'tls'.
Subject Sets the Subject of the message.
Username Sets SMTP username.
WordWrap Sets word wrapping on the body of the message to a given number of characters.

Creating a Contact Form

Duration: 30 to 45 minutes.

In this exercise, you will process the submission of the simple contact form created here:

Code Sample:

Email/Exercises/phppoetry.com/contact.php
<?php
ini_set('display_errors', '1');
$pageTitle = 'Contact Us';
require 'includes/header.php';

$f = [];

// Trim and Assign Form Entries
$f['first-name'] = trim($_POST['first-name'] ?? '');
$f['last-name'] = trim($_POST['last-name'] ?? '');
$f['email'] = trim($_POST['email'] ?? '');
$f['message'] = trim($_POST['message'] ?? '');
$f['placeholder'] = 'Be it poetry. Be it prose.
This is where your message goes.';

echo '<main id="contact-form">';
echo "<h1>$pageTitle</h1>";

// You will do your work here.
  ?>
  <form method="post" action="contact.php" novalidate>
    <label for="first-name">First Name*:</label>
    <input name="first-name" id="first-name"
      value="<?= $f['first-name'] ?>" required>
    <label for="last-name">Last Name*:</label>
    <input name="last-name" id="last-name"
      value="<?= $f['last-name'] ?>" required>
    <label for="email">Email*:</label>
    <input type="email" name="email" id="email"
      value="<?= $f['email'] ?>" required>
    <label for="message">Message*:</label>
    <textarea placeholder="<?= $f['placeholder'] ?>" id="message"
      name="message"><?= $f['message'] ?></textarea>
    <button name="send" class="wide">Send</button>
  </form>
</main>
<?php
  require 'includes/footer.php';
?>

Code Explanation

Navigate to http://localhost:8888/Webucator/php/Email/Exercises/phppoetry.com/contact.php to open the page in your browser. Submit the form and you will notice that the page simply refreshes. You need to write the processing code.

Things to notice:

  1. The action of the form is "contact.php". The page is submitting to itself. The processing code should only run if the form has been submitted.
  2. We include the novalidate attribute in the <form> tag so that we can submit bad data to test our PHP validation. In a production environment, we would probably remove this attribute unless we were customizing the client-side validation with JavaScript.
  1. Open Email/Exercises/phppoetry.com/includes/constants.php in your editor. This file contains many constants that we will use throughout the rest of the course. We will use the first two in this exercise: POEM_MAIL_FAIL and POEM_MAIL_SUCCESS.
  2. Open Email/Exercises/phppoetry.com/includes/header.php in your editor. Notice that we now require constants.php.
  3. Open Email/Exercises/phppoetry.com/contact.php. This is where you will process the form.
  4. The form processing code should only run if the form has been submitted. Write an if condition checking to see if the form has been submitted by checking for the presence of 'send' in the $_POST array. There are a variety ways to do this. The rest of the code you write will go within this if block.
  5. Include mail-config.php.
  6. We will track any errors in the form submission in an $errors array. To start that, set an empty $errors array.
  7. Check to make sure $f['first-name'] and $f['last-name'] have values, that $f['email'] is a valid email address, and that $f['message'] is at least 10 characters long. For the email error, you may want to use the POEM_INVALID_EMAIL constant.
  8. If there are errors, report them in an ordered list with the class of "error": Contact Form submitted. Validation Errors.
  9. If there are no errors, attempt to send an email to the person who submitted the form. Include the data from the form in the body of the email. For example, your email body might read:
    Oh, happy day, we are touched
    You took the time to contact us
    We will take note
    Of what you wrote
    Thank you very, very much!
    
    Your Name: Nat Dunn
    Your Email: nat@example.com
    Your Message
    I am writing to say hi.
  10. If the email sends, output the POEM_MAIL_SUCCESS constant to the browser in a <p> tag with the class of "success": Contact Form submitted. Email success.
  11. If there is an exception, log the error in utilities.php using the logError() function: Contact Form submitted. Exception.
  12. Visit http://localhost:8888/Webucator/php/Email/Exercises/phppoetry.com/contact.php to test your solution in your browser.

Solution:

Email/Solutions/phppoetry.com/contact.php
---- C O D E   O M I T T E D ----
if (isset($_POST['send'])) {
  require_once 'mail-config.php';
  
  $errors = [];

  if (!$f['first-name']) {
    $errors[] = 'First name is required.';
  }

  if (!$f['last-name']) {
    $errors[] = 'Last name is required.';
  }

  if (!$f['email']) {
    $errors[] = 'Email is required.';
  } elseif (!filter_var($f['email'], FILTER_VALIDATE_EMAIL)) {
    $errors[] = nl2br(POEM_INVALID_EMAIL);
  }

  if (strlen($f['message']) < 10) {
    $errors[] = 'Your message must be at least 10 characters long.';
  }

  if ($errors) {
    echo '<h3>Please correct the following errors:</h3>
    <ol class="error">';
    foreach ($errors as $error) {
      echo "<li>$error</li>";
    }
    echo '</ol>';
  } else {
    $to = $f['email'];
    $toName = $f['first-name'] . ' ' . $f['last-name'];
    $subject = 'Contact Form Submission';

    $html = "<p>Oh, happy day, we are touched<br>
    You took the time to contact us<br>
    We <em>will</em> take note<br>
    Of what you wrote<br>
    Thank you <em>very</em>, very much!</p>
    <p>Name: $toName</p>
    <p>Email: " . $f['email'] . "</p>
    <h3>Your Message</h3>" . nl2br($f['message']);

    $text = "Oh, happy day, we are touched
You took the time to contact us
We will take note
Of what you wrote
Thank you very, very much!
    
* Name: $toName
* Email: " . $f['email'] . "
* Your Message: " . $f['message'];

    echo '<article class="poem">';
    try {
      // Pass true to createMailer() to enable debugMode
      $mail = createMailer();
      $mail->addAddress($to, $toName);
      // $mail->addBcc('you@example.com');
      $mail->Subject = $subject;
      $mail->Body = $html;
      $mail->AltBody = $text;
  
      $mail->send();
      echo '<p class="success">' . nl2br(POEM_MAIL_SUCCESS) . '</p>';
    } catch (Exception $e) {
      echo '<p class="error">' . nl2br(POEM_MAIL_FAIL) . '</p>';
      logError("<h3>Mailer Exception: </h3>" . $mail->ErrorInfo);
    }
    echo '</article>';
  }
}
?>
---- C O D E   O M I T T E D ----