A contact form is an absolute must for a professional blog or website. It is quite common these days to give your readers the opportunity to send you a message (also with an attachment) straight from your website without accessing their e-mailboxes. In this tutorial I will show you how to enclose such functionality inside a short WordPress plugin.
There are at least a dozen contact form WordPress plugins, but I always prefer to write code from scratch to sharpen my plugin development skills and to better understand how different things work inside WordPress. I encourage you to do the same. In this tutorial, we will discuss and use the following WordPress features:
init
hookWe will also learn how to validate the contents of the contact form fields by using JavaScript.
For simplicity, we will use a WordPress default template and focus on the sending method, paying no attention to the form styling issues.
Let's start with creating our plugin file named contact-form.php
. We add the file directly to the wp-content/plugins
folder inside WordPress installation. We don't have to create a separate directory for our plugin, since it consists of one file only. At the beginning of our plugin file we have to include the following standardized comments that allow WordPress to recognize our plugin:
<?php /* Plugin Name: Contact Form Plugin URI: http://www.kminek.pl/lab/wordpress-contact-form-with-attachment-support/# Description: Simple WordPress contact form with attachment support and JavaScript validation. Author: Grzegorz Wójcik Version: 1.0 Author URI: http://www.kminek.pl/ */ ?>
It's now time to create our contact form markup:
<div id="commentform"><h3>Contact Form</h3> <form action="" method="post" enctype="multipart/form-data" style="text-align: left"> <p><input type="text" name="author" id="author" value="" size="22" /> <label for="author"><small>Your name*</small></label></p> <p><input type="text" name="email" id="email" value="" size="22" /> <label for="email"><small>Your contact e-mail*</small></label></p> <p><input type="text" name="subject" id="subject" value="" size="22" /> <label for="subject"><small>Subject*</small></label></p> <p><textarea name="message" id="message" cols="100%" rows="10">type your message here...</textarea></p> <p><label for="attachment"><small>Attachment</small></label> <input type="file" name="attachment" id="attachment" /></p> <p><input name="send" type="submit" id="send" value="Send" /></p> <input type="hidden" name="contact_form_submitted" value="1"> </form> </div>
As you can see, there is nothing unusual about the above markup - it is just a basic HTML form with some standard fields (such as author, email, subject, message, attachment). However, there are a few things that are worth mentioning here:
POST
method and also we need to specify enctype="multipart/form-data"
attribute for <form>
element<input type="file">
in the attachment field, enabling the user to click the Browse... button and to pick a file to be attached contact_form_submitted
; later we will intercept this variable and other form variables using WordPress init
hook (we will discuss it below)id="commentform"
to div
containing contact form JUST to take advantage of form style applied by CSS file shipped with default template (wp-content/themes/default/style.css
). If you plan to use your own styling you should give a more appropriate and descriptive id
(like id="contact-form"
). Next we will create a PHP function named contact_form_markup()
which will return our contact form markup. I'd like to emphasize that we do not want to echo the form markup; we just want the markup to be returned. This is required by WordPress Shortcode API to work correctly.
function contact_form_markup() { $form_action = get_permalink(); $author_default = $_COOKIE['comment_author_'.COOKIEHASH]; $email_default = $_COOKIE['comment_author_email_'.COOKIEHASH]; $markup = <<<EOT <div id="commentform"><h3>Contact Form</h3> <form action="{$form_action}" method="post" enctype="multipart/form-data" style="text-align: left"> <p><input type="text" name="author" id="author" value="{$author_default}" size="22" /> <label for="author"><small>Your name*</small></label></p> <p><input type="text" name="email" id="email" value="{$email_default}" size="22" /> <label for="email"><small>Your contact e-mail*</small></label></p> <p><input type="text" name="subject" id="subject" value="" size="22" /> <label for="subject"><small>Subject*</small></label></p> <p><textarea name="message" id="message" cols="100%" rows="10">type your message here...</textarea></p> <p><label for="attachment"><small>Attachment</small></label> <input type="file" name="attachment" id="attachment" /></p> <p><input name="send" type="submit" id="send" value="Send" /></p> <input type="hidden" name="contact_form_submitted" value="1"> </form> </div> EOT; return $markup; }
We place the form markup inside $markup
variable using heredoc syntax available in PHP. At this point, if we wouldn't like to create a shortcode, we can just manually add our contact form to any template file using the following code:
if (function_exists('contact_form_markup')) echo contact_form_markup();
While the plugin is disabled it is a good practice to check if the plugin function exists in order to prevent display errors.
But it is far better to use a shortcode solution, since it will enable us to move the contact form around and to insert it in various locations inside WordPress post/page content. Actually, it is a matter of adding just one line to our plugin code to create a usable contact form shortcode:
add_shortcode('contact_form', 'contact_form_markup');
The above code will create the [contact_form]
shortcode which can be placed inside post / page content:
At the end of this step, let me discuss briefly three variables defined at the beginning of contact_form_markup()
function:
$form_action
- this variable holds URL to the page with our contact form. This URL is returned by core WP function - get_permalink()
. Basically you can treat get_permalink()
as a counterpart of $_SERVER['REQUEST_URI']
in WordPress environment. So if we place our contact form shortcode inside 'About us' page content, this variable might have following value: www.samplewordpressblog.com/about-us/
. In fact we are just posting contact form data to main WordPress index.php
file which is just hidden behind pretty permalinks.$author_default
, $email_default
- those variables are filled with information gathered from cookies, which WordPress sets when somebody leaves comment on your blog. So, if such a visitor encounters a page with contact form, corresponding form fields will be pre-filled with information saved in cookies (nice little feature).init
hookHooks provided by WordPress allow us to call our plugin functions at a specific time or in a specific context. One of the most powerful and frequently used is init
hook which runs after WordPress has finished loading but before any HTTP headers are sent. It is ideal for intercepting GET or POST variables or performing custom redirects.
Now we will write the contact_form_process()
function that will be the heart of our plugin. This function will process contact form data, perform validation checks and send an e-mail with an optional attachment. We will hook it to the init
hook. Remember, by doing so WordPress will execute our function on every generated page.
if ( !isset($_POST['contact_form_submitted']) ) return;
Do you remember the hidden form variable from the first step? We are using it here to tell WordPress that if our form was not submitted, then leave the contact_form_process()
function and do nothing (return
always stops execution of the function).
Next we perform some standard data sanitization, stripping any redundant whitespaces and possible malicious code in submitted data:
$author = ( isset($_POST['author']) ) ? trim(strip_tags($_POST['author'])) : null; $email = ( isset($_POST['email']) ) ? trim(strip_tags($_POST['email'])) : null; $subject = ( isset($_POST['subject']) ) ? trim(strip_tags($_POST['subject'])) : null; $message = ( isset($_POST['message']) ) ? trim(strip_tags($_POST['message'])) : null;
Now, it's time to validate the data. We check to see if the user filled in the required contact form fields (marked with the asterisk). We also validate the e-mail address by using the is_email()
WordPress core function. Inside your plugin you can use just about any function loaded by default by the WordPress core:
if ( $author == '' ) wp_die('Error: please fill the required field (name).'); if ( !is_email($email) ) wp_die('Error: please enter a valid email address.'); if ( $subject == '' ) wp_die('Error: please fill the required field (subject).');
We also used another WordPress core function - wp_die()
. It is a WordPress wrapper function for standard exit()
call in PHP, producing nice layout around exit message passed as parameter:
At this point our function should look like this:
function contact_form_process() { if ( !isset($_POST['contact_form_submitted']) ) return; $author = ( isset($_POST['author']) ) ? trim(strip_tags($_POST['author'])) : null; $email = ( isset($_POST['email']) ) ? trim(strip_tags($_POST['email'])) : null; $subject = ( isset($_POST['subject']) ) ? trim(strip_tags($_POST['subject'])) : null; $message = ( isset($_POST['message']) ) ? trim(strip_tags($_POST['message'])) : null; if ( $author == '' ) wp_die('Error: please fill the required field (name).'); if ( !is_email($email) ) wp_die('Error: please enter a valid email address.'); if ( $subject == '' ) wp_die('Error: please fill the required field (subject).'); //we will add e-mail sending support here soon header('Location: ' . $_SERVER['HTTP_REFERER']); exit(); }
At the end of our function we perform redirect in order to take the user back to our contact form page. We don't have to worry where the contact form is placed because we know where the form originated. Global PHP Server variable - $_SERVER['HTTP_REFERER']
holds that URL for us.
We hook our form processing function to init
hook as follows:
add_action('init', 'contact_form_process');
Now you can safely activate the plugin inside the WordPress admin area and play with the form and validation checks. Of course no mail will be sent yet, since we will add mail sending support in the next step.
If you look at the wp-includes
directory inside WordPress distribution, you will find the class-phpmailer.php
file. By default this file is not included by the WordPress core. It is included only when you use the wp_mail()
WordPress function which acts as a wrapper for the phpMailer class. But we will disregard the wp_mail()
function and learn how to interact with the phpMailer class directly.
Within our contact_form_process()
function, first we include the class file and create a class object as follows:
require_once ABSPATH . WPINC . '/class-phpmailer.php'; $mail_to_send = new PHPMailer();
Notice how the file path is constructed by using constants offered by WordPress. ABSPATH
is one of the best known WordPress constants. It holds a filesystem path to the WordPress installation. WPINC
holds the name of includes directory within distribution.
Next, we set some basic object properties (assigning variables defined earlier) required to send an e-mail successfully. The following is rather self-explanatory:
$mail_to_send->FromName = $author; $mail_to_send->From = $email; $mail_to_send->Subject = $subject; $mail_to_send->Body = $message; $mail_to_send->AddReplyTo($email); $mail_to_send->AddAddress('[email protected]'); //contact form destination e-mail
Now it's time to handle the attachment field. We will be working with the $_FILES
super global PHP variable. This variable is a multi-dimensional array containing information about all submitted files. When someone inserts a file inside our attachment form field, all information about this upload will be stored inside the $_FILES['attachment']
array:
$_FILES['attachment']['tmp_name']
- contains the path to the temporary file that resides on the server after successful submission. The file should exist on the server in a temporary directory with a temporary name. This temporary file is destroyed when script execution ends.$_FILES['attachment']['name']
- holds real name of the uploaded file (for instance apple.jpg
)$_FILES['attachment']['error']
- holds file upload status code ( 0 - successful; 1 - failed; 2 - failed; 3 - failed; 4 - no file was uploaded )To attach a file to an e-mail when using PHPMailer class you invoke AddAttachment()
method - which accepts following parameters:
AddAttachment($attachment_file_path, $attachment_file_name);
so we can easily attach our uploaded file with following statement:
$mail_to_send->AddAttachment($_FILES['attachment']['tmp_name'], $_FILES['attachment']['name']);
Below you can see the entire attachment handling part (notice the use of the is_uploaded_file()
PHP function to add extra security to our script):
if ( !$_FILES['attachment']['error'] == 4 ) { //something was send if ( $_FILES['attachment']['error'] == 0 && is_uploaded_file($_FILES['attachment']['tmp_name']) ) $mail_to_send->AddAttachment($_FILES['attachment']['tmp_name'], $_FILES['attachment']['name']); else wp_die('Error: there was a problem with the file upload. Try again later.'); }
We have the upload in place so now all we have to do is to send an e-mail:
if ( !$mail_to_send->Send() ) wp_die('Error: unable to send e-mail - status code: ' . $mail_to_send->ErrorInfo);
The Send()
method sends an email and returns true
(success) or false
(failure, for instance when SMTP sever is down).
There is one final task. We want to display the message 'Thank you for your inquiry. We will contact you shortly to answer your questions.' once the inquiry is successfully submited (just as on the screenshot above). To do so we must:
session_start()
at the begining of the contact_form_process()
function$_SESSION['contact_form_success']
session variable just before the redirect inside contact_form_process()
functioncontact_form_markup()
function - if it is present - we will add the message to the contact form markup and destroy session variable.PHP sessions are the best way to remember variable states between successive requests. Here is our plugin code with the above changes (with header comment stripped for clarity):
<?php function contact_form_markup() { $form_action = get_permalink(); $author_default = $_COOKIE['comment_author_'.COOKIEHASH]; $email_default = $_COOKIE['comment_author_email_'.COOKIEHASH]; if ( ($_SESSION['contact_form_success']) ) { $contact_form_success = '<p style="color: green"><strong>Thank you for your inquiry. We will contact you shortly to answer your questions.</strong></p>'; unset($_SESSION['contact_form_success']); } $markup = <<<EOT <div id="commentform"><h3>Contact Form</h3> {$contact_form_success} <form action="{$form_action}" method="post" enctype="multipart/form-data" style="text-align: left"> <p><input type="text" name="author" id="author" value="{$author_default}" size="22" /> <label for="author"><small>Your name*</small></label></p> <p><input type="text" name="email" id="email" value="{$email_default}" size="22" /> <label for="email"><small>Your contact e-mail*</small></label></p> <p><input type="text" name="subject" id="subject" value="" size="22" /> <label for="subject"><small>Subject*</small></label></p> <p><textarea name="message" id="message" cols="100%" rows="10">type your message here...</textarea></p> <p><label for="attachment"><small>Attachment</small></label> <input type="file" name="attachment" id="attachment" /></p> <p><input name="send" type="submit" id="send" value="Send" /></p> <input type="hidden" name="contact_form_submitted" value="1"> </form> </div> EOT; return $markup; } add_shortcode('contact_form', 'contact_form_markup'); function contact_form_process() { session_start(); if ( !isset($_POST['contact_form_submitted']) ) return; $author = ( isset($_POST['author']) ) ? trim(strip_tags($_POST['author'])) : null; $email = ( isset($_POST['email']) ) ? trim(strip_tags($_POST['email'])) : null; $subject = ( isset($_POST['subject']) ) ? trim(strip_tags($_POST['subject'])) : null; $message = ( isset($_POST['message']) ) ? trim(strip_tags($_POST['message'])) : null; if ( $author == '' ) wp_die('Error: please fill the required field (name).'); if ( !is_email($email) ) wp_die('Error: please enter a valid email address.'); if ( $subject == '' ) wp_die('Error: please fill the required field (subject).'); require_once ABSPATH . WPINC . '/class-phpmailer.php'; $mail_to_send = new PHPMailer(); $mail_to_send->FromName = $author; $mail_to_send->From = $email; $mail_to_send->Subject = $subject; $mail_to_send->Body = $message; $mail_to_send->AddReplyTo($email); $mail_to_send->AddAddress('[email protected]'); //contact form destination e-mail if ( !$_FILES['attachment']['error'] == 4 ) { //something was send if ( $_FILES['attachment']['error'] == 0 && is_uploaded_file($_FILES['attachment']['tmp_name']) ) $mail_to_send->AddAttachment($_FILES['attachment']['tmp_name'], $_FILES['attachment']['name']); else wp_die('Error: there was a problem with file upload. Try again later.'); } if(!$mail_to_send->Send()) wp_die('Error: unable to send e-mail - status code: ' . $mail_to_send->ErrorInfo); $_SESSION['contact_form_success'] = 1; header('Location: ' . $_SERVER['HTTP_REFERER']); exit(); } add_action('init', 'contact_form_process'); ?>
Our contact form is now fully operational. We have server-side validation in place. To enhance user's experience a bit, we will add simple JavaScript validation. Remember, you should NEVER rely on the client-side validation alone.
To begin with, we attach our JavaScript validation function to form's onsubmit
event inside contact_form_markup()
function:
<form onsubmit="return validateForm(this);" action="{$form_action}" method="post" enctype="multipart/form-data" style="text-align: left">
If the validateForm()
JavaScript function returns true
, our form will be submitted. Otherwise it won't be. JavaScript keyword this
always points to the current object, i.e. contact form in our example.
function validateForm(form) { var errors = ''; var regexpEmail = /\w{1,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/; if (!form.author.value) errors += "Error: please fill the required field (name).\n"; if (!regexpEmail.test(form.email.value)) errors += "Error: please enter a valid email address.\n"; if (!form.subject.value) errors += "Error: please fill the required field (subject).\n"; if (errors != '') { alert(errors); return false; } return true; }
At the beginning we defined the errors
variable as an empty string. Then afterwards we check each form field to see if it meets a certain condition and if it does, we append the error message to this variable. If there are no errors, the errors
variable will remain an empty string. But if there are any errors we alert this variable and stop the execution of the function by returning false
. We test e-mail address against quite fancy regular expression (borrowed from wForms 2.0 library).
Now we will output our JavaScript code inside <head>
section on every WordPress page. First you have to create the PHP function that echos the code:
function contact_form_js() { ?> <script type="text/javascript"> function validateForm(form) { var errors = ''; var regexpEmail = /\w{1,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/; if (!form.author.value) errors += "Error: please fill the required field (name).\n"; if (!regexpEmail.test(form.email.value)) errors += "Error: please enter a valid email address.\n"; if (!form.subject.value) errors += "Error: please fill the required field (subject).\n"; if (errors != '') { alert(errors); return false; } return true; } </script> <?php }
Then, hook it to the wp_head
hook:
add_action('wp_head', 'contact_form_js');
That's all. Hope you enjoyed it! :)
Like it or not? Leave your comments and suggestions here.
See also:
by Grzegorz Wójcik
© 2007-2025 kminek.pl