不理解PHP中的这些$ _POST,数组和验证技术

I'm learning by way of tutorials, and the instructor used a validation routine that I'm confused about.

On the form page, he has input fields with the following names:

  • menu_name
  • position
  • visible

On the form processing page, he has the following block of php (let's call it block A):

$menu_name = mysql_prep($_POST['menu_name']);
$position = mysql_prep($_POST['position']);
$visible = mysql_prep($_POST['visible']);

Below this block is another php block that inserts the data into MySQL -- this all works fine.


He then added the following php block above block A (let's call it block B):

$errors = array();
$required_fields = array('menu_name', 'position', 'visible');

foreach ($required_fields as $fieldname) {
    if (!isset($_POST[$fieldname]) || empty($_POST[$fieldname])) {
        $errors[] = $fieldname;
    } 
}   
if (!empty($errors)) {
    redirect_to("new_subject.php");
    exit;
}

Question 1

I'm confused why in his $required_fields array, he is referencing the field names directly. Why not move block A above block B and then just reference the variables that were assigned from the $_POST?

Then just use those variables in the if statement within the foreach loop.

I guess I'm asking if my alternative approach is valid? Is there an apparent reason for why he took his approach?

(FYI the mysql_prep is a custom function he built to remove slashes and such.)


Question 2

If I'm understanding his code correctly, his first if statement is testing if the $fieldname is !isset (i.e. not set) or empty.

What's the difference? Since I don't know the difference, I'm also not clear on why he used the || operator. Can you please explain?


Question 3

And finally, it seems his first if statement is capturing any errors and putting them into the $errors array at the top of block B.

He then uses a second if statement to check if that $errors array has anything in it, and re-directs + exits if it does.

Is there a discernible reason for this approach? In my mind, it seems the first if statement could redirect + exit if any errors were found. Why capture them in that $errors array?

Question 1

What happens here is he checks for the existence of certain variables first. If they do not exist, you need to redirect.
I don't know what the prep function does, but it would be illogical to call a prep function on a possible empty variable. You could turn it around, but that would be.. well.. turning stuff around ;)

First check if you've got all you need, and then start cleaning up.

Question 2

Not set means it is not available in the POST. This will happen for checkboxes (if you don't check them , they don't excist. Text inputs will be empty.
Even if you have only text inputs, it is good for to be sure that they exist (there could be a problem in the calling post, someone might be hacking your form), before you check their contents: PHP is very forgiving ofcourse, but it's not really nice to check the contents of something that does not exist.

Summary: isset is looking if it is there at all, and empty is checking what it's value is.

Question 3

You could put the redirect and exit statements in the if, and this would be a tiny bit faster. But not so much, and what you do is unexpected for some programmers: you change the flow of the program somewhere in the middle of a loop (2 loops). This is readable for me, but I don't see any problem with exiting at the first 'error'.

Later on you might want to do something with the missing POST values (all of them), like giving them a certain class, so that's a possible reason to do it this way later on?

  1. As far as I understand you, there is no difference if you want to check that input exists before you are preparing it or the other way around.

  2. isset() checks if the variable is set (exists) while empty() will return true if the variable exists, but does not hold any data. The || operator is a OR operator. The statement is true if isset() returns false (!) OR empty() returns true.

  3. I suspect the reason why he is populating the error array is to be able to display all errors with the form, and not just the first one, and when the user resubmits he gets the next.

When learning how to code students are most often challenged not by the syntax of the language but by how to code in logical structures. Your explanation of what the code does shows understanding and you ask valid questions. IMO the code is written in a logical and concise way. Here's why:

Block A preps each of the 3 POST vars before they are committed to the db. Clear enough. What is significant in terms of code execution in Block A is the fact that, should any of those 3 vars not exist then PHP will throw an exception and stop. Also, should all the vars exist but any of them be empty then, possibly, MySQL will throw an exception and also cause PHP to stop execution. This is why we have Block B to a) receive the POST vars; b) to validate them; and c) to handle any errors, before passing the vars to Block A. Let's consider the 3 logical parts of Block B:

  1. 'declare' 2 arrays
  2. validate each expected var against 2 conditions, putting the var name in an errors list should it fail validation
  3. check the errors list: if it contains any var name then redirect the user to another page, else proceed to var prepping & db commit

$required_fields is an array of 3 expected var names (harvested from 'required fields' in a preceding form). The content of $required_fields is therefore not the POST variables themselves, but simply a list of their names.

Each item (var name) in the $required_fields list is then iterated (used sequentially) by the foreach loop to circumvent the need for us to write out the if statement 3 times (once for each var). So regarding your 1st question, the $required_fields array is created because it will be needed by the convenience foreach loop (imagine, for example, if there were 20 vars!).

abstract:

foreach (list as item) {
    if (condition1 is true) or (condition2 is true) {
        #do something
    } 
}

actual:

foreach ($required_fields as $fieldname) {
    if (!isset($_POST[$fieldname]) || empty($_POST[$fieldname])) {
        $errors[] = $fieldname;
    } 
}

The if statement will (e.g. during the first iteration of the foreach loop) evaluate the var $_POST['menu_name'] - firstly to determine if it is not set (i.e. does not exist - such as when it was never passed on from the form) OR secondly, it is set (i.e. does exist) but is empty (has no value). If either of these conditions are True then the var name (now contained in var $fieldname) will be added to the $errors list. Your 3rd question correctly identifies this action.

To answer Q3: yes, the first if statement could redirect & exit in case of errors but this would confuse the logical separation that we have in the code at the moment. Think about it: if error handling was also done by the first if statement then we would have a redirect and exit (of this script) as soon as any one var failed validation. By separating the code into blocks with specific functions (as it currently is), we now have a list of errors that we can use elsewhere in our web app, e.g.

echo "You must enter values in the following required fields: ";
foreach ($errors as &$item) {
    echo $item . " ";
}

Hope that clarifies the code structure and logic.