Get up to 80 % extra points for free! More info:

Lesson 9 - Improving the e-mail form in PHP

In the previous lesson, E-mail form in PHP, we programmed a simple e-mail form. In today's lesson, we're going to improve it a bit.

Pre-filling forms

With the way we have our form set up if we enter something wrong or leave a field empty, the script will display an error message and empty the form. This is because we enter the information, be it right or wrong, we would already be on a different page.

Since we are submitting and processing data in the same script, we can easily fill the submitted values from the POST back to the form. Then, it'll look like the form was never cleared.

We'll fill the $name, $email and $message variables with the values from $_POST in the PHP block right above the form. It kind of goes without saying, but we will only be able to use them when the values exist in POST. If there aren't, we'll fill the variables with empty strings.

With that in mind, we'd probably end up writing something like this:

if (isset($_POST['name']))
    $name = $_POST['name'];
else
    $name = ";

The code is kind of long and writing it out for each of the variables sounds rather unsavory, to me at least. It would only get more and more annoying if the form had a larger amount of fields. As a workaround, we'll use the ternary operator, which is a shorter version of the if ... else sequence. The ternary operator always returns a value, that means that we can't use it in place of all conditions. It consists of 3 parts. In the first part, we write a condition followed by a question mark and the value that we want to be returned when the condition is true. Then, we write a colon and last of all, the value that will be returned when the condition is false.

One way of doing it would be like this:

$name = (isset($_POST['name'])) ? $_POST['name'] : "";

Let's take a couple of steps back and see what we've got. If the given key exists in POST, we fill the $name variable with its value. If it doesn't, we assign an empty string to it, which is done simply by writing out two empty quotes.

We'll do the same for the $email and the $message.

Now let's modify the form to make the variables fill in the inputs' value properties. If the form hasn't been submitted, an empty string will be inserted in its place. Otherwise, the data that the user provided will be set as the values.

Here's one way we could do it:

<input name="name" type="text" value="<?php echo $name ?>" />

However, if all we need to do is print content in a PHP sequence, we could use the shorter version of the <?= directive. You would get the same result by doing this:

<input name="name" type="text" value="<?= $name ?>" />

There are still a couple of more things we have to add/fix. Since the text in the $name variable comes from the user, we will have to validate it. What if the user filled it with HTML code? It would become a part of our website.

In our case, it won't matter too much, but there are cases when it would. Imagine that you're printing messages from users, e.g. comments under an article, and you get a user that submits an HTML code instead of a form linked to the user's processing script on a different website. What if their processing form requires that your visitors enter their password in order to submit "your" form? If your form actually prints the message, its code would end up displaying the malicious form. Some of your visitors might actually fill their password in which would then be sent to the attacker...and you'd be in trouble.

This kind of attack is called XSS (Cross Site Scripting). The protecting yourself against it is pretty simple, we'll use the htmlspecialchars() function before printing any variable to our HTML code. The function will convert angle HTML brackets and other special characters to HTML entities. The potentially dangerous code would then be treated as regular text and the web browser no longer process it as HTML code.

Keep in mind that we have to sanitize each and every variable, even those that don't contain user data. You never truly know whether a variable's content has been entered by the user, you simply cannot control everything. Therefore, we sanitize every single variable at the exact place where we're print it. Once you get to the more advanced lessons and you know a good bit about object-oriented architecture, you'll be able to automate this process (you won't have to sanitize data manually).

Let's take a look at our form's HTML code, including the directive above:

<?php
    if ($notice)
        echo('<p>' . htmlspecialchars($notice) . '</p>');

    $name = (isset($_POST['name'])) ? $_POST['name'] : "";
    $email = (isset($_POST['e-mail'])) ? $_POST['e-mail'] : "";
    $message = (isset($_POST['message'])) ? $_POST['message'] : "";
?>

<form method="POST">
    <table>
        <tr>
            <td>Your name</td>
            <td><input name="name" type="text" value="<?= htmlspecialchars($name) ?>"/></td>
        </tr>
        <tr>
            <td>Your e-mail</td>
            <td><input name="e-mail" type="e-mail" value="<?= htmlspecialchars($email) ?>"/></td>
        </tr>
        <tr>
            <td>Current year</td>
            <td><input name="abc" type="number" /></td>
        </tr>
    </table>
    <textarea name="message"><?= htmlspecialchars($message) ?></textarea>
    <br />

    <input type="submit" value="Send" />
</form>

Let's try it out. We'll leave a field empty and submit it. An error notification will appear but the data we entered will be preserved:

Contact form
localhost/mail­form.php

Redirecting

There are two more crucial issues with our form now. It's always pre-filled, even if we send the message successfully. Also, if the user presses the F5 key, the form will re-send the email. Every single form that submits and processes data in the same file has this issue.

When the form processing is done, we'll redirect to the same exact URL. By doing this, the data in $_POST will be lost, and any further page refreshing will not automatically send data.

We'll redirect using the header() function, which sends a header to the web browser. Headers are able to carry redirecting instructions when the Location keyword is included.

Let's redirect our form right after assigning the success notice:

if ($success)
{
    $notice = 'E-mail was successfully sent, I'll get back to you soon.';
    header('Location: mailform.php');
    exit;
}

We terminate the script using the exit() function because redirecting won't do it for us. All redirecting does is tell the visitor's browser to go to another location.

The problem with re-submitting the form when the page is refreshed has now been resolved. However, it no longer displays the success notice. This happened because when we redirected the user to the "other site", the contents stored in the variables were lost altogether, including the $notice. There are several solutions to this issue, the simplest one is to pass a variable through the GET method in the new site. Based off of that, the site would recognize whether it was redirected to it after a successful form submission or not, and display the appropriate notice. Let's modify the code to suit our new needs:

header('Location: mailform.php?success=yes');
exit;

Let's move back to the very beginning of our script where we set the notice to an empty string. We'll check if we received the success parameter in the address. If that is the case, we'll set the notice to the text that it would print out after a successful send. As we already know, the parameters from the URL address are accessed via the $_GET array:

$notice = "";
if (isset($_GET['success']))
    $notice = 'E-mail was successfully sent, I'll get back to you soon.';

Congratulations, You now have a fully functioning form!

BEWARE! You can only redirect if we have not yet printed any HTML. At the moment where anything is printed, PHP sends a header stating that you're sending an HTML document. The header can only be sent once, so if you tried to redirect in the middle of the file, you'd get a "Headers already sent" error message and no redirection would occur:

<html>
    <body>
        <?php
            // This code is wrong
            header('Location: index.php');
            exit;
        ?>
    </body>
</html>

You should always remain aware of what you're doing because mistakes aren't always that obvious:

-- Empty line added by new line character --
<?php

    header('Location: index.php');

?>
<html>
    <body>
    </body>
</html>

There is a new-line character at the beginning of the file. Even that one character starts the output which forces PHP to send the header. The same problem comes with whitespaces. If you save your files using UTF-8 encoding with BOM, it may cause issues like this as well. However, if you use a smart IDE, you won't run into this sort of problem.

Note: There are people out there who redirect by echoing a JavaScript code that changes the browser window's address. Sort of like this:

// This code is wrong
echo('<script type="text/javascript">
    window.location = "index.php"
</script>');

Doing it this way is reckless and can also prove to be dangerous in certain scenarios. Don't do it.

We could style the form as well, to make it look nicer. The message could roll out as a bubble or something, but that's not what our PHP lessons are about, we have other courses where you can learn all about styling HTML documents. :) In the next lesson, Putting web pages together using PHP, you'll learn how to create websites dynamically. The completed e-mail form is available for download below.


 

Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.

Download

By downloading the following file, you agree to the license terms

Downloaded 82x (1.29 kB)
Application includes source codes in language PHP

 

Previous article
E-mail form in PHP
All articles in this section
PHP Basic Constructs
Skip article
(not recommended)
Putting web pages together using PHP
Article has been written for you by David Capka Hartinger
Avatar
User rating:
1 votes
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn university David learned IT at the Unicorn University - a prestigious college providing education on IT and economics.
Activities