PHP Contact Form
Summary: in this tutorial, you’ll learn how to build a contact form in PHP that includes form validation, sending email, honeypot, etc.
Introduction to the PHP contact form
A contact form allows visitors of a website to leave messages. Typically, a contact form has the name, email, subject, and message input fields.
The visitors need to fill out these fields and click the submit (or send) button to send a message. In PHP, you can validate the form data and send the entered message to an intended email address.
The contact form is a target for spammers who use spambots to send unsolicited messages for advertising, phishing, spreading malware, etc.
A spambot is software that automates the spam activities like filling out and submitting contact forms automatically.
To build a spam-free contact form, you can add a captcha to it. However, sometimes, captchas are impossible to read. As a result, they create a terrible experience for legitimate users.
To avoid using a captcha while protecting the contact from spam, you can use a honeypot to trick the spambots.
A honeypot is a field on the form that the visitor cannot see, but spambots can. When a spambot sees the honeypot field, it fills the field with a value. In PHP, you can check if the honeypot has a value and skip sending the message.
To create a honeypot, you need to have a CSS class that completely hide the honeypot field as follows:
.user-cannot-see {
display:none
}
Code language: CSS (css)
And you have a honeypot field on the form:
<label for="nickname" aria-hidden="true" class="user-cannot-see"> Nickname
<input type="text"
name="nickname"
id="nickname"
class="user-cannot-see"
autocomplete="off"
tabindex="-1">
</label>
Code language: HTML, XML (xml)
Note that the name of the honeypot should look legitimate. Recently, the spambot has become smarter that can detect the honeypot.
To deal with these smart spambots, you need a smart honeypot. For example, a smart honeypot may have a different name for each request. Also, its location on the form is changed randomly.
Create the contact form
We’ll build a contact form as shown in the following picture:
The contact form has the following features:
- Form validation
- Sending message via email
- Prevent spam
- Prevent double submit
First, create the following folders & files:
├── config
| └── app.php
├── css
| └── style.css
├── inc
| ├── footer.php
| ├── get.php
| ├── header.php
| ├── mail.php
| └── post.php
└── index.php
Code language: PHP (php)
header.php
The header.php
file contains the header part of the contact form:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/style.css">
<title>PHP Contact Form</title>
</head>
<body>
<main>
Code language: HTML, XML (xml)
footer.php
The footer.php
file contains the enclosing tags of the header’s tags:
</main>
</body>
</html>
Code language: HTML, XML (xml)
get.php
The get.php
file shows the contact form:
if (isset($message)) :
<div class="alert alert-success">
<?= $message ?>
</div>
<?php endif ?><form action="index.php" method="post">
<header>
<h1>Send Us a Message</h1>
</header>
<div>
<label for="name">Name:</label>
<input type="text" value="<?= $inputs['name'] ?? '' ?>" name="name" id="name" placeholder="Full name">
<small><?= $errors['name'] ?? '' ?></small>
</div>
<div>
<label for="email">Email:</label>
<input type="email" name="email" id="email" value="<?= $inputs['email'] ?? '' ?>" placeholder="Email address">
<small><?= $errors['email'] ?? '' ?></small>
</div>
<div>
<label for="subject">Subject:</label>
<input type="subject" name="subject" id="subject" value="<?= $inputs['subject'] ?? '' ?>" placeholder="Enter a subject">
<small><?= $errors['subject'] ?? '' ?></small>
</div>
<div>
<label for="message">Message:</label>
<textarea id="message" name="message" rows="5"><?= $inputs['message'] ?? '' ?></textarea>
<small><?= $errors['message'] ?? '' ?></small>
</div>
<label for="nickname" aria-hidden="true" class="user-cannot-see"> Nickname
<input type="text" name="nickname" id="nickname" class="user-cannot-see" tabindex="-1" autocomplete="off">
</label>
<button type="submit">Send Message</button>
</form>
Code language: PHP (php)
How it works.
First, display a success message if it is set:
if (isset($message)) :
<div class="alert alert-success">
<?= $message ?>
</div>
<?php endif ?>
Code language: PHP (php)
Second, fill the input fields with data from the $inputs array. If the field has invalid data, show the error message from the $errors array.
Third, add a honeypot to the form:
<label for="nickname" aria-hidden="true" class="user-cannot-see"> Nickname
<input type="text" name="nickname" id="nickname" class="user-cannot-see" tabindex="-1" autocomplete="off">
</label>
Code language: PHP (php)
post.php
The post.php
handles the form submission:
// check the honeypot
$honeypot = filter_input(INPUT_POST, 'nickname', FILTER_SANITIZE_STRING);
if ($honeypot) {
header($_SERVER['SERVER_PROTOCOL'] . ' 405 Method Not Allowed');
exit;
}
// validate name
$name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_STRING);
$inputs['name'] = $name;
if (!$name || trim($name) === '') {
$errors['name'] = 'Please enter your name';
}
// validate email
$email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
$inputs['email'] = $email;
if ($email) {
$email = filter_var($email, FILTER_SANITIZE_EMAIL);
if (!$email) {
$errors['email'] = 'Please enter a valid email';
}
} else {
$errors['email'] = 'Please enter an email';
}
// validate subject
$subject = filter_input(INPUT_POST, 'subject', FILTER_SANITIZE_STRING);
$inputs['subject'] = $subject;
if (!$subject || trim($subject) === '') {
$errors['subject'] = 'Please enter the subject';
}
// validate message
$message = filter_input(INPUT_POST, 'message', FILTER_SANITIZE_STRING);
$inputs['message'] = $message;
if (!$message || trim($message) === '') {
$errors['message'] = 'Please enter the message';
}
Code language: PHP (php)
The post.php
checks the honeypot and returns the 405 HTTP status code if it detects the spambot. Otherwise, it sanitizes and validates the input fields, including name, email, subject, and message.
config.php
The config.php
stores the configuration information e.g., the receiver’s email address:
return [
'mail' => [
'to_email' => 'webmaster@phptutorial.net'
]
];
Code language: PHP (php)
mail.php
The mail.php
gets the email address of the receiver from the app.php configuration file. It sends to the message entered in the contact form using the mail()
function:
// get email from the config file
$config = require_once __DIR__ . '/../config/app.php';
$recipient_email = $config['mail']['to_email'];
// contact information
$contact_name = $inputs['name'];
$contact_email = $inputs['email'];
$message = $inputs['message'];
$subject = $inputs['subject'];
// Email header
$headers[] = 'MIME-Version: 1.0';
$headers[] = 'Content-type: text/html; charset=utf-8';
$headers[] = "To: $recipient_email";
$headers[] = "From: $contact_email";
$header = implode('\r\n', $headers);
mail($recipient_email, $subject, $message, $header);
Code language: PHP (php)
index.php
The index.php
file contains the main logic:
session_start();
$errors = [];
$inputs = [];
$request_method = strtoupper($_SERVER['REQUEST_METHOD']);
if ($request_method === 'GET') {
// show the message
if (isset($_SESSION['message'])) {
$message = $_SESSION['message'];
unset($_SESSION['message']);
} elseif (isset($_SESSION['inputs']) && isset($_SESSION['errors'])) {
$errors = $_SESSION['errors'];
unset($_SESSION['errors']);
$inputs = $_SESSION['inputs'];
unset($_SESSION['inputs']);
}
// show the form
require_once __DIR__ . '/inc/get.php';
} elseif ($request_method === 'POST') {
// check the honeypot and validate the field
require_once __DIR__ . '/inc/post.php';
if (!$errors) {
// send an email
require_once __DIR__ . '/inc/mail.php';
// set the message
$_SESSION['message'] = 'Thanks for contacting us! We will be in touch with you shortly.';
} else {
$_SESSION['errors'] = $errors;
$_SESSION['inputs'] = $inputs;
}
header('Location: index.php', true, 303);
exit;
}
Code language: PHP (php)
How it works.
First, show the contact form if the HTTP request method is GET. Also, get the $message
, $errors
, and $inputs
data from the $_SESSION
.
Second, handle the form submission if the HTTP request method is POST, send an email if no error, and redirect to the contact form. Note that we use the PRG (Post-Redirect-Get) technique to avoid the double submit problem.