12.1. Email Validation

Validating a user's email address helps you avoid basic spam attempts. Some spam bots simply add garbage text into a form's inputs. By ensuring that the email input is filled out with a properly formatted email address, you can fend off the most primitive types of spam attacks.

However, you also want to keep in mind that people sometimes make typos, and if your comment form can't validate a user with a typo in her email address, you don't want to erase her comment completely and make her start over; this will frustrate your user and might prevent this person from joining the discussion on your blog.

To avoid this problem, you can save the comment in a session that you destroy only if the comment is posted successfully (or the session times out). This way, you won't penalize a real user who makes a simple mistake, giving her a chance to fix her mistake on the form before posting.

To validate email addresses before you save comments, you need to make the following modifications to the Comments class:

  • Add a method to validate email addresses using a regular expression

  • Modify the saveComment() method to call the validation function

  • Save the contents of a comment form in session variables after posting

  • Generate an error message if errors occur, so the user knows what went wrong

  • Fill out the comment form with session values if they exist

  • Destroy the session after a successful comment post

12.1.1. Adding a Method to Validate Email

Your first task is to write a new method that verifies whether the email address provided is a valid email format.

NOTE

This method doesn't verify that the email address is real; rather, it ensures that the format is valid. A user can still provide a fake email address, such as [email protected], and post a comment successfully.

You should make this method, called validateEmail(), private because you call it only from the Comments class. It's short and sweet: when called, it accepts the user's email address as an argument ($email) and matches the provided email address against a regular expression; the address is valid if the expression returns TRUE, but FALSE otherwise.

Open comments.inc.php and add validateEmail() just below the saveComment() method:

private function validateEmail($email)
    {
        // Matches valid email addresses
        $p = '/^[w-]+(.[w-]+)*@[a-z0-9-]+'
            .'(.[a-z0-9-]+)*(.[a-z]{2,4})$/i';

        // If a match is found, return TRUE, otherwise return FALSE
        return (preg_match($p, $email)) ? TRUE : FALSE;
    }

The regular expression you use in this method looks incredibly complex, but you can break it down into three relatively simple sections: the part of the email address before the at (@) symbol, the part between the at (@) symbol and the period (.), and the top level domain (such as .com or .net).

The first section needs to match any hyphens (-) and word characters (symbolized by w—this matches the letters, numbers, and underscore [_]) from the beginning of the string to the at (@) symbol. However, you should keep in mind that some email addresses use periods (.) as well, as in [email protected]. This means you need to accommodate this email construction in your pattern, but not require it. Put this all together, and this section of the regular expression looks like this:

^[w-]+(.[w-]+)*@

By placing the second part of the pattern in parentheses with the star (*) following, you can make this section required zero or more times, which effectively makes it optional.

Remember that the period (.) in regular expressions serves as a wildcard and matches nearly anything. To match only a period, be sure to escape the period with a backslash (.).


The third section must match a domain name. Domain names cannot contain the underscore character (_), so this part of the pattern cannot use the word character shortcut (w). Instead, you have to set up a character set manually that allows for letters, numbers, and the hyphen (-), and then require that one or more matches exist. As in the first section, your pattern must accommodate an optional set that matches the period (.) in the event a user's email address is from a subdomain, such as [email protected]. The third section of the pattern looks like this:

[a-z0-9-]+(.[a-z0-9-]+)*

Finally, the pattern must match a top-level domain, such as .com or .net. This is as simple as creating a set of the letters a-z and requiring between two and four total characters that are at the end of the string; you express this requirement like this:

(.[a-z]{2,4})$

Next, wrap this pattern in the required forward slash (/) delimiter, then append the i flag to make the pattern case-insensitive.

12.1.2. Validating Email Addresses

So far you've written a method to validate submitted email addresses. Next, you can modify saveComment() to call validateEmail(); this ensures that you save only comments with valid email addresses.

Inside saveComment(), add a conditional statement that performs an important check. If the returned value of validateEmail() is FALSE when the value of $p['email'] is passed, it terminates the function by returning FALSE.

Add these modifications by inserting the bold code into saveComment():

// Save comments to the database
    public function saveComment($p)
    {
        // Make sure the email address is valid first
        if($this->validateEmail($p['email'])===FALSE)
        {
            return FALSE;
        }

        // Sanitize the data and store in variables
        $blog_id = htmlentities(strip_tags($p['blog_id']),
            ENT_QUOTES);
        $name = htmlentities(strip_tags($p['name']), ENT_QUOTES);
        $email = htmlentities(strip_tags($p['email']), ENT_QUOTES);
        $comment = htmlentities(strip_tags($p['comment']),
            ENT_QUOTES);

// Generate an SQL command
        $sql = "INSERT INTO comments (blog_id, name, email, comment)
                VALUES (?, ?, ?, ?)";
        if($stmt = $this->db->prepare($sql))
        {
            // Execute the command, free used memory, and return true
            $stmt->execute(array($blog_id, $name, $email, $comment));
            $stmt->closeCursor();
            return TRUE;
        }
        else
        {
            // If something went wrong, return false
            return FALSE;
        }
    }

To test this, navigate to http://localhost/simple_blog/blog and select an entry. Now try to post a comment with a bad email address (such as "notarealemail") and click the Post Comment button. The script in update.inc.php will fail and output an error message because saveComment() returns FALSE when a valid email isn't supplied:

Something went wrong while saving the comment.

12.1.3. Saving Comments in Sessions

Next, you need to save the user's input in a session. As explained earlier, you do this so the user isn't forced to start from scratch if an error occurs when submitting the comment form.

In comments.inc.php, modify saveComment() by adding the following code in bold:

// Save comments to the database
    public function saveComment($p)
    {
        // Save the comment information in a session
        $_SESSION['c_name'] = htmlentities($p['name'], ENT_QUOTES);
        $_SESSION['c_email'] = htmlentities($p['email'], ENT_QUOTES);
        $_SESSION['c_comment'] = htmlentities($p['cmnt'],
            ENT_QUOTES);

        // Make sure the email address is valid first
        if($this->validateEmail($p['email'])===FALSE)
        {
            return FALSE;
        }

// Sanitize the data and store in variables
        $blog_id = htmlentities(strip_tags($p['blog_id']),
            ENT_QUOTES);
        $name = htmlentities(strip_tags($p['name']), ENT_QUOTES);
        $email = htmlentities(strip_tags($p['email']), ENT_QUOTES);
        $comment = htmlentities(strip_tags($p['comment']),
            ENT_QUOTES);

        // Generate an SQL command
        $sql = "INSERT INTO comments (blog_id, name, email, comment)
                VALUES (?, ?, ?, ?)";
        if($stmt = $this->db->prepare($sql))
        {
            // Execute the command, free used memory, and return true
            $stmt->execute(array($blog_id, $name, $email, $comment));
            $stmt->closeCursor();
            return TRUE;
        }
        else
        {
            // If something went wrong, return false
            return FALSE;
        }
    }

12.1.3.1. Displaying the Stored Comment Information

You've saved the comment information in a session; now you can use the session variables to keep the comment form populated with the user's information. You can do this by modifying showCommentForm() to check whether the session variables are set, then store for output in the form.

You can add the code in bold to make showCommentForm() start showing stored comments:

// Display a form for users to enter new comments with
    public function showCommentForm($blog_id)
    {
        // Check if session variables exist
        if(isset($_SESSION['c_name']))
        {
            $n = $_SESSION['c_name'];
        }
        else
        {
            $n = NULL;
        }

if(isset($_SESSION['c_email']))
        {
            $e = $_SESSION['c_email'];
        }
        else
        {
            $e = NULL;
        }
        if(isset($_SESSION['c_comment']))
        {
            $c = $_SESSION['c_comment'];
        }
        else
        {
            $c = NULL;
        }

        return <<<FORM
<form action="/simple_blog/inc/update.inc.php"
    method="post" id="comment-form">
    <fieldset>
        <legend>Post a Comment</legend>
        <label>Name
            <input type="text" name="name" maxlength="75"
                value="$n" />
        </label>
        <label>Email
            <input type="text" name="email" maxlength="150"
                value="$e" />
        </label>
        <label>Comment
            <textarea rows="10" cols="45"
                name="comment">$c</textarea>
        </label>
        <input type="hidden" name="blog_id" value="$blog_id" />
        <input type="submit" name="submit" value="Post Comment" />
        <input type="submit" name="submit" value="Cancel" />
    </fieldset>
</form>
FORM;
    }

To test this, navigate to http://localhost/simple_blog/blog and select an entry. Enter a comment with a valid email address (you want to avoid the error screen for now), then click the Post Comment button to place a new comment on the blog entry.

After posting the comment, this script sends you back to the entry, where you can see the comment displayed both in the comment view as well as in the comment form. Your users might be confused if the form stays populated after a successful post, so you need to unset the session variables after a comment is stored properly.

You can do this by modifying saveComment() with the code in bold:

// Save comments to the database
    public function saveComment($p)
    {
        // Save the comment information in a session
        $_SESSION['c_name'] = htmlentities($p['name'], ENT_QUOTES);
        $_SESSION['c_email'] = htmlentities($p['email'], ENT_QUOTES);
        $_SESSION['c_comment'] = htmlentities($p['cmnt'],
            ENT_QUOTES);

        // Make sure the email address is valid first
        if($this->validateEmail($p['email'])===FALSE)
        {
            return FALSE;
        }

        // Sanitize the data and store in variables
        $blog_id = htmlentities(strip_tags($p['blog_id']),
            ENT_QUOTES);
        $name = htmlentities(strip_tags($p['name']), ENT_QUOTES);
        $email = htmlentities(strip_tags($p['email']), ENT_QUOTES);
        $comment = htmlentities(strip_tags($p['comment']),
            ENT_QUOTES);

        // Generate an SQL command
        $sql = "INSERT INTO comments (blog_id, name, email, comment)
                VALUES (?, ?, ?, ?)";
        if($stmt = $this->db->prepare($sql))
        {
            // Execute the command, free used memory, and return true
            $stmt->execute(array($blog_id, $name, $email, $comment));
            $stmt->closeCursor();

            // Destroy the comment information to empty the form
            unset($_SESSION['c_name'], $_SESSION['c_email'],
                $_SESSION['c_comment']);
            return TRUE;
        }

else
        {
            // If something went wrong, return false
            return FALSE;
        }
    }

You can test this script by posting a comment successfully. The comment should be displayed, but the form should be empty. Now enter a comment with a bad email address, which should throw up an error. From the error screen, navigate back to the entry you were testing with; you should see that no comment was posted, but the information you entered remains in the form.

12.1.4. Adding Error Messages

If you don't inform your users what went wrong, they might become confused and frustrated with your comment form. To counter this, you need to generate and display error messages when applicable; this will help the user fill out the form properly.

You're already using sessions, so here you can store error messages in an additional session variable, $_SESSION['error']. The saveComment() method lets you store any errors generated as an error code; you can then use the showCommentForm() to match an error with the appropriate error message you want to display on the form.

12.1.4.1. Identifying Errors in saveComment()

Begin by identifying the errors that might occur. The first error is an unexpected failure of the database query. Give this error the error code, 1. The second error occurs when the script encounters an invalid email, which you can identify with the error code 2. If necessary, you can add more error codes in the future.

You don't need to display an error if the user posts a comment successfully, so you don't store an error code when no errors occur. Also, you must unset the error session variable after a successful post to prevent the user from seeing the same error again, after the problem has been corrected.

Your script now dealings with error codes, rather than successful or unsuccessful scripts, so your script no longer needs to return TRUE or FALSE. Instead, you can make your script set a session variable with an error code and return nothing.

Implement these changes by adding the code in bold to saveComment():

// Save comments to the database
    public function saveComment($p)
    {
        // Save the comment information in a session
        $_SESSION['c_name'] = htmlentities($p['name'], ENT_QUOTES);
        $_SESSION['c_email'] = htmlentities($p['email'], ENT_QUOTES);
        $_SESSION['c_comment'] = htmlentities($p['cmnt'],
            ENT_QUOTES);

// Make sure the email address is valid first
        if($this->validateEmail($p['email'])===FALSE)
        {
            $_SESSION['error'] = 2;
            return;
        }

        // Sanitize the data and store in variables
        $blog_id = htmlentities(strip_tags($p['blog_id']),
            ENT_QUOTES);
        $name = htmlentities(strip_tags($p['name']), ENT_QUOTES);
        $email = htmlentities(strip_tags($p['email']), ENT_QUOTES);
        $comment = htmlentities(strip_tags($p['comment']),
            ENT_QUOTES);

        // Generate an SQL command
        $sql = "INSERT INTO comments (blog_id, name, email, comment)
                VALUES (?, ?, ?, ?)";
        if($stmt = $this->db->prepare($sql))
        {
            // Execute the command, free used memory, and return true
            $stmt->execute(array($blog_id, $name, $email, $comment));
            $stmt->closeCursor();

            // Destroy the comment information to empty the form
            unset($_SESSION['c_name'], $_SESSION['c_email'],
                $_SESSION['c_comment'], $_SESSION['error']);
            return;
        }
        else
        {
            // If something went wrong
            $_SESSION['error'] = 1;
            return;
        }
    }

12.1.4.2. Modifying update.inc.php

saveComment() no longer returns TRUE or FALSE, so you must modify update.inc.php to execute saveComment() rather than checking its output. Additionally, you need to account for the user pressing the comment form's Cancel button. When that happens, update.inc.php must unset the session variables to avoid problems.

Your first task is to fix issues with saving comments; make the following modifications, shown in bold and starting at line 94, to update.inc.php:

// If a comment is being posted, handle it here
else if($_SERVER['REQUEST_METHOD'] == 'POST'
    && $_POST['submit'] == 'Post Comment')
{
    // Include and instantiate the Comments class
    include_once 'comments.inc.php';
    $comments = new Comments();

    // Save the comment
    $comments->saveComment($_POST);

    // If available, store the entry the user came from
    if(isset($_SERVER['HTTP_REFERER']))
    {
        $loc = $_SERVER['HTTP_REFERER'];
    }
    else
    {
        $loc = '../';
    }

    // Send the user back to the entry
    header('Location: '.$loc);
    exit;
}

Next, you can unset the session variables if a user presses the Cancel button by adding the code in bold to the last else block at the bottom of update.inc.php:

else
{
    unset($_SESSION['c_name'], $_SESSION['c_email'],
        $_SESSION['c_comment'], $_SESSION['error']);
    header('Location: ../'),
    exit;
}

?>

12.1.4.3. Matching Error Codes in showCommentForm()

Your script now stores error codes; next, you need to modify showCommentForm() to identify an error code and match it with a corresponding error message, which you then display on the comment form.

Begin by creating an array in which the error codes are the keys, and the messages are the values. Next, verify whether an error code was stored, and, if so, store the correct error message from the array in a variable ($error); otherwise, set the variable to NULL. Finally, insert the error message into the output for display.

Add these changes by inserting the code in bold into showCommentForm():

// Display a form for users to enter new comments with
    public function showCommentForm($blog_id)
    {
        $errors = array(
            1 => '<p class="error">Something went wrong while '
                . 'saving your comment. Please try again!</p>',
            2 => '<p class="error">Please provide a valid '
                . 'email address!</p>'
        );
        if(isset($_SESSION['error']))
        {
            $error = $errors[$_SESSION['error']];
        }
        else
        {
            $error = NULL;
        }

        // Check if session variables exist
        if(isset($_SESSION['c_name']))
        {
            $n = $_SESSION['c_name'];
        }
        else
        {
            $n = NULL;
        }
        if(isset($_SESSION['c_email']))
        {
            $e = $_SESSION['c_email'];
        }

else
        {
            $e = NULL;
        }
        if(isset($_SESSION['c_comment']))
        {
            $c = $_SESSION['c_comment'];
        }
        else
        {
            $c = NULL;
        }

        return <<<FORM
<form action="/simple_blog/inc/update.inc.php"
    method="post" id="comment-form">
    <fieldset>
        <legend>Post a Comment</legend>$error
        <label>Name
            <input type="text" name="name" maxlength="75"
                value="$n" />
        </label>
        <label>Email
            <input type="text" name="email" maxlength="150"
                value="$e" />
        </label>
        <label>Comment
            <textarea rows="10" cols="45"
                name="comment">$c</textarea>
        </label>
        <input type="hidden" name="blog_id" value="$blog_id" />
        <input type="submit" name="submit" value="Post Comment" />
        <input type="submit" name="submit" value="Cancel" />
    </fieldset>
</form>
FORM;
    }

Now your users receive an error message if they make a mistake or something goes wrong with the submission. Try entering a bad email address to see your new error handling system in action (see Figure 12-1).

Figure 12.1. The error message displayed when a bad email address is entered

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset