Error-handling / Form Submission Script Does Not Run a Second Time

Hello all,

This pertains to my landing page submission form script. When the user first interacts with the e-mail submission box, #emailbox, in the lower left corner and the submission form pops up after a valid e-mail address submission, the confirmform.php script handles all the errors perfectly, as it should. If the user decides to press the Cancel input button which is a reset type input and then decides to come back and try to submit again, the PHP script handles no input. The form stays at a stand-still. Why does the ‘Cancel’ input mess with my script’s activity? :eek:

Tyler

Can’t do much to help you without seeing your code.

confirmform.php


<?php
	$instance = new CheckForm;
	$instance -> checkSubmission();
	
	class CheckForm
	{
		public function checkSubmission()
		{	
			$origEmail = $_POST['origEmail'];
			$confirmEmail = strip_tags($_POST['confirmEmail']);
			$name = trim(preg_replace('/ +/', ' ', preg_replace('/[^A-Za-z0-9 ]/', ' ', urldecode(html_entity_decode(strip_tags($_POST['name']))))));
			$age = $_POST['age'];
			$gender = $_POST['gender'];
			$country = $_POST['country'];
			$catcher = strip_tags($_POST['catcher']);
			$mathAnswer = strip_tags($_POST['addition']);
			$rightAnswer = $_POST['mathAnswer'];
			$submissionTime = $_POST['submissionTime'];
			$status = 0;
		
			$response = array("validation" => "fail", "errorMessage" => " ", "database" => " ", "name" => " ", "email" => " ");
			
			if ($origEmail != $confirmEmail || empty($confirmEmail)) {
				if (empty($name) || !ctype_alpha($name)) {
					if ($country == "Select Location") {
						if (!ctype_digit($age)) {
							if ($mathAnswer != $rightAnswer) {
								$response['errorMessage'] = "Error: that's not a valid submission.";
							} else
								$response['errorMessage'] = "Error: e-mails don't match, invalid name entered, non-numeric age, and no location selected.";
						} elseif ($mathAnswer != $rightAnswer) {
							$response['errorMessage'] = "Error: e-mails don't match, invalid name given, no location specified, and math answer was incorrect.";
						} else
							$response['errorMessage'] = "Error: e-mails don't match, invalid name specified, and no location specified.";
					} elseif (!ctype_digit($age)) {
						if ($mathAnswer != $rightAnswer) {
							$response['errorMessage'] = "Error: e-mails don't match, invalid name supplied, non-numeric age entered, and math answer is incorrect.";
						} else
							$response['errorMessage'] = "Error: e-mails don't match, invalid name supplied, and age can only be numeric.";
					} elseif ($mathAnswer != $rightAnswer) {
						$response['errorMessage'] = "Error: e-mails don't match, invalid name supplied, and the math answer was incorrect.";
					} else
						$response['errorMessage'] = "Error: e-mails don't match and invalid name entered (letters only).";
				} elseif (!ctype_digit($age)) {
					if ($country == "Select Location") {
						if ($mathAnswer != $rightAnswer) {
							$response['errorMessage'] = "Error: e-mails don't match, age must be numeric, no location selected, and math answer is incorrect.";
						} else
							$response['errorMessage'] = "Error: e-mails don't match, age must be numeric, and no location selected.";
					} elseif ($mathAnswer != $rightAnswer) {
						$response['errorMessage'] = "Error: e-mails don't match, invalid (non-numeric) age supplied, and math answer is incorrect.";
					} else
						$response['errorMessage'] = "Error: e-mails don't match and non-numeric age was entered.";
				} elseif ($country == "Select Location") {
					if ($mathAnswer != $rightAnswer) {
						$response['errorMessage'] = "Error: e-mails don't match, no location selected, and math answer is incorrect.";
					} else
						$response['errorMessage'] = "Error: e-mails don't match and no location was selected.";
				} elseif ($mathAnswer != $rightAnswer) {
					$response['errorMessage'] = "Error: e-mail addresses don't match and math answer was incorrect.";
				} else
					$response['errorMessage'] = "Error: e-mall addresses don't match.";
			} elseif (empty($name)) {
				if ($country == "Select Location") {
					if (!ctype_digit($age)) {
						if ($mathAnswer != $rightAnswer) {
							$response['errorMessage'] = "Error: no name supplied, non-numeric age entered, no location selected, and math answer is incorrect.";
						} else
							$response['errorMessage'] = "Error: no name entered, age not in numeric format, and no location selected.";
					} elseif ($mathAnswer != $rightAnswer) {
						$response['errorMessage'] = "Error: no name entered, no location selected, and math answer is incorrect.";
					} else
						$response['errorMessage'] = "Error: no name entered and no location selected.";
				} elseif (!ctype_digit($age)) {
					if ($country == "Select Location") {
						if ($mathAnswer != $rightAnswer) {
							$response['errorMessage'] = "Error: no name entered, non-numeric age supplied, no location selected, and math answer is incorrect.";
						} else
							$response['errorMessage'] = "Error: no name entered, non-numeric age supplied, and no location selected.";
					} elseif ($mathAnswer != $rightAnswer) {
						$response['errorMessage'] = "Error: no name entered, non-numeric age entered, and math answer is incorrect.";
					} else
						$response['errorMessage'] = "Error: no name entered and non-numeric age supplied.";
				} elseif ($mathAnswer != $rightAnswer) {
					$response['errorMessage'] = "Error: no name entered and math answer is incorrect.";
				} else
					$response['errorMessage'] = "Error: no name entered.";
			} elseif (!ctype_alpha($name)) {
				if ($country == "Select Location") {
					if (!ctype_digit($age)) {
						if ($mathAnswer != $rightAnswer) {
							$response['errorMessage'] = "Error: name can only contain letters, age must be numerical, no location selected, and math answer is incorrect.";
						} else
							$response['errorMessage'] = "Error: name can only contain letters, non-numeric age supplied, and no location is selected.";
					} elseif ($mathAnswer != $rightAnswer) {
						$response['errorMessage'] = "Error: name can only contain letters, no location selected, and math answer is incorrect.";
					} else
						$response['errorMessage'] = "Error: name can only have letters and no location is selected.";
				} elseif (!ctype_digit($age)) {
					if ($country == "Select Location") {
						if ($mathAnswer != $rightAnswer) {
							$response['errorMessage'] = "Error: name can only contain letters, age must be numeric, no location selected, and math answer is incorrect.";
						} else
							$response['errorMessage'] = "Error: name can only contain letters, non-numeric age supplied, and no location is selected.";
					} elseif ($mathAnswer != $rightAnswer) {
						$response['errorMessage'] = "Error: name can only have letters, non-numeric age entered, and math answer is incorrect.";
					} else
						$response['errorMessage'] = "Error: name must have letters only and age can only be numeric.";
				} elseif ($mathAnswer != $rightAnswer) {
					$response['errorMessage'] = "Error: name must only have letters and math answer is incorrect.";
				} else
					$response['errorMessage'] = "Error: name can only contain letters.";
			} elseif (!ctype_digit($age)) {
				if ($country == "Select Location") {
					if ($mathAnswer != $rightAnswer) {
						$response['errorMessage'] = "Error: age can only be numbers, no location selected, and math answer is incorrect.";
					} else
						$response['errorMessage'] = "Error: age can only have numbers and no location is selected.";
				} elseif ($mathAnswer != $rightAnswer) {
					$response['errorMessage'] = "Error: age can only contain numbers and math answer is incorrect.";
				} else
					$response['errorMessage'] = "Error: age can only contain numbers.";
			} elseif ($country == "Select Location") {
				if ($mathAnswer != $rightAnswer) {
					$response['errorMessage'] = "Error: no location is selected and math answer is incorrect.";
				} else
					$response['errorMessage'] = "Error: no location is selected.";
			} elseif ($mathAnswer != $rightAnswer) {
				$response['errorMessage'] = "Error: math answer is incorrect.";
			} else
				$status = 1;
				
			
			if ($status == 1) {
			  require_once("categoryfinder.php"); 
			  $categoryFinder = new CategoryFinder; 
			  $category = $categoryFinder -> getCategory(); 
							 
			  $response['validation'] = "pass"; 
			  $response['name'] = $name;
			  $response['email'] = $confirmEmail;
			  				 
			  require_once('databasewriter.php'); 
			  $dbWriter = new DatabaseWriter; 
			  $dbCode = $dbWriter -> writeUserToDatabase($confirmEmail, $name, $age, $gender, $country, $category); 
			  
			  if ($gender == "Male")
				$gender = "M";
			  elseif ($gender == "Female")
				$gender = "F";
			  else
				$gender = NULL;
			   
			  if ($dbCode == 1) { 
				$response['database'] = "pass"; 
				require_once('emailsender.php');
				$emailSender = new EmailSender;
				$emailSender -> sendWelcomeEmail($confirmEmail, $name);
			  } else {
				$response['database'] = "fail"; 
				$response['validation'] = "fail"; 
			  } 
			  if ($dbCode == 2) { 
				$response['errorMessage'] = "Server error. Please try again later or <a href=\\"mailto:admin@worldreviewgroup.com\\">let us know</a> about the issue."; 
			  } elseif ($dbCode == 3) { 
				$response['errorMessage'] = "That e-mail address already exists!"; 
			  } 
			} 
			echo json_encode($response);  
		}
	}
?>

lpemailbox.js


// Emailbox and form handler script
	$("#go").focus(function(){
		if ($("#go").val() == $("#go").prop('defaultValue')){
			$("#go").val('');
		}
	});
 
	$("#go").on('focus blur', function(e) {
	  var v = $(this).val()
	  if (e.type == "focus"){
		v = (v == "your e-mail")? "" : v;
	  } else {
		v = (v == "")? "your e-mail" : v;
	  }
	  $(this).val(v);
	});
      
      //this is the function called by the success value of the first .ajax() call
      function testFirstResults(response){
        if (response.indexOf("Submission Successful") != -1){
		  $("#blackoverlay, #submissionform").fadeIn(400);
        } else if (response.indexOf("Invalid E-mail") != -1){
		  $("#invalidemail").fadeIn(1200).delay(1400).fadeOut(1800);
		}
      }
	  
	  function testSecondResults(data){
		if (data['validation'] == "pass"){
			$("#submissionform").css("display", "none");
			$("#person").append(data['name']);
			$("#email").append(data['email']);
			$("#successfulsubmit").css("display", "block");
			$("#blackoverlay, #successfulsubmit").delay(5600).fadeOut(800);
			setTimeout(function(){
			  $(".exit").delay(2800).append("Click to Exit");
		      $("#blackoverlay, #successfulsubmit").click(function(e){
			  $("#blackoverlay, #successfulsubmit").remove();
		    });
  		    }, 2800);
			$("#emailbox input").each(function(){
                $(this).prop("disabled", "disabled");
            });
			$("#emailbox :submit").css("width", "60px").val('Thanks!');
			$("#go").css("width", "108px");
		} else {
			$("#errormessage").css("display", "block");
			$("#errormessage p").empty().append(data['errorMessage']);
		}
	  }
	  
	  var mathAnswer = 0;
	  var startTime = 0;
	  var endTime = 0;
	  var submissionTime;
	  
	  function generateEquation(){
	  	var num1 = Math.floor(Math.random() * 5) + 1;
		var num2 = Math.floor(Math.random() * 5) + 1;
		mathAnswer = num1 + num2;
		$("#math").append("What is " + num1 + " + " + num2 + "?");
	  }
	  
      $(document).ready(function(){
		
		generateEquation();
		
        $("#emailbox").submit(function(e){
		  startTime = jQuery.now();
		  var email = $("#go").val();
          e.preventDefault();
          $.ajax({
            type: $(this).attr('method'),
            dataType: 'html',
            cache: false,
            url: "../Scripts/emailtester.php",
            data: {email:email},
            success: function(data){
              testFirstResults(data);
			}
          });
        });
		
		$("#submissionform").submit(function(e){
			var origEmail = $("#go").val();
			var confirmEmail = $("#confirmemail").val();
			var name = $("#name").val();
			var age = $("#age").val();
			var gender = $("#gender").val();
			var country = $("#country").val();
			var catcher = $("#aicatcher").val();
			var addition = $("#addition").val();
			endTime = jQuery.now();
			submissionTime = endTime - startTime;
			
			e.preventDefault();
		
			$.ajax({
			  type: "POST",
			  dataType: 'json',
			  cache: false,
			  url: "../Scripts/confirmform.php",
			  data: { 
				  origEmail: origEmail,
				  confirmEmail: confirmEmail,
				  name: name,
				  age: age,
				  gender: gender,
				  country: country,
				  catcher: catcher,
				  addition: addition,
				  mathAnswer: mathAnswer,
				  submissionTime: submissionTime 
			  },
			  success: function(data) {
				testSecondResults(data);
			  },
			});
		});
		
		$("#cancel").click(function(){
			$("#blackoverlay").fadeOut(300);
			$("#errormessage").empty();
			$("#submissionform").fadeOut(300);
		});
      });

Good god.

I’d just throw this code away and start again.

You do know you are not writing object oriented code here, right?

I don’t see any problem with the way my code is structured. Pullo (SitePoint mentor) helped me write this. I simply can’t afford the time lost throwing the code away and starting all over again.

I guess I can try to figure what the problem is on my own.

It is generally NOT recommended to have such a tremendously long series if if/else statements in code. Period. LIke building a house of cards - one minor change to the logic behind that structure and you risk breaking the whole thing and having the code come tumbling down. Maybe not you - but the poor programmer who follows in your footsteps. Of course if it is you, I hope you remember how you built it months or a year or two from now. If someone ‘helped’ you write that, they did not do you a favor.

Hi Taylor,

I’ve just tried the form with two sets of dummy details, and I can’t recreate the problem as you describe. What I did find, though, is that the form won’t allow you to input a name with any spaces (i.e. John is fine, John Smith is not), instead the form button disappears and the form itself remains on screen. Checking the requests with the Chrome dev tools shows that the server sends back the following response:


{"validation":"fail","errorMessage":"Error: name can only contain letters.","database":" ","name":" ","email":" "}

but your page never displays the error to the user.

I agree, and of course if you want to change one of the validation rules or errors, you have to alter them in several locations as you’re repeating sections of code. You can rewrite the code to be much simpler and clearer:


$isValid = TRUE;
$errors = array();

if ($origEmail != $confirmEmail || empty($confirmEmail)) {
    $errors[] = "e-mails don't match";
    $isValid = FALSE;
}

if (empty($name) || !ctype_alpha($name)) {
    $errors[] = "invalid name given";
    $isValid = FALSE;
}
        
if ($country == "Select Location") {
    $errors[] = "no location specified";
    $isValid = FALSE;
}

if (!ctype_digit($age)) {
    $errors[] = "non-numeric age";
    $isValid = FALSE;
}
                
if ($mathAnswer != $rightAnswer) {
    $errors[] = "and math answer was incorrect";
    $isValid = FALSE;
}

This checks each condition separately, and sets the $isValid flag to false if any one of the tests fail, and appends the error to an array (which you can easily implode to a string to send back to the client).

Okay, that’s much more clear now. Thank you for the criticism.

The long conditional statement was my own doing. Oops. :x

Maybe I should return an array and make an unordered list in order to account for multiple errors in the submission. That’s going to take some figuring out.
For example,


if (empty($name))
      $response['errorMessage'] += "Name is empty.";
elseif (!ctype_digit($age))
      $response['errorMessage'] += "Age is non-numeric.";

That would not produce a list though. It would generate a string that would look like this:

Name is empty.Age is non-numeric.

Instead, I think it would be nice if it output something like this.

The following errors were encountered with your submission:
-Name is empty.
-Age is non-numeric.

Now, there was an error being returned in the console when the name contains a space. That won’t do. You also brought to my attention that the error isn’t even being displayed to the user in this case. That predefined ctype_alpha() is not exactly doing what I want it to.
So I have to substitute in an equivalent reg-ex that returns an invalid submission if the name contains numbers. I know next to nothing about regular expressions.

Thanks all,
Tyler :confused:

Try this:


if (empty($name) || preg_match('/[^A-Za-z ]/', $name)) {

The regex pattern I’ve used here will match anything that is not a letter or a space, so the preg_match function will return 1 (truthy) if an invalid character is found.

Well, I tried to send back an unordered list by changing the code up to the following:


$status = 1;
		
			$response = array("validation" => "fail", "errorMessage" => "<ul>", "database" => " ", "name" => " ", "email" => " ");
			
			if ($origEmail != $confirmEmail || empty($confirmEmail)) {
				$response['errorMessage'] += '<li>E-mail addresses don\\'t match.</li>';
				$status = 0;
			}
			if (empty($name) || !preg_match('/[^A-Za-z ]/', $name)) {
				$response['errorMessage'] += '<li>No name entered (name can only have letters).</li>';
				$status = 0;
			}
			if (!ctype_digit($age)) {
				$response['errorMessage'] += '<li>Age must be numeric.</li>';
				$status = 0;
			}
			if ($country == "Select Location") {
				$response['errorMessage'] += '<li>No location selected.</li>';
				$status = 0;
			}
			if ($mathAnswer != $rightAnswer) {
				$response['errorMessage'] += '<li>Math answer incorrect.</li>';
				$status = 0;
			}
			
			$response['errorMessage'] += '</ul>';

Notice that at the bottom of the script, I am running a function, json_encode(), to send back to the JS which data type the JS is expecting. I am seeing ‘0’ printed to the #errormessage div, as you will see as well if you make an invalid submission. Should I change the .ajax() data type to HTML to achieve the desired result?

Hey Tyler,

I’d send back just a JSON array of validation errors and build the list in your JS.


$response = array("validation" => "fail", "errorMessage" => array(), "database" => " ", "name" => " ", "email" => " ");

if ($origEmail != $confirmEmail || empty($confirmEmail)) {
    $response['errorMessage'][] = "E-mail addresses don't match.";
    $status = 0;
}

// Rest of the validation here..

function testSecondResults(data){
    if (data['validation'] == "pass"){
        // Code not shown
    } else {
        $("#errormessage").css("display", "block");
        var fragment = document.createDocumentFragment();
        $.each(data['errorMessage'], function (i, e) {
            var item = document.createElement('li');
            item.innerText = e;
            fragment.appendChild(item);
        });
        $("#errormessage ul").empty().append(fragment.childNodes);
    }
}

Hi Fretburner,

After scouring for how to build a list dynamically, I returned back to this thread, seeing that you had posted JavaScript code as to how to do that. I attempted to use that code, and I received blank entries into the list. I noticed that you did not post any HTML changes, which I had added in a <ul> into the #errormessage div.

You can see what is occurring on this page. I’ll give it a try without my HTML changes.

-Tyler

Hi Tyler,

I checked out the form and it seems to be working OK, and displaying the errors correctly in a ul. Are you still having any problems with it?

Yes, I am having problems with it. I took a screenshot and uploaded it here. I am seeing that room is made for the list inside the HTML for the errors to be displayed, but nothing is there. I attempted to highlight any text inside there to see what it says, but there is absolutely nothing there. Weird! Could this be a CSS issue?

Current code:


<?php
	$instance = new CheckForm;
	$instance -> checkSubmission();
	
	class CheckForm
	{
		public function checkSubmission()
		{	
			$origEmail = $_POST['origEmail'];
			$confirmEmail = strip_tags($_POST['confirmEmail']);
			$name = trim(preg_replace('/ +/', ' ', preg_replace('/[^A-Za-z0-9 ]/', ' ', urldecode(html_entity_decode(strip_tags($_POST['name']))))));
			$age = $_POST['age'];
			$gender = $_POST['gender'];
			$country = $_POST['country'];
			$catcher = strip_tags($_POST['catcher']);
			$mathAnswer = strip_tags($_POST['addition']);
			$rightAnswer = $_POST['mathAnswer'];
			$submissionTime = $_POST['submissionTime'];
			$status = 1;
		
			$response = array("validation" => "fail", "errors" => array(), "database" => " ", "name" => " ", "email" => " ");
			
			if ($origEmail != $confirmEmail || empty($confirmEmail)) {
				$response['errors'][] = 'E-mail addresses don\\'t match.';
				$status = 0;
			}
			if (empty($name) || !preg_match('/[^A-Za-z ]/', $name)) {
				$response['errors'][] = 'No name entered (name can only have letters).';
				$status = 0;
			}
			if (!ctype_digit($age)) {
				$response['errors'][] = 'Age must be numeric.';
				$status = 0;
			}
			if ($country == "Select Location") {
				$response['errors'][] = 'No location selected.';
				$status = 0;
			}
			if ($mathAnswer != $rightAnswer) {
				$response['errors'][] = 'Math answer incorrect.';
				$status = 0;
			}
			if (!empty($catcher)){
				$response['errors'][] = 'Bot submission.';
				$status = 0;
			}
			if ($submissionTime < 9000){
				$response['errors'][] = 'Form submitted too quickly.';
				$status = 0;
			}

			if ($status == 1) {
			  require_once("categoryfinder.php");
			  $categoryFinder = new CategoryFinder;
			  $category = $categoryFinder -> getCategory();
							
			  $response['validation'] = "pass";
			  $response['name'] = $name;
			  $response['email'] = $confirmEmail;
			  				
			  require_once('databasewriter.php');
			  $dbWriter = new DatabaseWriter;
			  $dbCode = $dbWriter -> writeUserToDatabase($confirmEmail, $name, $age, $gender, $country, $category);
			
			  if ($gender == "Male")
				$gender = "M";
			  elseif ($gender == "Female")
				$gender = "F";
			  else
				$gender = NULL;
			
			  if ($dbCode == 1) {
				$response['database'] = "pass";
				require_once('emailsender.php');
				$emailSender = new EmailSender;
				$emailSender -> sendWelcomeEmail($confirmEmail, $name);
			  } else {
				$response['database'] = "fail";
				$response['validation'] = "fail";
			  }
			  if ($dbCode == 2) {
				$response['errors'] = "Server error. Please try again later or <a href=\\"mailto:admin@worldreviewgroup.com\\">let us know</a> about the issue.";
			  } elseif ($dbCode == 3) {
				$response['errorMessage'] = "That e-mail address already exists!";
			  }
			}
			echo json_encode($response);
		}
	}
?>

JavaScript:


//previous code omitted
			$("#emailbox input").each(function(){
                $(this).prop("disabled", "disabled");
            });
			$("#emailbox :submit").css("width", "60px").val('Thanks!');
			$("#go").css("width", "108px");
		} else {
			$("#errormessage").css("display", "block");
			var fragment = document.createDocumentFragment();
			$.each(data['errors'], function (i, e) {
				var item = document.createElement('li');
				item.innerText = e;
				fragment.appendChild(item);
			});
			$("#errormessage ul").empty().append(fragment.childNodes);
		}
	  }

Thanks,
Tyler :confused:

Possibly, or more likely a JS issue… what browser are you using to test?

It works just fine in IE9 but not in Firefox 19.0.2

I’ve been looking into the code in a bit more and you’ve got a couple of other bugs:

  1. On this line:
if (empty($name) || !preg_match('/[^A-Za-z ]/', $name)) {

you need to remove the ! from in front of the preg_match function, otherwise you’re reversing the effect and making all valid names invalid (and vice versa).

  1. In Scripts/lpemailbox.js, you have a trailing comma:
  success: function(data) {
    testSecondResults(data);
  }, // Remove trailing comma
});

which causes problems on IE6 & 7.

  1. While not really a bug, you could improve your input filtering by using PHP’s [fphp]filter_input[/fphp] function:

$origEmail      = filter_input(INPUT_POST, 'origEmail', FILTER_SANITIZE_EMAIL);
$confirmEmail   = filter_input(INPUT_POST, 'confirmEmail', FILTER_SANITIZE_EMAIL);
$name           = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_STRING);
$age            = filter_input(INPUT_POST, 'age', FILTER_SANITIZE_NUMBER_INT);
$gender         = filter_input(INPUT_POST, 'gender', FILTER_SANITIZE_STRING);
$country        = filter_input(INPUT_POST, 'country', FILTER_SANITIZE_STRING);
$catcher        = filter_input(INPUT_POST, 'catcher', FILTER_SANITIZE_STRING);
$mathAnswer     = filter_input(INPUT_POST, 'addition', FILTER_SANITIZE_NUMBER_INT);
$rightAnswer    = filter_input(INPUT_POST, 'mathAnswer', FILTER_SANITIZE_NUMBER_INT);
$submissionTime = filter_input(INPUT_POST, 'submissionTime');

This prevents you from getting errors if any of the variables aren’t set, and you can avoid any regex gymnastics when sanitizing the values.

Wowzers! That’s a really informative post.

I haven’t tried it out yet (specifically in Firefox), but #3 filtering method will replace the strip_tags() function I’m using as well as the regex line or two that’s in there? I hope this will maintain the same level of security in filtering out potentially harmful input.