Handling PHP Form Postbacks: Same-Page Validation

This is the second in a three-part series on PHP form validation. Part 1 discussed the challenges in invalidating the $_POST superglobal. This entry talks about validating a PHP form on the same page, prior to posting the results. Part 3 will discuss strategies for ensuring a form cannot be posted back.

An easy way to handle server-side validation of form variables, prior to processing scripts, is to assume the form won’t validate.

That way, you can easily build your validation tools into your form, gracefully handle errors, and gracefully exit the page / process the inputs once they do validate.

To accomplish this goal, your form page should contain the following:

  • A call to print / echo an error message, that will be defined when we have errors in our page; and
  • calls to the appropriate $_POST fields for all our form variables, to ensure we see the original values when posting back.

I describe these methods in an earlier post. But to recap, your form would look something like this:

<h1>My form</h1>
<?php echo $msg; ?>
<form method="post" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>">
	<label for="firstName">First name: <input type="text" name="firstName" value="<?php echo $_POST['firstName']; ?>" /></label>
	<label><input type="radio" name="mudkipz" value="yes" <?php if (!isset($_POST['mudkipz']) || $_POST['mudkipz'] == "yes") { echo "checked"; } else { echo ""; } ?> /> I liek mudkipz.</label>
	<label><input type="radio" name="mudkipz" value="no" <?php if(isset($_POST['mudkipz']) && $_POST['mudkipz'] == "no") { echo "checked"; } else { echo ""; } ?> /> I do not liek mudkipz.</label>
	<label><input type="checkbox" name="rock[]" value="I wanna rock and roll all night"
	<?php 
	if(isset($_POST['rock'])) { 
	   if(is_array($_POST['rock'])) { 
		  foreach($_POST['rock'] as $choice) {
			 if($choice == "I wanna rock and roll all night") {
				echo "checked";
				break;
			 }
		  }
	   }
	}
	?>
	/> I wanna rock and roll all night.</label>
	<label><input type="checkbox" name="rock[]" value="and party every day"
	<?php 
	if(isset($_POST['rock'])) { 
	   if(is_array($_POST['rock'])) { 
		  foreach( $_POST['rock'] as $choice ) {
			 if($choice == "and party every day") {
				echo "checked";
				break;
			 }
		  }
	   }
	}
	?>
	/> And party every day.</label>
	<input type="submit" name="submit" value="Submit" />
</form>

Note that at Line 2 I am echoing out the status message. As long as I don’t wrap it with any HTML markup (and assign whatever markup I want to use as I build the message in PHP), it won’t affect layout if the form hasn’t yet been submitted.

Throughout the form variables themselves, I am setting values to be their respective assignment to the $_POST superglobal, once the form is submitted. That’s how I retain form values on postback: If the form is invalid, setting their values to be the same as what was posted means the user will be able to see what he submitted, and more important, not have to re-enter input that was correct.

Since those values won’t be set until the form is posted, I don’t have to worry about the values prior to form submission; the $_POST superglobal fields won’t be set yet, so nothing will appear as the value.

At Line 5 I ensure that the value of my radio button has been set, and choose the previously selected value if it is; if not, I default to the first value.

It’s necessary to evaluate whether this post variable is set because we’re checking its value; if we don’t make sure there is a value to be evaluated, PHP will issue a warning that the variable does not exist, and that may show up on user’s screens, depending on our error reporting settings in PHP.

Beginning at Lines 9-11, I have a similar set of tests for the checkboxes.

Because checkboxes are usually passed to PHP as an array, I not only need to ensure that the variable exists, but also that it is an array, before I try to iterate it via foreach. If I don’t, PHP may well throw errors that are visible to end users.

A note on JavaScript validation: I am very fond of saying that you should never rely on JavaScript for form validation; you should always assume users are trying to poison your inputs; and you should always sanitize form fields server-side prior to processing them.

I’ll repeat all that advice here, once again. However … you really should include JavaScript validation of your forms before posting back to a server.

Let’s face it: Most people these days are running JavaScript. Most of them expect client-side form validation. Most of them aren’t going to put up with waiting for postbacks to process, only to receive a litany of complaints about how the form isn’t properly filled out. They’ll move along if they can. So help them fill out the form properly, and part of that is providing real-time, client-side validation of inputs.

To that end, I am quite fond of the jQuery Validation Plugin by Jörn Zaefferer. You’ll need to know your way around jQuery to use it, but it’s not difficult to perform basic validation with it … and let’s face it, everyone on Earth is using jQuery these days, so it’s not intrusive.

The Form Validation Routine

Now I’m ready to start building my form validation. Let’s do it:

if(isset($_POST['submit'])) {
	//boolean for whether form is valid
	//assume valid, set to invalid as bad inputs fail validation
	$ok = true;
	
	//prepare error notification message
	$msg = "<p><strong>Sorry, there are some problems with this post.</strong> Please correct the following:</p>";
	$msg .= "<ul>";
	
	//check for empty text field
	if(empty($_POST['firstName'])) {
		$ok = false;
		$msg .= "<li>Please provide a first name.</li>";
	}
	//select a radio button
	if(empty($_POST['mudkipz']) || !in_array($_POST['mudkipz'], array('yes', 'no'))) {
		$ok = false;
		$msg .= "<li>Please confirm or deny your love of mudkipz.</li>";
	}
	if(!is_array($_POST['rock']) || count($_POST['rock']) < 1 ) {
		$ok = false;
		$msg .= "<li>You must at least want to rock and roll all night, or party every day.</li>";
	}
	else {
		foreach($_POST['rock'] as $kiss) {
			if(!in_array($kiss, array('I wanna rock and roll all night', 'and party every day'))) {
				$ok = false;
				$msg .= "<li>You can either want to rock and roll all night, or party every day, or both, but not anything else.</li>";
			}
		}
	}
	
	$msg .= "</ul>";
	
	if($ok) {
		unset($msg);
		//code to process form goes here
	}
}

Line 1 of this code block determines if a specific form variable has been set; in this case, the submit button. Since this will only be present once the form has been posted back to the server, it prevents the form validation routine from running before the form is submitted.

Line 4 sets a Boolean value to true. This value will be evaluated after form validation; if it remains true, we will process the form. If not, we’ll show the form again and display the error message.

Lines 7-8 set up the error message we’ll show to users. We’ll unset this variable later if the form is valid.

Line 11 assesses whether the variable exists and has at least one non-whitespace characters. You could alternatively call strlen here to require a minimum number of characters; or use preg_match if you want what is entered to comply to a pattern, such as a ZIP Code, phone number, credit card number, email address, etc.

Line 16 determines if at least one valid value has been selected from a radio button list.

Because all the radio buttons are named the same, we know we will get back one value for whichever one is checked. We can therefore create an array of all the values assigned to those radio buttons, and ensure whatever is sent to the server was not altered, client-side, by the user.

Line 20 establishes that $_POST[‘rock’] is an array and has at least 1 item.

Lines 25-26 ensure the selected values of $_POST[‘rock’] are within the realm of possible answers.

Line 33 wraps up the message by closing our unordered list tag.

At Line 35, we see if $ok is still set to true. If so, we know we can process the form; all inputs are valid. Otherwise, we proceed to the form’s markup, which will both echo out the values submitted by the user, and the form validation message, instructing the user as to what needs correcting.

You can see a working demo of this at http://demo.dougv.com/php_form_validation/index.php

This code as a Github Gist: https://gist.github.com/dougvdotcom/520cdf13fb8928e4108d67d61359c8a3

That’s it for now. In Part 3, we’ll find ways to effectively destroy the $_POST superglobal, to prevent postback.

Form validation need not be intimidating. Photo by luiskarlos74 via Pixabay, in the public domain.
Form validation need not be intimidating. Featured photo by luiskarlos74 via Pixabay, in the public domain.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  • Check out the Commenting Guidelines before commenting, please!
  • Want to share code? Please put it into a GitHub Gist, CodePen or pastebin and link to that in your comment.
  • Just have a line or two of markup? Wrap them in an appropriate SyntaxHighlighter Evolved shortcode for your programming language, please!