1

You probably know this image:

enter image description here

Lets say I want to validate a single user input through various methods, and each ones depends on the success of the previous one. How can I avoid this enourmus and ugly if nesting?

I can't think of other way, which validates correctly and in the event of an error, returns the correct message, but it does look ugly and could probably a pain in the ass to mantain.

Any ideas?

Andres
  • 249
  • 3
  • 16
  • You should simplify by default the validation. Most probably you DO NOT need more than 2 checks to be fully sure that the user fails. and more than 2 other to be sure it is ok. – Royal Bg Jul 08 '14 at 20:45
  • you can just return after each error, you dont need to keep going –  Jul 08 '14 at 20:47

5 Answers5

3

My experiece so far says if you have more than 4-5 validations for the same goal, you are most probably doing something wrong. For validating user input, you might need certain pattern of chars, which you could achieve with regex, you might need all fields to be populated, some exotic things like the user to be from certain country IP or to have checked at least 18 years old in a dropdown. All other things, either beolng to the group of these, or chances are - they are redundant.

As the others stated, you, also, should not nest the blocks, but return the execution if the conditions are not met.

For example:

function isFormPopulated() {
    $requiredFields = array('user', 'pass', 'age', 'sex');
    foreach ($requiredFields as $field) {
        if (empty($_POST[$field])) { 
            $_SESSION['msg'] = $field . ' field should be populated'; // here better throw exception, instead of session saving the message
            return false;
        }
    }
    return true;
}

function isPatternValid() {
     $fieldPatterns = array('user' => '/w+/', 'pass' => 'someOtherRegex')
     foreach ($fieldPatterns as $field => $pattern) {
         if (!preg_match($pattern, $_POST[$field])) {
             return false;
         }
     }
     return true;
}

function isUserAllowed() {
    if (in_array($_SERVER['REMOTE_ADDR']......) {
        return false;
    }
    return true;
}

function isUserLegal() {
    if ($_POST['age'] < self::MIN_ALLOWED_AGE) {
        return false;
    }
    return true;
}

function isValid() {
     // here you can check with && the evaluation of the previous functions
     // or you can check one by one and return false aswell
     // return true at the end
}


function register() {
     if (!isValid()) {
         return false;
     }
     // continue execution of the registration process
}

Validating a user input is in most of the cases not coupled on each condition. E.g. a regex condition is not coupled to the legal condition. However, if by design (which I assume wrong) you have coupling. you can chain validation like

function validationFour() {
    if (!validationThree() || !current_validation) {
        return false;
    }
    return true;
}

function validationThree() {
    if (!validationTwo() || !current_validation) {
        return false;
    }
    return true;
}
Royal Bg
  • 6,988
  • 1
  • 18
  • 24
2

A good practice is to prevent deeply nested statements. So better to find the negative condition, and deal with it straight away. For instance:

if(!$_POST['user_name']){
    // exit
}

if(!$_POST['password']){
    // exit
}

// if we get to here, we assume all previous checks passed

This creates clean code because deeply nested code is harder to maintain, and deals with conditions as soon as it finds them.

Alex
  • 1,565
  • 2
  • 9
  • 13
0

You could use a try/catch block and throw an exception when validation fails. For example:

try {
  if (!$_POST['user_name']) {
    throw new Exception('Invalid username');
  }

  if (!$_POST['user_password_new']) {
    throw new Exception('Invalid password');
  }

} catch (Exception $e) {
  echo 'Caught exception: ',  $e->getMessage(), "\n";
}
Gergo Erdosi
  • 40,904
  • 21
  • 118
  • 94
  • so at the end you have 20 if's, which is the same as in the picture? – Royal Bg Jul 08 '14 at 20:54
  • @RoyalBg Yes, but they are not nested. – Gergo Erdosi Jul 08 '14 at 20:55
  • If checking N fields for emptiness is the condition, why not keep a map with the field names and respective messages, and just check in a loop for emptiness of one of the keys, to return it's mapped message? – Royal Bg Jul 08 '14 at 20:58
  • @RoyalBg Read the question again. OP says: "validate a single user input through various methods". A `foreach` won't help in that case. This code is just an example to give OP an idea how to do it. – Gergo Erdosi Jul 08 '14 at 20:58
  • Then, why not each method check the succes of the previous, e.g. a `validPassword()` method checks `emptyFields() && strlen(...) > N`, `emptyFields()` checks `someOtherDependantCondition() && empty(...)`, and then in `register()` only a `isValid()` method is called, which calls the last one in the chain? – Royal Bg Jul 08 '14 at 21:02
  • @RoyalBg Why don't you make an answer if you think you can do it better? – Gergo Erdosi Jul 08 '14 at 21:02
  • My experience so far says that any validation needs no more than 2 checks in order to be sure you should stop the process, and no more than 2 others, to be sure you can proceed. More than summary 4 validations and respectively more than 4 `if`'s says that there are redundant conditions or, you are doing something wrong. – Royal Bg Jul 08 '14 at 21:04
  • I have posted an answer, however, I discuss to see people viewpoints, I never said I'm right, and other are just wrong :) – Royal Bg Jul 08 '14 at 21:36
0
if (!$_POST['user_name']) {
    $_SESSION['msg'] = 'Empty Username';
    return register_form();
}

if (!$_POST['user_password_new']) {
    $_SESSION['msg'] = 'Empty Password';
    return register_form();
}

...

create_user();
...
Rich Remer
  • 2,123
  • 1
  • 21
  • 22
  • I would use array with keys -> post fields, values -> necessary message, then iterate through array, if $_POST[$key] is empty -> $_SESSION['msg'] = $value. Instead of `if` for every field and return. – Royal Bg Jul 08 '14 at 20:56
0

Based on the following statement, it seems that you can just stack them up, and return immediately if the previous one failed. If the previous one succeeded, then it is safe to perform the next check.

each ones depends on the success of the previous one

merlin2011
  • 71,677
  • 44
  • 195
  • 329